cancel
Showing results for 
Search instead for 
Did you mean: 

UART / SBUS Data Aqcuisition

JGiem.1
Associate III

Hey folks. So for a beginner DIY fixed-wing flight controller, I am trying to incorporate servo values from an external receiver via SBUS [by FrSky (0-> Low, 1-> High; contrary to the standard SBUS by Futaba)] into my software.

I found a good amount of code for the sum frame decoding, but appear to not receive any UART communication at all from the receiver.

A UART RX Callback function generates an interrupt whenever there's a new frame received. The function is supposed to write a message via another UART to my Putty-terminal (which works fine), upon calling.

Further into it, the SBUS frame gets torn apart and the individual channel values generated - but I'm not even at this stage, and by the looks of it, it should work fine. My code:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint16_t failsafe_status;
uint8_t buf[25];
uint16_t CH[18];
int lenght=0;
uint16_t USB_Send_Data[]={0};
char msg[25];


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart1)
{
			sprintf(msg, "FRAME AQCUIRED\r\n");
			HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

	if (buf[0] == 0x0F) {
		CH[0] = (buf[1] >> 0 | (buf[2] << 8)) & 0x07FF;
		CH[1] = (buf[2] >> 3 | (buf[3] << 5)) & 0x07FF;
		CH[2] = (buf[3] >> 6 | (buf[4] << 2) | buf[5] << 10) & 0x07FF;
		CH[3] = (buf[5] >> 1 | (buf[6] << 7)) & 0x07FF;
		CH[4] = (buf[6] >> 4 | (buf[7] << 4)) & 0x07FF;
		CH[5] = (buf[7] >> 7 | (buf[8] << 1) | buf[9] << 9) & 0x07FF;
		CH[6] = (buf[9] >> 2 | (buf[10] << 6)) & 0x07FF;
		CH[7] = (buf[10] >> 5 | (buf[11] << 3)) & 0x07FF;
		CH[8] = (buf[12] << 0 | (buf[13] << 8)) & 0x07FF;
		CH[9] = (buf[13] >> 3 | (buf[14] << 5)) & 0x07FF;
		CH[10] = (buf[14] >> 6 | (buf[15] << 2) | buf[16] << 10) & 0x07FF;
		CH[11] = (buf[16] >> 1 | (buf[17] << 7)) & 0x07FF;
		CH[12] = (buf[17] >> 4 | (buf[18] << 4)) & 0x07FF;
		CH[13] = (buf[18] >> 7 | (buf[19] << 1) | buf[20] << 9) & 0x07FF;
		CH[14] = (buf[20] >> 2 | (buf[21] << 6)) & 0x07FF;
		CH[15] = (buf[21] >> 5 | (buf[22] << 3)) & 0x07FF;

		if (buf[23] & (1 << 0)) {
			CH[16] = 1;
		} else {
			CH[16] = 0;
		}

		if (buf[23] & (1 << 1)) {
			CH[17] = 1;
		} else {
			CH[17] = 0;
		}

		// Failsafe
		failsafe_status = SBUS_SIGNAL_OK;
		if (buf[23] & (1 << 2)) {
			failsafe_status = SBUS_SIGNAL_LOST;
		}

		if (buf[23] & (1 << 3)) {
			failsafe_status = SBUS_SIGNAL_FAILSAFE;
		}

		//	SBUS_footer=buf[24];

	}
}
/* USER CODE END 0 */

This snippet is not mine, and I've got the feeling that the translation from the values into the buffer is missing. Could that be? If so, how would that look? To keep things simple I would like to avoid DMA in the beginning.

 The while-loop is basically just the channel values 1-6 being printed to the serial terminal repeatedly.

JGiem1_0-1704717971165.png

CONCLUSION: Since the serial communication itself works, but neither gets any values nor notifies for a new frame, I think the issue lies within the SBUS -> UART part of it. Means, the Callback doesnt get executed for whatever reason. Additionally, here's my .ioc:

JGiem1_1-1704718124386.png

JGiem1_2-1704718174444.png

Iv'e tried it with 9 or 8 bits word length, but that doesnt change anything. Since FrSky is using the Inverted-SBUS, which is inverted itself (0->H, 1->L), I figured the data polarity is correct aswell as the jumper wires and config settings on the RX.

Regarding the transfer to the buffer AND/OR the Callback, what are your thoughts?

35 REPLIES 35
STea
ST Employee

Hello @JGiem.1 ,

Are you sure that 100000 is the correct Baudrate? for the UART configuration i also would recommend to have a serial analyser to get the output of the controller to make sure that it is sending the buffers.Also you can try to change the UART instance as it can be used for other purposes on your board check in there is any other hardware configuration (solder bridges and so on ).

I recommend at first to ensure that the UART communication is established and from there you can continue your development .

BR

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
Karl Yamashita
Lead III

You haven't show any code that shows you've put the UART receive in interrupt mode?

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.

