STM32解析SBUS信号例程详解
文章目录
- 1. SBUS信号简介
- 2. STM32F7解析SBUS信号例程
- (1) 串口配置
- (2) 串口中断接收
- (3) 信号解析
1. SBUS信号简介
最近在搞一个项目的通信和控制,用到了SBUS,记录一下心得。
SBUS全称serial-bus,是一种串口通信协议,广泛应用于航模遥控器(接收机)中。只用一根信号线就能传输多达16通道的数据,比多路PWM捕获高效且省资源。
- 串口配置:
100k波特率,8位数据位,2位停止位,偶校验(EVEN),无控流,25个字节。 - 协议格式:(8字节)
[startbyte] [data1][data2]…[data22][flags][endbyte]
startbyte=0x0f;
endbyte=0x00;
data1…data22: LSB(低位在前),对应16个通道(ch1-ch16),每个通道11bit(22 × 8=16 × 11);
flag位标志遥控器的通讯状态,我使用的乐迪AT9S在遥控器通上的时候是0x00,断开的时候是0xC0,可以通过查询flag位来采取失控保护。 - 数据范围
航模遥控器输出的PWM值是1000~2000,中值为1500,sbus输出的会不一样,例如乐迪AT9S的范围为300 ~ 1700,中值1000,这个我估计跟遥控器厂家有关。 - sbus的负逻辑
这个地方一定要万分注意,必须加硬件反相器,因为SBUS的信号是采用的负逻辑,也就是电平相反,不要试图在软件里面取反,因为软件里面只能操作数据位(记得串口配置里面的数据位8么),你是操作不了停止位、校验位啥的!!
如果是自己画板子也很简单,如图所示
5. 数据读取
一般的串口调试助手可能没有100K波特率的选项,推荐一个串口调试助手MicroLab,可以自定义串口波特率,还有其他好功能自己探索叭。
2. STM32F7解析SBUS信号例程
清楚了通信协议,解析就很简单了。我使用的是正点原子的阿波罗F7开发板,其他的板子是一样的。
(1) 串口配置
首先一些变量声明,串口uart.c里用到的
#define USART_REC_LEN 100 //定义最大接收字节数 200
#define RXBUFFERSIZE 1 //缓存大小u8 USART1_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
u16 USART1_RX_STA = 0; //接收状态标记
u8 aRxBuffer1[RXBUFFERSIZE]; //HAL库使用的串口接收缓冲
UART_HandleTypeDef UART1_Handler; //UART句柄
串口初始化函数
void uart1_init(u32 bound)
{//UART 初始化设置UART1_Handler.Instance = USART1; //USART1UART1_Handler.Init.BaudRate = bound; //波特率UART1_Handler.Init.WordLength = UART_WORDLENGTH_9B; //字长为8位数据格式UART1_Handler.Init.StopBits = UART_STOPBITS_1; //一个停止位UART1_Handler.Init.Parity = UART_PARITY_EVEN; //无奇偶校验位UART1_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; //无硬件流控UART1_Handler.Init.Mode = UART_MODE_TX_RX; //收发模式HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer1, RXBUFFERSIZE); //该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{//GPIO端口设置GPIO_InitTypeDef GPIO_Initure;if (huart->Instance == USART1) //如果是串口1,进行串口1 MSP初始化{__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟GPIO_Initure.Pin = GPIO_PIN_9; //PA9GPIO_Initure.Mode = GPIO_MODE_AF_PP; //复用推挽输出GPIO_Initure.Pull = GPIO_PULLUP; //上拉GPIO_Initure.Speed = GPIO_SPEED_FAST; //高速GPIO_Initure.Alternate = GPIO_AF7_USART1; //复用为USART1HAL_GPIO_Init(GPIOA, &GPIO_Initure); //初始化PA9GPIO_Initure.Pin = GPIO_PIN_10; //PA10HAL_GPIO_Init(GPIOA, &GPIO_Initure); //初始化PA10#if EN_USART1_RXHAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道HAL_NVIC_SetPriority(USART1_IRQn, 3, 2); //抢占优先级3,子优先级3
#endif}
}
这里有个诡异的地方就是stm32要设置成9个数据位,一个停止位,我一开始按照8个数据位、两个停止位读出来的数据是错的,后来改了之后才正常了。是不是和stm32内部的串口配置有关,哪位大神弄明白了告诉我哈。
(2) 串口中断接收
串口中断函数,在中断函数里面接收数据,进行SBUS信号解析。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{int i;while (huart->Instance == USART1) //如果是串口1{USART1_RX_BUF[USART1_RX_STA] = aRxBuffer1[0];if (USART1_RX_STA == 0 && USART1_RX_BUF[USART1_RX_STA] != 0x0F) break; //帧头不对,丢掉USART1_RX_STA++;if (USART1_RX_STA > USART_REC_LEN) USART1_RX_STA = 0; ///接收数据错误,重新开始接收if (USART1_RX_BUF[0] == 0x0F && USART1_RX_BUF[24] == 0x00 && USART1_RX_STA == 25) //接受完一帧数据{update_sbus(USART1_RX_BUF);for (i = 0; i<25; i++) //清空缓存区USART1_RX_BUF[i] = 0;USART1_RX_STA = 0;}break;}
}
(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;}
}u16 sbus_to_pwm(u16 sbus_value)
{float pwm;pwm = (float)SBUS_TARGET_MIN + (float)(sbus_value - SBUS_RANGE_MIN) * SBUS_SCALE_FACTOR;// 1000 300 1000/1400if (pwm > 2000) pwm = 2000;if (pwm < 1000) pwm = 1000;return (u16)pwm;
}
上面定义了一个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;
u16 sbus_to_pwm(u16 sbus_value)
很好理解了,就是把sbus的值转化为标准的1000-2000的pwm值,因为我用的遥控器sbus值是300-1700,大家用的时候具体数值到时候可以通过串口直接读出来看看。
这样就读出了16个通道的数据啦。同时通过读取ConnectState位判断遥控器的状态,在主函数中采取失控保护。
上面这段解析数据的代码是国际通用的,可以用在任何使用sbus协议的场合,可以很方便的移植到arduino、51、树莓派上面。
最后main函数里面就很简单了,只注意初始化串口设置为100K波特率。
void main()
{/* 省略 */uart1_init(100000);/* 省略 */
}
本文代码已上传至CSDN,独乐乐不如众乐乐,提供免费下载,点击下载,欢迎交流讨论。别忘了点个赞哦!
STM32解析SBUS信号例程详解相关推荐
- STM32 USB设备远程唤醒机制详解
目录 1.USB Resume信号 2.USB远程唤醒的方式 3.J状态和K状态 4.STM32 USB设备远程唤醒机制详解 5.主机对设备远程唤醒功能状态的获取.清除和设置 1.USB Resume ...
- 进阶篇——树莓派OLED模块的使用 大量例程详解
树莓派OLED模块的使用教程大量例程详解 #树莓派# 简介 Python有两个可以用的OLED库 [Adafruit_Python_SSD1306库]->只支持SSD1306 [Luma.ole ...
- python cv2 轮廓的包络 面积_Python 基于FIR实现Hilbert滤波器求信号包络详解
在通信领域,可以通过希尔伯特变换求解解析信号,进而求解窄带信号的包络. 实现希尔伯特变换有两种方法,一种是对信号做FFT,单后只保留单边频谱,在做IFFT,我们称之为频域方法:另一种是基于FIR根据传 ...
- ISO7816协议深度解析-简单易懂协议详解(一)-- 复位,字符帧,及ATR
1. 摘要 IC卡必须支持T=0或T=1的协议,但不是同时支持这两种协议,而终端则必须同时支持T=0和T=1的协议. • T=0通讯协议是异步半双工字符传输协议: • T=1通讯协议是异步半双工块传输 ...
- STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号
STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号 一. 问题描述 二. 方法一--使用HAL_UART_Receive_DMA 三. 方法二--使用HAL_UARTEx_Rece ...
- python3d动态图-Python图像处理之gif动态图的解析与合成操作详解
本文实例讲述了Python图像处理之gif动态图的解析与合成操作.分享给大家供大家参考,具体如下: gif动态图是在现在已经司空见惯,朋友圈里也经常是一言不合就斗图.这里,就介绍下如何使用python ...
- STM32接口FSMC/FMC难点详解
STM32接口FSMC/FMC难点详解 转载 http://blog.sina.com.cn/s/blog_808bca130102x94k.html STM32F767的FMC将外部存储器划分为 ...
- STM32之NVIC的深入详解
朋友们,如果你需要在STM32上移植RTOS,那么首先必须深入理解它的中断系统.[转载]STM32之NVIC的深入详解 什么是NVIC?即嵌套向量中断控制器(Nested Vectored Inter ...
- Halcon例程详解(植物测量) —— measure_plant.hdev
文章目录 前言 一.过程 1.1 筛选ROI区域 1.2 分割 1.3 三维建模与仿射变换 1.4 确定叶子的角度分布和高度 1.5 确定叶子的面积和树干的直径 1.6 对叶子的角度进行分析 二.例程 ...
- Halcon例程详解(基于卡尺工具的匹配测量方法) —— measure_stamping_part.hdev
前言 1卡尺工具介绍 Halcon中的Metrology方法即为卡尺工具,可用来拟合线,圆,这种方法对于目标比背景很明显的图像尺寸测量是很方便的,不需要用blob进行边缘提取等,但缺点也很明显,需要目 ...
最新文章
- 怎么写遮罩层 css,css案例 - mask遮罩层的华丽写法
- 用数学方法构造神经网路的迭代次数1-9
- 计算机期末考试操作题简单,计算机期末考试操作题及答案计算机期末考试操作题及答案.doc...
- linux:gpg加密和解密
- MySQL的环境变量配置详细步骤
- iview vue 打包图标不显示_VueCLI3.0干货系列之集成iview
- MapReduce 学习指南
- java编程思想(注释文档)
- 【转】What is an entity system framework for game development?
- Rayman的绝顶之路——Leetcode每日一题打卡19
- xp计算机无法正常启动,Windows XP电脑开机出错无法正常启动的解决方法
- iOS 开发 申请299美金企业开发账号
- 感谢同事的临别赠言,愿自己一路顺风。
- unity3d的playmaker插件使用教程,五、进入区域改变平台颜色
- Android百度地图SDK:隐藏比例尺,隐藏百度LOGO,隐藏缩放控件
- 【日语口语词典学习】第0004页
- spring boot 2.0 redis 分布式锁
- 汇编:裴波那契数列前50项
- 帝国cms插件支持7.0/7.2 7.5/UTF-8 微信登入插件 一键登入
- [开发探索]知行合一