你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】stm32 解析sbus

[复制链接]
STMCU小助手 发布时间:2022-1-27 22:58
1. S.BUS
2 K* S5 K( q; u6 x- M3 a1.1 协议介绍
S.BUS是FUTABA提出的舵机控制总线,全称Serial Bus,别名S-BUS或SBUS,也称 Futaba S.BUS。/ H7 V1 z& A$ W4 y8 v  M
S.BUS是一个串行通信协议,也是一个数字串行通信接口(单线),适合与飞控连接。它可以连接很多设备,每个设备通过一个HUB与它相连,得到各自的控制信息。
. ]4 t( ]. {( M6 X3 WS.BUS可以传输16个比例通道和2个数字(bool)通道。其硬件上基于RS232协议,采用TTL电平,但高位取反(负逻辑,低电平为“1”,高电平为“0”),通信波特率为100K(不兼容波特率115200)。
1.2 协议解析
# J5 S4 [; j7 l# K& u# M通信接口:USART(TTL)
  z' U9 N2 j  A: @通信参数:1个起始位+8个数据位+偶校验位+2个停止位,波特率=100000bit/s,电平逻辑反转。  R/ w# ]. z: F+ S) `
通信速率:每14ms(模拟模式)或7ms(高速模式)发送,即数据帧间隔为 11ms(模拟模式)或4ms(高速模式)。  [5 Q: r( u& n$ @' A3 i1 b
数据帧格式:[1]
6 P4 F1 a0 V$ A) n1 e' I9 s; E+ ^, C字节位 byte1 byte2-23 byte24 byte255 n; W1 M! x. o' k3 O5 A5 P/ ^8 {
类型 开始字节 通道数据字节(含16个脉宽通道) 标志位字节(含2个数字通道) 结束字节6 J  p) `. X6 s% k# {
数据 0x0F 通道数据范围11Bits = [0,2047] 2个数字通道位+2个状态位 0x00! \* [1 N+ F2 U- d: b3 }
byte1:
+ W9 z! M- F$ j/ Q1 i/ F5 pstartbyte = 0000 1111b (0x0F)
byte2-23:. G0 V& E/ m1 U, i% V8 ?
databytes = 22bytes = 22 x 8Bits = 16 x 11Bits(CH1-16)
& G. |. H) F$ E8 @3 Y7 ?+ {通道数据低位在前,高位在后,每个数据取11位,具体协议如下:
2 J: w5 }9 b5 n7 E. `读取的databyte值:
byte 2 3 4 5 6 7 etc
4 l+ T2 [. J2 m4 U9 P) W内容 12345678 12345678 12345678 12345678 12345678 12345678 etc! K: K# s) J' Y& Z
转化后的通道值:
通道 CH01 CH02 CH03 CH04 etc3 }5 K; K# |! }
内容 67812345678 34567812345 81234567812 56781234567 etc0 y/ E' O5 b8 y" l
byte24:
Bit 7 6 5 4 3 2 1 09 h: G' N' Y) z7 x& D/ _9 h
含义 数字通道CH17 数字通道CH18 帧丢失位 故障保护激活位 N/A N/A N/A N/A
) L% h7 `" o7 \; m, ]byte25:
* a& o' `8 l4 x6 W" b' [8 Hendbyte = 0000 0000b (0x00)

* z% f* q, N$ h, s! F4 ]' V
2. 硬件设计
0 n: s1 T+ M& d  C  C2.1 硬件参数8 _: B9 I: G# n2 {3 a# v6 W
主控芯片:STM32F103VET6# C( ~4 ~: p) ~# s
接收端口:USART2(带反相电路)
, k& L( p$ \2 Q# {" XS.BUS设备:walkera RX-SBUS[2](配DEVO 10遥控器)

. @& ]: J) _8 v  v; v2.2 反相电路8 s$ h! ^$ \% d7 o$ B6 N* V
由于此芯片串口不带反相器,我们需要外部搭建反相电路。如果芯片串口内部带反相器,可以省略此步。反相电路设计如下图:

