文章底部附代码
一般来说,遥控器的接收机都有三根线,电源线,GND,SBUS信号线。
通过这根信号线可以获得遥控器所有通道的数据。
由接收机图可以看到三根线。
另外,硬件上设计时,信号必须取反!
SBUS一般采用10000的波特率。
遥控器发给接收机的数据帧总长度25个字节:
(1)起始字节0x0F,一个字节8位
(2)24个通道Channel(每个通道11位,总共264位,占用24字节)。
数据范围: 0x000 到 0x7FF (11位,即 0000 0000 000 到 1111 1111 111 )
数据解析:通过位运算从字节中提取通道数据,并组合成完整的11位数据。
(3)校验字节。
其中通道Channel里的帧格式如下:
• 起始位:1位
• 数据位:8位
• 校验位:1位
• 停止位:2位
所以每个通道有11位。
什么是通道Channel?
通道指的是可以独立控制的信号路径,每个通道可以传输一个独立的控制信号。
通过不同的通道,可以分别控制无人机或者小车的不同功能。
比如:
通道1,控制小车前进和后退,或者无人机的油门
通道2,控制左右原地转
通道3,控制上升下降
通道4,控制航拍摄像头的pitch轴
通道5,控制航拍摄像头的YAW轴
通道6,按一下能切换运动模式或者其它跟拍什么的模式。
所以。任何功能都可以,像SBUS协议有24个通道,你就可以根据你的需要,比如你在做无人机,那一般用几个通道就行,不需要那么多。
那你可能有疑问了
为什么一个通道可以控制左转或者右转呢?
它是通过解析SBUS数据帧中的特定通道值来控制模型的转向。
通道值较小(接近 0x000):通常表示向左转
通道值较大(接近 0x7FF):通常表示向右转
比如代码:
void Sbus_Data_Count(void)
{
CH[0] = ((int16_t)buf[ 2] >> 0 | ((int16_t)buf[ 3] << 8 )) & 0x07FF;// 左右方向
CH[1] = ((int16_t)buf[ 3] >> 3 | ((int16_t)buf[ 4] << 5 )) & 0x07FF;// 上下方向
CH[2] = ((int16_t)buf[ 4] >> 6 | ((int16_t)buf[ 5] << 2 ) | (int16_t)buf[ 6] << 10 ) & 0x07FF;// 上下方向
CH[3] = ((int16_t)buf[ 6] >> 1 | ((int16_t)buf[ 7] << 7 )) & 0x07FF;// 左右方向
CH[4] = ((int16_t)buf[ 7] >> 4 | ((int16_t)buf[ 8] << 4 )) & 0x07FF;// 摇杆
CH[5] = ((int16_t)buf[ 8] >> 7 | ((int16_t)buf[ 9] << 1 ) | (int16_t)buf[10] << 9 ) & 0x07FF;// 摇杆
CH[6] = ((int16_t)buf[10] >> 2 | ((int16_t)buf[11] << 6 )) & 0x07FF;
}
我们以 CH[0] 控制左右方向为例进行详细解释:
这行代码的目的是将buf[2]和buf[3]两个字节的数据组合成一个11位的值
1. 提取数据:
buf[2] :第3个字节(从0开始计数),假设为0x1A,二进制表示为 0001 1010,八位即一个字节
buf[3] :第4个字节,假设为0xB4,二进制表示为1011 0100,八位即一个字节
buf是一个数组,用作缓冲区(buffer)来临时存储通过USART接收到的数据帧,用于临时存储数据流。
我们知道在SBUS中,一个通道有11位,由于一个字节只有八位,所以需要两个字节来存储11位的数据。
你可能会有疑问。为什么从buf[2]开始呢,然后为什么buf[2]和buf[3]包含通道0的11位数据?
首先,我们知道我们操控遥控器传给接收机的数据帧,第一个字节是起始字节,所以占了buf[0]。
然后,通道数据从数据帧的第二个字节开始,即 buf[1] 。然而,由于每个通道的数据占用11位,而每个字节只有8位,因此一个通道的数据会跨越两个字节。
所以通道0,取的是buf[2]的8位和buf[3]的低3位拼成11位
2、位操作:
>> 0 :将 buf[2] 的低8位右移0位(即保持不变)。
<< 8 :将 buf[3] 左移8位。由于我们只考虑8位,所以实际上这会将原始值移到一个更高的字节位置,并在原位置补0。移动后,原始的8位值将占据高8位的位置,而低8位将被填充为0。
| :将两个结果进行按位或操作,组合成完整的11位数据。其中包含了 buf[2] 的低3位和 buf[3] 的高8位。
0000 0000 0001 1010
| 1011 0100 0000 0000
-------------------
1011 0100 0001 1010
即0xB42A
3. 掩码操作:
& 0x07FF :通过掩码操作,确保数据范围为11位( 0x000 到 0x7FF )
1011 0100 0001 1010
& 0000 0111 1111 1111
-------------------
0000 0100 0001 1010
完整代码:
c文件
#include "sbus.h"
void Usart5_Init(u32 bound)// RX PD2
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOD, &GPIO_InitStructure);
//USART5 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_9b;//!字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_2;//2个停止位
USART_InitStructure.USART_Parity = USART_Parity_Even;//偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx| USART_Mode_Tx ; //收发模式
USART_Init(UART5, &USART_InitStructure); //初始化串口5
//Usart5 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;//串口5中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //抢占优先级4
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
USART_Cmd(UART5, ENABLE); //使能串口5
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);//接收到8位数据就中断一次
USART_ITConfig(UART5, USART_IT_IDLE, ENABLE);
}
uint8_t buf[26];
void UART5_IRQHandler(void)
{
uint8_t res;
uint8_t clear = 0;
static uint8_t Rx_Sta = 1;
if(USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)
{
res =UART5->DR;
buf[Rx_Sta++] = res;
}
//一包数据发送完成 准备调整一下后,去接收下一包数据 中间有4MS/14MS延迟供你来检测
else if(USART_GetITStatus(UART5, USART_IT_IDLE) != RESET)
{//检测到空闲线路时,该位由硬件置 1,该位由软件序列清零(读入 USART_SR 寄存器,然后读入 USART_DR 寄存器)。
clear = UART5->SR;
clear = UART5->DR;
buf[0] = Rx_Sta - 1;//在Xms内接收到的数据数量
Rx_Sta = 1;
}
}
uint16_t CH[18]; // 通道值
uint8_t rc_flag = 0;
void Sbus_Data_Count(void)
{
CH[0] = ((int16_t)buf[ 2] >> 0 | ((int16_t)buf[ 3] << 8 )) & 0x07FF;//you 左右方向 0000 0111 1111 1111
CH[1] = ((int16_t)buf[ 3] >> 3 | ((int16_t)buf[ 4] << 5 )) & 0x07FF;//you 上下方向
CH[2] = ((int16_t)buf[ 4] >> 6 | ((int16_t)buf[ 5] << 2 ) | (int16_t)buf[ 6] << 10 ) & 0x07FF;//zou 上下方向
CH[3] = ((int16_t)buf[ 6] >> 1 | ((int16_t)buf[ 7] << 7 )) & 0x07FF;//zou 左右方向
CH[4] = ((int16_t)buf[ 7] >> 4 | ((int16_t)buf[ 8] << 4 )) & 0x07FF;//zou 摇杆
CH[5] = ((int16_t)buf[ 8] >> 7 | ((int16_t)buf[ 9] << 1 ) | (int16_t)buf[10] << 9 ) & 0x07FF;//you 摇杆
CH[6] = ((int16_t)buf[10] >> 2 | ((int16_t)buf[11] << 6 )) & 0x07FF;
}
//没有用到的通道
/*CH[ 7] = ((int16_t)buf[11] >> 5 | ((int16_t)buf[12] << 3 )) & 0x07FF;
CH[ 8] = ((int16_t)buf[13] << 0 | ((int16_t)buf[14] << 8 )) & 0x07FF;
CH[ 9] = ((int16_t)buf[14] >> 3 | ((int16_t)buf[15] << 5 )) & 0x07FF;
CH[10] = ((int16_t)buf[15] >> 6 | ((int16_t)buf[16] << 2 ) | (int16_t)buf[17] << 10 ) & 0x07FF;
CH[11] = ((int16_t)buf[17] >> 1 | ((int16_t)buf[18] << 7 )) & 0x07FF;
CH[12] = ((int16_t)buf[18] >> 4 | ((int16_t)buf[19] << 4 )) & 0x07FF;
CH[13] = ((int16_t)buf[19] >> 7 | ((int16_t)buf[20] << 1 ) | (int16_t)buf[21] << 9 ) & 0x07FF;
CH[14] = ((int16_t)buf[21] >> 2 | ((int16_t)buf[22] << 6 )) & 0x07FF;
CH[15] = ((int16_t)buf[22] >> 5 | ((int16_t)buf[23] << 3 )) & 0x07FF;*/
.h文件
#ifndef _SBUS_H
#define _SBUS_H
#include "sys.h"
extern uint16_t CH[18];
void Usart5_Init(u32 bound);
void Sbus_Data_Count(void);
#endif
代码理解如下:
使用串口5的中断。只要接收到8位数据就进入中断
这里可以看到,这个buf数组就是用来接收遥控器传过来的数据帧的。每次发8位
先将串口5的DR数据寄存器赋给res,然后buf[Rx_Sta++]从1开始累加。