[无线通信] 【每周分享】SBUS通讯协议简介与代码实现

[复制链接]
141|0
yuyy1989 发表于 2025-11-21 10:59 | 显示全部楼层 |阅读模式

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. 读取数据帧的第1个字节,判断是否为头部标志0x0F,如果不是,则丢弃该数据帧。
  2. 读取数据帧的第25字节,判断是否为尾部标志0x00,如果不是,则丢弃该数据帧。
  3. 为保证判断准确应额外读取第26字节,判断是否为0x0F,如果不是,则丢弃该数据帧。
  4. 读取数据帧的第2个字节到第23字节,每个字节表示一个通道的数据,共11个通道,每个通道占用2字节,共22字节。
  5. 读取数据帧的第24字节,最高位2bit分别表示数字通道17和数字通道18,为0表示连接正常,0x0C表示断连。
  6. 将每个通道的数据转换为对应的数值,例如,将0x3FF表示为最大值,0x200表示为中间值,0x000表示为最小值。
  7. 根据需要,将解析后的数据传递给相应的处理程序或设备。

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 */
}
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

170

主题

835

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部