STM32解析SBUS信号例程详解

26 篇文章 33 订阅

1. SBUS信号简介

最近在搞一个项目的通信和控制,用到了SBUS,记录一下心得。


SBUS全称serial-bus,是一种串口通信协议,广泛应用于航模遥控器(接收机)中。只用一根信号线就能传输多达16通道的数据,比多路PWM捕获高效且省资源。

  1. 串口配置:
    100k波特率,8位数据位,2位停止位,偶校验(EVEN),无控流,25个字节。
  2. 协议格式:(8字节)
    [startbyte] [data1][data2]…[data22][flags][endbyte]
    startbyte=0x0f;
    endbyte=0x00;
    data1…data22: LSB(低位在前),对应16个通道(ch1-ch16),每个通道11bit(22 × 8=16 × 11);
    flag位标志遥控器的通讯状态,我使用的乐迪AT9S在遥控器通上的时候是0x00,断开的时候是0xC0,可以通过查询flag位来采取失控保护。
  3. 数据范围
    航模遥控器输出的PWM值是1000~2000,中值为1500,sbus输出的会不一样,例如乐迪AT9S的范围为300 ~ 1700,中值1000,这个我估计跟遥控器厂家有关。
  4. sbus的负逻辑
    这个地方一定要万分注意,必须加硬件反相器,因为SBUS的信号是采用的负逻辑,也就是电平相反,不要试图在软件里面取反,因为软件里面只能操作数据位(记得串口配置里面的数据位8么),你是操作不了停止位、校验位啥的!!
    如果是自己画板子也很简单,如图所示
    在这里插入图片描述5. 数据读取
    一般的串口调试助手可能没有100K波特率的选项,推荐一个串口调试助手MicroLab,可以自定义串口波特率,还有其他好功能自己探索叭。

2. STM32F7解析SBUS信号例程

清楚了通信协议,解析就很简单了。我使用的是正点原子的阿波罗F7开发板,其他的板子是一样的。

(1) 串口配置

首先一些变量声明,串口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句柄

串口初始化函数

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
	}
}

这里有个诡异的地方就是stm32要设置成9个数据位,一个停止位,我一开始按照8个数据位、两个停止位读出来的数据是错的,后来改了之后才正常了。是不是和stm32内部的串口配置有关,哪位大神弄明白了告诉我哈。

(2) 串口中断接收

串口中断函数,在中断函数里面接收数据,进行SBUS信号解析。

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) 信号解析

上面中断函数里面有一个update_sbus函数,原型为u8 update_sbus(u8 *buf),解析subs信号全靠它了!!新建一个sbus.c文件,输入如下代码

#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;
}

上面定义了一个SBUS_CH_Struct 结构体类型的变量SBUS_CH,该结构体在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)很好理解了,就是把sbus的值转化为标准的1000-2000的pwm值,因为我用的遥控器sbus值是300-1700,大家用的时候具体数值到时候可以通过串口直接读出来看看。
这样就读出了16个通道的数据啦。同时通过读取ConnectState位判断遥控器的状态,在主函数中采取失控保护。


上面这段解析数据的代码是国际通用的,可以用在任何使用sbus协议的场合,可以很方便的移植到arduino、51、树莓派上面。

最后main函数里面就很简单了,只注意初始化串口设置为100K波特率。

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

本文代码已上传至CSDN,独乐乐不如众乐乐,提供免费下载,点击下载,欢迎交流讨论。别忘了点个赞哦!

  • 60
    点赞
  • 361
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论
SBUS是一种串行总线通信协议,用于无人机和遥控器之间的数字信号传输。SBUS是Futaba公司开发的一种数字通信协议,可以通过单根信号线传输16个通道的数据,同时还可以传输其他的控制信息。 STM32是一种单片机,可以通过编程实现对SBUS信号解析。以下是STM32解析SBUS的步骤: 1. 配置串口 首先需要选择一个可用的串口,并将其配置为接收模式。在STM32中,可以使用USART或UART模块实现串口通信。需要设置串口的波特率、数据位数、停止位数和校验位等参数。 2. 接收数据 配置好串口后,可以通过读取串口接收缓冲区中的数据来接收SBUS信号SBUS信号以帧的形式传输,每一帧的长度为25个字节。读取串口缓冲区中的数据,并判断接收到的数据是否为完整的一帧,如果是,则进行下一步处理。 3. 解析数据 接收到完整的一帧数据后,需要对其中的数据进行解析SBUS信号的第一字节为0x0F,用于标识这是一帧SBUS数据。接下来的16个字节分别对应16个通道的数据,每个通道的数据占用11位,最高位为标识位。还有两个字节分别用于标识数字信号的状态和传输错误的计数。 4. 处理数据 解析出每个通道的数据后,可以根据需要进行相应的处理,比如将数据转换为实际控制量,或者存储到数组中以备后续使用。 5. 发送数据 如果需要将处理后的数据发送到其他设备,则需要将数据通过串口发送出去。发送数据的方法与接收数据的方法类似,只是需要将数据写入串口发送缓冲区中。 以上就是STM32解析SBUS信号的基本步骤,需要对串口通信和数据解析有一定的了解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何为其然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值