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

【经验分享】stm32 解析sbus

[复制链接]
STMCU小助手 发布时间:2022-1-27 22:58
1. S.BUS
3 J; V  m, f1 r! O" |  B& ]0 y1.1 协议介绍
S.BUS是FUTABA提出的舵机控制总线,全称Serial Bus,别名S-BUS或SBUS,也称 Futaba S.BUS。% `# i- {( |6 ]0 x3 ?. k# F
S.BUS是一个串行通信协议,也是一个数字串行通信接口(单线),适合与飞控连接。它可以连接很多设备,每个设备通过一个HUB与它相连,得到各自的控制信息。2 |- Z  w6 k6 L; o* B# Z
S.BUS可以传输16个比例通道和2个数字(bool)通道。其硬件上基于RS232协议,采用TTL电平,但高位取反(负逻辑,低电平为“1”,高电平为“0”),通信波特率为100K(不兼容波特率115200)。
1.2 协议解析7 |7 D; c8 a! U* I2 G
通信接口:USART(TTL)* f* _* l/ M+ R# \8 H
通信参数:1个起始位+8个数据位+偶校验位+2个停止位,波特率=100000bit/s,电平逻辑反转。
" E& f* f# `6 ^+ _- x& k7 g4 z1 T$ P通信速率:每14ms(模拟模式)或7ms(高速模式)发送,即数据帧间隔为 11ms(模拟模式)或4ms(高速模式)。7 c  e0 a: A" P- s
数据帧格式:[1]
! _' u, T! b: ]( H+ M! L! k字节位 byte1 byte2-23 byte24 byte25
1 ?  X5 t7 P. I0 N' m类型 开始字节 通道数据字节(含16个脉宽通道) 标志位字节(含2个数字通道) 结束字节0 g+ Q& Q! a+ Y& R/ G
数据 0x0F 通道数据范围11Bits = [0,2047] 2个数字通道位+2个状态位 0x00
  M' f: w& K; E9 o) B3 ubyte1:
