Electronic – arduino – My For loop isn’t exiting and I don’t know why

arduinoavrcsd

I'm programming an ATMEGA328p on a breadboard and using an arduino board to do the USB to Serial conversion. Part of some code I'm writing involves a for loop that is being used to take the 8 bit output from the SPI MOSI line (connected to an SD card) and stick it into a 64 bit binary number so I can output it to the serial monitor. I've been testing it, and I'm getting an infinite loop and I don't know what is wrong. The code is posted below. Don't worry about the USART code, it works. The test case (the array purple[5]) is being used to find out why the for loop is infinite.

Also, if you know an easier way to output the 40 bit code that an SD card outputs after using the commands SEND_IF_COND and SD_SEND_OP_COND that would be helpful as well.

#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');

}

Best Answer

In your code you have the following suspect line:

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

The key problem with this is that as loop_bit is an 'int'. In avr-gcc this is a 16bit signed data type. You have a loop condition which is loop_bit < 254 (and the redundant loop_bit < 8). As such if you keep subtracting 1, you have to count all the way down to -32768 and then one iteration further to have it wrap around to 32767 before the for loop will exit.

If you want to count down from 7 to 0 inclusive, you can do one of the following two things:

  1. This is closest to what you currently have

    for (int loop_bit=7; loop_bit>=0; loop_bit--){
        ...
    }
    
  2. This is more efficient on an 8-bit AVR

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

As a side note, and just my personal opinion, in another part of the code, you also have this for loop:

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

This will work fine as bit is declared as a uint8_t, but it would be more readable to declare 'bit' as a signed int8_t type and use the following loop:

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

Using the signed type allows the number to go negative and cause the loop to exit. Functionally (and probably in assembly) the two loops are identical as I say, but personally I find the latter easier to follow.