Meine For-Schleife wird nicht beendet und ich weiß nicht warum

Ich programmiere einen ATMEGA328p auf einem Steckbrett und verwende ein Arduino-Board, um die USB-zu-Seriell-Konvertierung durchzuführen. Ein Teil eines Codes, den ich schreibe, beinhaltet eine for-Schleife, die verwendet wird, um die 8-Bit-Ausgabe von der SPI-MOSI-Leitung (verbunden mit einer SD-Karte) zu nehmen und sie in eine 64-Bit-Binärzahl zu stecken, damit ich sie an ausgeben kann Serieller Monitor. Ich habe es getestet, und ich bekomme eine Endlosschleife und ich weiß nicht, was falsch ist. Der Code ist unten gepostet. Machen Sie sich keine Sorgen um den USART-Code, er funktioniert. Der Testfall (das Array lila[5]) wird verwendet, um herauszufinden, warum die for-Schleife unendlich ist.

Wenn Sie auch eine einfachere Möglichkeit kennen, den 40-Bit-Code auszugeben, den eine SD-Karte nach Verwendung der Befehle SEND_IF_COND und SD_SEND_OP_COND ausgibt, wäre dies ebenfalls hilfreich.

#define USART_BAUDRATE 9600
#define F_CPU 16000000
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/power.h>

void Init_USART(void);
void newLine(void);
void transmitByte(uint8_t my_byte);
void printNumber(uint8_t n);
void print64BitNumber(uint64_t bits);
void printBinaryByte(uint8_t byte);
void printString(const char myString[]);

