I am trying to send a string "hello" continiously through UART1 from Atmega 328P to the BLUEPILL board. I am doing the reception by by interrupt. The problem is after few times it starts to give "ello" "hello" alternatively on my serial monitor, I am not able to figure out whats going wrong. BAUD RATE-9600 parity bit- NONE STop bit- 1
main.c:
The usart_IRQ is called here under "usart code begin here":
#include "main.h"
#include "uart.h"
UART_HandleTypeDef huart1;
char *readBuffer;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE END 2 */
The string received is stored in the "readBuffer" using the readstring and the printed to the serial monitor using the writestring forwhich i created a separate UART library.
while (1)
{
/* USER CODE END WHILE */
readBuffer=serialReadString1();
writeString1(readBuffer);
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}
USART1_IRQHandler
I have enabled the Global interrupt and called the received function there, and used the return because I dont want to use the HAL_irq:
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
receivestring1();
return;
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
uart.c
for write string:
#include"main.h"
#include "uart.h"
#define RX_BUFFER_SIZE1 144
char rxBuffer1[RX_BUFFER_SIZE1];
uint16_t rx1ReadPos;
void sendChar1( uint8_t ch)
{
while(!(USART1->SR & USART_SR_TXE));
USART1->DR = ch;
}
void writeString1(const char c[])
{
uint16_t i = 0;
for (i = 0; i<= strlen(c); i++)
{
sendChar1(c[i]);
}
memset(rxBuffer1,0,RX_BUFFER_SIZE1);
rx1ReadPos = 0;
}
for read string:
unsigned char receivedChar1(void)
{
while(!(USART1->SR & USART_SR_RXNE));//check whether any tx is going on
return USART1->DR;
}
void receivestring1()
{
while(!(USART1->SR & USART_SR_RXNE));//check whether any tx is going on
rxBuffer1[rx1ReadPos] = USART1->DR;
if (rxBuffer1[rx1ReadPos] != '\n')
{
rx1ReadPos++;
if (rx1ReadPos >= RX_BUFFER_SIZE1)
{
rx1ReadPos = 0;
}
}
}
char* serialReadString1()
{
return rxBuffer1;
}
I can spot several potential issues in your code:
You should not skip the call of
HAL_UART_IRQHandler()
inUSART1_IRQHandler()
. Each interrupt usually needs to be acknowledged (cleared) in either the interrupt controller (NVIC) and/or the respective peripheral unit (the UART in your case). If this is not done, you might end up with the interrupt handler being called again right away, although there has not been a real new event.The
while(!(USART1->SR & USART_SR_RXNE))
at the start ofreceivedChar1()
andreceivestring1()
should not be necessary, since the interrupt should only be generated if the RX is not empty. Your interrupt configuration looks correct, so this might be only necessary due to issue #1 (see above).The variables
rxBuffer1
andrx1ReadPos
are manipulated both inreceivestring1()
andwriteString1()
. Sincereceivestring1()
is called fromUSART1_IRQHandler()
whilewriteString1()
is called frommain()
this manipulation can happen virtually in parallel. This can become problematic for two reasons:rxBuffer1
might change at any (e.g. also while processing thefor
loop inwriteString1()
). This can be avoided by adding thevolatile
keyword torxBuffer1
,rx1ReadPos
and also the argument ofwriteString1()
. This way the compiler will re-evaluatestrlen(c)
on every pass of thefor
loop.rx1ReadPos
at virtually the same time, e.g. when a new character is received byreceivestring1()
just in the moment beforerx1ReadPos
is reset to zero inwriteString1()
, which would lead to losing that character. This is called a race-condition and can only be avoided by either blocking interrupts for a limited time or by constructing the code in a way that makes sure, that parallel execution can not lead to data-loss. One possible solution could be the use of two separate buffers - one used for receiving and one used for writing.writeString1()
is writing the characters viasendChar1()
to the same UART1 that is receiving the data. This is not a principally okay but sending the data is thus using the same slow 9600 baud-rate. This makes it really probable, thatwriteString1()
gets interrupted byUSART1_IRQHandler()
, i.e. that additional data is received. It might be better to use a different UART configured to a higher baud-rate for debug purposes.I suppose that your
"ello"
problem is primarily caused by issue #3.Addendum
To check whether the stm32 correctly receives the characters from the Atmega you could simplify your code by sending out each received character directly from the interrupt handler (avoiding the complexity of the asynchronous buffer handling). This could look like this:
Addendum #2
By request here is a sketch how the buffer handling could be improved:
Note: This sketch tries to modify your code as little as possible. The key ideas are:
rxBuffer1
andrxBuffer2
) -- one for receiving data and one for sending received data out again.main()
serialReadString1()
via the flagSwitchBufferRequest
. Buffer switching itself is done within the ISR to avoid any race conditions.serialReadString1()
: First the pointer to the latest received data is copied in a separate variable beforeSwitchBufferRequest
is set totrue
becauserxBufferPtr
might change at any time afterwards.writeString1()
is only modified slightly by removing the reset ofrxBufferPos
(now done in ISR) and clearing the RX buffer which is not used by the RX ISR.