: k0 P9 y9 ~, i5 A- b+ C3 k* gstartbyte = 0000 1111b (0x0F)
byte2-23:
! c7 M( i& c! I+ A, N- Q- ^databytes = 22bytes = 22 x 8Bits = 16 x 11Bits(CH1-16)
! z  B, f3 h+ J7 P3 I1 W+ E通道数据低位在前,高位在后,每个数据取11位,具体协议如下:* g+ Q: o$ U; d2 m
读取的databyte值:
byte 2 3 4 5 6 7 etc
2 u' P" e! G$ ~内容 12345678 12345678 12345678 12345678 12345678 12345678 etc
2 c: ?4 Z7 g$ v( B4 V& Y7 t8 Z! U. s转化后的通道值:
通道 CH01 CH02 CH03 CH04 etc
' k# U1 u: _1 V内容 67812345678 34567812345 81234567812 56781234567 etc* N0 a* g% s$ ]4 D7 y( j6 i
byte24:
Bit 7 6 5 4 3 2 1 0. h+ y3 `' H' U; {
含义 数字通道CH17 数字通道CH18 帧丢失位 故障保护激活位 N/A N/A N/A N/A
/ d2 Q. c/ p) c/ r' Xbyte25:
$ n9 F# G) _3 nendbyte = 0000 0000b (0x00)
( C9 c$ J2 u& C4 I
2. 硬件设计
& Z: t" W8 N& G2.1 硬件参数8 J9 l- s/ i! d9 [3 u; R" }
主控芯片:STM32F103VET6/ ^3 _+ ^( ?4 J, D# K% X
接收端口:USART2(带反相电路)1 H; T  ]  k5 B  U) }8 u5 Z
S.BUS设备:walkera RX-SBUS[2](配DEVO 10遥控器)

: [9 G) `- A. j& p: p4 w6 [2.2 反相电路! t5 O1 J/ }: k' t. f: Z- A
由于此芯片串口不带反相器,我们需要外部搭建反相电路。如果芯片串口内部带反相器,可以省略此步。反相电路设计如下图:

& A7 o/ r# U, k7 k0 f2 n0 EJ1为4Pin排针,适配S.BUS接口,可5V输出为SBUS接收机供电。8 ]) x1 @+ Q& L3 G- M$ e4 u, I! m
J1的Pin-4接S.BUS数据发送端,连接一个由NPN三极管构成的反相器,将反相后的信号送入芯片USART2的RXD引脚。
* k! d9 ?6 z" w( x  \4 e% _
3.程序设计
+ P: c" ~5 s5 x( ]5 d  n/ z3.1 数据接收
6 x& S5 G' D& [分析一:根据 1.2 的协议解析,开始字节(0x0F)和结束字节(0x00)都是数据字节中很容易出现的字节,所以不能完全作为数据帧接收开始和结束的标志。5 @0 Y  I$ q2 b0 Q; a9 k
分析二:每个数据帧之间的间隔至少4ms,则可以利用这个空闲时间来接收数据帧。(需要设计一个系统时钟)0 t' e- H8 D# G6 g3 q+ p  w
分析三:STM32 USART或UART有空闲中断,即检测到总线空闲(无数据传输),就产生中断。
; c6 C4 @& e) }+ @+ n% ^4 ~7 `接收程序设计:综上,利用USART2接收中断(RXNE)来接收每个字节,利用USART2空闲中断(IDLE)来判断数据帧是否接收完毕。
USART2 初始化函数代码如下:
/**/ E; x/ W% h- D0 v" c1 l
* @name SBUS_Configuration) v9 @# y$ Z7 e. J6 H8 ]$ p
* @brief Configure SBUS(Usart2) clock, gpio and nvic:+ d2 M' E( w. q7 Q% y: y( U, A% ]
* SBUS_RX USART2_RX PD6
& a5 c: |* I7 S' }* @param None
) s' \1 O% p( v' l* @retval None" G7 n1 j+ s! Z% G$ H/ {
*/
2 ]1 H" y) g; t. s3 Hvoid SBUS_Configuration(void)0 \% S5 Y! K, N5 u1 Q
{
- z% w+ _0 u) ]$ `6 ]GPIO_InitTypeDef GPIO_InitStructure;3 D" {* B8 X0 O% x
USART_InitTypeDef USART_InitStructure;
# t7 H* }1 q0 D1 E# L6 F( V9 [NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);& f* S5 h  h; Q/ P( G
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
. ]' z' A. h1 w
7 ^6 B- X) y( k# @$ K! |6 ?2 x' mGPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;( Q# ?, P0 o  k9 c5 I( L* C
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
9 z# o) g) r5 a! ^GPIO_Init(GPIOD, &GPIO_InitStructure);
: T. q* V- Z6 }& W  n1 ^9 z, p$ y% S( K( X% c" |1 Y! l
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
// 波特率100000 8个数据位 偶校验位 2个停止位
2 Y; ?" t1 ]# }( wUSART_InitStructure.USART_BaudRate = 100000;
& Q6 R' o9 e& N8 Q9 JUSART_InitStructure.USART_WordLength = USART_WordLength_8b;  //注意这里是9个数据长度(8个数据位+偶校验位)7 f& Q- B1 C2 f% L
USART_InitStructure.USART_StopBits = USART_StopBits_2;
& N9 p! h4 V* e  B- E8 m  YUSART_InitStructure.USART_Parity = USART_Parity_Even;
, o& c( u8 p! D* Z9 z% D* j- qUSART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;4 l& X% b" v8 u! R2 s
USART_InitStructure.USART_Mode = USART_Mode_Rx;
USART_Init(USART2, &USART_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;! D" w1 _; t5 @8 e, S! [2 ^
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;! b9 P6 `- j  Z6 a# {* C9 O* g( s
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
4 r" ^6 F. z; ?% Q3 l* X4 o6 FNVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;$ {  e( X- Z; p7 |
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);( @5 y1 I$ Z3 A( |
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
USART_Cmd(USART2, ENABLE);  Y. q; Z' t! N; S
}
% A6 c/ V( F( C$ z1 Q3 O
! [/ K9 Q6 r2 q: n" E& UUSART2 中断函数代码如下:

+ F% X4 ]5 I5 I( P' C  {) Juint8_t USART2_RX_BUF[26];
/**' p% ^7 j0 X( P: C% h% S
* @name USART2_IRQHandler: }" t& `( k( t+ {8 ~, O; V
* @brief This function handles USART2 Handler
0 t( ]1 ~: o/ Q8 m) ~% G* @param None
: ~( j; z) ^/ G9 [3 Y! P5 w* @retval None; s( C2 n5 Z+ v' X& b6 h- K4 h
*/
$ V+ Y. |6 Q9 t  S& }void USART2_IRQHandler(void)- P9 }7 \7 @) Q; _) o4 ~9 N
{
3 W6 H& _6 b' |4 ~# H9 W' ]uint8_t res;
+ _( ^6 j! q' }$ a/ G; K; [, suint8_t clear = 0;' \/ Y% e7 c# v8 u9 n6 p3 r
static uint8_t Rx_Sta = 1;( G3 q" |8 d, W1 W+ N6 w
& G) }3 i0 O, C. a
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
. l. Y& s0 L# B4 a+ d; P: W8 K, q{
, I2 F5 }4 T9 s0 u3 H0 Tres =USART2->DR;) {# N( v: y) s! t0 f6 Q* g
USART2_RX_BUF[Rx_Sta++] = res;
) e  }( ^9 q: U0 o0 l- O  i}6 S: s# |% c! B0 H2 ]% F5 d* u
else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET), _9 M& k, _( r
{
' k. k2 u4 c, Q- jclear = USART2->SR;! r$ ?  ?8 O+ V5 @' J
clear = USART2->DR;5 R6 L+ Y" ]& Z7 L# K
USART2_RX_BUF[0] = Rx_Sta - 1;6 Y; W& m! Q' v9 A
Rx_Sta = 1;
- g/ s0 f' j0 }& T" H}0 T; a$ T. `( `( c: d. S
}" b1 m' i! W4 @" z6 [% \& ~; M

" e: d4 T1 z( z* S" W- _: h0 SUSART2_RX_BUF为接收缓存区,定义为26个字节,第一个字节USART2_RX_BUF[0]为接收到的字节个数,后面为接收到的数据。/ E9 x( V# [4 ^- y
USART2_RX_BUF[0]可以作为数据帧字节长度的判断。8 Q* C0 v+ V. l- b5 W" p
中断服务函数具体解释请参考STM32 串口接收不定长字节数据。
- H- c) d$ w! f7 }+ D$ K8 |
3.2 数据处理
' d& _4 A: ^+ m) o1 b1 Q$ l* q& p直接上代码:
uint16_t CH[18]; // 通道值# F% E' `: U( [& ]/ J9 N. M, V
uint8_t rc_flag = 0;
void Sbus_Data_Count(uint8_t *sBusData)( j) P% \& d4 W$ J0 s8 N% h: v
{
tempData[ 0 ] = ( (sBusData[ 2]&0x07) << 8 ) + sBusData[ 1]; //sBus[ 2] low3 + sBus[ 1] low8
/ {* o7 z/ Q+ e: StempData[ 1 ] = ( (sBusData[ 3]&0x3F) << 5 ) + (sBusData[ 2] >> 3 ); //sBus[ 3] low6 + sBus[ 2] high5
, p1 g% Y( Y3 H5 w0 rtempData[ 2 ] = ( (sBusData[ 5]&0x01) << 10) + (sBusData[ 4] << 2 ) + (sBusData[ 3] >> 6); //sBus[ 5] low1 + sBus[ 4] low8 + sBus[ 3] high2' }+ u# s" t8 \7 w, r  ~. i
tempData[ 3 ] = ( (sBusData[ 6]&0x0F) << 7 ) + (sBusData[ 5] >> 1 ); //sBus[ 6] low4 + sBus[ 5] high7
* M+ w! F4 L% ^9 stempData[ 4 ] = ( (sBusData[ 7]&0x7F) << 4 ) + (sBusData[ 6] >> 4 ); //sBus[ 7] low7 + sBus[ 6] high4
! H" ?+ r; U9 ^2 r8 ^tempData[ 5 ] = ( (sBusData[ 9]&0x03) << 9 ) + (sBusData[ 8] << 1 ) + (sBusData[ 7] >> 7); //sBus[ 9] low2 + sBus[ 8] low8 + sBus[ 7] high10 V1 p# b) u! V
tempData[ 6 ] = ( (sBusData[10]&0x1F) << 6 ) + (sBusData[ 9] >> 2 ); //sBus[10] low5 + sBus[ 9] high6; T( L$ j1 {3 ?" Y' g4 }
tempData[ 7 ] = ( (sBusData[11]&0xFF) << 3 ) + (sBusData[10] >> 5 ); //sBus[11] low8 + sBus[10] high3: |2 F  D! g1 ^* G( d0 R( H9 c
2 _8 H. }! N7 o5 A4 R9 F' l7 D$ N: ^
tempData[ 8 ] = ( (sBusData[13]&0x07) << 8 ) + sBusData[12]; //sBus[13] low3 + sBus[12] low89 w5 Q4 e+ R0 B& r
tempData[ 9 ] = ( (sBusData[14]&0x3F) << 5 ) + (sBusData[13] >> 3 ); //sBus[14] low6 + sBus[13] high5) q$ S0 A3 ^' ]8 w9 e$ K' O% S
tempData[ 10 ] = ( (sBusData[16]&0x01) << 10) + (sBusData[15] << 2 ) + (sBusData[14] >> 6); //sBus[16] low1 + sBus[15] low8 + sBus[14] high26 w! @; [) R5 x/ g1 C2 D
tempData[ 11 ] = ( (sBusData[17]&0x0F) << 7 ) + (sBusData[16] >> 1 ); //sBus[17] low4 + sBus[16] high79 B$ }, u" B1 |  Y, i& R% c
tempData[ 12 ] = ( (sBusData[18]&0x7F) << 4 ) + (sBusData[17] >> 4 ); //sBus[18] low7 + sBus[17] high4
4 ^4 O" W7 ^, Z; j7 b8 Q! i" J( jtempData[ 13 ] = ( (sBusData[20]&0x03) << 9 ) + (sBusData[19] << 1 ) + (sBusData[18] >> 7); //sBus[20] low2 + sBus[19] low8 + sBus[18] high1
) f  D: `# f7 X0 V& OtempData[ 14 ] = ( (sBusData[21]&0x1F) << 6 ) + (sBusData[20] >> 2 ); //sBus[21] low5 + sBus[20] high6
; p. Q- k' E6 y! y, EtempData[ 15 ] = ( (sBusData[22]&0xFF) << 3 ) + (sBusData[21] >> 5 ); //sBus[22] low8 + sBus[21] high3
# d' n- S7 w& A
}8 `  |" R# L4 O2 G7 ^
接收到的报文和解析出来的数据如下:* o7 x* e, t* a0 d$ M  [$ {
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 00. O/ s4 R) e2 W  T
CH: 992 992 352 992 352 352 352 352 352 352 992 992 000 000 000 0003 z) q1 e& j# I5 j
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
2 F2 K; q, v4 j$ M8 _CH: 352 352 352 992 1632 1632 1632 992 1632 992 992 992 000 000 000 000
接收的byte24数据并非和协议解析中的一样,无论断开遥控器还是连接遥控器,读取的值都是0x03。( W1 Y- B3 v3 O
接收机只支持12个通道,所以通道13-16没有值。; V2 x7 ~1 ^6 l8 |
读取的通道值中间值为992,最大值为1632,最小值为352。
sbus数据转为 PWM数据
RcData = (uint16_t)(sbusData * 1.2504 + 1761.1) / 2;
1 h9 V- b3 X4 ?. L
4. 最后
: U' `1 Z$ Z# S2 z7 Z提供两个应用优化方向:
使用DMA+双缓存器+串口空闲中断读取和解析数据,提升MCU的工作效率。
, A7 x6 s9 ^- a0 H将读取的通道值转化成脉宽(0.5-2.5ms)输出,用来控制模拟信号设备。% \! Y4 A1 a# I* H+ |' b  F
本协议解析就写到这里。
: F" h! G+ b" e: g
收藏 1 评论1 发布时间:2022-1-27 22:58

举报

1个回答
cqxhhe 回答时间:2022-8-4 11:11:04
学习( G4 j& m$ V9 r/ }, E

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版