Detailed explanation of STM32 parsing SBUS signal routine

1. Introduction to SBUS signal

Recently, I was engaged in the communication and control of a project, and I used SBUS. Record my experience.


The full name of SBUS is serial-bus, which is a serial communication protocol, which is widely used in remote controllers (receivers) of model airplanes. Only one signal line can transmit up to 16 channels of data, which is more efficient and resource-saving than multi-channel PWM capture.

  1. Serial port configuration:
    100k baud rate, 8 data bits, 2 stop bits, even parity (EVEN), no flow control, 25 bytes.
  2. Protocol format: (8 bytes)
    [startbyte] [data1][data2]…[data22][flags][endbyte]
    startbyte=0x0f;
    endbyte=0x00;
    data1…data22: LSB (low bit first), corresponding to 16 channels (Ch1-ch16), each channel is 11bit (22 × 8=16 × 11); the
    flag bit indicates the communication status of the remote control. When the remote control is connected, it is 0x00 when the remote control is disconnected. 0xC0, you can take out-of-control protection by querying the flag bit.
  3. Data range
    The PWM value output by the model airplane remote control is 1000~2000, the median value is 1500, and the sbus output will be different. For example, the range of the Letie AT9S is 300~1700, the median value is 1000. I guess this is related to the remote control manufacturer.
  4. Negative logic of sbus
    This place must be very careful, hardware inverter must be added , because the signal of SBUS adopts negative logic, that is, the level is opposite. Don't try to reverse it in the software, because the software can only manipulate the data bits. (Remember the data bit 8 in the serial port configuration), you can't operate the stop bit, check bit or something! !
    It’s easy if you draw your own board, as shown in Figure
    Insert picture description here5. Data reading
    General serial debugging assistants may not have the option of 100K baud rate. We recommend a serial debugging assistant MicroLab , which can customize the serial baud rate, and others. Explore good features by yourself.

2. STM32F7 parsing SBUS signal routine

When the communication protocol is clear, the analysis is very simple. I use the Apollo F7 development board of Punctual Atom, and the other boards are the same.

(1) Serial port configuration

First, some variable declarations, which are used in the serial port uart.c

#define USART_REC_LEN  			100  	//定义最大接收字节数 200
#define RXBUFFERSIZE   			1 		//缓存大小

u8 USART1_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
u16 USART1_RX_STA = 0; //接收状态标记
u8 aRxBuffer1[RXBUFFERSIZE];		  //HAL库使用的串口接收缓冲
UART_HandleTypeDef UART1_Handler; //UART句柄

Serial port initialization function

void uart1_init(u32 bound)
{
    
    
	//UART 初始化设置
	UART1_Handler.Instance = USART1;					//USART1
	UART1_Handler.Init.BaudRate = bound;				//波特率
	UART1_Handler.Init.WordLength = UART_WORDLENGTH_9B; //字长为8位数据格式
	UART1_Handler.Init.StopBits = UART_STOPBITS_1;		//一个停止位
	UART1_Handler.Init.Parity = UART_PARITY_EVEN;		//无奇偶校验位
	UART1_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; //无硬件流控
	UART1_Handler.Init.Mode = UART_MODE_TX_RX;			//收发模式
	HAL_UART_Init(&UART1_Handler);						//HAL_UART_Init()会使能UART1

	HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer1, RXBUFFERSIZE); //该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    
    
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_Initure;
	if (huart->Instance == USART1) //如果是串口1,进行串口1 MSP初始化
	{
    
    
		__HAL_RCC_GPIOA_CLK_ENABLE();  //使能GPIOA时钟
		__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟

		GPIO_Initure.Pin = GPIO_PIN_9;			  //PA9
		GPIO_Initure.Mode = GPIO_MODE_AF_PP;	  //复用推挽输出
		GPIO_Initure.Pull = GPIO_PULLUP;		  //上拉
		GPIO_Initure.Speed = GPIO_SPEED_FAST;	  //高速
		GPIO_Initure.Alternate = GPIO_AF7_USART1; //复用为USART1
		HAL_GPIO_Init(GPIOA, &GPIO_Initure);	  //初始化PA9

		GPIO_Initure.Pin = GPIO_PIN_10;		 //PA10
		HAL_GPIO_Init(GPIOA, &GPIO_Initure); //初始化PA10

#if EN_USART1_RX
		HAL_NVIC_EnableIRQ(USART1_IRQn);		 //使能USART1中断通道
		HAL_NVIC_SetPriority(USART1_IRQn, 3, 2); //抢占优先级3,子优先级3
#endif
	}
}

There is a weird place here is that stm32 has to be set to 9 data bits and one stop bit. The data I read out according to 8 data bits and two stop bits was wrong at first, but it was normal after I changed it. Is it related to the serial port configuration inside the stm32? Who can figure it out and tell me?

(2) Serial port interrupt reception

The serial port interrupt function receives data in the interrupt function and analyzes the SBUS signal.

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    
    
	int i;
	while (huart->Instance == USART1) //如果是串口1
	{
    
    
		USART1_RX_BUF[USART1_RX_STA] = aRxBuffer1[0];
		if (USART1_RX_STA == 0 && USART1_RX_BUF[USART1_RX_STA] != 0x0F) break; //帧头不对,丢掉
		USART1_RX_STA++;
		if (USART1_RX_STA > USART_REC_LEN) USART1_RX_STA = 0;  ///接收数据错误,重新开始接收
		if (USART1_RX_BUF[0] == 0x0F && USART1_RX_BUF[24] == 0x00 && USART1_RX_STA == 25)	//接受完一帧数据
		{
    
    
			update_sbus(USART1_RX_BUF);
			for (i = 0; i<25; i++)		//清空缓存区
				USART1_RX_BUF[i] = 0;
			USART1_RX_STA = 0;
		}
		break;
	}
}

