SBUS协议介绍和标准例程

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(起始字节)Flags0x00

每个通道数据占 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中定义

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

  • 6
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 云卓T12是一款智能平板电脑。 云卓T12采用了高质量的材料制造,外形精美,手感舒适。它配备了一块12英寸的高清触摸屏,显示清晰细腻,色彩鲜艳。 在性能方面,云卓T12搭载了一颗强悍的处理器,能够进行多任务处理,并且运行速度快。其内存容量大,可以满足人们的日常办公和娱乐需求。此外,云卓T12支持多种传输接口,如USB、HDMI等,方便用户连接其他设备。 云卓T12也是一台全能的办公工具。它预装了各种办公软件,如文档处理、表格制作、幻灯片演示等,可以满足个人和企业办公的需要。同时,云卓T12还支持手写输入,用户可以直接在屏幕上书写,方便快捷。 在娱乐方面,云卓T12具有出色的音效和影音播放能力。它内置了音频和视频播放器,支持各种常见的音视频格式。用户可以在云卓T12上欣赏高清电影,收听音乐,享受视听盛宴。 此外,云卓T12还支持无线网络功能,用户可以方便地上网冲浪,浏览网页,使用社交媒体等。 总的来说,云卓T12是一款功能强大,外观精美的智能平板电脑。它适用于各种场景,可实现办公和娱乐的多种需求。云卓T12的出现为用户提供了更多的选择,给人们的日常生活带来了便利和乐趣。 ### 回答2: 云卓T12 是一款电脑产品,并不具体指代某个特定的问题或主题,因此无法进行准确的回答。云卓T12 是一款云计算和超融合技术的产品,具有强大的计算能力和存储容量,能够满足用户对于高性能计算和大数据处理的需求。云卓T12 还拥有先进的虚拟化技术和智能管理系统,能够实现资源的弹性分配和优化,提升效率和灵活性。此外,云卓T12 的易用性和稳定性也是其优点之一,用户可以轻松地进行系统配置和管理,同时享受高性能和可靠性。总之,云卓T12 是一款功能强大、性能稳定的电脑产品,适用于各种计算和存储需求。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值