cancel
Showing results for 
Search instead for 
Did you mean: 

Emulated UART - Timer triggered DMA for reading GPIOB->IDR in STM32L433RBT6

YPath
Associate II

Hello,

I am using STM32L433RBT6 for receiving data from NEO6M GPS module. The requirement to add the module was given after the circuit board was delivered. Hence, I need to implement the UART reception using emulated uart (bitbanging technique). I have implemented the uart using the technique described below. However, I am getting lots of junk characters mixed up with the target data.

Description:

I have initialized the pin, PB3, as EXTI and am using TIM2 (in update mode, not capture compare) for timing generation. Timer runs at a frequency twice the baud rate. The timing is calculated as follows:

APB clk=72MHz

Baud Rate=9600bps

Timer CNT=72000000/(2*9600) (for twice baud rate, timer count is divided by 2)

Timer CNT=3750=3749 (actual load value)

When EXTI detects a falling edge, it starts the timer by setting the EN bit of the TIM2->CR register whilst serving its ISR. All interrupts are disabled including the PB3 EXTI itself but not the timer interrupt, until the reception of 10 bits (1 stop bit, 1 start bit, 8 bit data, no parity) is over. When the timer generates an interrupt, a counter is incremented in the timer's ISR.

If the interrupt is generated by an odd count, the digital value of the pin is sampled. Else the ISR just checks whether the count has exceeded the frame length i.e. 19 timer counts. In odd counts, the GPIOB->IDR is stored in a buffer variable unprocessed. Once all bits are received. the data is parsed in the main code's infinite loop by masking the corresponding bit in the stored value of GPIOB->IDR. A screenshot of the received, parsed data is attached.

This issue is not board specific. I also tried this code on Nucleo-F767ZI board and found the exact same problem occurring with it too. The reception using a hardware uart is flawless as expected.

I have tried different optimization techniques but to no avail.

Reading GPIO-IDR through timer triggered DMA is the only route I haven't explored yet. However, I am not able to figure out a way to make this DMA work. I have set up a DMA on timer update event. The DMA configuration is given in the stm32l4xx_hal_msp.c file. Please advice on how to make the DMA work on timer update event for transferring GPIOB->IDR register to a buffer memory location.

14 REPLIES 14
Danish1
Lead II

Disabling all interrupts might not be the best approach. It could be that an EXTI interrupt from an edge in the received character is left "pending" and fires when you re-enable interrupts, causing reception of a blank, or premature triggering if the next character follows on without a gap. I suspect things would be better if you just disabled both edges of the EXTI.

Have you looked at ST AN4457 "Implementing an emulated UART on STM32F4 microcontrollers". I have used that successfully on both stm32f4 and stm32l4.

https://www.st.com/en/embedded-software/stsw-stm32156.html

Hope this helps,

Danish

Thanks for your reply.

This was also my first guess. If you refer the code, all pending interrupt flags are cleared before enabling and after disabling the EXTI. If EXTI is not disabled, its ISR will reset the timer while it is receiving and hence cause the reception to stop.

As can be seen in the the output screenshot, first few characters are received correctly. Junk characters are generated only after good reception of the first few characters, around 15 nos.

I have already referred to ST AN4457 as well as the example code STSW-STM32156. I have also referred to different discussion forums, both on ST's and other manufacturers's site.

The only difference between my implementation and ST AN4457 is that I haven't used DMA for sampling GPIOB->IDR.

Danish1
Lead II

Have you tried tweaking the baud rate slightly faster or slower? In particular, does this affect how many good characters you receive before things go crazy?

