1
  • Microcontroller: dsPIC33EP512MU810
  • Compiler: MikroC

I'm trying to request multiple bytes from a remote device via UART. To get the needed info, you send a single request byte in order to receive a single data byte. I'm having difficulties identifying data bytes when requesting more then one.

The UART receiving is handled via interrupt:

void UART2RXInterrupt() iv IVT_ADDR_U2RXINTERRUPT 
{
   uart_rd2[LoopVar0] = UART2_Read();       //Read into buffer
   LoopVar0++;
   if (LoopVar0 >= 1)
   {
      LoopVar0 = 0;
      ready0 = 1;
   }
   U2RXIF_bit = 0;                          //Reset interrupt flag
}

My code for a single data byte looks as follows:

 UART2_Write(0x11);    //Request byte
 if (ready0 == 1)      //Data received and ready
 {
   //Parse data byte
   ready0 = 0;         //Reset data received bit
 }

This works fine.

If I need more then just the one data byte I do as follows

 UART2_Write(0x11);    //Request byte 11h
 if (ready0 == 1)      //Data received and ready
 {
   //Parse data byte
   ready0 = 0;         //Reset data received bit
 }
 UART2_Write(0x14);    //Request byte 14h
 if (ready0 == 1)      //Data received and ready
 {
   //Parse data byte
   ready0 = 0;         //Reset data received bit
 }

The problem with this method is that the next request byte could be already transmitted without being certain that the last data byte is properly received.

This causes the data to get "scrambled". Meaning that the data byte from the last request could be parsed at the current request, and so on.

I cannot seem to find a proper solution for this. Any suggestions would be great!

Circular buffer implementation:

UART interrupt:

void UART2RXInterrupt() iv IVT_ADDR_U2RXINTERRUPT 
{
  uart_rcv[write_buffer_pointer0] = UART2_Read();   // put received char in circular buffer
  write_buffer_pointer0++;                // increment pointer
  if (write_buffer_pointer0 > 511) {
  write_buffer_pointer0 = 0;            // reset pointer
  }

  U2RXIF_bit = 0;
 }

Main loop handling:

void service_UART()
 {
 UART_send(0x14);       //MAP request
 UART_send(0x1D);       //ECT request

 if (read_buffer_pointer0 != write_buffer_pointer0) {  // new data in circular buffer
    ser_in();                                // handle incoming serial data
  }
 }

UART send routine:

void UART_send(volatile char data0)
{
uart_snd[write_buffer_pointer1] = data0;
write_buffer_pointer1++;                // increment pointer
if (write_buffer_pointer1 > 511) {
  write_buffer_pointer1 = 0;            // reset pointer
}
UART2_Write(data0);
}

Handling received data:

void ser_in() 
{
volatile char rqst;
volatile char resp;

resp = uart_rcv[read_buffer_pointer0]; // read next character from buffer
read_buffer_pointer0++;                 // increment read buffer pointer
if (read_buffer_pointer0 > 511) {       // if pointer past end of buffer
    read_buffer_pointer0 = 0;         // reset pointer
}
rqst = uart_snd[read_buffer_pointer1];
read_buffer_pointer1++;
if (read_buffer_pointer1 > 511) {
   read_buffer_pointer1 = 0;
}
// do something with the data here.
if (rqst == 0x14)    //If MAP request
{
//Handle MAP Data
} 
if (rqst == 0x1D)    //If ECT request
{
//Handle ECT Data
}   
}
Lorenzo
  • 3,293
  • 4
  • 29
  • 56