int main(void){
    Init_USART();
    uint8_t purple[5];
    purple[0] = 0b11110111;
    purple[1] = 0b00010000;
    purple[2] = 0b11111111;
    purple[3] = 0b00110000;
    purple[4] = 0b11111111;
    uint8_t counter = 0;
    uint64_t push_bit = 1;
    uint64_t error_codes = 0;
    for (int i=0; i<5; i++){
        for (int loop_bit=7; ((loop_bit < 8)||(loop_bit < 254)); loop_bit--){
            push_bit = 1;
            printNumber(loop_bit);
            print64BitNumber(error_codes);
            newLine();
            printNumber(counter);
            newLine();
            if(bit_is_set(purple[i],loop_bit)){
                error_codes |= (push_bit << (loop_bit+(i*8)));
            }
            else{
                error_codes &= ~(push_bit <<(loop_bit+(i*8)));
            }
        }
        counter += 1;
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////
//USART Functions///////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void Init_USART(void)
{
    clock_prescale_set(clock_div_1);
    UCSR0B = (1<<RXEN0)|(1<<TXEN0); //Enables the USART transmitter and receiver
    UCSR0C = (1<<UCSZ01)|(1<<UCSZ00)|(1<<USBS0); //tells it to send 8bit characters (setting both USCZ01 and UCSZ00 to one)
    //now it has 2 stop bits.

    UBRR0H = (BAUD_PRESCALE >> 8); //loads the upper 8 bits into the high byte of the UBRR register
    UBRR0L = BAUD_PRESCALE; //loads the lower 8 bits
}

void printBinaryByte(uint8_t byte){
    uint8_t bit = 0;
    //This code is really efficient.  Instead of
    //using large ints to loop, it uses small uint8_t's.
    for (bit=7; bit<255; bit--){
        if(bit_is_set(byte,bit)){
            transmitByte('1');
        }
        else{
            transmitByte('0');
        }
    }
}
//uint8_t is used for 8bit chars
void transmitByte(uint8_t my_byte){
    do{}while(bit_is_clear(UCSR0A, UDRE0));
    //UDR0 is the transmit register.
    UDR0 = my_byte;
}
void newLine(void){
    printString("\r\n");
}
void printString(const char myString[]){
    uint8_t i = 0;
    while(myString[i]){
        while ((UCSR0A &(1<<UDRE0)) == 0){}//do nothing until transmission flag is set
        UDR0 = myString[i]; // stick Chars in the register.  They gets sent.
        i++;
    }
}
//Prints 64 bit number.
void print64BitNumber(uint64_t bits){
    printBinaryByte(bits >> 56);
    printBinaryByte(bits >> 48);
    printBinaryByte(bits >> 40);
    printBinaryByte(bits >> 32);
    printBinaryByte(bits >> 24);
    printBinaryByte(bits >> 16);
    printBinaryByte(bits >> 8);
    printBinaryByte(bits);
}


void printNumber(uint8_t n){//This function Prints a number to the serial monitor
    //Algorithm to convert 8 bit binary to 3 digit decimal.
    //N=16(n1)+1(n0)
    //N=n1(1*10+6*1)+n0(1*1)
    //N=10(n1)+1(6(n1)+1(n0))
    //Also: N = 100(d2)+10(d1)+1(d0)
    //Then make: a1 = n1 and a0 = (6(n1)+1(n0))
    //Then get rid of the carries since n0 can be from 0-15, etc.
    //c1 = a0/10 This is the number of 10's to carry over
    //d0 = a0%10 This is the decimal that's outputed.
    //c2 = (a1+c1)/10
    //d1 = (a1+c1)%10
    //d2 = c2
    uint8_t d2, d1, q;
    uint16_t d0;
    //0xF is 15
    //00010000 this is 16 (n)
    //d0 = 00010000 & 00001111, d0 = 00000000
    d0 = n & 0xF;
    //If you AND n then the bits in the original number show in d0,d1 and d2
    //d1 = (00010000 >> 4) same as 00000001, & 00001111, d1 = 00000001
    //this sees if there's anything in the second 4 bits
    d1 = (n>>4) & 0xF;
    d2 = (n>>8);
    //this sets d2 to 0.

    d0 = 6*(d2+d1)+d0;
    q = d0/10;
    d0 = d0%10;

    d1 = q + 5*d2 + d1;
    if (d1!=0){
        q = d1/10;
        d1 = d1%10;

        d2 = q+2*d2;
        if (d2!=0){
            transmitByte(d2 + '0');
            //remember to add '0' because its an ASCII char
        }
        transmitByte(d1 + '0');
    }
    transmitByte(d0 + '0');

}
Hier gibt es eine anständige Menge an Code. Können Sie uns ein minimal funktionierendes Beispiel geben? (dh: Wie viel können Sie herausreißen und es trotzdem für immer wiederholen?) Es könnte sich sogar herausstellen, dass Sie das Problem selbst lösen können, wenn Sie das Problem ohne weitere 100 Codezeilen drumherum sehen.
Dies ist das minimale Arbeitsbeispiel. Der einzige Code, mit dem ich mich hier befasse, sind die verschachtelten for-Schleifen in main. Ich habe nur den Rest des Codes gepostet, falls jemand ein Arduino herumliegen hat und es selbst mit Kitt oder einem anderen Terminal ausprobieren wollte.
"Dies ist das minimal funktionierende Beispiel. ... Ich habe gerade den Rest des Codes gepostet" - dann ist es kein minimal funktionierendes Beispiel.
Dies ist nicht der gesamte Code. Ich wollte nur den USART-Code einfügen, damit jemand die Ausgabe als Codeschleife leicht sehen kann.
In einer Ihrer for-Schleifen haben Sie eine Variable „loop_bit“, die als „int“ deklariert ist, aber Sie subtrahieren 1 davon und prüfen dann, ob sie < 254 ist (und sinnlos, wenn sie < 8 ist, was davon impliziert wird <254 sein). Das dauert viele, viele Iterationen (muss bis -32768 heruntergezählt werden, dann bis 32767), bevor es beendet wird.
Oh wow. Ich kann nicht glauben, dass ich das verpasst habe. Danke. Es funktioniert jetzt. Geben Sie dies als Antwort ein und ich gebe Ihnen ein grünes Häkchen. :D

Antworten (1)

In Ihrem Code haben Sie die folgende verdächtige Zeile:

for (int loop_bit=7; ((loop_bit < 8)||(loop_bit < 254)); loop_bit--){
    ...
}

Das Hauptproblem dabei ist, dass as loop_bitein 'int' ist. In avr-gcc ist dies ein 16-Bit-Datentyp mit Vorzeichen. Sie haben eine Schleifenbedingung, die ist loop_bit < 254(und die redundante loop_bit < 8). Wenn Sie also weiterhin 1 subtrahieren, müssen Sie den ganzen Weg bis -32768 herunterzählen und dann eine Iteration weiter zählen, damit es auf 32767 umläuft, bevor die for-Schleife beendet wird.

Wenn Sie von 7 bis einschließlich 0 herunterzählen möchten, können Sie eines der folgenden zwei Dinge tun:

  1. Dies kommt dem, was Sie derzeit haben, am nächsten

    for (int loop_bit=7; loop_bit>=0; loop_bit--){
        ...
    }
    
  2. Dies ist auf einem 8-Bit-AVR effizienter

    for (int8_t loop_bit=7; loop_bit>=0; loop_bit--){ //assuming avr-gcc understands 'int8_t', if not you can do 'char'
        ...
    }
    

Als Randnotiz, und nur meine persönliche Meinung, haben Sie in einem anderen Teil des Codes auch diese for-Schleife:

for(bit=7; bit<255; bit--){
    ...
}

Dies funktioniert gut, da bit als deklariert ist uint8_t, aber es wäre lesbarer, "bit" als signierten int8_tTyp zu deklarieren und die folgende Schleife zu verwenden:

for(bit=7; bit>=0; bit--){
    ...
}

Die Verwendung des vorzeichenbehafteten Typs ermöglicht es, dass die Zahl negativ wird und die Schleife beendet wird. Funktionell (und wahrscheinlich auch in der Montage) sind die beiden Schleifen wie gesagt identisch, aber ich persönlich finde letztere einfacher zu folgen.

int8Ich bin nicht mit einem Standard-Ganzzahltyp vertraut . Ist es spezifisch für Arduino? Ich kann es nicht in ihrem Kernquellcode finden ... Ich denke, Sie meinen vielleicht, int8_twas in definiert ist stdint.hund wie der Dateiname andeutet, ist eine ST- und D- ARD- INT- Eger-Typdefinition.
@Majenko du hast Recht, int8_t ist das, woran ich dachte, ich wusste, dass da noch etwas anderes drin war. Ich werde die Antwort aktualisieren.
@TomCarpenter Ich habe es nur in Frage gestellt, weil ich gesehen habe, dass Leute int8 und uint8 als ihre eigenen benutzerdefinierten Typedefs verwenden. Ich wünschte, die Leute würden standardisieren ... Ich habe alle Kombinationen von s8, u8, si8, ui8, int8, uint8 usw. gesehen, die überall verwendet wurden, wo die Verwendung des Standards int8_t und uint8_t sinnvoller wäre ...
@TomCarpenter - das ist ein bisschen pedantisch, aber int_least8_teine bessere Wahl als int8_t. Letzteres existiert nicht auf (zugegebenermaßen seltenen) Systemen, die keinen nativen 8-Bit-Typ haben. Ersteres wird immer funktionieren.