AVR GCC fel: behandlar 16 bit int som 8-bitars

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
jesse
Inlägg: 9235
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

AVR GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av jesse »

kompilator: AVR GCC.
Miljö: Winavr med AVRStudio 4.
Processor: Atmega48


Jag vet inte vad jag gör för fel, men tokfel blir det.

Jag anropar en rutin som läser in ett 16-bitars tal från SPI och skriver ut talet med en egen rutin printD(...) som kan skriva ut heltal upp till 32 bitar. Men nånstans på vägen tror GCC att jag bara har 8 bitar och förlorar därmed höga byten på vägen. Kan inte fatta varför!

Ska försöka få med all relevant kod:
Först kommer funktionen som skickar ut en instruktion och läser in två bytes.

Kod: Markera allt

uint16_t adc_read_data(void) { 
    // läser 16 bitar data
    // channel = CHANNEL1 eller CHANNEL2
    adc_wait();
    volatile uint16_t value;
    device = DEVICE_ADC; // för att välja chip-select
    SPI_start(device, ADC_DATA | READ | channel);
    SPI_ReceiveByte(); // dummybyte
    value  = ((uint16_t)SPI_ReceiveByte() << 8); // bit 15-8 **
    value |=  SPI_ReceiveLastByte();   // bit  7-0
    return value;
}
** (uint16_t) typecast tillagt utan att det påverkar något

För att göra det idiotsäkert har jag förenklat SPI_receiveByte() och SPI_ReceiveLastByte() till att bara returnera ett konstant värde, 3 samt 22 - vilket borde ge value = 790. Det blir det också, ända tills det ska skickas vidare till printD() i main():

Kod: Markera allt

    uint16_t nummer;
    ...
            nummer = adc_read_data();
            put = UART;
            printD(nummer+1000); // skicka ut acd-värde på UART
printD deklareras som void printD(int32_t nummer);

Men när talet 1000 ska adderas till "nummer", så raderas höga byten i nummer:

Kod: Markera allt

Disassembler: addera 1000 till R24:r25:
159:                  printD(nummer+1000); // skicka ut acd-värde på UART
+00000105:   E090        LDI       R25,0x00       Load immediate
+00000106:   5188        SUBI      R24,0x18       Subtract immediate
+00000107:   4F9C        SBCI      R25,0xFC       Subtract immediate with carry
Hur kan den göra det? Var har jag gjort fel (eller kan kompilatorn göra fel?) Är det något uppenbart som jag missat?

Anledningen till att jag adderar 1000 var att jag ville försäkra mig om att printD verkligen tog emot 16 bitar. Och det gör den - den skriver ut talet 1022. Men den borde alltså skriva ut 1790.

Inne i själva funktionen adc_read_data() verkar den göra helt rätt: den flyttar den först inlästa byten till "hög byte" i två processor-register och nollställer "låg" byte. Sedan läses andra värdet in, låg byte, och görs om till 16-bitars det också genom att hög byte nollställs. sedan OR-as båda 16-bitars-talen och resultatet 0x0316 returneras (i r24:r25). SPI-fukntionerna returnerar uint8_t.
Senast redigerad av jesse 8 oktober 2010, 22:54:49, redigerad totalt 1 gång.
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av blueint »

Kod: Markera allt

    value  = ((uint16_t)SPI_ReceiveByte() << 8); // bit 15-8 **
    value |=  SPI_ReceiveLastByte();   // bit  7-0
vs

Kod: Markera allt

    value  =  ((uint16_t)SPI_ReceiveByte()) << 8; // bit 15-8
    value |=  ((uint16_t)SPI_ReceiveLastByte());   // bit  7-0

Kod: Markera allt

   nummer = adc_read_data();
            put = UART;
            printD(nummer+1000); // skicka ut acd-värde på UART
vs

Kod: Markera allt

   nummer = (uint16_t)adc_read_data();
            put = UART;
            printD((int32_t)(nummer+((uint16_t)1000))); // skicka ut adc-värde på UART
Användarvisningsbild
Icecap
Inlägg: 26227
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av Icecap »

Och ett synnerligt potent problem: då "volatile uint16_t value;" deklareras INUTI rutinen "finns" den inte när programmet lämnar rutinen! I så fall ska den vara "static" i stället för "volatile" och "volatile" är det ingen anledning att deklarera den som!

