Skip to main content
Robert Ritchey
Senior
November 28, 2024
Solved

USART receiving 8-bit data with parity

I am supposed to be receiving 8-bit data with even parity.  The blocks are defined with a header of 'F0', this is S.Bus data if anyone knows that that is.  I setup a simple program that just does receive data.  I get data but nowhere does it show an 'F0'.  Am I supposed to use 'LL_USART_ReceiveData8' or 'LL_USART_ReceiveData9' to read this data?  Is the parity supposed to be read with the data and discarded?  It's not at all clear how the parity is handled. 

This topic has been closed for replies.
Robert Ritchey
Robert RitcheyBest answer
Senior
December 4, 2024

You are correct.  I had the pin inverted at first, then re-read the documention, and set it to non-inverted. You try to make sense of this:

Bit 16 RXINV: RX pin active level inversion
This bit is set and cleared by software.
0: RX pin signal works using the standard logic levels (VDD =1/idle, Gnd=0/mark)
1: RX pin signal values are inverted (VDD =0/mark, Gnd=1/idle).

Anyway, I misread this documentation.   The 9-bit data is correct with parity.  I don't know what you mean by "2 parity" bits.  The format is 8-bit data, parity and 2 stop bits.  

It works now, at least I get the "0F" where I expect it.  I need to check the number if IDLEs I got. 

Thanks for sticking with me though this. 

22 replies

mƎALLEm
ST Technical Moderator
November 29, 2024

Hello,

You posted your thread in STM32CubeIDE (MCUs) which is not the right forum board. I moved it to STM32 MCUs Products.

Could you please also provide the MCU part number you are using?

To give better visibility on the answered topics, please click "Best answer" on the reply which solved your issue or answered your question.
Robert Ritchey
Senior
November 29, 2024

Sorry.  I am using STM32G030C6.  My code is 

 

 while (1)
 {
 int i = 0;
 while(i < 500)
 {
 if(LL_USART_IsActiveFlag_FE(USART2))
 {
 LL_USART_ClearFlag_FE(USART2);
 FE++;
 }
 if(LL_USART_IsActiveFlag_PE(USART2))
 {
 LL_USART_ClearFlag_PE(USART2);
 PE++;
 }
 if(LL_USART_IsActiveFlag_NE(USART2))
 {
 LL_USART_ClearFlag_NE(USART2);
 NE++;
 }
 if(LL_USART_IsActiveFlag_ORE(USART2))
 {
 LL_USART_ClearFlag_ORE(USART2);
 ORE++;
 }
 if(LL_USART_IsActiveFlag_IDLE(USART2))
 {
 LL_USART_ClearFlag_IDLE(USART2);
 IDLE++;
 }
 if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART2))
 {
// array[i] = LL_USART_ReceiveData8(USART2);
 array[i] = LL_USART_ReceiveData9(USART2);
 i++;
 }
 }
 while(1) {};
 }
Ozone
Principal
December 2, 2024

> I am using STM32G030C6.  My code is  ...

If you debug the code, can you see any of the error counters go up ?


And on a related note ...
Serial communication, including the UART, can be tricky to debug.
The debugger might clear receive flags, and thus change the code execution path.

Ozone
Principal
November 29, 2024

> I get data but nowhere does it show an 'F0'.  

I had to read it up, to be honest.
But yes, you should read the header value. Although the first hit I found suggests it is "0x0F", and not "0xF0". 

Additionally, S.Bus requires 2 stop bits, and the uncommon baudrate of 100.000 bps.
Perhaps you can share the UART init code.

> Is the parity supposed to be read with the data and discarded?  It's not at all clear how the parity is handled.

If the received parity value doesn't match, the PE error should trigger -> a.k.a. "parity error". In this case, the RD register values is usually not updated, and invalid.

Robert Ritchey
Senior
November 29, 2024

