Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

UART Interrupt with buffer problem

Status
Not open for further replies.

tyassin

Advanced Member level 1
Joined
Jul 22, 2004
Messages
467
Helped
86
Reputation
170
Reaction score
41
Trophy points
1,308
Activity points
3,803
Hello,

I have a PC sending 9 bytes to my uP. These are received with an interrupt function and saved in a buffer. When the buffer is full the program add some data to some of the bytes and then call the USART Transmit Function shown below. This send the 9 bytes back to the PC.
However this is where I get some strange behavoir. After I have received the 9 bytes I set the buffer index to zero(highlighted in red). As shown in the code below this is done in the transmit function. Now this scenario seems to work fine and I receive the data back with the bytes in the right order and some of hem modified correctly.

But when I set the buffer index to zero inside the Receive ISR of the UART, then when I send data back, the data has been left shifted randomly.

I have tried making variable static volatile but nothing seems to work. Only by having the index sat to zero inside the transmit function seem to work.

Please if anybody has an idea let me know.

Thank you

Code:
/*USART transmit function*/
#include <mega164a.h>
#include <delay.h>
#include "PA_USART.h"
#include "PA_DataSatByPC.h"

extern unsigned char PCReadRequest;
extern unsigned char PCWriteRequest;
extern unsigned char rx_buffer0[9];
extern volatile unsigned char PC_Buffer[9];
unsigned char rx_wr_index0;
//-----------------------------------------------------USART Transmit function-------------------------------------------------------------------------
void USART_Transmit( unsigned char data[] )
{
    unsigned char x,y;
 
    PORTD.6=1; 
    delay_ms(10); 
      
    for (x=0; x<=8; x++)
     {  
        while ( !( UCSR0A & (1<<UDRE0)) );
        UDR0=data[x];
     }

     delay_ms(10);
     PORTD.6=0;
     delay_ms(10);
     PCReadRequest=0;
     for(x=0; x<=8; x++){
     rx_buffer0[x]=0;
     }
     [COLOR="#FF0000"]rx_wr_index0=0;[/COLOR]
     
}


//-----------------------------------------------------USART Receive Interrupt Routine-------------------------------------------------------------------------

interrupt [USART0_RXC] void usart0_rx_isr(void)             //Datastring is received from PC
{
static unsigned char status,x,data; 
static unsigned char RS485_address=0x01;                    //Address of the RS 485 module. Skal ændres til at være hardware eller PC bestemt.

status=UCSR0A;
data=UDR0;                                                  //IMPORTANT that "UDR0" is read otherwise the interrupt flag is never reset.
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
{
    rx_buffer0[rx_wr_index0++]=data;                         //Data from PC is saved in buffer/array

    if(rx_wr_index0==9)
    {
       [COLOR="#FF0000"] //rx_wr_index0=0;[/COLOR]
        PCReadRequest=1;
    }
}
}
 

