
航模遥控器
-
2023年2月25日发(作者:小学英语教学案例)STM32解析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,这个我估计跟遥控器⼚家有关。
的负逻辑
这个地⽅⼀定要万分注意,必须加硬件反相器,因为SBUS的信号是采⽤的负逻辑,也就是电平相反,不要试图在软件⾥⾯取反,因
为软件⾥⾯只能操作数据位(记得串⼝配置⾥⾯的数据位8么),你是操作不了停⽌位、校验位啥的!!
如果是⾃⼰画板⼦也很简单,如图所⽰
5.数据读取
⼀般的串⼝调试助⼿可能没有100K波特率的选项,推荐⼀个串⼝调试助⼿,可以⾃定义串⼝波特率,还有其他好功能⾃⼰探索叭。
32F7解析SBUS信号例程
清楚了通信协议,解析就很简单了。我使⽤的是正点原⼦的阿波罗F7开发板,其他的板⼦是⼀样的。
(1)串⼝配置
⾸先⼀些变量声明,串⼝uart.c⾥⽤到的
#defineUSART_REC_LEN100//定义最⼤接收字节数200
#defineRXBUFFERSIZE1//缓存⼤⼩
u8USART1_RX_BUF[USART_REC_LEN];//接收缓冲,最⼤USART_REC_LEN个字节.
u16USART1_RX_STA=0;//接收状态标记
u8aRxBuffer1[RXBUFFERSIZE];//HAL库使⽤的串⼝接收缓冲
UART_HandleTypeDefUART1_Handler;//UART句柄
串⼝初始化函数
voiduart1_init(u32bound)
{
//UART初始化设置
UART1_ce=USART1;//USART1
UART1_te=bound;//波特率
UART1_ngth=UART_WORDLENGTH_9B;//字长为8位数据格式
UART1_ts=UART_STOPBITS_1;//⼀个停⽌位
UART1_=UART_PARITY_EVEN;//⽆奇偶校验位
UART1_Ctl=UART_HWCONTROL_NONE;//⽆硬件流控
UART1_=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,并且设置接收缓冲以及
接收缓冲接收最⼤数据量
}
voidHAL_UART_MspInit(UART_HandleTypeDef*huart)
{
//GPIO端⼝设置
GPIO_InitTypeDefGPIO_Initure;
if(huart->Instance==USART1)//如果是串⼝1,进⾏串⼝1MSP初始化
{
__HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE();//使能USART1时钟
GPIO_=GPIO_PIN_9;//PA9
GPIO_=GPIO_MODE_AF_PP;//复⽤推挽输出
GPIO_=GPIO_PULLUP;//上拉
GPIO_=GPIO_SPEED_FAST;//⾼速
GPIO_ate=GPIO_AF7_USART1;//复⽤为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure);//初始化PA9
GPIO_=GPIO_PIN_10;//PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure);//初始化PA10
#ifEN_USART1_RX
HAL_NVIC_EnableIRQ(USART1_IRQn);//使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,3,2);//抢占优先级3,⼦优先级3
#endif
}
}
这⾥有个诡异的地⽅就是stm32要设置成9个数据位,⼀个停⽌位,我⼀开始按照8个数据位、两个停⽌位读出来的数据是错的,后来改了
之后才正常了。是不是和stm32内部的串⼝配置有关,哪位⼤神弄明⽩了告诉我哈。
(2)串⼝中断接收
串⼝中断函数,在中断函数⾥⾯接收数据,进⾏SBUS信号解析。
voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{
inti;
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函数,原型为u8update_sbus(u8*buf),解析subs信号全靠它了!!新建⼀个sbus.c⽂件,输⼊如下
代码
#include"sbus.h"
SBUS_CH_StructSBUS_CH;
//将sbus信号转化为通道值
u8update_sbus(u8*buf)
{
inti;
if(buf[23]==0)
{
SBUS_tState=1;
SBUS_1=((int16_t)buf[1]>>0|((int16_t)buf[2]<<8))&0x07FF;
SBUS_2=((int16_t)buf[2]>>3|((int16_t)buf[3]<<5))&0x07FF;
SBUS_3=((int16_t)buf[3]>>6|((int16_t)buf[4]<<2)|(int16_t)buf[5]<<10)&0x07FF;
SBUS_4=((int16_t)buf[5]>>1|((int16_t)buf[6]<<7))&0x07FF;
SBUS_5=((int16_t)buf[6]>>4|((int16_t)buf[7]<<4))&0x07FF;
SBUS_6=((int16_t)buf[7]>>7|((int16_t)buf[8]<<1)|(int16_t)buf[9]<<9)&0x07FF;
SBUS_7=((int16_t)buf[9]>>2|((int16_t)buf[10]<<6))&0x07FF;
SBUS_8=((int16_t)buf[10]>>5|((int16_t)buf[11]<<3))&0x07FF;
SBUS_9=((int16_t)buf[12]<<0|((int16_t)buf[13]<<8))&0x07FF;
SBUS_10=((int16_t)buf[13]>>3|((int16_t)buf[14]<<5))&0x07FF;
SBUS_11=((int16_t)buf[14]>>6|((int16_t)buf[15]<<2)|(int16_t)buf[16]<<10)&0x07FF;
SBUS_12=((int16_t)buf[16]>>1|((int16_t)buf[17]<<7))&0x07FF;
SBUS_13=((int16_t)buf[17]>>4|((int16_t)buf[18]<<4))&0x07FF;
SBUS_14=((int16_t)buf[18]>>7|((int16_t)buf[19]<<1)|(int16_t)buf[20]<<9)&0x07FF;
SBUS_15=((int16_t)buf[20]>>2|((int16_t)buf[21]<<6))&0x07FF;
SBUS_16=((int16_t)buf[21]>>5|((int16_t)buf[22]<<3))&0x07FF;
return1;
}
else
{
SBUS_tState=0;
return0;
}
}
u16sbus_to_pwm(u16sbus_value)
{
floatpwm;
pwm=(float)SBUS_TARGET_MIN+(float)(sbus_value-SBUS_RANGE_MIN)*SBUS_SCALE_FACTOR;
//1/1400
if(pwm>2000)pwm=2000;
if(pwm<1000)pwm=1000;
return(u16)pwm;
}
上⾯定义了⼀个SBUS_CH_Struct结构体类型的变量SBUS_CH,该结构体在sbus.h中定义
typedefstruct
{
uint16_tCH1;//通道1数值
uint16_tCH2;//通道2数值
uint16_tCH3;//通道3数值
uint16_tCH4;//通道4数值
uint16_tCH5;//通道5数值
uint16_tCH6;//通道6数值
uint16_tCH7;//通道7数值
uint16_tCH8;//通道8数值
uint16_tCH9;//通道9数值
uint16_tCH10;//通道10数值
uint16_tCH11;//通道11数值
uint16_tCH12;//通道12数值
uint16_tCH13;//通道13数值
uint16_tCH14;//通道14数值
uint16_tCH15;//通道15数值
uint16_tCH16;//通道16数值
uint8_tConnectState;//遥控器与接收器连接状态0=未连接,1=正常连接
}SBUS_CH_Struct;
u16sbus_to_pwm(u16sbus_value)很好理解了,就是把sbus的值转化为标准的1000-2000的pwm值,因为我⽤的遥控器sbus值是300-
1700,⼤家⽤的时候具体数值到时候可以通过串⼝直接读出来看看。
这样就读出了16个通道的数据啦。同时通过读取ConnectState位判断遥控器的状态,在主函数中采取失控保护。
上⾯这段解析数据的代码是国际通⽤的,可以⽤在任何使⽤sbus协议的场合,可以很⽅便的移植到arduino、51、树莓派上⾯。
最后main函数⾥⾯就很简单了,只注意初始化串⼝设置为100K波特率。
voidmain()
{
/*省略*/
uart1_init(100000);
/*省略*/
}
本⽂代码已上传⾄CSDN,独乐乐不如众乐乐,提供免费下载,,欢迎交流讨论。别忘了点个赞哦!