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

【经验分享】stm32 解析sbus

[复制链接]
STMCU小助手 发布时间:2022-1-27 22:58
1. S.BUS- U$ g! @; Y' D# ?6 o# l7 u" k
1.1 协议介绍
S.BUS是FUTABA提出的舵机控制总线,全称Serial Bus,别名S-BUS或SBUS,也称 Futaba S.BUS。
5 V3 E/ {! r2 U0 S- P. W% Q( m0 M/ eS.BUS是一个串行通信协议,也是一个数字串行通信接口(单线),适合与飞控连接。它可以连接很多设备,每个设备通过一个HUB与它相连,得到各自的控制信息。6 \* N% o7 d6 U( Z
S.BUS可以传输16个比例通道和2个数字(bool)通道。其硬件上基于RS232协议,采用TTL电平,但高位取反(负逻辑,低电平为“1”,高电平为“0”),通信波特率为100K(不兼容波特率115200)。
1.2 协议解析* O1 ]( E7 K4 _, m7 T# w
通信接口:USART(TTL)
, u& t/ d7 O3 j; t* O通信参数:1个起始位+8个数据位+偶校验位+2个停止位,波特率=100000bit/s,电平逻辑反转。
2 u5 P4 x4 c/ w8 N* `通信速率:每14ms(模拟模式)或7ms(高速模式)发送,即数据帧间隔为 11ms(模拟模式)或4ms(高速模式)。
3 e3 Q" |- L, q8 S! V数据帧格式:[1]
3 [% X6 N; e: _- a- {# D4 ~1 y字节位 byte1 byte2-23 byte24 byte25" p0 \. ~5 f- z% [7 o
类型 开始字节 通道数据字节(含16个脉宽通道) 标志位字节(含2个数字通道) 结束字节: j1 }" |, c3 I9 \) L" n4 u8 N
数据 0x0F 通道数据范围11Bits = [0,2047] 2个数字通道位+2个状态位 0x00
6 q7 F2 p- y" x( v3 X) D8 Z$ U* ybyte1:( I  Y+ \' i) V4 j; N. h6 A
startbyte = 0000 1111b (0x0F)
byte2-23:
, k. u4 H: x, fdatabytes = 22bytes = 22 x 8Bits = 16 x 11Bits(CH1-16)3 i# D& I, H  }" h0 [6 W) k$ L. _& R
通道数据低位在前,高位在后,每个数据取11位,具体协议如下:
0 @0 i* m8 y. \- A读取的databyte值:
byte 2 3 4 5 6 7 etc
, c. ~! L% C. O+ }% Z' l$ H内容 12345678 12345678 12345678 12345678 12345678 12345678 etc; L  |3 x* q6 Z) H" I1 x
转化后的通道值:
通道 CH01 CH02 CH03 CH04 etc; f4 ^; e; E7 T6 M9 n
内容 67812345678 34567812345 81234567812 56781234567 etc, X" g( M6 p$ Y  f7 p) h
byte24:
Bit 7 6 5 4 3 2 1 0
9 ]% ^  @  {2 }3 m) t5 _含义 数字通道CH17 数字通道CH18 帧丢失位 故障保护激活位 N/A N/A N/A N/A1 J. p; l% ?5 R4 v
byte25:
1 \% V9 J: l) t% C* U. ]' I3 Pendbyte = 0000 0000b (0x00)

7 e; l, M# L8 {& q
2. 硬件设计
3 O1 k0 y" K7 Q, a1 K# V' m2.1 硬件参数
7 z0 y2 Z1 I* w9 w( e. V1 F: s0 T主控芯片:STM32F103VET6
8 _$ n( ^2 C, r8 _接收端口:USART2(带反相电路)
! h/ {. N( ?. y6 [' LS.BUS设备:walkera RX-SBUS[2](配DEVO 10遥控器)
" D2 A7 _& u9 d& o& v" b3 ]- k
2.2 反相电路
, }" b/ J. ]7 }8 W6 f0 o% }由于此芯片串口不带反相器,我们需要外部搭建反相电路。如果芯片串口内部带反相器,可以省略此步。反相电路设计如下图:
! }3 K7 x6 k* H2 z- }% M
J1为4Pin排针,适配S.BUS接口,可5V输出为SBUS接收机供电。
. J: G2 V1 i3 r4 t; |' |! pJ1的Pin-4接S.BUS数据发送端,连接一个由NPN三极管构成的反相器,将反相后的信号送入芯片USART2的RXD引脚。
2 }; ?5 v1 z5 F* ^3 ^
3.程序设计
3 y, ^3 K- r" S; m3.1 数据接收
9 }5 W7 O+ J0 [+ N8 \分析一:根据 1.2 的协议解析,开始字节(0x0F)和结束字节(0x00)都是数据字节中很容易出现的字节,所以不能完全作为数据帧接收开始和结束的标志。, r4 y5 f* q* g( z' [$ s
分析二:每个数据帧之间的间隔至少4ms,则可以利用这个空闲时间来接收数据帧。(需要设计一个系统时钟)
6 Y. e$ l( ]  R, l; R) n9 F分析三:STM32 USART或UART有空闲中断,即检测到总线空闲(无数据传输),就产生中断。0 \/ I6 j6 B$ f* V% \0 P$ N2 ~
接收程序设计:综上,利用USART2接收中断(RXNE)来接收每个字节,利用USART2空闲中断(IDLE)来判断数据帧是否接收完毕。
USART2 初始化函数代码如下:
/**
9 X  u( f" ~& J* J* @name SBUS_Configuration
# V7 X: t  s8 _) }% n; {* @brief Configure SBUS(Usart2) clock, gpio and nvic:
9 f! h' K" C, N; t9 s  S: S' \* SBUS_RX USART2_RX PD63 g- G" D% a' I
* @param None2 a6 P5 ]4 P/ `6 ^0 k/ C
* @retval None
9 j- h1 ^) {. r8 v8 p*/! ~; F0 u# }7 ~, N* |
void SBUS_Configuration(void)
1 d' R- i3 L, B6 N{
0 W$ C# b* A+ J! y2 CGPIO_InitTypeDef GPIO_InitStructure;
: ~' x) Z9 d( hUSART_InitTypeDef USART_InitStructure;' }) J. L' x, Y4 Y+ }
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
2 h6 ?# z% {3 c% RRCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
' x/ O0 l6 ]! h$ A# R  t2 _1 `. t0 i5 R5 G
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;5 {+ k! w: J; J) M3 \
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
$ _' H6 O1 V8 F; B5 P* @GPIO_Init(GPIOD, &GPIO_InitStructure);& G/ I8 D; r3 Y/ w6 g5 Z/ h

/ J% h( y3 M. w/ v; Y& \GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
// 波特率100000 8个数据位 偶校验位 2个停止位
. I$ A- ~2 e* K1 N8 L6 AUSART_InitStructure.USART_BaudRate = 100000;
, k5 p, Q+ B' w  n' o! F$ K3 |' hUSART_InitStructure.USART_WordLength = USART_WordLength_8b;  //注意这里是9个数据长度(8个数据位+偶校验位)# |5 Y" t4 k8 h; M& i7 t& U: f
USART_InitStructure.USART_StopBits = USART_StopBits_2;- R9 o+ Q7 @) r
USART_InitStructure.USART_Parity = USART_Parity_Even;
7 ~9 I. V$ G3 |  q( P7 qUSART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
: y/ F, U) K3 [. i. q- sUSART_InitStructure.USART_Mode = USART_Mode_Rx;
USART_Init(USART2, &USART_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
6 O$ v0 _+ V+ A0 I* ENVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
4 v) y  w# F8 h# E' kNVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
+ @* a; }. ~* L: Y# `* f3 tNVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
6 `* q! y# _1 g- cNVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
! a# d  G* R; X5 ?$ wUSART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
USART_Cmd(USART2, ENABLE);) d( f1 K% f) r/ Y
}
" t2 t. {1 Y  s. C$ v5 Q) O, _6 |" n2 _1 @( P( X5 E
USART2 中断函数代码如下:
% x' A$ V/ g$ ]% f* B% s5 E3 p
uint8_t USART2_RX_BUF[26];
/**
" x' d" Y' M/ [4 G; D- j) `. w* @name USART2_IRQHandler- D1 p: ~, H1 }4 q8 E& @# }
* @brief This function handles USART2 Handler
/ G0 U% }% \4 ?4 n2 F! y* @param None: l- _: N+ `1 b+ i
* @retval None* S+ M1 Q# n  q9 i, u  j  G: ?% H
*/
1 R. X; N% y; w* J- Hvoid USART2_IRQHandler(void)
! }# D0 R  M1 ?3 N1 t7 T8 H  T2 Z{# I8 D. r6 w, y: I7 w
uint8_t res;' ~" e9 ?% d. D" q
uint8_t clear = 0;4 _  K) B1 N' m! d  `" l* S
static uint8_t Rx_Sta = 1;
7 l5 I4 x: m% C$ C$ z+ s0 ?; n& d  a2 @( t) f6 z
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)" G8 U/ N6 E" _) p
{# b4 ~7 Z7 B1 p4 E' b/ F; U
res =USART2->DR;
) I4 K  G5 `1 |  aUSART2_RX_BUF[Rx_Sta++] = res;
: F- t6 X+ H6 A}; N8 V: R  [) y# ?" N
else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)% m# l3 p% W5 O
{
2 U' |$ {  U8 `3 j! a. ]- Qclear = USART2->SR;6 e2 D) u! T% w1 N9 k2 H) ]
clear = USART2->DR;. i( \  R  }  s+ W( Q
USART2_RX_BUF[0] = Rx_Sta - 1;
. o! y9 r" U) mRx_Sta = 1;
+ s' L% G8 x3 }4 X1 u6 j}8 |% G& W. ?" v8 `" n3 ?
}
3 E6 G5 j, d5 D4 `" x( J6 H/ i. ^( n: o& m  h( A
USART2_RX_BUF为接收缓存区,定义为26个字节,第一个字节USART2_RX_BUF[0]为接收到的字节个数,后面为接收到的数据。' A, a2 U, Z' Q& b
USART2_RX_BUF[0]可以作为数据帧字节长度的判断。
  m5 B) W% I) _3 \# _& ~中断服务函数具体解释请参考STM32 串口接收不定长字节数据。
5 _- G' j3 B- G$ v+ f; I* ]5 k
3.2 数据处理
4 |; F/ L; {8 I: G直接上代码:
uint16_t CH[18]; // 通道值- B6 V4 t$ V9 b5 j
uint8_t rc_flag = 0;
void Sbus_Data_Count(uint8_t *sBusData)7 d" s0 L" F9 n' |+ @
{
tempData[ 0 ] = ( (sBusData[ 2]&0x07) << 8 ) + sBusData[ 1]; //sBus[ 2] low3 + sBus[ 1] low8& D- v- \5 X% O5 [2 f
tempData[ 1 ] = ( (sBusData[ 3]&0x3F) << 5 ) + (sBusData[ 2] >> 3 ); //sBus[ 3] low6 + sBus[ 2] high5% N) x1 b8 k- K: m. p
tempData[ 2 ] = ( (sBusData[ 5]&0x01) << 10) + (sBusData[ 4] << 2 ) + (sBusData[ 3] >> 6); //sBus[ 5] low1 + sBus[ 4] low8 + sBus[ 3] high2
" l. ?; H7 \5 i( ytempData[ 3 ] = ( (sBusData[ 6]&0x0F) << 7 ) + (sBusData[ 5] >> 1 ); //sBus[ 6] low4 + sBus[ 5] high78 o! Z/ Q: m1 B& {# Z* ^$ p, b& v
tempData[ 4 ] = ( (sBusData[ 7]&0x7F) << 4 ) + (sBusData[ 6] >> 4 ); //sBus[ 7] low7 + sBus[ 6] high4) r5 q7 q5 c1 E/ h
tempData[ 5 ] = ( (sBusData[ 9]&0x03) << 9 ) + (sBusData[ 8] << 1 ) + (sBusData[ 7] >> 7); //sBus[ 9] low2 + sBus[ 8] low8 + sBus[ 7] high1' @0 ~" d, j$ o/ r1 L/ b
tempData[ 6 ] = ( (sBusData[10]&0x1F) << 6 ) + (sBusData[ 9] >> 2 ); //sBus[10] low5 + sBus[ 9] high6
3 p, ^. [7 k' F* b. N, |tempData[ 7 ] = ( (sBusData[11]&0xFF) << 3 ) + (sBusData[10] >> 5 ); //sBus[11] low8 + sBus[10] high3+ g3 ?5 j9 w) j1 |- E0 V. ]! o

* c4 s8 u3 i, J4 |, k  O8 otempData[ 8 ] = ( (sBusData[13]&0x07) << 8 ) + sBusData[12]; //sBus[13] low3 + sBus[12] low8
0 A3 e( e& @3 xtempData[ 9 ] = ( (sBusData[14]&0x3F) << 5 ) + (sBusData[13] >> 3 ); //sBus[14] low6 + sBus[13] high54 c. C$ O( |$ d$ C! L
tempData[ 10 ] = ( (sBusData[16]&0x01) << 10) + (sBusData[15] << 2 ) + (sBusData[14] >> 6); //sBus[16] low1 + sBus[15] low8 + sBus[14] high27 X' b" J# y. F+ V8 ^/ A& Z% N
tempData[ 11 ] = ( (sBusData[17]&0x0F) << 7 ) + (sBusData[16] >> 1 ); //sBus[17] low4 + sBus[16] high7
8 m( M/ g" X- |( dtempData[ 12 ] = ( (sBusData[18]&0x7F) << 4 ) + (sBusData[17] >> 4 ); //sBus[18] low7 + sBus[17] high4
/ J. |# l7 K, `$ D; `1 Y, }, `1 VtempData[ 13 ] = ( (sBusData[20]&0x03) << 9 ) + (sBusData[19] << 1 ) + (sBusData[18] >> 7); //sBus[20] low2 + sBus[19] low8 + sBus[18] high18 E* \; Z6 C  d1 b: v5 x3 b' L6 d6 a9 W4 V
tempData[ 14 ] = ( (sBusData[21]&0x1F) << 6 ) + (sBusData[20] >> 2 ); //sBus[21] low5 + sBus[20] high65 c0 v8 S4 z$ h1 p# s
tempData[ 15 ] = ( (sBusData[22]&0xFF) << 3 ) + (sBusData[21] >> 5 ); //sBus[22] low8 + sBus[21] high3
; y% [1 S/ v9 C- h8 @+ Z& l
}( a+ k& O! _/ ?1 T) B
接收到的报文和解析出来的数据如下:
5 L7 ], h* C- s- J, X$ b" g7 M- kRX:0F E0 03 1F 58 C0 07 16 B0 80 05 2C 60 01 0B F8 C0 07 00 00 00 00 00 03 00
& n9 J+ W" O% k0 d7 ZCH: 992 992 352 992 352 352 352 352 352 352 992 992 000 000 000 000
* p# m* b/ q$ ORX:0F 60 01 0B 58 C0 07 66 30 83 19 7C 60 06 1F F8 C0 07 00 00 00 00 00 03 001 @! L" J" w, c- x& J2 c/ q0 m8 a
CH: 352 352 352 992 1632 1632 1632 992 1632 992 992 992 000 000 000 000
接收的byte24数据并非和协议解析中的一样,无论断开遥控器还是连接遥控器,读取的值都是0x03。
9 q* m, L& u3 c0 o接收机只支持12个通道,所以通道13-16没有值。
8 `9 M! x- O- [, Y读取的通道值中间值为992,最大值为1632,最小值为352。
sbus数据转为 PWM数据
RcData = (uint16_t)(sbusData * 1.2504 + 1761.1) / 2;
7 f+ {  M! H3 C  r2 |& g
4. 最后
; E& F' `1 N% n' q% [提供两个应用优化方向:
使用DMA+双缓存器+串口空闲中断读取和解析数据,提升MCU的工作效率。
5 H6 C7 ^  _' I1 y将读取的通道值转化成脉宽(0.5-2.5ms)输出,用来控制模拟信号设备。8 n! t# C" z  a$ }+ A
本协议解析就写到这里。

6 ?- g- w1 m1 v# O
收藏 1 评论1 发布时间:2022-1-27 22:58

举报

1个回答
cqxhhe 回答时间:2022-8-4 11:11:04
学习: Y+ ]! [* Q! o! }7 \2 Y! U, l

所属标签

相似分享

官网相关资源

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