(3) Signal analysis

There is an update_sbus function in the above interrupt function, the prototype is u8 update_sbus(u8 *buf), it depends on it to parse the subs signal! ! Create a new sbus.c file and enter the following code

#include "sbus.h"

SBUS_CH_Struct SBUS_CH;

//将sbus信号转化为通道值
u8 update_sbus(u8 *buf)
{
    
    
    int i;
    if (buf[23] == 0)
    {
    
    
        SBUS_CH.ConnectState = 1;
        SBUS_CH.CH1 = ((int16_t)buf[ 1] >> 0 | ((int16_t)buf[ 2] << 8 )) & 0x07FF;
        SBUS_CH.CH2 = ((int16_t)buf[ 2] >> 3 | ((int16_t)buf[ 3] << 5 )) & 0x07FF;
        SBUS_CH.CH3 = ((int16_t)buf[ 3] >> 6 | ((int16_t)buf[ 4] << 2 ) | (int16_t)buf[ 5] << 10 ) & 0x07FF;
        SBUS_CH.CH4 = ((int16_t)buf[ 5] >> 1 | ((int16_t)buf[ 6] << 7 )) & 0x07FF;
        SBUS_CH.CH5 = ((int16_t)buf[ 6] >> 4 | ((int16_t)buf[ 7] << 4 )) & 0x07FF;
        SBUS_CH.CH6 = ((int16_t)buf[ 7] >> 7 | ((int16_t)buf[ 8] << 1 ) | (int16_t)buf[9] << 9 ) & 0x07FF;
        SBUS_CH.CH7 = ((int16_t)buf[ 9] >> 2 | ((int16_t)buf[10] << 6 )) & 0x07FF;
        SBUS_CH.CH8 = ((int16_t)buf[10] >> 5 | ((int16_t)buf[11] << 3 )) & 0x07FF;
        SBUS_CH.CH9 = ((int16_t)buf[12] << 0 | ((int16_t)buf[13] << 8 )) & 0x07FF;
        SBUS_CH.CH10 = ((int16_t)buf[13] >> 3 | ((int16_t)buf[14] << 5 )) & 0x07FF;
        SBUS_CH.CH11 = ((int16_t)buf[14] >> 6 | ((int16_t)buf[15] << 2 ) | (int16_t)buf[16] << 10 ) & 0x07FF;
        SBUS_CH.CH12 = ((int16_t)buf[16] >> 1 | ((int16_t)buf[17] << 7 )) & 0x07FF;
        SBUS_CH.CH13 = ((int16_t)buf[17] >> 4 | ((int16_t)buf[18] << 4 )) & 0x07FF;
        SBUS_CH.CH14 = ((int16_t)buf[18] >> 7 | ((int16_t)buf[19] << 1 ) | (int16_t)buf[20] << 9 ) & 0x07FF;
        SBUS_CH.CH15 = ((int16_t)buf[20] >> 2 | ((int16_t)buf[21] << 6 )) & 0x07FF;
        SBUS_CH.CH16 = ((int16_t)buf[21] >> 5 | ((int16_t)buf[22] << 3 )) & 0x07FF;
        return 1;
    }
    else 
    {
    
    
        SBUS_CH.ConnectState = 0;
        return 0;
    }
}

u16 sbus_to_pwm(u16 sbus_value)
{
    
    
    float pwm;
    pwm = (float)SBUS_TARGET_MIN + (float)(sbus_value - SBUS_RANGE_MIN) * SBUS_SCALE_FACTOR;
    //                1000                                   300              1000/1400
    if (pwm > 2000) pwm = 2000;
    if (pwm < 1000) pwm = 1000;
    return (u16)pwm;
}

A variable SBUS_CH of the SBUS_CH_Struct structure type is defined above, which is defined in sbus.h

typedef struct
{
    
    
	uint16_t CH1;//通道1数值
	uint16_t CH2;//通道2数值
	uint16_t CH3;//通道3数值
	uint16_t CH4;//通道4数值
	uint16_t CH5;//通道5数值
	uint16_t CH6;//通道6数值
    uint16_t CH7;//通道7数值
    uint16_t CH8;//通道8数值
    uint16_t CH9;//通道9数值
    uint16_t CH10;//通道10数值
    uint16_t CH11;//通道11数值
    uint16_t CH12;//通道12数值
    uint16_t CH13;//通道13数值
    uint16_t CH14;//通道14数值
    uint16_t CH15;//通道15数值
    uint16_t CH16;//通道16数值
	uint8_t ConnectState;//遥控器与接收器连接状态 0=未连接,1=正常连接
}SBUS_CH_Struct;

u16 sbus_to_pwm(u16 sbus_value)It is easy to understand, that is, to convert the sbus value into a standard 1000-2000 pwm value, because the remote control sbus value I use is 300-1700. When you use it, you can read the specific value directly through the serial port. .
In this way, 16 channels of data are read. At the same time, the status of the remote control is judged by reading the ConnectState bit, and the out-of-control protection is adopted in the main function.


The above code for parsing data is international and can be used in any occasion where the sbus protocol is used, and it can be easily transplanted to arduino, 51, and raspberry pie.

Finally, the main function is very simple, just pay attention to the initial serial port is set to 100K baud rate.

void main()
{
    
    
	/* 省略 */
	uart1_init(100000);
	/* 省略 */
}

The code of this article has been uploaded to CSDN, and it is not as good as all. It is free to download. Click to download . Welcome to exchange and discuss. Don’t forget to like it!

Guess you like

Origin blog.csdn.net/qq_30267617/article/details/108209250