2019-08-01 01:22 AM
Hello. I have packet each 200ms (8 byte) and i have sometimes from computer register set function, again 8 bytes.
And time to time getting USART ovverrun error. That is not a big deal, but i just don't want to miss correct register write to MCU event.
How do i handle this so never get overrun ? System needs to be 100% reliable, i was thinking about timeouts, but it looks to be too dificult to implement.
Any idea how to play this ? ( Rx is on interrupt, while Tx is on Timer 5Hz)
2019-08-01 01:29 AM
On the receiving side?
Avoid doing things in a callback that will block or take more than one byte time.
An interrupt managing rx and tx data into ring buffers should do this without problems.
I don't use HAL for this, far too much overhead and potential to fail.
2019-08-01 02:04 AM
Hi. Thank you for answer. i am using LL libraries ( I am getting overrun on STM32L496)
Sending is simple, just sending array and that's about it. I do this at 5Hz
uint8_t UART_SendReg(uint8_t reg_adr, uint8_t Data[])
{
uint32_t i = 0;
while(i<8)
{
while (!LL_USART_IsActiveFlag_TXE(USART1));
LL_USART_TransmitData8(USART1, Data[i]);
i++;
}
while (!LL_USART_IsActiveFlag_TC(USART1));
return 0;
}
While in receive i read data into 256 array with u8 index, so no overfilling is possible. I do this on all characters. (Communication is at 115200Bps)
void USART1_IRQHandler(void)
{
// k++;
if(LL_USART_IsActiveFlag_RXNE(USART1))
{
RxData[RxIndex]=LL_USART_ReceiveData8(USART1);
RxIndex++;
if(RxIndex>7)
{
uint32_t i = 0;
while(i<RxIndex-7)
{
if(RxData[i]==0xBB)
{
if(RxData[i+7]==0x99)
{
if(RxData[i+6]==( RxData[i+1]^RxData[i+2]^RxData[i+3]^RxData[i+4]^RxData[i+5]^0xAA))
{
RegADR = RxData[i+1];
RegDATA = RxData[i+2]+RxData[i+3]*256+RxData[i+4]*65536+RxData[i+5]*16777216;
RxIndex = 0;
break;
}
}
}
i++;
}
}
}
else
{
USART1->ICR = 0x000F;
UART_RX_ERR++; // I get this fired at 1-3s intervals
}
}
Even trying to wait while index register is not zero ( zero meaning i jest get register and i reset it to zero ) did not help, even worse, generated more Tx problems on recieving side for some reason.
Somputer is sending data at 10ms ( just to get ovverrun error more frequent to make debugging simpler)
2019-08-01 04:15 AM
never get an overrun by using DMA Rx like this:
Super simple to use Rx in a circular DMA buffer,
Basically, initialise the RxPtrOut to zero.
RxPrtOut =0;
the RxPtrIn is a hardware register the DMA buffer pointer, which is pointing to the next byte destination in the DMA buffer
so Read the DMA pointerIn and compare it to your pointerOut, that will tell you how many bytes are unread.
I set the circular buffer size to 256bytes,
initialization:
#define U1RxBufSize 0x100
void initUart1RxDMABuffer(void) {
TxDMA1BufHasData = false;
U1TxBufferPtrIN = 0; U1TxBufferPtrOUT = 0;
U1RxBufferPtrIN = 0; U1RxBufferPtrOUT = 0;
if (HAL_UART_Receive_DMA(&huart1, (uint8_t *)Usart1RxDMABuffer, U1RxBufSize) != HAL_OK)
{
sprintf(string, "initUart1RxDMABuffer Failed\n");
puts1(string);
}
// else
// {
// sprintf(string, "initUart1RxDMABuffer OK!\n");
// puts1(string);
// }
}
this code will tell you how many bytes are ready to read:
char readableU1(void) {
U1RxBufferPtrIN = U1RxBufSize - huart1.hdmarx->Instance->CNDTR;
return U1RxBufferPtrIN - U1RxBufferPtrOUT;
}
this code will let you take the received bytes
char readU1(void) {
char readByte = Usart1RxDMABuffer[U1RxBufferPtrOUT++];
if (U1RxBufferPtrOUT >= U1RxBufSize) U1RxBufferPtrOUT = 0;
return readByte;
}
this code will clear the Rx buffer, but I never use it, you should never use it
void clearRxBuffer(void) {
U1RxBufferPtrIN = U1RxBufSize - huart1.hdmarx->Instance->CNDTR;
U1RxBufferPtrOUT = U1RxBufferPtrIN;
}
2019-08-01 05:51 AM
Yes, already impelmented based on LL example example and yes, never overrun interrupt was triggered. Just now i have to somehow synchronize my header and data so i could read registers. Simple 8byte array and some folding algorithm is needed.
2019-08-01 06:23 AM
can you show me the data you are expecting ?
Show me 2 or 3 frames of data
2019-08-01 06:54 AM
Ok, so after few hours of coding have working code.
Idea is if NDTR is showing that at least single bite is transferred and it is not header, i will reinitialize DMA and that's it.
This check happens at 5Hz. mainly i need to avoid any false UART at turn up or any other event. Master that sends data should send it by 8 words, so after DMA reset, it will never trigger unless i inject any data to uart, that not suppose to be. (tested with million packets)
And DMA is transferring single frame size and generating TC interrupt in circle mode.
UReg = *(uint32_t*)0x4002005C; // NDTR address
if((UReg<8)&(RxData[0]!=0xBB))
{
Reset_Uart_Dma();
}
2019-08-01 01:59 PM
I use this to search for frames (edited , I missed a bracket)
char framestring[8];
if ( readableU1 >=8)
{
peekByte = peekRxU1(0);
if (peekByte == 0xbb)
for (i=0;i<8;i++)
frameString[i]=readU1();
}else readU1(); // and dump it
}
char peekRxU1(int offset) {
// offset of zero returns the next byte to be read
int relativePtr = (U1RxBufferPtrOUT + offset) & (U1RxBufSize - 1); // bufSize must be 2^n
char PeekRx = Usart1RxDMABuffer[relativePtr]; // just looking Don't increment pointer
return PeekRx;
2019-08-01 08:40 PM
if you want to use my method, then there is no need to re-initialse the DMAs.
2019-08-02 03:37 AM
This night get my code testing. Around 2 milion bidirectional packets was send, get no errors, not a single DMA reconfiguration was triggered, but that is in good conditions wituout any power ups. So it looks like very simple and effitient code :)