Here is the init code.  The funny thing is that I do not see any 'IDLE"s.  I should use these to delimit the blocks. 

 

 LL_USART_InitTypeDef USART_InitStruct = {0};

 LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

 /* Peripheral clock enable */
 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);

 LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
 /**USART2 GPIO Configuration
 PA2 ------> USART2_TX
 */
 GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
 GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
 GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
 GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
 GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
 GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
 LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

 /* USER CODE BEGIN USART2_Init 1 */

 /* USER CODE END USART2_Init 1 */
 USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
 USART_InitStruct.BaudRate = 100000;
 USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_9B;
 USART_InitStruct.StopBits = LL_USART_STOPBITS_2;
 USART_InitStruct.Parity = LL_USART_PARITY_EVEN;
 USART_InitStruct.TransferDirection = LL_USART_DIRECTION_RX;
 USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
 LL_USART_Init(USART2, &USART_InitStruct);
 LL_USART_DisableDMADeactOnRxErr(USART2);
 LL_USART_ConfigHalfDuplexMode(USART2);

 /* USER CODE BEGIN WKUPType USART2 */

 /* USER CODE END WKUPType USART2 */

 LL_USART_Enable(USART2);

 /* Polling USART2 initialisation */
 while((!(LL_USART_IsActiveFlag_TEACK(USART2))) || (!(LL_USART_IsActiveFlag_REACK(USART2))))
 {
 }

 

 Also, I enable the receive bit just before entering the loop. I should point out that I am getting a lot of parity errors (250) and framing errors (147).  These are pretty consistent run to run. 

Ozone
Principal
December 1, 2024

Is this all the init code ?
Being accustomed to either SPL code or direct access, the init for PA2 looks strange.  What alternate function (many pins of F3/F4 MCUs I frequently use have 7 alternate functions). And pull-up & OD at the same time ?

But most important, is there no Rx pin initialisation ?

If in doubt, I always check the respective reference manual section, and observe the peripheral register settings in a debugger.

waclawek.jan
Super User
December 2, 2024

@Robert Ritchey,

> I get data but nowhere does it show an 'F0'.

And what does it show?

Is there any external pullup? The internal is nominally 40kOhm, it may not be enough to produce clean edges.

@Ozone,

The UART is here set to HalfDuplex, and in that case it uses the Tx pin as the single IO pin.

JW

 

 

Ozone
Principal
December 2, 2024

> The UART is here set to HalfDuplex, and in that case it uses the Tx pin as the single IO pin.

I suspected that much.
But I have never used such half-duplex UART mode, especially not on the C0 devices.

waclawek.jan
Super User
December 2, 2024

Read out and check/post the UART registers content. Check the real system clock, too, and by that I don't mean just to look at what you've clicked in CubeMX, but actually measure it in some way. One good way to do that may be to transmit some pattern e.g. 0b0101010101 through the UART (with disconnected other transmitter of course), and observe using oscilloscope/LA.

JW

Robert Ritchey
Senior
December 2, 2024

I checked the clock, it is 64MHz.  I did it by configuring a timer channel to divide the system clock by 16 and output a square wave.  It showed a 250nsec period, or 16 MHz.  I also captured the USART registers.  They look fine but I am posting them. 

Ozone
Principal
December 3, 2024

Have you observed the s-bus signal on a scope, and compared with the spec ?
I think a logic analyser would do as well, AFAIK sigrok/pulseview even support protocol interpreters.

Robert Ritchey
Senior
December 3, 2024

By the way, here are some of the values I have captured.  You can see its repeating, just not the right values.  Also, do either of you know if the IDLE bit is set when it's not set to interrupt?  It doesn't say.  I keep expecting to see an IDLE when I am polling but it does not seem to get set. 

waclawek.jan
Super User
December 4, 2024

Try to transmit and observe the signal you've transmitted.

JW

Robert Ritchey
Senior
December 4, 2024

Thanks for stick with me though this.  The data was inverted.  I misread the documentation.  It's confusing as to what the USART expects.  

Bit 16 RXINV: RX pin active level inversion
This bit is set and cleared by software.
0: RX pin signal works using the standard logic levels (VDD =1/idle, Gnd=0/mark)
1: RX pin signal values are inverted (VDD =0/mark, Gnd=1/idle).