Jag tror alltså att det är blandat runt och använd fel ord, fixa detta till att deklarera:
static uint16_t value;
i stället.
thepirateboy
EF Sponsor
Inlägg: 2108
Blev medlem: 27 augusti 2005, 20:57:58
Ort: Borlänge

Re: GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av thepirateboy »

Provat att deklararera nummer som 32-bit?
SvenW
Inlägg: 1124
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av SvenW »

Alternativt tvärt om, gör om printD(uint16_t x) till att ta 16 bitars argument.
Om inte mitt minne sviker har jag haft en del problem med aritmetiken för 32 bitars tal i ATMega. Antar att det är den det gäller. (?)

Sedan bevisar assemblerkoden som presenteras inte så mycket. Kompilatorn är fri att addera till 'nummer' i efterkommande instruktioner.

Men det finns kanske fler bevis för att resultatet blir fel?
Användarvisningsbild
AntiZ
Inlägg: 318
Blev medlem: 22 februari 2007, 13:34:14
Ort: V. Husby
Kontakt:

Re: GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av AntiZ »

Jag håller på Icecap's förklaring!

R24:R25 återanvänds inte någonstans efter att man lämnat adc_read_data()?

Testa annars med en global variabel istället för att deklarera den precis innan användning!

/Mats
Användarvisningsbild
jesse
Inlägg: 9235
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av jesse »

Tack för intresset. Tyvärr ingen lösning än... ATmega48 handlar det om.

Icecap:
static är inte intressant här, eftersom jag inte ska bevara värdet. Jag läser in ett nytt värde varje gång funktionen anropas. volatile, däremot, är en garanti för att kompilatorn inte läser in byten i fel ordning (dock onödigt i detta fall). Variabeln som sådan bevaras givetvis inte efteråt, men det behövs inte heller då jag använder return på sedvanligt vis för att returnera värdet (och lägga det i variablen nummer i main. I samband med return läggs värdet i r24:r25, ret utförs och r24:r25 tas om hand senare inför "call printD". Men precis innan call printD nollas r25, så att 1000 adderas till enbar 0x0016 istället för 0x0316.

blueint: ehh, man ska väl inte behöva göra typecast i alla dessa lägen? I så fall hade inga av mina program fungerat, då jag massor av gånger har tilldelat 16-bitars heltal från 8-bitars:

Kod: Markera allt

uint8_t eightbit;
uint16_t sextonbit;
sextonbit = eightbit * 4;
Jag testade dina förslag, (och några fler variationer) utan någon skillnad. nummer = (uint16_t)adc_read_data(); verkar ju totalt onödigt, då adc_read_data() redan är deklarerad att returnera ett 16-bitars värde. Samma med (uint16_t)1000. 1000 är ju redan minst 16-bitar till sin natur. (ja, eller 10 bitar i alla fall :) )

SvenW: nu är det ju så att min printD är en annars väl beprövad rutin, och som ska kunna skriva ut tal större än 65535. Jag kan ju göra en separat 16-bitars i och för sig, men förstår inte att det kan bli fel som det är. (Skulle vara om det var ett fel i AVR GCC i så fall... ska leta efter kända fel). Kanske testar det ändå, om jag inte lyckas lösa problemet.

Min undran är fortfarande: är det jag som gjort något enkelt logikfel som alla hittills missat, eller gör verkligen kompilatorn fel här? :humm:
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Re: GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av cykze »

Kan du inte slänga ihop minsta möjliga kod som krävs för att visa felet, så att vi kan slänga in koden i våra kompilatorer och se vad som händer? :)
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: AVR GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av blueint »

"Ska" är ett dumt ord när man felsöker ;)

Ibland kan typecast behövas för att utesluta att det är där problemet ligger.

Prova:

Kod: Markera allt

    uint16_t nummer, temp;
    ...
            nummer = adc_read_data();
            put = UART;
            temp = nummer+1000;
            printD(temp); // skicka ut acd-värde på UART
            printD(12345);
Användarvisningsbild
jesse
Inlägg: 9235
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: AVR GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av jesse »

jättesvårt då den optimerar koden helt olika beroende på en massa saker runt omkring.... gjorde om koden och tog bort allt och ändrade printD() så att den bara skulle skicka ut de fyra byten i det 32-bitars talet efter varandra på PORTB. Den skickade då (från låg till hög): 0x16,0x00,0x00,0x00 så även här tappar den byte två som skulle innehållit 0x03.

Kod: Markera allt

#include <stdlib.h>
#include <stdint.h>
#include <avr/io.h>

#define UART 1;

 uint8_t put = 0;

