0
votes

I'm trying to implement interrupt driven UART communication with an ATMEGA328P. What I need to achieve is sending commands (char arrays) over UART in order to extract values to my variables in the main routine so that I can program a behaviour in my circuit.

The data I'm sending has this format: 2 255 0 255 0 0 255 1000 (it's for a RGB LED lamp) This describes long int values for mode, 2 RGB colors and duration.

So far this is what I have in main:

while(1)
{
    if(rxFlag==1){
        char * pEnd;
        mode = strtol(buffer,&pEnd,10);
        Ri = strtol (pEnd, &pEnd,10);
        Gi = strtol (pEnd, &pEnd,10);
        Bi = strtol (pEnd, &pEnd,10);
        Rf = strtol (pEnd, &pEnd,10);
        Gf = strtol (pEnd, &pEnd,10);
        Bf = strtol (pEnd, &pEnd,10);
        duration = strtol (pEnd,NULL,10);
        rxFlag=0;
    }

    switch(mode){
        case 1: // fixed color
            fixed(Rf, Gf, Bf);
            break;
        case 2: // yoyo pulse
            yoyo(Ri,Gi,Bi,Rf,Gf,Bf,duration);
            break;
        default:// red blinky
            fixed(0,255,255);
            _delay_ms(500);
            fixed(255,255,255);
            _delay_ms(500);
            break;

    }
}

And the ISR (interrupt service routine) that handles reception:

ISR(USART_RX_vect)
{   
   while ( !(UCSR0A & (1<<RXC0)) );
   if (rxn==80){ // if BUFFER_SIZE is reached, reset to start of buffer.
       rxn=0;
   }
   buffer[rxn++] = UDR0; // increment rxn and return new value.
   if(buffer[rxn]=='\0'){
    rxFlag=1; // notify main of receipt of data.
   }
}

As you can see, I'm trying to update the variables' values only when I detect the \0 at the end of the stream.

It's something like (in ISR):

  • Read the incoming byte and store it in the buffer that is at most 80 bytes long
  • If a \0 comes in, let main know it has new data to process

In main:

  • If there's new data, break the buffer into long int and store the values
  • clear the new data flag
  • act according to the new values.

The problem is that this isn't playing out as I wanted it and I'm kinda lost. I know my switch statement works correctly given the vars have correct values. I've narrowed down the problem to either the communication/buffer populating phase being broken OR the extraction of the variables' values being broken, but not sure which or if both.

Can anyone share any insight?

1

1 Answers

1
votes

Edit: Had some more time to think about this. Ignore my previous answer.

Make sure that your buffer is declared with the keyword volatile.

The fact that you're wrapping without performing any handling is also worrying. For a circular buffer, it's much better to keep track of head, tail, and the current size.

#define BUFF_MAX_BYTES 80

#define TRUE 1
#define FALSE 0

typedef unsigned char boolean;

static volatile unsigned char buff[BUFF_MAX_BYTES];

static volatile unsigned char buffHead = 0;
static volatile unsigned char buffTail = 0;
static volatile unsigned char buffSize = 0;

boolean store_byte(unsigned char b)
{
    boolean success = TRUE;

    if(buffSize < BUFF_MAX_BYTES){
        buff[buffTail] = b;
        buffSize++;
        buffTail = (buffTail + 1) % BUFF_MAX_BYTES;
    } else{
        success = FALSE;
    }

    return success;
}

boolean eol_received(void)
{
    unsigned char prev_index = (buffTail == 0) ? BUFF_MAX_BYTES - 1 : buffTail - 1;
    boolean eol = FALSE;

    if(buff[prev_index] == '\0'){
        eol = TRUE;
    }

    return eol;
}

// Because strtol has no conception of where the buffer ends and wraps, you
// will have to create a modified version that can handle this case.
//
// A simple approach would be to calculate the length of your message, copy
// it to another buffer for parsing (advancing buffHead as you read bytes),
// and then call strtol on this new buffer.

ISR(USART_RX_vect)
{   
    while ( !(UCSR0A & (1<<RXC0)) );

    if(!store_byte(UDR0)){
        // Use a flag to inform main() of overflow, handle as appropriate
    }

    rxFlag = eol_received() ? 1 : 0;
}