Skip to main content
Visitor II
February 10, 2024
Question

stm32f429 USART DMA FIFO error on transmit upon completion

  • February 10, 2024
  • 4 replies
  • 11412 views

Hi,

I am using DMA1 stream 3 for USART3 for tx on an stm32f429.

Driver code is based on cubemx generated st hal drivers firmware package version 1.24 

The uart and dma initalisation parameters are as follows:

huart3.Instance = USART3;
huart3.Init.BaudRate = 921600
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;

hdma_usart3_tx.Instance = DMA1_Stream3;
hdma_usart3_tx.Init.Channel = DMA_CHANNEL_4;
hdma_usart3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart3_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart3_tx.Init.Mode = DMA_NORMAL;
hdma_usart3_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

I use HAL_UART_Transmit_DMA(...) to transmit buffers (always from the same static buffer to which i copy the data to be transmitted first).

There is a semaphore before that to make sure there is only 1 action at a time till it is complete.

Now for some reason, I always get a FIFO error (LISR.FEIF3) at the exact same spot, 100 pct reproducible (and on multiple boards).
It is not the first message, more like the 30th.
And the message itself is transmitted correctly and completely.
This is confirmed by the DMA registers, LISR.TCIF3 is set to 1 and S3NDTR.NDT is 0.
These and other DMA registers at the moment the interrupt occurs are in the IAR screenshot in attachment.

If I look at the datasheet I get as possible reasons for the FIFO error:
• FIFO error: the FIFO error interrupt flag (FEIFx) is set if:
– A FIFO underrun condition is detected
– A FIFO overrun condition is detected (no detection in memory-to-memory mo
because requests and transfers are internally managed by the DMA)
– The stream is enabled while the FIFO threshold level is not compatible with th
size of the memory burst (refer to Table 48: FIFO threshold configurations)

+

In direct mode, the FIFO error flag can also be set under the following conditions:
• In the peripheral-to-memory mode, the FIFO can be saturated (overrun) if the memory
bus is not granted for several peripheral requests
• In the memory-to-peripheral mode, an underrun condition may occur if the memory bus
has not been granted before a peripheral request occurs

Since I am in direct mode it cannot be because of the threshold.
So if the fault is valid it has to be underrun, but then I would expect that the NDT is non zero to indicate at which point the DMA  encountered an underrun.

Am I overlooking something, or am I bumping into some DMA controller bug which causes the occasional spurious FIFO fault  ? But strange then that it is not random but at a fixed point in my flow.
For now I plan to modify the interrupt handler to ignore the error if NDT is 0 to deal with it and cross my fingers it only happens for complete transfers.

But it would be nice to completely understand the problem as I don't want to bury a potential real issue .