uint8_t SPI_ReceiveByte() {
    return 0x03;
}

uint8_t SPI_ReceiveLastByte() {
    return 0x16;
}

uint16_t adc_read_data(void) { 
    // läser 16 bitar data
    uint16_t value;
    SPI_ReceiveByte(); // dummybyte
    value  = (SPI_ReceiveByte() << 8); // bit 15-8
    value |=  SPI_ReceiveLastByte();   // bit  7-0
    return value;
}

void printD(uint32_t siffra) {
    for (uint8_t i = 0; i < 4; i++) {
        PORTB = siffra & 0xff;
        siffra = (siffra >> 8);
    }
}

int main( void ) {

    uint8_t nummer;
            nummer = adc_read_data();
            put = UART;
            printD(nummer); // skicka ut acd-värde på UART

        while(1);
}
Det lustiga med denna koden var att efter optimering blev det bara detta kvar :D

Kod: Markera allt

+00000060:   E186        LDI       R24,0x16       Load immediate
+00000061:   B985        OUT       0x05,R24       Out to I/O location
+00000062:   B815        OUT       0x05,R1        Out to I/O location
+00000063:   B815        OUT       0x05,R1        Out to I/O location
+00000064:   B815        OUT       0x05,R1        Out to I/O location
+00000065:   CFFF        RJMP      PC-0x0000      Relative jump
(för att kunna analysera bättre var det går snett ska jag kompilera med -o0 och stega mig fram tills 0x03 försvinner.
bearing
Inlägg: 11340
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: AVR GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av bearing »

Vad händer ifall du gör om:

Kod: Markera allt

            nummer = adc_read_data();
            put = UART;
            printD(nummer+1000); // skicka ut acd-värde på UART
Till:

Kod: Markera allt

            nummer = adc_read_data();
            if (nummer != 790) printf("error"); // (Eller annat lämpligt sätta att få ut ett felmeddelande)
            put = UART;
            printD(nummer+1000); // skicka ut acd-värde på UART
Användarvisningsbild
jesse
Inlägg: 9235
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: AVR GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av jesse »

Jag tror jag blivit fullständigt galen! Hittade felet, men kan svära på att jag kontrollerat flera gånger att jag i main deklarerade nummer som

Kod: Markera allt

uint16_t nummer;
men såg plötsligt att det står

Kod: Markera allt

uint8_t nummer;
detta är ju helt otroligt. Jag har kontrollerat detta flera gånger. Måste kollat fel variabel eller nåt. Men i den förenklade versionen blev det ju liksom lite tydligare. Hur jag missat detta är obegripligt, då jag undrat tusen gånger hur en 16-bitars variabel som heter nummer inte kan ta in mer än 8 bitar :wall: :wall: :wall:

som vanligt då, ett tokfel av mig.

EDIT: men sjutton också.. i koden i första inlägget har jag ju kopierat in deklarationen av nummer. Och då är den ju 16 bitars. Fattar nu ingenting. :oops:
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: AVR GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av blueint »

"Ska" är ett dumt ord när man felsöker ;)

Det som kunna hjälpa här, är att släppa alla antaganden. Och följa beroenden fram- och tillbaks. Gärna i kombination med stopstationer där man kan verifiera att ens föreställningar stämmer.
bearing
Inlägg: 11340
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: AVR GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av bearing »

Finns det möjligtvis flera variabel som heter "nummer" definierade i flera funktioner? och var det ifrån en av dessa funktioner definitionen kopierades till första inlägget?

Nu vet jag inte exakt vad variabeln "nummer" används till, men jag tycker att det inte låter som ett idealt namn på en variabel. Men jag vet ju också hur enkelt det kan bli att man väljer ett sånt namn.
Användarvisningsbild
jesse
Inlägg: 9235
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: AVR GCC fel: behandlar 16 bit int som 8-bitars

Inlägg av jesse »

Har ingen aning om hur det gick till. vet att jag använde sökfunktionen för att kolla alla förekomster av nummer (och det fanns bara en), och är fortfarande säker på att det stod uint16_t... men det måste varit jag som läste fel i alla fall. att jag läste på raden under eller nåt. Nåja, så fort jag bytte till uint16_t skrev den ut 790 direkt som den skulle (tog bort +1000). Några här på forumet fick lite att fundera över i alla fall :D

Då kan jag ta itu med nästa problem: varför min AD7705 Analog-digitalomvandlare skickar tokfel värden... :sick:
Skriv svar