cancel
Showing results for 
Search instead for 
Did you mean: 

USART communication (code problem)

klemen
Associate II
Posted on December 13, 2013 at 10:13

Dear Sir/Madam,

I have been trying to understand what I am doing wrong with my serial communication. I am using STM32L Discovery board and have successfully sent and received bytes on my board/PC. I would like to send bytes from my PC to the board until a NEWLINE constant is received and then continue with the main loop. My code works to some extent:

while(1) { 
// Check if receive data flag is set (byte received)
if(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == SET) {
// Incoming byte over serial
char incomingByte; 
while(1) { 
// Wait until receive data register is empty (the flag is automatically cleared after read)
while(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == RESET); 
// Read the byte
incomingByte = (char) USART_ReceiveData(USART2); 
// If end command is received, break
if(incomingByte == '\n') 
break;
// Check the data
USART_SendData(USART2,incomingByte); 
// Wait until transmit data register is empty
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET); 
}
} 
USART_SendData(USART2,'M'); 
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
USART_SendData(USART2,'\n');
Delay(1000);
} 

The problem is, when I send the data for the first time, the program is stuck in the inner loop (as it though did not receive the newline). When I send the data the second time, it works as I expect. Please see the image below for more clear explanation. 0690X00000604v1QAA.png Thank you for your help and best regards, K
5 REPLIES 5
Posted on December 13, 2013 at 13:49

You should test for TXE before all writes to the data register.

You have some issues with blocking loops, which might be better solved with some buffering, or a state machine.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
klemen
Associate II
Posted on December 13, 2013 at 17:37

Forgive me, but I sadly do not understand what you mean...

I would like to loop indefinitely - sending data to the serial port and react only, when the data is received over serial. So I first check the flag of the RXNE register and if it is set, the data in coming through, right? Then I automatically go into a while loop, since I know more data will come, until I receive a ' '. On that condition I break out of the loop. Is it wrong to check the RXNE flag before actually reading the data? What do you mean regarding the issues of the blocking loops? I thought that this logic was correct (guess not :))... I have changed the code to test for TXE before writing. Please explain, sorry for not understanding.

while(1) { 
// Check if receive data flag is set (byte received)
if(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == SET) {
// Incoming byte over serial
char incomingByte; 
while(1) { 
// Wait until receive data register is empty (the flag is automatically cleared after read)
while(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == RESET); 
// Read the byte
incomingByte = (char) USART_ReceiveData(USART2); 
// If end command is received, break
if(incomingByte == '
') 
break;
// Wait until transmit data register is empty
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET); 
// Check the data
USART_SendData(USART2,incomingByte); 
}
} 
// DO SOMETHING
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
USART_SendData(USART2,'M'); 
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET); 
USART_SendData(USART2,'
');
Delay(1000);
}

After reading the first byte, the program gets stucked in:

while(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == RESET); 

Best regards, K
Posted on December 14, 2013 at 06:44

Your code has a very awkward flow to it, you force linear serialization of various steps, which in fact should be occuring at the same time. For example you spin waiting for TXE and sending two characters, a time frame over which more than one character could be received. You also have a delay, of non-specific length, which again could account for missing characters.

Ring Buffers and State Machines are some pretty elementary concepts, a Google search may be enlightening if these are unfamiliar concepts.

Buffering would provide some elasticity in terms of when characters are echoed back, although it would have difficulty coping if the receiver were saturated, ie you receive one character, but need to send back two.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on December 14, 2013 at 07:36

As a quick blind implementation

{
int head, tail; // Pointers for ring content
char buffer[16]; // Ring buffer
head = 0; // Start with an empty buffer
tail = 0;
while(1)
{
if (USART_GetFlagStatus(USART2, USART_FLAG_TXE) != RESET) // Can we send a character?
{
if (head != tail) // Do we have a character to send
{
USART_SendData(USART2, buffer[tail++]); // send and advance tail
tail %= sizeof(buffer); // wrap tail
}
} // TXE
if (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) != RESET) // Have we received a character?
{
char incomming;
incomming = USART_ReceiveData(USART2); // Get it
if (incomming == '
') // Was it a NEWLINE, add processing as required
{
if ((head == tail) || (((head + 1) % sizeof(buffer)) != tail)) // cheap/expensive fullness check
{
buffer[head++] = 'M'; // echo an M to buffer and advance head
head %= sizeof(buffer); // wrap head
}
}
if ((head == tail) || (((head + 1) % sizeof(buffer)) != tail)) // cheap/expensive fullness check
{
buffer[head++] = incomming; // echo received, could be a NEWLINE
head %= sizeof(buffer); // wrap head
}
} // RXNE
} // while
}

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
klemen
Associate II
Posted on December 14, 2013 at 13:22

Dear Mr.clive1,

I really appreciate your quick responses and advices. Thank you. Looking at the code example I have doubts for my application. It seems if I want to receive for example 5 bytes of data, the entire while loop needs to execute 5 times (also sending some other data back to the serial port on each iteration). After 5 executions, the received data is not necessarily correct anymore. I need to be able to parse the entire command in one block and only then move forward (of course only if any data is received). I have redesigned the code and it seems to work exactly as I expect. All the testing so far was ok. Still, I will only be able to confirm that after some more testing:

while(1) {
// Check if receive data flag is set (byte received)
if(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == SET) { 
// First byte is always the status (convert char to uint8_t) 
bstatus = (uint8_t) ((char) USART_ReceiveData(USART2) - '0'); // for testing (later, binary will be used)!
// Check bstatus (if == 1, read serial data, else continue the main loop)
if(bstatus == 1) {
// Set buffer counter to 0
bcount = 0;
while(1) { 
// Check if receive data flag is set (byte received)
if(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) == SET) {
// Read the byte and write to incoming buffer
incomingByte = (char) USART_ReceiveData(USART2);
incomingBuffer[bcount] = incomingByte; 
// Increment the buffer counter
bcount++;
// If end command is received, add NULL termination character to buffer receive CRC then break
if(incomingByte == '
') {
incomingBuffer[bcount] = NULL; // NULL needed for ''string'' operations
// Read additional two CRC bytes (uint8_t pointer to uint16_t bCRC beginning)
pD_g = (uint8_t*) &bCRC;
// Wait until Rx flag is set
while(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) != SET);
// Read first byte
(*pD_g) = (uint8_t) USART_ReceiveData(USART2);
pD_g++;
// Wait until Rx flag is set
while(USART_GetFlagStatus(USART2,USART_FLAG_RXNE) != SET);
// Read second byte
(*pD_g) = (uint8_t) USART_ReceiveData(USART2);
break;
}
}
} // inner while loop
// DO SOMETHING WITH THE RECEIVED DATA (for example send the buffer and CRC to serial)
for(iG=0;iG<16;iG++) {
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
USART_SendData(USART2,incomingBuffer[iG]); 
} 
pD_g = (uint8_t*) &bCRC;
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
USART_SendData(USART2,(*pD_g));
pD_g++;
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
USART_SendData(USART2,(*pD_g)); 
}
}
// DO PROCESSING LOOP DIRECTLY INDEPENDENT OF THE RECEIVED DATA 
} // outer while loop (program main loop)

Best regards, K