1. SBUS信号简介
SBUS全称serial-bus,是一种串口通信协议,广泛应用于航模遥控器(接收机)中。只用一根信号线就能传输多达16通道的数据,比多路PWM捕获高效且省资源。
1.硬件标准
- TTL电平即3.3V。 使用负逻辑,即低电平为“1”,高电平为“0”。
- 波特率:100000(100k)
- 负逻辑必须加硬件反相器
硬件反相器电路如下:
2.软件标准
1.串口配置:
100k波特率,8位数据位(stm32-选择9位),2位停止位,偶校验(EVEN),无控流,25个字节。
2.协议格式:
接收器和遥控器支持多少个通道就有多少个
[startbyte] | [data1] | [data2] | … | [data10] | … | [flags] | [endbyte] |
---|---|---|---|---|---|---|---|
0xf0(起始字节) | Flags | 0x00 |
每个通道数据占 11bit (低位在前一字节、高位在后一字节)
3.数据范围
航模遥控器输出的PWM值是1000~2000,中值为1500,sbus输出的会不一样,每个遥控器厂家各有不同。
注意Sbus 速率比较快,且满速传输。容易产生串口ORE错误,例程中有解决方法
参考这篇文章:STM32常见错误-持续更新
4、间隔问题
它分两种模式(高速模式和普通模式)
高速模式:每隔7ms一帧数据,因为两帧的间隔只有超过3ms,才会被接受;而根据波特率计算一下,发送25字节需要的时间+3~4ms=7ms
普通模式:每隔14ms一帧数
据;
2. STM32F4_Sbus (DMA+串口 )
(1) 串口配置
void uart4_sbus_init(void)//串口2初始化
{
NVIC_InitTypeDef NVIC_InitStructure ;//定义中断结构体
GPIO_InitTypeDef GPIO_InitStructure;//定义IO初始化结构体
USART_InitTypeDef USART_InitStructure;//定义串口结构体
//设置IO口和串口4时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);//打开串口对应的外设时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_UART4); //GPIOC10复用为UART4
GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_UART4); //GPIOC10复用为UART4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; //管脚指定
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //管脚模式:输出口
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //类型:推挽模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉下拉设置
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化
// 1 初始化串口及参数指定
USART_InitStructure.USART_WordLength = USART_WordLength_9b;//9位数据位
USART_InitStructure.USART_StopBits = USART_StopBits_2;//两位停止位
USART_InitStructure.USART_Parity = USART_Parity_Even;//偶校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无控流
USART_InitStructure.USART_Mode = USART_Mode_Rx;
USART_InitStructure.USART_BaudRate = 100000;
USART_Init(UART4, &USART_InitStructure);
// 2 配置中断优先级并使能
NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn; //通道设置为串口中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //中断占先等级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //中断响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开中断
NVIC_Init(&NVIC_InitStructure);
// 3 配置进入中断方式
USART_ITConfig(UART4, USART_IT_IDLE, ENABLE); //接收到一帧数据就进入中断
USART_ITConfig(UART4, USART_IT_ERR, ENABLE);
// 4 使能串口的DMA接收
USART_DMACmd(UART4, USART_DMAReq_Rx, ENABLE);
// 5 开启串口
USART_Cmd(UART4, ENABLE);
}
(2) 串口中断接收
串口中断函数,在中断函数里面接收数据,进行SBUS信号解析。
void UART4_IRQHandler(void) //串口2中断服务函数
{
if (USART_GetFlagStatus(UART4, USART_FLAG_FE) != RESET)
{
watch_usart4_fe_cnt++;
USART_ReceiveData(UART4);
USART_ClearFlag(UART4, USART_FLAG_FE);
}
if(USART_GetFlagStatus(UART4, USART_FLAG_PE) != RESET)
{
watch_usart4_pe_cnt++;
USART_ReceiveData(UART4);
USART_ClearFlag(UART4, USART_FLAG_PE);
}
UART4_IRQHandler_cnt++;
// USART_FLAG_ORE
if(USART_GetFlagStatus(UART4, USART_FLAG_ORE) != RESET)
{
watch_usart4_ore_cnt++;
USART_ReceiveData(UART4);
}
int i = 0;
if(USART_GetITStatus(UART4, USART_IT_IDLE) != RESET) //接收中断,若为1则表示已经接收到数据
{
//先读SR,再读DR,清除IDLE中断标志
i = UART4->SR; //读SR寄存器
i = UART4->DR;
DMA_Cmd(DMA1_Stream2, DISABLE);
rx_len = sbus_data_len - DMA_GetCurrDataCounter(DMA1_Stream2);
if(rx_len != 0 && rec_sbus_data[0] == 0x0f) //判断数据长度并丢掉无用数据
{
for(i = 0; i < rx_len; i++)
{
sbus_data[i] = 0;
}
for(i = 0; i < rx_len; i++)
{
get_sbus_data(rec_sbus_data);//保存接收到的数据
}
for(i = 0; i < rx_len; i++) //将数据缓存区清零
{
rec_sbus_data[i] = 0; //清空缓存
}
}
DMA_ClearFlag(DMA1_Stream2, DMA_FLAG_TCIF2 );
DMA_SetCurrDataCounter(DMA1_Stream2, sbus_data_len); //设置DMA接收单元的长度
DMA_Cmd(DMA1_Stream2, ENABLE); //打开DMA
}
}
(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;
}
}
上面定义了一个SBUS_CH_Struct 结构体类型的变量SBUS_CH,该结构体在sbus.h中定义文章来源:https://www.toymoban.com/news/detail-516886.html
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;
(4) DMA初始化
void DMA1_SBUS_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;//定义DMA结构体
//启动DMA时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);//DMA通道配置
//DMA通道配置
DMA_DeInit(DMA1_Stream2);
while (DMA_GetCmdStatus(DMA1_Stream2) != DISABLE);
DMA_InitStructure.DMA_Channel = DMA_Channel_4;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&UART4->DR);//外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)rec_sbus_data;//内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//dma传输方向
DMA_InitStructure.DMA_BufferSize = sbus_data_len;//设置DMA在传输时缓冲区的长度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA的外设一个外设
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//设置DMA的内存递增模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据字长
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//内存数据字长
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//设置DMA的传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//设置DMA的优先级别
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
//配置DMA2的通道
DMA_Init(DMA1_Stream2, &DMA_InitStructure);
//使能DMA通道
DMA_Cmd(DMA1_Stream2, ENABLE);
}
参考:振华OPPO文章来源地址https://www.toymoban.com/news/detail-516886.html
到了这里,关于SBUS协议介绍和标准例程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!