Skip to main content
Graduate II
January 8, 2024
Question

UART / SBUS Data Aqcuisition

  • January 8, 2024
  • 7 replies
  • 21256 views

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?

    This topic has been closed for replies.

    7 replies

    ST Employee
    January 9, 2024

    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

    JGiem.1Author
    Graduate II
    January 9, 2024

    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.

    Graduate II
    January 9, 2024

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

    JGiem.1Author
    Graduate II
    January 9, 2024
    /*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.

    Graduate II
    January 10, 2024

    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

    Graduate II
    January 10, 2024

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

    Graduate II
    January 10, 2024

    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();
     }
     }
    }
    
    JGiem.1Author
    Graduate II
    January 10, 2024

    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?

     

     

    ST Employee
    January 10, 2024

    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

    Visitor II
    January 19, 2024

    I have been experiencing a similar issue with a different receiver.

    I think that you are receiving an inverted signal.

    I suggest changing the data visualization to bits for a better understanding of the problem. In my case, I received consistent buffer data that looked inverted, but some channels where overlaping.

    danielscm_1-1705670319826.png

    Inverting the bits by software also resulted in incorrect data.

    danielscm_2-1705670348858.png

    The processor is taking the wrong bits as header, parity, and stop. Moving the joysticks on the remote controller changes these bits, causing the data stream to shift and disrupting the order of the repetitive patterns.

    If you get something similar, you will need an external hardware inverter. A simple transistor will be enough.

    JGiem.1Author
    Graduate II
    January 31, 2024

    So today I had the time to try out the inverted signal. Buckle up.

    I am inverting the signal with a p-ch MOSFET (IRF5305) using this example and verified it working as expected by testing it with an LED (Gate HIGH = Led LOW; Gate LOW = Led HIGH; 3.3V Logic Level). The gate gets discharged via 10k to GND, but leaving it away made essentionally no difference.

    I tried both, inverted and uninverted input from the receiver to the F446.

    @Karl YamashitaThe appearance of patterns that seem to shift stopped when reducing the RX buffer size to 24 instead of 25 bits. Note that in most SBUS tutorials that seem to work it is being worked with 25 bytes as frame size. But the shifting disappeared and you can now clearly see when you move a stick on the Tx that the approximate index of the frame (where the channel value is stored) differs from the one before. Meaning, moving the Throttle input (as CH1), it changes the frame from the beginning whereas when I am moving Aileron (on CH4) the difference in frames is only noticeable further to the end of the 24 byte hex output. I want to stress that while this looks "more right" it is actually against what can be read about SBUS (25 byte frame size). Could the fact that one byte is "missing" be an effect caused by some intricacy in the way HAL UART works?

    Working with 8 bits incl. parity results in said "clean" appearance, whereas with 9 bits incl. parity you have it shifting again - that was more of a "why not try it"-idea but kinda confirmed the apparent frame length of 24 not 25 byte.

    9b. incl p. and 25byte frame size resulted in the shift too.

    Inverting the signal did change the output in its values, but it still only occasionally recognizes the starting flag (0x0F or dec 15), preferably with throttle (CH1) in the lowest position and the rest centered. The effects I first described above could be observed wether inverted or not.

    Just an example of how the output looks like:

    JGiem1_3-1706739250597.png

    Taken while moving a stick.

     

    @danielscmThanks for your ideas. As described above I went to work and unfortunately did not yield the same results as you seem to have gotten - may I ask what receiver you are using, and wether it was made by FrSky (with their own little interpretation of SBUS)? Inverting the signal seems to not make huge difference, it still can't catch the start flag and in the rare event that it does, the given values for each channel are not realistic (given that ~1024 is the middle value for 3 of 4 actively monitored channels and that value never remotely appeared). Unfortunately I will yet have to read into making the bare binary numbers visible. Will update.

     

    @STeaRegarding the logic analyzer, I was having some compatibility issues that need fixing as to be able to use it properly. I was unknowingly locked down to 20kHz which is way too slow for a 100k baud signal. Will update.

    Last but not least I want to tell you about two more phenomena I observed that I can't make anything out of.

    1) After turning off the TX the receiver is entering failsafe mode where it is instructed to pass on "no pulses"; It feeds this:

    JGiem1_0-1706732495797.png

    It's a pattern that again appears shifted again regardless of 24byte-frame size, with the 7c in the end being a constant. Remember this for 2)... Unfortunately, the manual does not go into detail as to how the "no pulses mode" behaves. Out of a forum of OPENTX (the transmitters firmware), I get something along the lines of "No Pulse: on loss of signal the receiver produces no pulses on any channel. To use this type, select it in the menu and wait 9 seconds for the failsafe to take effect." - whatever that means for the SBUS output.

    2) When disconnecting the RX from the F446's input (D2) something remarkable happens:

    JGiem1_1-1706732579501.png

    Now in order to make anything out of this, you probably need my current version of the code:

     

     

     

    /* USER CODE BEGIN 0 */
    uint16_t failsafe_status;
    uint8_t buf[24];
    uint16_t CH[16];
    int length = 0;
    uint16_t USB_Send_Data[]={0};
    char msg[25];
    bool valid = false;
    bool newDataFlag = false;
    HAL_StatusTypeDef hal_status;
    
    void UART_EN_Interrupt(void)
    {
     hal_status = HAL_UART_Receive_IT(&huart1, (uint8_t*)buf, sizeof(buf)); // re-enable interrupt
    }
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
     newDataFlag = true;
     UART_EN_Interrupt();
    }
    /* USER CODE END 0 */
    /**
     * @brief The application entry point.
     * @retval int
     */
    int main(void)
    {
     /* USER CODE BEGIN 1 */
    	 //uint16_t raw;
    	UART_EN_Interrupt(); // initially start UART interrupt mode
     /* USER CODE END 1 */
     /* MCU Configuration--------------------------------------------------------*/
     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
     HAL_Init();
     /* USER CODE BEGIN Init */
     /* USER CODE END Init */
    
     /* Configure the system clock */
     SystemClock_Config();
    
     /* USER CODE BEGIN SysInit */
    
     /* USER CODE END SysInit */
    
     /* Initialize all configured peripherals */
     MX_GPIO_Init();
     MX_USART2_UART_Init();
     MX_USART1_UART_Init();
     /* USER CODE BEGIN 2 */
     /* USER CODE END 2 */
     /* Infinite loop */
     /* USER CODE BEGIN WHILE */
     while (1)
     {
    	 if(newDataFlag)
    	 {
    	 newDataFlag = false;
    	 valid = true;
    	 //sprintf(msg, "FRAME AQCUIRED: %x\r\n", buf[0]);
    	 //HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 5);
    	 }
    
    	 if(hal_status == HAL_BUSY)
    	 {
    	 hal_status = HAL_OK;
    	 UART_EN_Interrupt();
    	 }
    //PRINTING EACH ACQUIRED FRAME TO THE SERIAL TERMINAL IN SEPERATE HEX VALUES
    	 for(int i = 0; i < 25; i++){
    	 sprintf(msg, "%x ", buf[i]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    	 if(i==24){
    	 sprintf(msg, "\n\r ");
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    	 }
    	 }
    	 //HAL_Delay(8);
    //DECODING THE SBUS FRAME IF START FLAG (0x0f) IS FOUND 
    	 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;
    //PRINT EACH CONVERTED CHANNEL VALUE IF START FLAG WAS FOUND 
    	 sprintf(msg, "%d ", buf[0]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
     sprintf(msg, "%d ", CH[1]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    	 sprintf(msg, "%d ", CH[2]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    	 sprintf(msg, "%d ", CH[3]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    	 sprintf(msg, "%d ", CH[4]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    	 sprintf(msg, "%d ", CH[5]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    //SO ON AND SO FORTH	 	 			 
    	 sprintf(msg, "%d ", CH[23]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    	 sprintf(msg, "%d ", CH[24]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    	 sprintf(msg, "%d \r\n", CH[25]);
    	 HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
    //MAKE IT NOTICEABLE	 
     HAL_Delay(10000);
    	 }
    
    .
    .
    .
     }
    }

     

     

     

     

    Now as far as I'm aware, the UART is working with an Interrupt to fill the buffer so it probably wont be interfering with the transmitting to the serial terminal (please correct me if Im mistaken).

    First things first: Despite the frame now theoretically only consisting of "0" due to no signal, it yields the 7c (124 in dec) in the end of it - any ideas why that is?

    But most importantly: Note the slow and not - as I would have expected it - instantaneous changing to 0 of the frame's bytes after disconnecting! (Discharge resistor applied to the MOSFETs Gate doesnt change anything).

    Is there a reason for that in the code or could that be residual signal somewhere along the wire (can't be a real thing)? This is becoming way trickier than I thought it could get. Is it a timing issue in my communication? Any ideas as to why this happens?

    Lastly, in the Receivers manual, i found this snippet of info:

    JGiem1_2-1706733968136.png

    Along with an email address of the technical support, which by now I am frustrated enough to contact for further info on the SBUS output. Wether this is changing anything or not is beyond me right now.

    FURTHER INFO ON SBUS: https://www.youtube.com/watch?v=IqLUHj7nJhI

    Wiki-page in video above: https://github.com/uzh-rpg/rpg_quadrotor_control/wiki/SBUS-Protocol

    (not promoting anything, just found these helpful)

    Maybe I'll take a look at the receiver with an osci at work next week.

    HUGE THANK YOU FOR STICKING WITH ME! "WINNER" CAN EXPECT COFFEE :)

    ST Employee
    February 1, 2024

    Hello @JGiem.1 ,

    i have some suggestion you can try:

    1. "Note the slow and not - as I would have expected it - instantaneous changing to 0 of the frame's bytes after disconnecting!" to flush the UART buffers using fflash (0); after each print to see the affect on the displayed results on the terminal because this is apparently a sync issue delaying the instant clearing of the buffers after unplugging the RX pin 
    2. Move the 
      UART_EN_Interrupt();​
      after 
      HAL_Init();​
    3. try a different UART instance (switch UART1/2) and see the effect of it on your following comment"1) After turning off the TX the receiver is entering failsafe mode where it is instructed to pass on "no pulses""

    BR

    Explorer II
    February 6, 2024

    I have had the same problem of trying to decode SBUS the past couple days and I have managed to get it to work (mostly, more about that at the end of my post).

    First let me start by saying that SBUS is always inverted, no matter if you have a Futaba or FrSky receiver. So you will always need an inverter for the F1 or F4 series. But building one from two resistors and an arbitrary NPN transistor is very easy and you should be able to find the parts almost anywhere.

    There are some receivers where you can access the non-inverted signal by opening the rx and tap the signal before it gets inverted. The only exception might be some truly ancient receivers that I am forgetting right now, but for all others that should be true. 

    That said, your screen grabs are actually showing a non-inverted signal, which is a little bit odd... What receiver are you using and have you tried a different one yet? 

     

    Now down to business of decoding the SBUS signal. For the sake of completion for people finding this post later I will include absolutely everything, because I myself am just starting with the STM uCs and many things are not as obvious as we think.

    First you want to start by setting up the USART (I used USART2) with a baud rate of 100000 Bits/s, word length of 9 Bits (inc. Parity) , even parity bit and 2 stop bits. Additionally don't forget to turn on the global interrupt in the NVIC settings, been there done that and wondered why it didn't work... 

    pic1.png

     

    pic2.png

     

    I also set up USART1 to print the frames and processed data over serial.

     

    Now the code:

    Since there is no boolean type in C you need to define it yourself. Additionally you need to include stdio.h and string.h if you want to use the same commands I used to print over serial.

    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include <stdio.h>
    #include <string.h>
    /* USER CODE END Includes */
    
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
    typedef enum { false, true } bool;
    /* USER CODE END PTD */

     Then the variables & functions as described in posts in this thread. A buffer length of 25 bytes worked for me, but I'm basing this only on what I've read about SBUS by 3rd parties and not the actual protocol definition or my own investigation.

    /* USER CODE BEGIN 0 */
    char msg[25]; //used for debugging: for printing the frames & processed data over serial
    
    // variables used for the processing of the sbus data
    uint16_t sbusChannels[16];
    #define sbusChannelsLength 16
    bool lost_frame;
    bool failsafe;
    bool ch17, ch18;
    
    uint8_t buffer[25];
    #define bufferLength 25
    
    bool newDataFlag = false;
    HAL_StatusTypeDef hal_status;
    
    void UART_EN_Interrupt(void)
    {
    	hal_status = HAL_UART_Receive_IT(&huart2, (uint8_t*)buffer, bufferLength);
    }
    
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    	newDataFlag = true;
    	UART_EN_Interrupt();
    }
    
    /* USER CODE END 0 */

    Start the interrupt mode before the infinite while loop.

    /* USER CODE BEGIN 2 */
     UART_EN_Interrupt(); //initially start UART interrupt mode
    /* USER CODE END 2 */

    After that you can do the data processing etc.

    I've taken the processing from Bolder Flight Systems SBUS library.

     /* Infinite loop */
     /* USER CODE BEGIN WHILE */
     while (1)
     {
    	 if(newDataFlag)
    	 	{
    	 		newDataFlag = false;
    
    	 		//print received frames over UART 1
    	 		uint8_t index1 ;
    	 		for(index1 = 0 ; index1 < bufferLength; index1 ++)
    	 		{
    	 		sprintf(msg, "FRAME %u: %X\r\n", index1, buffer[index1]);
    	 		HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 5);
    	 		}
    	 		sprintf(msg, "\n");
    	 		HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 5);
    
    	 		// process data
    			sbusChannels[0] = (uint16_t) ((buffer[1] | buffer[2] <<8) & 0x07FF);
    			sbusChannels[1] = (uint16_t) ((buffer[2]>>3 | buffer[3] <<5) & 0x07FF);
    			sbusChannels[2] = (uint16_t) ((buffer[3]>>6 | buffer[4] <<2 | buffer[5]<<10) & 0x07FF);
    			sbusChannels[3] = (uint16_t) ((buffer[5]>>1 | buffer[6] <<7) & 0x07FF);
    			sbusChannels[4] = (uint16_t) ((buffer[6]>>4 | buffer[7] <<4) & 0x07FF);
    			sbusChannels[5] = (uint16_t) ((buffer[7]>>7 | buffer[8] <<1 | buffer[9]<<9) & 0x07FF);
    			sbusChannels[6] = (uint16_t) ((buffer[9]>>2 | buffer[10] <<6) & 0x07FF);
    			sbusChannels[7] = (uint16_t) ((buffer[10]>>5 | buffer[11]<<3) & 0x07FF);
    			sbusChannels[8] = (uint16_t) ((buffer[12] | buffer[13]<<8) & 0x07FF);
    			sbusChannels[9] = (uint16_t) ((buffer[13]>>3| buffer[14]<<5) & 0x07FF);
    			sbusChannels[10] = (uint16_t) ((buffer[14]>>6| buffer[15]<<2 | buffer[16]<<10) & 0x07FF);
    			sbusChannels[11] = (uint16_t) ((buffer[16]>>1| buffer[17]<<7) & 0x07FF);
    			sbusChannels[12] = (uint16_t) ((buffer[17]>>4| buffer[18]<<4) & 0x07FF);
    			sbusChannels[13] = (uint16_t) ((buffer[18]>>7| buffer[19]<<1 | buffer[20]<<9) & 0x07FF);
    			sbusChannels[14] = (uint16_t) ((buffer[20]>>2| buffer[21]<<6) & 0x07FF);
    			sbusChannels[15] = (uint16_t) ((buffer[21]>>5| buffer[22]<<3) & 0x07FF);
    			ch17 = buffer[23] & 0x01;
    			ch18 = buffer[23] & 0x02;
    			lost_frame = buffer[23] & 0x04;
    			failsafe = buffer[23] & 0x08;
    
    			//print processsed data over UART 1
    			uint8_t index2 ;
    			for(index2 = 0 ; index2 < sbusChannelsLength; index2 ++)
    			{
    			sprintf(msg, "CH %u: %u\r\n", (index2 + 1), sbusChannels[index2]);
    			HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 5);
    			}
    			if (ch17)
    			{
    			 sprintf(msg, "ch17: true\n");
    			}
    			else
    			{
    			 sprintf(msg, "ch17: false\n");
    			}
    			HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 5);
    			if (ch18)
    			{
    			 sprintf(msg, "ch18: true\n");
    			}
    			else
    			{
    			 sprintf(msg, "ch18: false\n");
    			}
    			HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 5);
    			if (lost_frame)
    			{
    			 sprintf(msg, "lost frame: true\n");
    			}
    			else
    			{
    			 sprintf(msg, "lost frame: false\n");
    			}
    			HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 5);
    			if (failsafe)
    			{
    			 sprintf(msg, "failsafe: true\n");
    			}
    			else
    			{
    			 sprintf(msg, "failsafe: false\n");
    			}
    			HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 5);
    
    			sprintf(msg, "\n");
    			HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 5);
    
    			//wait 500 ms
    			HAL_Delay(500);
    
    	 	}
    
    	 	if(hal_status == HAL_BUSY)
    	 	{
    	 		hal_status = HAL_OK;
    	 		UART_EN_Interrupt();
    	 	}
    
     /* USER CODE END WHILE */
    
     /* USER CODE BEGIN 3 */
     }
     /* USER CODE END 3 */

    Bolder Flight Systems also mention that "FrSky receivers will output a range of 172 - 1811 with channels set to a range of -100% to +100%.", which is exactly what I'm getting with a FrSky XM. I haven't tried any other receivers yet, but they should all work the same.

     

    Now, as mentioned earlier I'm just starting out with the STM uCs and therefore don't really know what I'm doing. There is one problem, to which I haven't figured out the solution yet:

    Depending on when the uC starts up the first byte received might not be the starting byte of the SBUS transition. You can trigger this by pressing the reset button a couple times; at some point you'll run into the issue.

    The question is how to solve this. I need a way to basically to reset/reframe when the interrupt gets triggered whenever the first frame is not the SBUS header (0x0F). I'd really appreciate if somebody could guide me in the right direction!

     

    Cheers,

    Stefan

    JGiem.1Author
    Graduate II
    February 6, 2024

    Thanks for your thoughts. I will try your version by myself later.

    In the meantime, to your synchronizing issue - how I understood it. Your issue is to recognize when a frame starts. In my version of the code, the values only get decoded if the first buffer value is the SBUS-specific starting flag "0x0F". All other values would hint towards the frame being incomplete or containing errors. To make it even safer, you can check for the two "stop" flags in the end. I solved that by a simple if-addition before decoding. Since the signal is continously streamed, you should end up synchronized eventually.

    Explorer II
    February 6, 2024

    Yes and no,

    my issue is that the 25 bytes that get received might not necessarily be one whole SBUS packet but the second half of one and then the first half of the next, if you know what I mean.

    Simply only decoding valid packets does not solve the problem since there will never be any valid packets if you happen to be unlucky with the timing while booting up the uC. It will not synchronise automatically since you only ever receive the 25 bytes specified in interrupt.

    EDIT:

    The only fix I could think of right now would be to increase the buffer size to double the length aka 50 and then search for the SBUS packet every time. But that does not seem like the right solution...

    EDIT end

    Visitor II
    February 14, 2025

    Hi, I encountered this problem and can't find a solution on my own. Did you manage to resolve it?