What I have in mind is that you might not be waiting for the start-bit of subsequent characters. Or that your processing might be too slow (it shouldn't be at 9600 baud).

Can you control your sending-end and get it to put 2 stop-bits rather than just 1 - merely for testing purposes? Or use another serial source to try this.

Some sample outputs stored on SD card are as follows:

GPRMC,,V,,,,,,,,,,KNKNNITKK''''¥ª*ÒÕS$

GPRMC,V,,,,,,,,,,ªšRbbbbbbb9A,99+30

$

GPRMC,,V,,,,,,,,,,b‚‚bÊÊrÊÊbbbbbbR¢ÂAMNN64

$

GPRMC/V,,,,,,,,,,LNNIKK'''§ªÒÕS$

GPRMC,T,,,,,,,,,ªªSÒÕHŠŠŠ‰‰‰‰ÉHø$

GPRMC,,V,,,,,,,,,,KNNNITKK''''¥ª*ÒÕS$

GPRMC,,V,,,,,,,,,,ªš"bbbbbb9A99*30

$

GPRMC,,V,,,,,,,,,,ªš"bbbbbbb9A9*30

$

GPRMC,,\,,,,,,,,,,ªš"bbbbbbb9A5e‚RºÊ"bbbb¢þ$

GPRMC,,V,,,,,,,,,b‚‚bÊÊrÊÊbbbbbbR¢ÂªªSÒÕ)HŠŠŠ‰‰‰‰‰ÉHø$

As you can see, the first few characters of the NEMA sentence are good but characters after around 10-15 count are garbage.

Changing the value of htim2.Init.Period register does have an effect on reception. After changing several values around the calculated 3750 count, I found that 3748 works better than others.

I am using 72MHz as my system clock to get an integer number for timer period (72000000 is an integeral multiple of 9600).

Also, as you suggested, I tried not disabling EXTI. Instead, I introduced an if condition in the EXTI ISR to check if the timer count is 0. If the timer count is zero, i.e. no reception is under progress (timer count is set to zero when all 10 bits are received), and EXTI detects a falling edge (i.e. start bit), timer is started. The output is completely bizarre. And there is no effect of changing the timer update period (even tried doubling, halving etc). Output is attached for your reference. None of the received characters are good.

I tried using two stop bits as you suggested (using hardware UART on NUCLEO-F767ZI board) . Nucleo board sends "HELLO123HELLO$" string. However, my code just receives "HELLO123HELHELLO123HEL...". I double checked my parsing code as well as parsing timing. I inserted a sleep state when the loop is idle. The code does enter sleep state indicating sufficient time for the parsing loop to finish before reception of next character.

Interrupts might work for 9600Baud, just ditch Cube entirely (you've started already) and have control over latencies (make the interrupts in question the highest priority). To see the effect, toggle a GPIO pin in the EXTI/TIM ISR and observe it on oscilloscope/LA together with the incoming USART data, to see where do you do the sampling.

You might also utilize the fact that PB3 = TIM2_CH2 at AF1, so you can 1. use the iput filter on TIM2_CH2; 2. use it as input to the slave-mode controller, in trigger mode, to start the timer. The filter will remove any glitches; the hardware start will remove much of the variability associated with the EXTI interrupt latency.

In the 'F4, can't use TIM2 to DMA from GPIO to RAM; you'd need to use TIM1 or TIM8 for that as only DMA2 has access to GPIO through the busmatrix.

JW

> As you can see, the first few characters of the NEMA sentence are good but characters after around 10-15 count are garbage.

 This might be to the fact that you are trying to receive back-to-back characters, but your receiver runs too long, potentially missing the startbit and starting on the next high-to-low transition.

The "original" UART receiver wraps up and starts waiting for the next character after detecting the *middle* of the stopbit.

JW

Unfortunately, my board does not have any other GPIO coming into the connector. Is it safe to use a programming pin as output for this purpose?

Also, as the board is already printed and assembled, I cannot connect the output of TIM2 into TIM1 ETR.

I checked the reference manual. Unlike the F4, the reference manual for STM32L43x is very specific in available bus connection for DMA. GPIO is not listed as one. Could you please check the reference manual and suggest how can I configure my DMA to work with GPIO?

Manual Link: https://www.st.com/resource/en/reference_manual/dm00151940-stm32l41xxx-42xxx-43xxx-44xxx-45xxx-46xxx-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf

The parsing code only accepts/stores frames whose lsb is zero (start bit) and msb is 1 (stop bit). if this condition is not met, the frame is discarded. Also the reception can only be initiated by EXTI falling edge. Hence, if the falling edge of the start bit is missed, entire character is dropped.

What I'm talking about is, that in the timer ISR you are waiting until the *end* of stopbit (20th timeslot), to finish the Rx and start waiting for the startbit 1-to-0 leading edge, whereas you've should go back to EXTI sooner, at the *middle* of stopbit, where you're sampling it.

JW