Felix
  • 89
  • 1
  • 12
  • What is you platform? What is your Framework/SDK? How and where `ready0` is managed? We need more infos and code to help you. – LPs Sep 25 '15 at 15:06
  • I added the info. Thnx! – Felix Sep 25 '15 at 15:22
  • Why do you reset your buffer index each time a byute is received? – LPs Sep 25 '15 at 15:25
  • Because I deal with only one byte responses I figured the buffer only needs to have space for one byte? – Felix Sep 25 '15 at 15:27
  • Where is your receive side code running? In some kind of infinite loop? You need to wait until you've definitely received your first response before you transmit the next command. Or it sounds like @LPs would recommend you could increase the size of your receive buffer so they appear in the buffer in the order they were received. – Bob Sep 25 '15 at 15:28
  • 1
    I'm not getting you. I guess that you should change `if (ready0 == 1)` with `while (ready0 == 0)`. This grants that you wait for each single char after a single request. – LPs Sep 25 '15 at 15:29
  • That will mean that after a request, it will wait for data to become available. Then you'll need to parse the data and reset `ready0 = 1` after the while loop. Do I understand that correctly? – Felix Sep 25 '15 at 15:34
  • Yes, it is what i meant. – LPs Sep 25 '15 at 15:39
  • @Ian This is all the code. Its not adequate, thats true. Increasing buffer size can be done of course, but how to know which buffer location exactly contains which data byte? – Felix Sep 25 '15 at 15:39
  • 1
    If you want to increase the buffer size you must implement a spooler. Then a input index in interrupt and an output index for the consumer. In this case your buffer become a circular queue. – LPs Sep 25 '15 at 15:42

1 Answers1

1

Implement two circular queues with head and tail pointers (indexes), large enough to accommodate throughput rates. Determine the minimum time the responder requires between requests and respect it. Each time you send a request add it to the first queue, the “request sent queue”. When a response arrives add it to the second queue in your interrupt routine and increment the head pointer. This is your “response received queue”.

You can then process transactions asynchronously in your main loop. Compare the second queue head to its tail. Each time the second queue becomes imbalanced (head does not equal tail) you have received at least one response. The first queue tells you the request it was a response to. Each time you complete processing a response advance the tail pointers of both queues.

You probably want to reset the queues if you don’t get a response (if queues remain imbalanced for too long) and make sure the queues don't overwrite.

jolati
  • 700
  • 11
  • 19
  • I'm trying to get my head arounds this, since I wasn't yet familiair with circular queues. If I understand correctly, I need to do this: **-** _Each time a request is send add it to the 'request queue'_ **-** _Each time the 'receive queue' is advanced because a UART Rx interrupt has occured; Dequeue or "pop" from both queues in order to get the request byte with the corresponding data byte._ – Felix Sep 25 '15 at 21:26
  • 1
    @Felix Yes, the interrupt routine will keep pushing new data into the queue as it arrives, whether or not the processing of previous exchanges has completed. Maintaining the request queue tells you which request the next response you pop belongs to. – jolati Sep 25 '15 at 22:59
  • Ok. I will need to find some time to implement and test. It sounds like a good solution tho. Thinking the size should be equivalent to the amount of different requests in total? – Felix Sep 26 '15 at 03:33
  • @Felix No, the size is determined throughput rate. Consider the case with only one type of request, but the responder can reply faster than the requester can process the replies. If multiple requests are sent too quickly the queues will buffer the exchanges until the requester can catch up. – jolati Sep 26 '15 at 12:47
  • I added my first attempt at a circular buffer structure in the original post. Haven't had a chance to test it yet though. What do you think? – Felix Sep 28 '15 at 09:38
  • @Felix Yes, that’s one way. Independent head and tail pointers for each queue would add flexibility handling imbalance (more requests sent than responses received). See how your tests go. – jolati Sep 29 '15 at 13:44
  • I do have independent pointers for each queue, right? I'm having some trouble finding a way to handle a non response. At the moment the queues work, but if one response isn't received everything still shifts. – Felix Oct 01 '15 at 14:51
  • Shouldn't `service_UART()` test the head of the input queue against its own tail, not the sent queue. And you probably need to maintain a timer that deletes an unanswered request if it expires. – jolati Oct 01 '15 at 16:13