Does this ring any bells or any suggestions what else I can check ?

    This topic has been closed for replies.

    4 replies

    Graduate II
    February 10, 2024

    You need to show your code on how you're transmitting. Are you checking HAL status?

    Visitor II
    February 10, 2024

    Hi Karl,

    I am using the st driver code 1.24 (as mentioned in my post).

    I don't understand what you mean by 'checking hal status' as the DMA transfer is working autonomously up to the point where the fifo error interrupt occurs.

    Anyway the relevant functions to setup the transfer from the driver package are:

     

    /**
     * @brief Sends an amount of data in DMA mode.
     * @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
     * the sent data is handled as a set of u16. In this case, Size must indicate the number
     * of u16 provided through pData.
     * @PAram huart Pointer to a UART_HandleTypeDef structure that contains
     * the configuration information for the specified UART module.
     * @PAram pData Pointer to data buffer (u8 or u16 data elements).
     * @PAram Size Amount of data elements (u8 or u16) to be sent
     * @retval HAL status
     */
    HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    {
     uint32_t *tmp;
    
     /* Check that a Tx process is not already ongoing */
     if (huart->gState == HAL_UART_STATE_READY)
     {
     if ((pData == NULL) || (Size == 0U))
     {
     return HAL_ERROR;
     }
    
     /* Process Locked */
     __HAL_LOCK(huart);
    
     huart->pTxBuffPtr = pData;
     huart->TxXferSize = Size;
     huart->TxXferCount = Size;
    
     huart->ErrorCode = HAL_UART_ERROR_NONE;
     huart->gState = HAL_UART_STATE_BUSY_TX;
    
     /* Set the UART DMA transfer complete callback */
     huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;
    
     /* Set the UART DMA Half transfer complete callback */
     huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt;
    
     /* Set the DMA error callback */
     huart->hdmatx->XferErrorCallback = UART_DMAError;
    
     /* Set the DMA abort callback */
     huart->hdmatx->XferAbortCallback = NULL;
    
     /* Enable the UART transmit DMA stream */
     tmp = (uint32_t *)&pData;
     HAL_DMA_Start_IT(huart->hdmatx, *(uint32_t *)tmp, (uint32_t)&huart->Instance->DR, Size);
    
     /* Clear the TC flag in the SR register by writing 0 to it */
     __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC);
    
     /* Process Unlocked */
     __HAL_UNLOCK(huart);
    
     /* Enable the DMA transfer for transmit request by setting the DMAT bit
     in the UART CR3 register */
     SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);
    
     return HAL_OK;
     }
     else
     {
     return HAL_BUSY;
     }
    }

     

    and

     

    /**
     * @brief Start the DMA Transfer with interrupt enabled.
     * @PAram hdma pointer to a DMA_HandleTypeDef structure that contains
     * the configuration information for the specified DMA Stream. 
     * @PAram SrcAddress The source memory Buffer address
     * @PAram DstAddress The destination memory Buffer address
     * @PAram DataLength The length of data to be transferred from source to destination
     * @retval HAL status
     */
    HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
    {
     HAL_StatusTypeDef status = HAL_OK;
    
     /* calculate DMA base and stream number */
     DMA_Base_Registers *regs = (DMA_Base_Registers *)hdma->StreamBaseAddress;
     
     /* Check the parameters */
     assert_param(IS_DMA_BUFFER_SIZE(DataLength));
     
     /* Process locked */
     __HAL_LOCK(hdma);
     
     if(HAL_DMA_STATE_READY == hdma->State)
     {
     /* Change DMA peripheral state */
     hdma->State = HAL_DMA_STATE_BUSY;
     
     /* Initialize the error code */
     hdma->ErrorCode = HAL_DMA_ERROR_NONE;
     
     /* Configure the source, destination address and the data length */
     DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);
     
     /* Clear all interrupt flags at correct offset within the register */
     regs->IFCR = 0x3FU << hdma->StreamIndex;
     
     /* Enable Common interrupts*/
     hdma->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;
     
     if(hdma->XferHalfCpltCallback != NULL)
     {
     hdma->Instance->CR |= DMA_IT_HT;
     }
     
     /* Enable the Peripheral */
     __HAL_DMA_ENABLE(hdma);
     }
     else
     {
     /* Process unlocked */
     __HAL_UNLOCK(hdma);	 
     
     /* Return error status */
     status = HAL_BUSY;
     }
     
     return status;
    }

     

     

    Something extra I was just looking at now is that the bootloader that is also in my board is actually using an older version of the st drivers (1.22 ?) and that version had

     /* Enable Common interrupts*/
    hdma->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;
    hdma->Instance->FCR |= DMA_IT_FE;

    in HAL_DMA_Start_IT.

    So it enabled the DMA_IT_FE ( = FEIE) interrupt (and apparently never disabled the interrupt, except on DMA aborts, for some mysterious and probably incorrect reason), which is how it still remains enabled by the time my main application starts running.

    But it seems someone at ST decided to just disable the fifo interrupt in later versions.... which is also incorrect because it can occur, so that is burying problems.

    It would be interesting to know what the rationale was behind these changes, maybe ST also figured out there was a problem with it and decided to disable it completely ?

    Graduate II
    February 10, 2024

    You're showing the HAL drivers. Show YOUR own code that you've wrote to call the HAL driver. 

    Super User
    February 10, 2024

    > the bootloader that is also in my board

    > So it enabled the DMA_IT_FE ( = FEIE) interrupt (and apparently never disabled the interrupt, except on DMA aborts, for some mysterious and probably incorrect reason), which is how it still remains enabled by the time my main application starts running.

    Cube/HAL - quite expectantly - assumes every peripheral to be in its reset state when it starts to work. So, generally, it's a bad idea not to put everything to that state before moving execution from bootloader to application. In other works, your bootloader is buggy by not resetting all peripherals, but you still can hotfix it by doing it before Cube/HAL is started in application.

    > which is also incorrect because it can occur, so that is burying problems.

    If you don't use DMA FIFO (a.k.a. "Direct mode"), the FIFO interrupt is harmless except for very extreme resource starvation, which is harmless with master transmitters (as they determine the pace of transmission themselves and a few cycles of delay seldom matters); certainly not the case with UART which is compared to bus clocks very slow; and even with faster peripherals such as slave SPI at its highest baudrate, if such resource/bus starvation occurs, you have a graver problem to solve and should have DMA FIFO enabled anyway.

    The harmless FIFO interrupt occurs because of the sequencing of peripheral enable vs. DMA enable (if UART Tx DMA is enabled before DMA, the TXE signal towards DMA is active sooner than DMA, so at the moment DMA is enabled it already sees the peripheral request but it does not have buffered the byte to be transmitted from the memory yet - and as I've said above, this particular case is *absolutely* harmless even in resource-starved situation, as it only means that the UART transmitter starts to transmit a few nanoseconds later than if the enabling sequence would be reversed, i.e. DMA first then UART's Tx DMA.

    The change to disable this interrupt came as - as I've said -  it is harmless, and it was much easier than to make a quite substantial change in the sequencing of how the various peripherals are enabled/set up in Cube/HAL.

    JW

     

    Visitor II
    February 10, 2024

    application. In other works, your bootloader is buggy by not resetting all peripherals

    True (though to nitpick, you cannot reset everything, eg IWDG :) ).


    > which is also incorrect because it can occur, so that is burying problems.

    If you don't use DMA FIFO (a.k.a. "Direct mode"), the FIFO interrupt is harmless except for very extreme resource starvation, which is harmless with master transmitters (as they determine the pace of transmission themselves and a few cycles of delay seldom matters);

    Ah right, and I see in the manual that the DMA controller  just continues in case of an overrun/underrun condition.

    If the DMEIFx or the FEIFx flag is set due to an overrun or underrun condition, the faulty
    stream is not automatically disabled and it is up to the software to disable or not the stream
    by resetting the EN bit in the DMA_SxCR register. This is because there is no data loss
    when this kind of errors occur

    Now,  I understand that there is no data loss in an underrun TX scenario, but in an RX overrun scenario there will be data loss imho unless flow control is enabled ? So for RX it seems to me FEIE should be enabled (so you at least know you lost data), and since both RX and TX use the same HAL_DMA_Start_IT function,  it should always be enabled....

     

    if such resource/bus starvation occurs, you have a graver problem to solve and should have DMA FIFO enabled anyway.


    I think we agree on that, I am also arguing problems are potentially buried by not enabling the FEIE in the more recent drivers.


    The harmless FIFO interrupt occurs because of the sequencing of peripheral enable vs. DMA enable (if UART Tx DMA is enabled before DMA, the TXE signal towards DMA is active sooner than DMA, so at the moment DMA is enabled it already sees the peripheral request but it does not have buffered the byte to be transmitted from the memory yet


    Except that does not seem to be the case here. In HAL_UART_Transmit_DMA the sequence is done in a correct way: DMA is enabled first, then the DMAT is set.

     

     /* Enable the UART transmit DMA stream */
     tmp = (uint32_t *)&pData;
     HAL_DMA_Start_IT(huart->hdmatx, *(uint32_t *)tmp, (uint32_t)&huart->Instance->DR, Size);
    
     /* Clear the TC flag in the SR register by writing 0 to it */
     __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC);
    
     /* Process Unlocked */
     __HAL_UNLOCK(huart);
    
     /* Enable the DMA transfer for transmit request by setting the DMAT bit
     in the UART CR3 register */
     SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);

     

    So this should not trigger the FIFO error because of that reason (unless this order is still wrong ??).

     


    The change to disable this interrupt came as - as I've said -  it is harmless, and it was much easier than to make a quite substantial change in the sequencing of how the various peripherals are enabled/set up in Cube/HAL.



    So in RX scenarios I don't see how it is harmless (except for cases with flow control). And it seems the order is already correct.

    So I am wondering if I am actually running into the real underrun situation with my uart at 921600 baud. HCLK is set to 48Mhz.

    Super User
    February 11, 2024

    Hi @Peeters.Bram ,

    >> [bootloader should reset peripherals as that's what Cube expects at the beginning]

    > True (though to nitpick, you cannot reset everything, eg IWDG)

    Fair point; but then we can also argue that the completely opposite approach could be used, too: make the bootloader part of the application, thus peripheral initializations made in bootloader are (mostly) those needed for application and they don't need to be replicated in application. However, this does not fare well with the architecture of Cube/HAL, and - as this thread shows, too - fails in the face of Cube/HAL being updated for whatever reason.

    But I digress.

     

    Now, I understand that there is no data loss in an underrun TX scenario, but in an RX overrun scenario there will be data loss imho unless flow control is enabled ?

    Not necessarily.

    First, UART/SPI/I2C are double-buffered, i.e. at the moment when the holding register gets full and the peripheral indicates this by RXNE or similar flag to DMA, there is still one frame's time for the DMA to succeed storing the first two data.

    Second, and here I am speculating as it would need insider access to be sure, IMO "direct mode" still uses the whole FIFO mechanism, except that the trigger levels are at 1. It means, that for Tx direction it attempts to prefetch from memory port only one frame, and for Rx direction it attempts to store to memory port immediately after reading from peripheral port. But, for the latter, IMO, still the full FIFO is available. An indication - yes, not proof - is in this wording from RM:

    In direct mode, the FIFO error flag can also be set under the following conditions:
    • In the peripheral-to-memory mode, the FIFO can be saturated (overrun) if the memory
    bus is not granted for several peripheral requests

    ( @STOne-32 , this (i.e. what's the risk of data loss in direct mode of the dual-port DMA in peripheral->memory direction, and whether the FIFO error interrupt is indicative of such data loss, and whether FIFO is or is not in fact used in direct mode i.e. whether the number of items stored in DMA is one or up to FIFO size in direct mode) may be an example of topic worthy of "knowledge base"; although, it would be way better to put it properly into AN4031).

    I see your point, though - the FIFO error may indicate that something is astray (although not necessarily in fatal way) and needs attention, at least during development. It happens in only in extreme cases, though, and Cube/HAL is written for the masses (it probably perspires that I don't use Cube/HAL and despise it to a certain extent). I too personally wouldn't consider setting up and then investigating the FIFO interrupt in direct mode for slow peripherals unless I see data loss; OTOH for fast peripherals I do use the FIFO as appropriate.

    Now to your particular case.

    1Mbps UART is unusual as is 64MHz system clock in 'F4, but that still leaves around 640 system clock periods per UART frame. Bus arbitration is round-robin, it means, that all other bus masters would need to hold up the bus for 640 ticks for a problem to occur. Unless...

    ... unless the symptom stems from UART requesting two frames in quick succession. And it indeed does - if its transmitter has not been disabled previously (so that the idle preamble does not apply), and if its holding register is empty, after writing to holding register it "immediately" transfers that frame to the shift register and indicates holding register empty (TXE) again. I don't know how "immediate" this process is.

    ... unless the DMA is held up by other, equal or higher priority, DMA streams occupying the memory port for an extensively long time. From the screenshot in your opening post, only one other DMA stream appears to be used, the same UART's Rx, and I don't think that could hold up the memory port for significant time.

    ... unless the memory bus in question is held up by some other bus-master. The bus arbitrator is again round-robin, and AFAIK there are no priorities i.e. all busmasters are at the same priority, so the total worst case latency is the sum of worst case locked-bus cycles of all other busmasters. Besides the processor (which IMO won't lock the bus for more than 3 cycles worst case, for the worst case unaligned access), it's the other DMA (which can be set to locked bursts, but rarely anybody does that), ETH (which can and probably is set to some bursts, maybe 16-beat?), and OTG HS (I'm not sure with that one, but again I wouldn't expect anything beyond 16-beat bursts). Are these used in your application?

    ... unless the accessed memory itself generates waitstates for whatever reason. And, indeed, looking again at the screenshot you've posted, you are using FMC (at 0x68xx'xxxx) for the DMA stream in question. Now the question is, what sort of memory is that, how exactly is it set up, and what is the resulting (worst case) latency of its access (read). That indeed may be one of the main source of unorthodox behaviour here, as those latencies apply also to all other busmasters' accesses, so latency stemming from the previous item multiplies with this one.

    ... unless there's something else I didn't think of. You've mentioned Sleep - well, maybe that's a factor too, I don't know, I don't have extensive experience with Sleep or other low-power modes. However, the thread you've mentioned did not come to a definitive conclusion, and it also was about 'F0, which has a different type of DMA.

    Now all this said, in your particular case, any latency means only that the outgoing frame will be delayed. It's rarely an issue. And, also, remember, that unless this is the case of FIFO error being generated due to UART TX DMA being enabled before DMA itself, the error comes in between frames; and it's when data are transferred from holding to shift register is when TXE is risen (so it generates the request DMA can't fulfill immediately as it did not succeed to prefetch yet), so shift register is full at that moment, and even if it takes DMA the 640 system clocks to fetch data and forward them to UART_DR, the net result is still a continuous uninterrupted UART Tx stream.

    JW

     

    Super User
    February 10, 2024

    > Now,  I understand that there is no data loss in an underrun TX scenario, but in an RX overrun scenario there will be data loss imho unless flow control is enabled ?

    Yes if you overrun in RX, data will be lost. But that is typically due to code bugs. A baud rate of 900k does not stress the bandwidth of the DMA.

    FEIF should be ignored if not using the FIFO, and FEIE should be cleared. It's a non-issue.

    Visitor II
    February 10, 2024


    FEIF should be ignored if not using the FIFO, and FEIE should be cleared. It's a non-issue.


    Fifo error means something even in direct mode as explicitly indicated in the documentation.

    And we have an error being generated by the hardware for which there is so far no explanation ( it is not because of a wrong order in the init sequence, and it is not because of bandwidth problems apparently )

    For me that is not a 'non-issue'.

    I understand for the TX path it will not cause data loss, but if the same thing happens on the RX path it might.

    It is not a nuclear reactor control system I am working on (thank god), but unexplained ignored problems have a tendency to bite you in the ass in the long run (and they are also great for getting deeper insights :) ).

    I will try lowering the baud rate to see if that changes something (though I don't think either outcome is hard proof for the bandwidth hypothesis).

     

     

    Super User
    February 10, 2024

    That's fair. I thought the flag was getting set if fifo was disabled, but now I see that is not the case.

    I was unable to replicate this behavior on a Nucleo-F429 board. The issue is probably specific to your code and something going on inside of it. Hard to know how that's manifesting as an error in the peripheral.

    Super User
    February 10, 2024

    @Karl Yamashita In your example there's a race condition, after successful HAL_UART_Transmit_DMA in line 23 the ongoing TX buffer is clobbered by sprintf in line 30. Is this OK you think?

     

    Graduate II
    February 11, 2024

    I don't know what kind of data the OP is sending so I am just sending string with a counter. The OP is using memcpy and i'm using sprintf to save to a global variable. I know what you mean by race condition but i'm just making it the worst scenario which doesn't seem create an issue.

     

    I myself use a Rx/Tx queue buffer to hold multiple messages or binary packets. So i don't have to deal with a race condition.

    I have logged a text file of the output of the counter incrementing over 100k times and though I haven't looked at every line, what i've looked i don't see any skipped counter numbers. So that means that currently there is no race condition. I can't see where to attach a text file so i'm just pasting a handful of the last part of the log. 

     

    Hello World, Counter= 116529<CR><LF>
    Hello World, Counter= 116530<CR><LF>
    Hello World, Counter= 116531<CR><LF>
    Hello World, Counter= 116532<CR><LF>
    Hello World, Counter= 116533<CR><LF>
    Hello World, Counter= 116534<CR><LF>
    Hello World, Counter= 116535<CR><LF>
    Hello World, Counter= 116536<CR><LF>
    Hello World, Counter= 116537<CR><LF>
    Hello World, Counter= 116538<CR><LF>
    Hello World, Counter= 116539<CR><LF>
    Hello World, Counter= 116540<CR><LF>
    Hello World, Counter= 116541<CR><LF>
    Hello World, Counter= 116542<CR><LF>
    Hello World, Counter= 116543<CR><LF>
    Hello World, Counter= 116544<CR><LF>
    Hello World, Counter= 116545<CR><LF>
    Hello World, Counter= 116546<CR><LF>
    Hello World, Counter= 116547<CR><LF>
    Hello World, Counter= 116548<CR><LF>
    Hello World, Counter= 116549<CR><LF>
    Hello World, Counter= 116550<CR><LF>
    Hello World, Counter= 116551<CR><LF>
    Hello World, Counter= 116552<CR><LF>
    Hello World, Counter= 1165
    2/10/2024 17:46:58.830 [RX] - 53<CR><LF>
    Hello World, Counter= 116554<CR><LF>
    Hello World, Counter= 116555<CR><LF>
    Hello World, Counter= 116556<CR><LF>
    Hello World, Counter= 116557<CR><LF>
    Hello World, Counter= 116558<CR><LF>
    Hello World, Counter= 116559<CR><LF>
    Hello World, Counter= 116560<CR><LF>
    Hello World, Counter= 116561<CR><LF>
    Hello World, Counter= 116562<CR><LF>
    Hello World, Counter= 116563<CR><LF>
    Hello World, Counter= 116564<CR><LF>
    Hello World, Counter= 116565<CR><LF>
    Hello World, Counter= 116566<CR><LF>
    Hello World, Counter= 116567<CR><LF>
    Hello World, Counter= 116568<CR><LF>
    Hello World, Counter= 116569<CR><LF>
    Hello World, Counter= 116570<CR><LF>
    Hello World, Counter= 116571<CR><LF>
    Hello World, Counter= 116572<CR><LF>
    Hello World, Counter= 116573<CR><LF>
    Hello World, Counter= 116574<CR><LF>
    Hello World, Counter= 116575<CR><LF>
    Hello World, Counter= 116576<CR><LF>
    Hello World, Counter= 116577<CR><LF>
    Hello World, Counter= 116
    2/10/2024 17:46:58.930 [RX] - 578<CR><LF>
    Hello World, Counter= 116579<CR><LF>
    Hello World, Counter= 116580<CR><LF>
    Hello World, Counter= 116581<CR><LF>
    Hello World, Counter= 116582<CR><LF>
    Hello World, Counter= 116583<CR><LF>
    Hello World, Counter= 116584<CR><LF>
    Hello World, Counter= 116585<CR><LF>
    Hello World, Counter= 116586<CR><LF>
    Hello World, Counter= 116587<CR><LF>
    Hello World, Counter= 116588<CR><LF>
    Hello World, Counter= 116589<CR><LF>
    Hello World, Counter= 116590<CR><LF>
    Hello World, Counter= 116591<CR><LF>
    Hello World, Counter= 116592<CR><LF>
    Hello World, Counter= 116593<CR><LF>
    Hello World, Counter= 116594<CR><LF>
    Hello World, Counter= 116595<CR><LF>
    Hello World, Counter= 116596<CR><LF>
    Hello World, Counter= 116597<CR><LF>
    Hello World, Counter= 116598<CR><LF>
    Hello World, Counter= 116599<CR><LF>
    Hello World, Counter= 116600<CR><LF>
    Hello World, Counter= 116601<CR><LF>
    Hello World, Counter= 116602<CR><LF>
    Hello World, Counter= 116
    2/10/2024 17:46:59.030 [RX] - 603<CR><LF>
    Hello World, Counter= 116604<CR><LF>
    Hello World, Counter= 116605<CR><LF>
    Hello World, Counter= 116606<CR><LF>
    Hello World, Counter= 116607<CR><LF>
    Hello World, Counter= 116608<CR><LF>
    Hello World, Counter= 116609<CR><LF>
    Hello World, Counter= 116610<CR><LF>
    Hello World, Counter= 116611<CR><LF>
    Hello World, Counter= 116612<CR><LF>
    Hello World, Counter= 116613<CR><LF>
    Hello World, Counter= 116614<CR><LF>
    Hello World, Counter= 116615<CR><LF>
    Hello World, Counter= 116616<CR><LF>
    Hello World, Counter= 116617<CR><LF>
    Hello World, Counter= 116618<CR><LF>
    Hello World, Counter= 116619<CR><LF>
    Hello World, Counter= 116620<CR><LF>
    Hello World, Counter= 116621<CR><LF>
    Hello World, Counter= 116622<CR><LF>
    Hello World, Counter= 116623<CR><LF>
    Hello World, Counter= 116624<CR><LF>
    Hello World, Counter= 116625<CR><LF>
    Hello World, Counter= 116626<CR><LF>
    Hello World, Counter= 116627<CR><LF>