tyassin said:
But when I set the buffer index to zero inside the Receive ISR of the UART, then when I send data back, the data has been left shifted randomly.
If PC sends 9 bytes and then it keeps sending data, then this scenario is unavoidable. This is because you respond to PC by sending those 9 bytes back. But in the meantime PC keeps sending, Rx interrupt is coming and rx_wr_index0 is incremented inside the ISR. The result would be exactly what you described, that is randomly shifted data in the Rx buffer. When you write zero inside the Tx function, then the next time the PC sends data, rx_wr_index0 is initialized and this is why everything is working fine in this case. Under some circumstances (if you don't care about those bytes lost in the time between receive and transmit), this solution is absolutely fine to implement.

I suppose that PC constantly keeps sending the same data until it receives new, and this is why the buffer is shifted. Otherwise you would receive completely different data. Unfortunately only assumptions could be made until you update us with the complete scenario of the application.

Hope this helps.
Alexandros
 

Hello Alexandros,
Thank you for the interest.

Yes you are right there are some more info.
Actually I am testing the software with at speed of 9600 bps with Docklight terminal program. The bytes are 10 bits long 1 start, 8 data bits and 1 stop bit. So each byte is around 100us. I send the 9 bytes from the PC....then wait(takes a couple of ms) for the response. So the PC is not pouring data into the uP. The first time I get a result that is usually correct, BUT sometimes the first byte is zero. After this it start to shift.

In the program I also use a timer(Timer2) to toggle a port pin with an interval of 100us. This is done by interrupt. There also is an ISR to Timer2 I use to increment a variable.
I have been thinking that maybee Timer2 ISR could make some errors.
But why there is a difference between the two scenarios of resetting rx_wr_index0 I really do not understand. I would have thought that the best way was to reset rx_wr_index0 in the UART RX ISR but apparently not.

I have not shown it here, originally there was more code inside the ISR, this send the 9 bytes back correct only one time and after that it would not transmit anymore data. So I have removed the code to what I present here but now it send the bytes back but in incorrect order.

Thank you
 

tyassin said:
In the program I also use a timer(Timer2) to toggle a port pin with an interval of 100us. This is done by interrupt. There also is an ISR to Timer2 I use to increment a variable.
I have been thinking that maybee Timer2 ISR could make some errors.
You could use asm("sei"); as the first line of those timer interrupts, so that you won't loose any incoming byte from the USART receive ISR.


tyassin said:
The bytes are 10 bits long 1 start, 8 data bits and 1 stop bit. So each byte is around 100us. I send the 9 bytes from the PC....then wait(takes a couple of ms) for the response. So the PC is not pouring data into the uP.
You mean that docklight sends every few ms? If so, then inside USART_Transmit() function there is a delay_ms(10); before data transmission. Did you make sure that docklight sends data to a slower rate? Try to increase the transmit time interval from docklight to 1sec or so. If this works, then you can start working with smaller intervals in between frames.


tyassin said:
But why there is a difference between the two scenarios of resetting rx_wr_index0 I really do not understand.
The difference is that when you clear the counter inside the ISR, there is a time in between until you send back the bytes to PC (10ms minimum, due to the delay before transmission inside the transmit function). During that time the PC will send data, so USART ISR will run and rx_wr_index0 will be increased. Thus when data transmission is over, data rx_wr_index0 will have a random value from 0 to 8 (it depends on how many times the ISR has run). If this value is 3 for instance, then the first byte received will be stored in rx_buffer0[3], while the last byte will be stored in rx_buffer0[2]. This is how shift is created. But when you transmit and after that rx_wr_index0 is cleared, then the next Rx ISR will find rx_wr_index0=0 and thus the byte is stored in the correct position. Also another 20ms delay after data transmission exists, where docklight makes it to send a couple of more frames.


tyassin said:
I would have thought that the best way was to reset rx_wr_index0 in the UART RX ISR but apparently not.
Normally this is the right thing to do. When a frame is completely received, then such counters and indexers are cleared. Since you are using docklight, try to adjust docklight on the MCU program and not the other way around. You can instruct docklight to respond only when it receives from the MCU, by creating a transmit sequence for a specific receive sequence (look to the left of docklight's communication window, at the sequences section).


Hope this answers to your questions.
 
Hi

Actually I am creating a send sequence of 9 bytes and then "manually" press the send button which send only that sequence. So the program should just handle those 9 bytes and nothing more. When I see the transmitted response from the uP in Docklight I then send the samme sequence again.
This is why I think it is very strange....
 

tyassin said:
Actually I am creating a send sequence of 9 bytes and then "manually" press the send button which send only that sequence. So the program should just handle those 9 bytes and nothing more or something similar. When I see the transmitted response from the uP in Docklight I then send the samme sequence again.
This doesn't make sence. This means you either edit rx_wr_index0 from somewhere else in the program, or you send from docklight different number of bytes than you expect from the MCU, or there is a mismatch between docklight's and MCU's communication settings or something similar like stack overflow etc. From your posted code, I cannot see a way of how such a behaviour could be explained.
If you are using an emulator first make sure that rx_wr_index0 is not edited from some other part of the code, try to send one byte at a time from docklight, place a breakpoint inside the ISR and observe how rx_wr_index0 value is changing.
 
Last edited:

Hello,
Well I agree completely. The index variable is only sat in the code you have seen. But I will do as you suggest. Thank you and I will report back hopefully when the problem is solved.

Thank you
 
Last edited:
  • Like
Reactions: alexxx

    alexxx

    Points: 2
    Helpful Answer Positive Rating
Hello again,

I have now tried with JTAG and simulation but only found a minor mistake where I sat RXC0 in the initialization of the UART. However this did not solve my problem. Instead I tried continuously transmitting data from the UART. This part is not shown in the code below. I do not send any data from the PC to the UART. When the UART Transmit function is called it send the data and fill up the "PCBuffer" with the numbers 0 to 7. But this should not be used anywhere.

But when I look at the data on the PC then suddenly and randomely I receive sequences of bytes from 1 to 7. To me it seems that the RX interrupt function is entered somehow and transmit the PCBuffer data.
I can not see how else that sequence can get transmitted?
I tried to disconnect the RX UART pin and then everything is transmitted correctly.
So it appears some noise is triggering my UART.

Code:
#include <mega164a.h>
#include <delay.h>

#define RX_BUFFER_SIZE0 8

bit rx_buffer_overflow0;

volatile unsigned char PCBuffer[8]={0,0,0,0,0,0,0,0};
volatile extern unsigned char rx_buffer0[8];

extern unsigned char PCReadRequest;
extern unsigned char PCWriteRequest;

volatile unsigned char rx_wr_index0,rx_counter0;

interrupt [USART0_RXC] void usart0_rx_isr(void)
{
char status,data,x;
status=UCSR0A;
data=UDR0;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
   PCBuffer[rx_wr_index0++]=data;
   rx_wr_index0 &= 0x07;

   if (rx_wr_index0 == 0)//RX_BUFFER_SIZE0) 
   {
   //rx_wr_index0=0; 
   if(PCBuffer[1]==0x41) PCReadRequest=1;
   if(PCBuffer[1]==0x42) PCWriteRequest=1;   
   for(x=0; x<8; x++)
   {
   rx_buffer0[x]=PCBuffer[x];
   }
   USART_Transmit(rx_buffer0);
   }
   if (++rx_counter0 == RX_BUFFER_SIZE0)
      {
      rx_counter0=0;
      rx_buffer_overflow0=1;
      }

   }
   
}

void USART_Transmit(unsigned char data[] )
{
    unsigned char x,dummy;
    PORTD.6=1; 
    delay_ms(10); 
    
    for (x=0; x<=7; x++)
    {  
        while ( !( UCSR0A & (1<<UDRE0)) );
        UDR0=data[x];
    }

     delay_ms(10);
     PORTD.6=0;
     delay_ms(10);
     PCReadRequest=0;
       
     for(x=0; x<=7; x++){
     PCBuffer[x]=x;
     }
     
     //rx_wr_index0=0;
    
}
 

tyassin said:
I have now tried with JTAG
.................................
To me it seems that the RX interrupt function is entered somehow and transmit the PCBuffer data.
I can not see how else that sequence can get transmitted?
I tried to disconnect the RX UART pin and then everything is transmitted correctly.

You need to debug a bit more. Place a breakpoint on the line the transmit from the MCU starts. If a transmit from the MCU starts but the breakpoint is not hit (something you eliminated as a possibility), it means that this part is called from somewhere else also. If the breakpoint is hit, then open the call stack window of the debugger and trace the path that was followed from function call to function call, until you spot the part of the code where the bug is originated.

PS: Transmit function is called by receive interrupt and uses a 20ms delay, plus the time 8 bytes need to leave the serial buffer. So the receive ISR lasts more that 20ms. This is a very big time for interrupt. The rest of the code is frozen at that time. Additionally, if other interrupts are expected during that time they are lost, except if you use the asm("sei") command inside the receive ISR.

Hope this helps,
Alexandros
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top