S.BUS通讯协议简介与代码实现
1.简介
S.BUS,全称Serial Bus,即串行通信总线。本质上是一种串口通信协议,采用100K的波特率,8位数据位,2位停止位,偶效验,即8E2的串口通信。
S.BUS可以传输16个比例通道和2个数字(bool)通道。其硬件上基于RS232协议,采用TTL电平,但高位取反(负逻辑,低电平为“1”,高电平为“0”)
2.应用场景
SBUS协议广泛应用于遥控器、四轴飞行器、机器人等领域,用于传输控制信号、传感器数据等。例如,在遥控器中,SBUS协议用于传输遥控器的控制信号,如油门、方向、开关等;在四轴飞行器中,SBUS协议用于传输四轴飞行器的飞行状态、传感器数据等;在机器人中,SBUS协议用于传输机器人的控制信号、传感器数据等。
3.数据格式
SBUS协议的数据格式如下:
| 字节序号 |
数据类型 |
数据长度 |
数据描述 |
| 0 |
8位 |
1字节 |
头部标志,固定为0x0F |
| 1-22 |
16位 |
11字节 |
通道数据,每个通道占用2字节,共11个通道,共22字节 |
| 23 |
8位 |
1字节 |
标志位,0x0C表示断连,不同接收器的代码可能不一样 |
| 24 |
8位 |
1字节 |
尾部标志,固定为0x00 |
4.数据解析
SBUS协议的数据解析过程如下:
- 读取数据帧的第1个字节,判断是否为头部标志0x0F,如果不是,则丢弃该数据帧。
- 读取数据帧的第25字节,判断是否为尾部标志0x00,如果不是,则丢弃该数据帧。
- 为保证判断准确应额外读取第26字节,判断是否为0x0F,如果不是,则丢弃该数据帧。
- 读取数据帧的第2个字节到第23字节,每个字节表示一个通道的数据,共11个通道,每个通道占用2字节,共22字节。
- 读取数据帧的第24字节,最高位2bit分别表示数字通道17和数字通道18,为0表示连接正常,0x0C表示断连。
- 将每个通道的数据转换为对应的数值,例如,将0x3FF表示为最大值,0x200表示为中间值,0x000表示为最小值。
- 根据需要,将解析后的数据传递给相应的处理程序或设备。
5.SBUS数据解析代码示例
typedef struct
{
uint16_t ch01;
uint16_t ch02;
uint16_t ch03;
uint16_t ch04;
uint16_t ch05;
uint16_t ch06;
uint16_t ch07;
uint16_t ch08;
uint16_t ch09;
uint16_t ch10;
uint16_t ch11;
uint16_t ch12;
uint16_t ch13;
uint16_t ch14;
uint16_t ch15;
uint16_t ch16;
uint8_t ch17:1;
uint8_t ch18:1;
uint8_t flag:4;
}SBUS_Data_t;
#define USART2_DMA_RX_BUFF_LEN 200
uint8_t usart2_dma_rx_buff[USART2_DMA_RX_BUFF_LEN];
//初始化串口开启dma接收
void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
LL_USART_InitTypeDef USART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_2|LL_GPIO_PIN_3;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART2 DMA Init */
/* USART2_RX Init */
LL_DMA_SetChannelSelection(DMA1, LL_DMA_STREAM_5, LL_DMA_CHANNEL_4);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_5, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_5, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA1, LL_DMA_STREAM_5, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_5, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_5, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_5, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_5, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_5);
/* USART2 interrupt Init */
NVIC_SetPriority(USART2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_Init 1 */
LL_DMA_SetPeriphAddress(DMA1,LL_DMA_STREAM_5,(uint32_t)LL_USART_DMA_GetRegAddr(USART2));
LL_DMA_SetMemoryAddress(DMA1,LL_DMA_STREAM_5,(uint32_t)usart2_dma_rx_buff);
LL_DMA_SetDataLength(DMA1,LL_DMA_STREAM_5,USART2_DMA_RX_BUFF_LEN);
/* USER CODE END USART2_Init 1 */
USART_InitStruct.BaudRate = 100000;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_9B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_2;
USART_InitStruct.Parity = LL_USART_PARITY_EVEN;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART2, &USART_InitStruct);
LL_USART_ConfigAsyncMode(USART2);
LL_USART_Enable(USART2);
/* USER CODE BEGIN USART2_Init 2 */
LL_USART_EnableIT_IDLE(USART2);
LL_DMA_EnableIT_TC(DMA1,LL_DMA_STREAM_5);
LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_5);
LL_USART_EnableDMAReq_RX(USART2);
/* USER CODE END USART2_Init 2 */
}
//中断函数
void DMA1_Stream5_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream5_IRQn 0 */
if(LL_DMA_IsActiveFlag_TC5(DMA1))
{
LL_DMA_ClearFlag_TC5(DMA1);
usart2_dma_tc_flag = 1;
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_5);
}
/* USER CODE END DMA1_Stream5_IRQn 0 */
/* USER CODE BEGIN DMA1_Stream5_IRQn 1 */
/* USER CODE END DMA1_Stream5_IRQn 1 */
}
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
/* USER CODE BEGIN USART2_IRQn 1 */
if(LL_USART_IsActiveFlag_IDLE(USART2))
{
LL_USART_ClearFlag_IDLE(USART2);
}
/* USER CODE END USART2_IRQn 1 */
}
void APP_RCSbus(uint8_t *buf,SBUS_Data_t *sdata)
{
sdata->ch01 = ((buf[1]| buf[2]<<8) & 0x07FF);
sdata->ch02 = ((buf[2]>>3 |buf[3]<<5) & 0x07FF);
sdata->ch03 = ((buf[3]>>6 |buf[4]<<2 |buf[5]<<10) & 0x07FF);
sdata->ch04 = ((buf[5]>>1 |buf[6]<<7) & 0x07FF);
sdata->ch05 = ((buf[6]>>4 |buf[7]<<4) & 0x07FF);
sdata->ch06 = ((buf[7]>>7 |buf[8]<<1 |buf[9]<<9) & 0x07FF);
sdata->ch07 = ((buf[9]>>2 |buf[10]<<6) & 0x07FF);
sdata->ch08 = ((buf[10]>>5|buf[11]<<3) & 0x07FF);
sdata->ch09 = ((buf[12] |buf[13]<<8) & 0x07FF);
sdata->ch10 = ((buf[13]>>3|buf[14]<<5) & 0x07FF);
sdata->ch11 = ((buf[14]>>6|buf[15]<<2|buf[16]<<10) & 0x07FF);
sdata->ch12 = ((buf[16]>>1|buf[17]<<7) & 0x07FF);
sdata->ch13 = ((buf[17]>>4|buf[18]<<4) & 0x07FF);
sdata->ch14 = ((buf[18]>>7|buf[19]<<1|buf[20]<<9) & 0x07FF);
sdata->ch15 = ((buf[20]>>2|buf[21]<<6) & 0x07FF);
sdata->ch16 = ((buf[21]>>5|buf[22]<<3) & 0x07FF);
sdata->ch17 = (buf[23]&0x80)==0?0:1;
sdata->ch18 = (buf[23]&0x40)==0?0:1;
sdata->flag = buf[23]&0x0F;
}
void APP_RCPrintSbusData(SBUS_Data_t *sdata)
{
uint16_t *p = (uint16_t *)sdata;
uint8_t i = 0;
printf("SBUS Data: ");
while(i < 16)
{
printf("CH%d:%d ",i+1,p[i]);
i += 1;
}
printf("CH17:%d CH18:%d Flag:%X\n",sdata->ch17,sdata->ch18,sdata->flag);
}
void APP_StartRCTask(void const * argument)
{
/* USER CODE BEGIN APP_StartRCTask */
uint8_t i = 0;
uint8_t len = 0;
SBUS_Data_t sdata;
/* Infinite loop */
for(;;)
{
if(usart2_dma_tc_flag > 0)
{
i = 0;
while(i < USART2_DMA_RX_BUFF_LEN)
{
if(i+25<USART2_DMA_RX_BUFF_LEN)
{
if(usart2_dma_rx_buff[i]==0x0F&&usart2_dma_rx_buff[i+24]==0x00&&usart2_dma_rx_buff[i+25]==0x0F)
{
APP_RCSbus(&usart2_dma_rx_buff[i],&sdata);
APP_RCPrintSbusData(&sdata);
i += 25;
}
else
i += 1;
}
else
break;
}
usart2_dma_tc_flag = 0;
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_5, USART2_DMA_RX_BUFF_LEN);
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_5);
}
osDelay(5);
}
/* USER CODE END APP_StartRCTask */
}