Thanks for the detailed analysis where to look. Via several basic flags to my PCs serial terminal I was able to "debug" it a little via Putty. As it seems, it does receive data - just not useful one. It receives changing, seemingly random values. Now, only 1 out of 100 matches the "0x0F" flag required for SBUS decoding; when this flag is received it converts the rest of the data as expected and throws some values. It appears to me that UART is having difficulties locking into the frame at the beginning or so. Reading out the first buffer value, it often says "0x7C", which is something like 1111100. Remember that SBUS starts a frame with "0x0F" which translates to 00001111 - Maybe the signal is indeed still inverted, against my expectations. Will update.

/*USER CODE BEGIN 1 */
//...other stuff, declarations

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  HAL_UART_Receive_IT(&huart1, (uint8_t*)buf, 25);
}
/* USER CODE END 0 */

...

while (1)
  {
                  HAL_UART_Receive_IT(&huart1, (uint8_t*)buf, 25);
	  	  	  	
	  	  sprintf(msg, "FRAME AQCUIRED: %x\r\n", buf[0]);
	  	  HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 5);   }
/...SBUS FRAME DECODING            

I modified it in the meantime, based on an interrupt based approach for UART and as of right now, it seems to work, a little at least. 

Now, via several basic flags to my PCs serial terminal I was able to "debug" it a little via Putty. As it seems, it does receive data - just not useful one. It receives changing, seemingly random values. Now, only 1 out of 100 matches the "0x0F" flag required for SBUS decoding; when this flag is received it converts the rest of the data as expected and throws some values. It appears to me that UART is having difficulties locking into the frame at the beginning or so. Reading out the first buffer value, it often says "0x7C", which is something like 1111100. Remember that SBUS starts a frame with "0x0F" at buffer[0] which translates to 00001111 - Maybe the signal is indeed still inverted, against my expectations. Will try to invert manually and update.

When you call HAL_UART_Receive_IT you are expecting 25 bytes before it'll interrupt. Are you expecting exactly 25 bytes?

 

If the messages are variable length then you're going to get data that is out of sync. If the packets have at least a 1 byte delay between each message then you're better off using HAL_UARTEx_ReceiveToIdle_DMA or if you don't want to use the DMA, use HAL_UARTEx_ReceiveToIdle_IT

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.
Karl Yamashita
Lead III

Never mind. I looked up the SBUS and it is indeed 25 bytes. How often does packets get received? 

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.
Karl Yamashita
Lead III

HAL_UART_Receive_IT should be called before the while loop to initiate UART interrupt mode. Set a flag in HAL_UART_Receive_IT to indicate you have new data which you can then process it.

 

bool newDataFlag = false;
HAL_StatusTypeDef hal_status;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    newDataFlag = true;
    UART_EN_Interrupt();
}

void UART_EN_Interrupt(void)
{
    hal_status = HAL_UART_Receive_IT(&huart1, (uint8_t*)buf, 25); // re-enable interrupt
}

int main(void)
{
    UART_EN_Interrupt(); // initially start UART interrupt mode

    while(1)
    {
        if(newDataFlag)
        {
            newDataFlag = false;

            sprintf(msg, "FRAME AQCUIRED: %x\r\n", buf[0]);
	  	    HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 5);


            // process data

            // if you can't process the data fast enough then new data will overwrite your buf array. 
            // if so, then use a ring buffer to hold multiple packets.
        }

        if(hal_status == HAL_BUSY)
        {
            hal_status = HAL_OK;
            UART_EN_Interrupt();
        }
    }
}
Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.

Thank you for explaining how to use the Interrupt structure, I think I understood it a little deeper now. Out of curiosity, is there a document that explains the functionalities and especially their structures (when to incorporate what or which purpose) of the HAL library a little better than the "Description of STM32F4 HAL and low-layer drivers (UM1725)" ? Had several issues when it comes to applying stuff I read in there.

Moving on - I copied your code, inserted my conversion snippet and tried it out:

JGiem1_1-1704888041500.png

It seems like most of the time, nothing changed too much - it's still picking up mostly random values. But by moving the sticks on the transmitter, I can occasionally (and not repeatedly) provoke a "valid" frame that leads to conversion and value output. To be fair, that worked before, too.

What I don't understand is this: SBUS is originally "inverted" to 1=LOW (confirmed by two independent sources, yet another one says the opposite). FrSky apparently chose to invert SBUS again to what I figure is 1=HIGH. As per definition, UART is 1=HIGH aswell so I should not  need an inverter. It seems like the MCU does receive frames via UART, it just somehow is missing the "00001111" flag as Byte1 of the frame or buf[0] in my code. Is there anything to this I am still missing?

A variant that doesn't check for the start flag and just converts the input to its according SBUS values doesnt give me conclusive behavior:

JGiem1_2-1704891010408.png

If filtered by buf[0]=0x0F, the occasional result yields no better values.

Is it maybe signal degradation due to the baud rate being 100k and the bread-board cable I use between the Rx and the STM32 being 20cm long?

Am I totally wrong about UART and indeed do need to use an inverter?

 

 

Hello @JGiem.1 ,

can you try to set the UART Baudrate to another value and check if you get the same results on putty also i suggest you change the cables used for the connection and try to visualize the effect as i quote that "But by moving the sticks on the transmitter, I can occasionally (and not repeatedly) provoke a "valid" frame that leads to conversion and value output."so this should not be the case if every thing is set up correctly.

BR

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.