基于stm32和富斯遙控器的SBUS波形分析和通訊實現
簡介
最近一個小項目用到了富斯的遙控器(使用的SBUS協議),目的是實現通過遙控器的各個通道對小車進行簡單控制(移動、燈光、不同工作模式等),一點小經驗和大家分享下。SBUS網上的資料很多,本篇更偏向于新人對SBUS的快速理解和直接應用,對一些不太常用的細則不再進行介紹。
因為是第一次使用SBUS協議,根據個人習慣在學習通訊協議時喜歡對照著實際波形理解,如果有朋友對硬件有簡單了解,建議接觸新的通訊協議時也用示波器配合實際波形來學習,能發現很多細節。當然這個不是必須的,僅是個人建議而已,實際波形我也會貼出供感興趣的朋友參考。
其他細節如有疏漏還請各位指出,共同進步。
軟件環境和硬件搭建
軟件環境
編譯軟件:KEIL MDK
庫:STM32標準庫
單片機I/O使用:PC11(串口USART4 RX端,TX端不接即可)
單片機外設使用:USART4(接收遙控數據)、TIM3(定時驗證數據正確性)
硬件搭建
發射裝置:富斯遙控器FS-I6S
接收裝置:接收機IA10B
MCU控制板:STM32F407電路板
外接電路:簡單的三極管反向電路(必須)
發射裝置和接收裝置之間只要是SBUS通訊方式,不同型號理論來說影響不大,程序可以通用。
因為只需要用到單片機的串口(為了驗證數據的正確性筆者多用了個定時器TIM3),所以只是實現通訊的話電路要求比較簡單,只要能正常工作并帶有串口外設的單片機板即可,比如某寶上賣的STM32F103最小系統板。
由于SBUS邏輯電平和常用的串口通訊極性剛好相反,所以需要搭建一個簡單的三極管反向電路,電路參考下圖。
遙控器需要配置為SBUS輸出模式:
接收機接線如下:
綠線為信號線-----接三極管反向電路的輸入端(Single)
黃線為電源+線-----接5V電源
藍線為電源地線 -----接電源GND
總體連接如下:
SBUS協議
SBUS協議
SBUS協議其實就是串口通訊(USART)的應用層協議,它的本質還是USART通訊。可以粗暴理解為一幀SBUS數據是由連續發送或接收25個字節(即25次)的串口數據構成,第一個字節固定為0x0F,最后一個字節固定為0x00,中間23個字節和起來構成了所需數據。所以使用它在程序上還是使用串口,只不過在串口配置上必須按照以下參數配置:
串口波特率為100000,數據位為8位,2個停止位,偶校驗,無硬件控流。
Sbus的編碼方式為每11位為一個數據,除去第一個字節和第25個字節,需要把中間23個字節的常規8位數據合在一起,并按每11位為一組的格式進行解析處理。具體解析方法網上教程較多,不再贅述。如果不想了解具體解析方法,可直接引用下文的解析函數得出解析后的結果即可。
SBUS波形分析
位長度:
SBUS的波特率固定為100K,所以每傳輸一位的時間為:1/100K=10us,
隨機用示波器抓取了一位,實測結果略微有誤差為11.7us,在接受范圍內。
字節長度:
SBUS一幀由25次串口接收或發送構成(25個字節),每次串口發送有12位組成:1個起始位+8個數據位+1個偶校驗位+2個停止位。下圖為截取一幀SBUS前幾個數據字節波形。由于發送順序遵循LSB(低位優先)原則,所以需要注意每個字節高位和低位的波形和實際結果顛倒的。如波形第一個字節為0xF0,實際數據為0x0F。
幀長度
SBUS一幀由25個字節構成,每個字節12位,每位長度10us,總長度=10us12位25個字節=3000us(糾正:圖中3000us單位錯打成了3000ms)。
幀間隔
SBUS兩幀間間隔約4.68ms,如果要求不能漏掉任何一幀,則需要注意其他程序處理時間必須在4.68ms內,不能影響一下幀的接收。
程序部分
程序流程
程序執行流程:上電-----配置外設(USART4、TIM3,默認使能都為關閉狀態,TIM3定時3ms)-----等待PC11出現持續一段時間的高電平后使能USART4,等待接收第一個字節(等待的持續高電平即為兩幀間的高電平間隔部分,確保能從第一個字節接收)-----當串口收到數據后使能TIM3-----當TIM3時間到后關閉TIM3和USART4判斷串口是否是剛好收到25個字節-----是則執行解析函數,不是則為接收錯誤-----重新等待持續的高電平。
Created with Rapha?l 2.2.0上電初始化USAER4、TIM3讀取PC11電平是否出現持續的高電平?使能USART4,等待接收第一個字節收到第一個字節則使能TIM3TIM3時間滿后(3ms)判斷是否完整收到25個字節對收到的數據進行解析yesnoyesno
核心程序
程序是基于STM32F407的,如果是103可能在系統頭文件名上報錯和USART配置時會有點小差別。
USART4配置及其中斷函數:
一定要注意因為有一個偶校驗位,數據長度要寫為9:
USART_InitStructure.USART_WordLength = USART_WordLength_9b。
中斷內的函數功能為:進中斷開TIM3定時器,把收到的串口數據進行保存。
#include
"sys.h"
#include
"usart.h" u8 rec_buff
[30]={0};
u8 rec_cnt
=0;
extern u32 WaitRec_cnt
;void Uart4_Init(u32 bound
){GPIO_InitTypeDef GPIO_InitStructure
;USART_InitTypeDef USART_InitStructure
;NVIC_InitTypeDef NVIC_InitStructure
;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC
,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4
,ENABLE); GPIO_PinAFConfig(GPIOC,GPIO_PinSource10
,GPIO_AF_UART4); GPIO_PinAFConfig(GPIOC,GPIO_PinSource11
,GPIO_AF_UART4); GPIO_InitStructure
.GPIO_Pin
= GPIO_Pin_10
| GPIO_Pin_11
; GPIO_InitStructure
.GPIO_Mode
= GPIO_Mode_AF
;GPIO_InitStructure
.GPIO_Speed
= GPIO_Speed_50MHz
; GPIO_InitStructure
.GPIO_OType
= GPIO_OType_PP
; GPIO_InitStructure
.GPIO_PuPd
= GPIO_PuPd_UP
; GPIO_Init(GPIOC,&GPIO_InitStructure
); NVIC_InitStructure
.NVIC_IRQChannel
= UART4_IRQn
;NVIC_InitStructure
.NVIC_IRQChannelPreemptionPriority
=3 ;NVIC_InitStructure
.NVIC_IRQChannelSubPriority
= 3; NVIC_InitStructure
.NVIC_IRQChannelCmd
= ENABLE; NVIC_Init(&NVIC_InitStructure
); USART_InitStructure
.USART_BaudRate
= bound
;USART_InitStructure
.USART_WordLength
= USART_WordLength_9b
;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_Mode_Tx
; USART_Init(UART4, &USART_InitStructure
); USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);USART_Cmd(UART4, DISABLE); }
void UART4_IRQHandler(void)
{if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET){rec_buff
[rec_cnt
]=UART4->DR;rec_cnt
++;WaitRec_cnt
=0;TIM_Cmd(TIM3, ENABLE); }USART_ClearITPendingBit(UART4, USART_IT_RXNE);
}
TIM3配置及其中斷函數:
TIM3時間在實際應用時是3ms進定時器中斷,理論上3ms能剛好把一幀SBUS(25個字節)接收完畢。因為是已經接收到第一個串口數據后才開的定時器,后續只會有24個字節的時間,所以實際上定時器3ms時間還留有一個字節的時間裕量。
TIM3的中斷函數功能:即為判斷串口是否正確接收了一幀SBUS(25個字節)數據,是則進行數據解析函數SbusDataParsing(u8 buf[]) ;,不是則錯誤位RecErr_Flag+1。
#include
"tim.h"
#include
"usart.h"
#include
"sbus.h"
u8 RecErr_Flag
;
extern u8 rec_cnt
;
u32 WaitRec_cnt
;
extern u8 rec_buff
[30];
void TIM3_Init(u16 arr
,u16 psc
)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure
;NVIC_InitTypeDef NVIC_InitStructure
;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3
, ENABLE); TIM_DeInit(TIM3);TIM_TimeBaseStructure
.TIM_Period
= arr
; TIM_TimeBaseStructure
.TIM_Prescaler
=psc
; TIM_TimeBaseStructure
.TIM_ClockDivision
= TIM_CKD_DIV1; TIM_TimeBaseStructure
.TIM_CounterMode
= TIM_CounterMode_Up
; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure
); TIM_ClearITPendingBit(TIM3, TIM_IT_Update
);TIM_ITConfig(TIM3,TIM_IT_Update
,ENABLE ); NVIC_InitStructure
.NVIC_IRQChannel
= TIM3_IRQn
; NVIC_InitStructure
.NVIC_IRQChannelPreemptionPriority
= 2; NVIC_InitStructure
.NVIC_IRQChannelSubPriority
= 2; NVIC_InitStructure
.NVIC_IRQChannelCmd
= ENABLE; NVIC_Init(&NVIC_InitStructure
); TIM_Cmd(TIM3, DISABLE);
}
void TIM3_IRQHandler(void)
{if (TIM_GetITStatus(TIM3, TIM_IT_Update
) != RESET) {USART_Cmd(UART4, DISABLE); TIM_Cmd(TIM3, DISABLE);TIM3->CNT=0;if(rec_cnt
==25){SbusDataParsing(rec_buff
); }else RecErr_Flag
++;rec_cnt
=0;WaitRec_cnt
=0;TIM_ClearITPendingBit(TIM3, TIM_IT_Update
); }
}
解析函數:
SbusDataParsing(u8 buf[])為解析函數,如果TIM3判斷正確接收了25個字節的數據,則把串口接收到的25個數據放入buf[]數組內,執行完的數組結果ch[]就是我們需要的最終結果。
#include
"sbus.h"u16 ch
[16]={0};
void SbusDataParsing(u8 buf
[])
{ch
[0] = ((u16
)buf
[ 1] >> 0 | ((int16_t
)buf
[ 2] << 8 )) & 0x07FF;ch
[1] = ((u16
)buf
[ 2] >> 3 | ((int16_t
)buf
[ 3] << 5 )) & 0x07FF;ch
[2] = ((u16
)buf
[ 3] >> 6 | ((int16_t
)buf
[ 4] << 2 ) | (int16_t
)buf
[ 5] << 10 ) & 0x07FF;ch
[3] = ((u16
)buf
[ 5] >> 1 | ((int16_t
)buf
[ 6] << 7 )) & 0x07FF;ch
[4] = ((u16
)buf
[ 6] >> 4 | ((int16_t
)buf
[ 7] << 4 )) & 0x07FF;ch
[5] = ((u16
)buf
[ 7] >> 7 | ((int16_t
)buf
[ 8] << 1 ) | (int16_t
)buf
[9] << 9 ) & 0x07FF;ch
[6] = ((u16
)buf
[ 9] >> 2 | ((int16_t
)buf
[10] << 6 )) & 0x07FF;ch
[7] = ((u16
)buf
[10] >> 5 | ((int16_t
)buf
[11] << 3 )) & 0x07FF;ch
[8] = ((u16
)buf
[12] << 0 | ((int16_t
)buf
[13] << 8 )) & 0x07FF;ch
[9] = ((u16
)buf
[13] >> 3 | ((int16_t
)buf
[14] << 5 )) & 0x07FF;ch
[10] = ((u16
)buf
[14] >> 6 | ((int16_t
)buf
[15] << 2 ) | (int16_t
)buf
[16] << 10 ) & 0x07FF;ch
[11] = ((u16
)buf
[16] >> 1 | ((int16_t
)buf
[17] << 7 )) & 0x07FF;ch
[12] = ((u16
)buf
[17] >> 4 | ((int16_t
)buf
[18] << 4 )) & 0x07FF;ch
[13] = ((u16
)buf
[18] >> 7 | ((int16_t
)buf
[19] << 1 ) | (int16_t
)buf
[20] << 9 ) & 0x07FF;ch
[14] = ((u16
)buf
[20] >> 2 | ((int16_t
)buf
[21] << 6 )) & 0x07FF;ch
[15] = ((u16
)buf
[21] >> 5 | ((int16_t
)buf
[22] << 3 )) & 0x07FF;
}
主函數:
主函數的主要功能:上電配置串口USART4和定時器TIM3,然后while循環檢查串口USART4的RX引腳PC11是否出現連續的高電平。每次while循環一次檢測是高則WaitRec_cnt+1,是低則清0,直到出現一段連續的高電平就表明進入了兩幀SBUS中間的幀間隔中,再開啟串口確保能從第一個字節開始接收。實際的WaitRec_cnt時間不用特別精確但需要大家進行調試,不同單片機主頻不同,執行while的時間也不同。STM32F407主頻168M,WaitRec_cnt執行到3000時大概700多us。同時需要自行考慮持續多久開啟串口中斷比較好,不要影響到其他程序的運行。
#include
"stm32f4xx.h"
#include
"usart.h"
#include
"tim.h"
extern u32 WaitRec_cnt
;int
main(void)
{ Uart4_Init(100000); TIM3_Init(29,8399);while(1){ if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_11
)==1) {WaitRec_cnt
++;}else WaitRec_cnt
=0;if(WaitRec_cnt
>3000) {USART_Cmd(UART4, ENABLE);}}
}
其他.h文件
sbus.h
#ifndef __SBUS_H
#define __SBUS_H
#include
"sys.h"
#define RightRocker_Horizontal ch
[0]
#define RightRocker_Vertical ch
[1]
#define LeftRocker_Vertical ch
[2]
#define LeftRocker_Horizontal ch
[3]
#define
SWA ch
[4]
#define
SWB ch
[5]
#define
SWC ch
[6]
#define
SWD ch
[7]
#define
VAA ch
[8]
#define
VAB ch
[9]
void SbusDataParsing(u8 buf
[]);
#endif
usart.h
#ifndef __UART_H
#define __UART_H
#include
"stdio.h"
#include
"sys.h" void Uart4_Init(u32 bound
);
void Uart1_Init(u32 bound
);
#endif
tim.h
#ifndef __TIMER_H
#define __TIMER_H
#include
"sys.h"void TIM3_Init(u16 arr
,u16 psc
);#endif
總結
至此,整個SBUS的通訊已經完成,通訊的最終結果存放在CH[]數組里以方便調用。遙控器上不同的搖桿和撥動開關對應不同的CH[]通道,sbus.h里也有進行宏定義以便大家進行遙控的按鈕和CH[]通道的對應:
如
#define RightRocker_Horizontal ch
[0]
實際就是遙控器右邊搖桿水平撥動時對應的是ch[0]中的值的變化。不撥動時ch[0]值是1033,右搖桿撥到最左邊時ch[0]是242,最右邊時ch[0]是1804.不同的遙控器中間值和最大最小值會有小范圍的偏差,一般不會超過幾十。其他搖桿和按鍵的對應關系請自行體會。也可以去B站看實際控制遙控器對應的CH[]變化,不過是16進制的看著不是很方便:
https://www.bilibili.com/video/BV1Kv411k7fQ
最后附一張剛買的一個遙控器的初始值調試結果的截圖:
總結
以上是生活随笔為你收集整理的基于stm32和富斯遥控器的SBUS波形分析和通讯实现的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。