' f1 T  s. r6 T7 U2 e* J# q/ u  k. i' gJ1为4Pin排针,适配S.BUS接口,可5V输出为SBUS接收机供电。! F( F6 r0 g( J$ N; @; j5 L/ ^7 v, `
J1的Pin-4接S.BUS数据发送端,连接一个由NPN三极管构成的反相器,将反相后的信号送入芯片USART2的RXD引脚。

. O7 W( m- q+ V9 y4 |3.程序设计
  [9 x) f1 u* s- l$ \+ }" v3.1 数据接收1 V# n5 U) f9 P" L1 X+ ?/ m' j
分析一:根据 1.2 的协议解析,开始字节(0x0F)和结束字节(0x00)都是数据字节中很容易出现的字节,所以不能完全作为数据帧接收开始和结束的标志。% k" [' T; ~4 ?' A7 E# Z
分析二:每个数据帧之间的间隔至少4ms,则可以利用这个空闲时间来接收数据帧。(需要设计一个系统时钟)
9 U. ~# d* i* \4 [; _, x分析三:STM32 USART或UART有空闲中断,即检测到总线空闲(无数据传输),就产生中断。
- K1 @8 M  i" C3 ]接收程序设计:综上,利用USART2接收中断(RXNE)来接收每个字节,利用USART2空闲中断(IDLE)来判断数据帧是否接收完毕。
USART2 初始化函数代码如下:
/**
: Y5 v7 H- D) H3 N# \8 N+ o, C6 k* @name SBUS_Configuration/ j8 h* b+ @) p7 D
* @brief Configure SBUS(Usart2) clock, gpio and nvic:( R' y+ @4 `, z& W
* SBUS_RX USART2_RX PD6
! j1 Y- _* i4 i1 q, m0 Z, D* @param None2 c/ D0 N# g! q2 v& W6 @+ H7 j
* @retval None
8 T% O: [2 K$ `- V*/) @" @" p& R9 n/ ?
void SBUS_Configuration(void)# J  \' s* c5 L, y! W* U" ?
{: u5 |6 J. V; N7 m$ q9 B6 Q4 N7 x- a$ a
GPIO_InitTypeDef GPIO_InitStructure;) T4 b: Y' V- m0 J8 r# n! d( t
USART_InitTypeDef USART_InitStructure;/ d! n: r/ s! ]6 `1 |$ B% i8 M5 _$ `
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
( e4 w8 `9 ]. g0 b! y9 t3 XRCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);6 C7 }9 V6 {, l1 A! @
: E1 }9 ]! y/ @% Y
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
; h5 b2 \4 U0 w4 a% n4 {. oGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;. \- n5 F" r0 G7 ]+ C% @$ G
GPIO_Init(GPIOD, &GPIO_InitStructure);
1 N& q+ A3 }: g$ {0 v7 M
- P0 ^7 z: ^' |& ^, e( XGPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
// 波特率100000 8个数据位 偶校验位 2个停止位
7 ^! Y, Z3 t8 y& g! MUSART_InitStructure.USART_BaudRate = 100000;+ h* S$ j; O1 J  z9 x) l
USART_InitStructure.USART_WordLength = USART_WordLength_8b;  //注意这里是9个数据长度(8个数据位+偶校验位): l, ?) @+ L- u& j! j
USART_InitStructure.USART_StopBits = USART_StopBits_2;2 A% h% V8 v* P6 F/ d
USART_InitStructure.USART_Parity = USART_Parity_Even;
/ |- b$ W, M8 O/ M6 a( r% q4 l6 qUSART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;3 E/ n' U' r  R8 V5 y4 _( k
USART_InitStructure.USART_Mode = USART_Mode_Rx;
USART_Init(USART2, &USART_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;6 Y" Q/ l4 u' Z, k5 Y6 \; t5 |- p
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
3 ^1 R$ x( v2 ?. h3 P" bNVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
6 k  g# A" ]: r0 o# n9 G1 l! |* o2 ]NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;0 ~4 Q0 i9 t! F
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);3 C0 y" @1 V8 H% ~
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
USART_Cmd(USART2, ENABLE);
7 \- {. S  E  x6 q}1 n5 K0 Q- c% [( r3 F" i8 O0 O

7 R8 I( X, {. Y; v  u3 P4 xUSART2 中断函数代码如下:
  j0 s7 W" d' M' v) z9 q6 Z
uint8_t USART2_RX_BUF[26];
/**
# d4 p9 F' c/ p3 \5 o$ N9 x* @name USART2_IRQHandler
2 L# V2 e! p- F! n* @brief This function handles USART2 Handler, r9 x% q/ p9 u
* @param None% |% u7 o4 L* |+ t2 d' n$ h3 Q( ~
* @retval None: t! N$ p9 C/ G" d$ h, f
*/
8 D# n; h; D7 mvoid USART2_IRQHandler(void)  B5 Y5 n( I5 d2 F$ \2 Y
{$ L: I+ }" J/ C% M6 \* ^. l* u
uint8_t res;
( p# ?# ^' F- k- j% f, X  Y- Huint8_t clear = 0;5 f" |5 b8 S7 Y& A& Z7 D
static uint8_t Rx_Sta = 1;
, y, m' H* x! h. x! v: i9 B
9 D+ p. C- U) o& E, ]if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
% h: U* V; U0 `7 I* I. Y, r{
5 X; d  G* n! k4 z2 hres =USART2->DR;
1 s2 `3 t+ I) S; c: |  k2 }USART2_RX_BUF[Rx_Sta++] = res;5 j( s" {$ l0 z- G1 k
}
. n; v6 N% R9 d) R) I7 [6 selse if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
" ]4 a( s: O$ s6 M( @# O0 T{) R$ U9 _# Y0 |
clear = USART2->SR;  J5 @' L: _% L6 {
clear = USART2->DR;
8 Y1 W% A2 y8 x* BUSART2_RX_BUF[0] = Rx_Sta - 1;
/ z& N+ ?9 n* O% VRx_Sta = 1;2 N6 n( |: V7 z* e4 Z1 `
}& L* a! p- g7 P0 l; F8 i( O% r
}
3 f( K" \5 ^* p9 x* `  {, _. u
4 |3 y6 S9 U: YUSART2_RX_BUF为接收缓存区,定义为26个字节,第一个字节USART2_RX_BUF[0]为接收到的字节个数,后面为接收到的数据。
8 J. W$ `9 x# P4 Z$ }USART2_RX_BUF[0]可以作为数据帧字节长度的判断。3 M5 k. v# j4 b% _1 n) `
中断服务函数具体解释请参考STM32 串口接收不定长字节数据。

: w: J5 t7 [! o3.2 数据处理
& |! J) O) D6 n* y6 T5 d' q直接上代码:
uint16_t CH[18]; // 通道值3 u* n" Z: E7 g+ y, [
uint8_t rc_flag = 0;
void Sbus_Data_Count(uint8_t *sBusData)1 I7 a+ }) g( i- w
{
tempData[ 0 ] = ( (sBusData[ 2]&0x07) << 8 ) + sBusData[ 1]; //sBus[ 2] low3 + sBus[ 1] low8
  ]+ W# W; w. @( h6 etempData[ 1 ] = ( (sBusData[ 3]&0x3F) << 5 ) + (sBusData[ 2] >> 3 ); //sBus[ 3] low6 + sBus[ 2] high5# a! [: w4 `2 c; c- \6 ^
tempData[ 2 ] = ( (sBusData[ 5]&0x01) << 10) + (sBusData[ 4] << 2 ) + (sBusData[ 3] >> 6); //sBus[ 5] low1 + sBus[ 4] low8 + sBus[ 3] high2
4 i% `& G+ [: H  ]2 [, |tempData[ 3 ] = ( (sBusData[ 6]&0x0F) << 7 ) + (sBusData[ 5] >> 1 ); //sBus[ 6] low4 + sBus[ 5] high75 q0 O! b4 s: ]0 i" _
tempData[ 4 ] = ( (sBusData[ 7]&0x7F) << 4 ) + (sBusData[ 6] >> 4 ); //sBus[ 7] low7 + sBus[ 6] high4
0 h% ^* e0 Y; H, u# Q8 ZtempData[ 5 ] = ( (sBusData[ 9]&0x03) << 9 ) + (sBusData[ 8] << 1 ) + (sBusData[ 7] >> 7); //sBus[ 9] low2 + sBus[ 8] low8 + sBus[ 7] high1
4 p6 e' H0 G- Y& VtempData[ 6 ] = ( (sBusData[10]&0x1F) << 6 ) + (sBusData[ 9] >> 2 ); //sBus[10] low5 + sBus[ 9] high6: Z9 U0 z- g. {$ y7 {
tempData[ 7 ] = ( (sBusData[11]&0xFF) << 3 ) + (sBusData[10] >> 5 ); //sBus[11] low8 + sBus[10] high3
+ \. r/ W* ~1 i1 p- P  J$ R6 w. u3 Y: r
tempData[ 8 ] = ( (sBusData[13]&0x07) << 8 ) + sBusData[12]; //sBus[13] low3 + sBus[12] low8
+ y  n# T2 i4 ~* P4 utempData[ 9 ] = ( (sBusData[14]&0x3F) << 5 ) + (sBusData[13] >> 3 ); //sBus[14] low6 + sBus[13] high5
! ?; I+ f% i/ ttempData[ 10 ] = ( (sBusData[16]&0x01) << 10) + (sBusData[15] << 2 ) + (sBusData[14] >> 6); //sBus[16] low1 + sBus[15] low8 + sBus[14] high26 D7 d* u. `! z6 ~* }
tempData[ 11 ] = ( (sBusData[17]&0x0F) << 7 ) + (sBusData[16] >> 1 ); //sBus[17] low4 + sBus[16] high70 ?5 w( b$ M. {
tempData[ 12 ] = ( (sBusData[18]&0x7F) << 4 ) + (sBusData[17] >> 4 ); //sBus[18] low7 + sBus[17] high4
; p+ I; |3 U) W: V9 U1 j' w' OtempData[ 13 ] = ( (sBusData[20]&0x03) << 9 ) + (sBusData[19] << 1 ) + (sBusData[18] >> 7); //sBus[20] low2 + sBus[19] low8 + sBus[18] high1% J$ [8 @) F; ]) q1 j" a
tempData[ 14 ] = ( (sBusData[21]&0x1F) << 6 ) + (sBusData[20] >> 2 ); //sBus[21] low5 + sBus[20] high6
  ~! U" X% f) V) otempData[ 15 ] = ( (sBusData[22]&0xFF) << 3 ) + (sBusData[21] >> 5 ); //sBus[22] low8 + sBus[21] high3

8 T" v6 t9 O- K/ c# p}
0 U5 d4 W+ L/ L# T8 b+ q0 `' S接收到的报文和解析出来的数据如下:( [  \2 ]7 I4 x* |
RX:0F E0 03 1F 58 C0 07 16 B0 80 05 2C 60 01 0B F8 C0 07 00 00 00 00 00 03 004 a7 c" r# a) o
CH: 992 992 352 992 352 352 352 352 352 352 992 992 000 000 000 000. {& t0 h5 K( K- j- b
RX:0F 60 01 0B 58 C0 07 66 30 83 19 7C 60 06 1F F8 C0 07 00 00 00 00 00 03 00, r6 ?% c& c1 J) v
CH: 352 352 352 992 1632 1632 1632 992 1632 992 992 992 000 000 000 000
接收的byte24数据并非和协议解析中的一样,无论断开遥控器还是连接遥控器,读取的值都是0x03。) a& w0 i7 U4 N3 b% i& v( D
接收机只支持12个通道,所以通道13-16没有值。" l0 N  O1 o9 G0 ^
读取的通道值中间值为992,最大值为1632,最小值为352。
sbus数据转为 PWM数据
RcData = (uint16_t)(sbusData * 1.2504 + 1761.1) / 2;

& {& F9 v6 |/ B
4. 最后1 j8 h" z* ?& n: i8 y, f/ {0 ^2 y
提供两个应用优化方向:
使用DMA+双缓存器+串口空闲中断读取和解析数据,提升MCU的工作效率。$ K& t) D! t# G) B: V2 K) S
将读取的通道值转化成脉宽(0.5-2.5ms)输出,用来控制模拟信号设备。
9 _4 C+ o& r7 I# `; V本协议解析就写到这里。

2 d' g# Y$ ^0 V! C4 |) V. w' ^  {
收藏 1 评论1 发布时间:2022-1-27 22:58

举报

1个回答
cqxhhe 回答时间:2022-8-4 11:11:04
学习) `) E7 @$ ?) d- g% O$ |, f9 q$ }

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版