日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于stm32和富斯遥控器的SBUS波形分析和通讯实现

發布時間:2023/12/20 编程问答 67 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于stm32和富斯遥控器的SBUS波形分析和通讯实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基于stm32和富斯遙控器的SBUS波形分析和通訊實現

  • 簡介
  • 軟件環境和硬件搭建
    • 軟件環境
    • 硬件搭建
  • SBUS協議
    • SBUS協議
    • 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端口設置 PC10 PC11GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE); //使能GPIOC時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE); //使能USART1時鐘//串口4對應引腳復用映射GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_UART4); //GPIOC10復用為USART4GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_UART4); //GPIOA11復用為USART4//USART1端口配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; //GPIOc10與GPIOc11GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復用輸出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化C10 C11//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_9b;//字長為8位數據格式USART_InitStructure.USART_StopBits = USART_StopBits_2;//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); //初始化串口1USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);//開啟串口接受中斷USART_Cmd(UART4, DISABLE); //使能串口1 } 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);//定時器TIM3初始化TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作為TIMx時鐘頻率除數的預分頻值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設置時鐘分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據指定的參數初始化TIMx的時間基數單位TIM_ClearITPendingBit(TIM3, TIM_IT_Update);TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中斷,允許更新中斷//中斷優先級NVIC設置NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中斷NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占優先級0級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //從優先級3級NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器TIM_Cmd(TIM3, DISABLE); //使能TIMx } void TIM3_IRQHandler(void) //TIM3中斷 {if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //檢查TIM3更新中斷發生與否{USART_Cmd(UART4, DISABLE); TIM_Cmd(TIM3, DISABLE);TIM3->CNT=0;if(rec_cnt==25){SbusDataParsing(rec_buff); //SBUS解析}else RecErr_Flag++;rec_cnt=0;WaitRec_cnt=0;TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx更新中斷標志 } }

解析函數:
SbusDataParsing(u8 buf[])為解析函數,如果TIM3判斷正確接收了25個字節的數據,則把串口接收到的25個數據放入buf[]數組內,執行完的數組結果ch[]就是我們需要的最終結果。

#include "sbus.h"u16 ch[16]={0}; void SbusDataParsing(u8 buf[]) //S-BUS解析 {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) //3000時大概為幾百us,持續幾百us都為高則開啟串口{USART_Cmd(UART4, ENABLE);}} }

其他.h文件
sbus.h

#ifndef __SBUS_H #define __SBUS_H #include "sys.h" #define RightRocker_Horizontal ch[0] //left:242 center:1033 right:1804 #define RightRocker_Vertical ch[1] //up:1807 center:1024 down:240 #define LeftRocker_Vertical ch[2] //up:1805 center:1024 down:240 #define LeftRocker_Horizontal ch[3] //left:240 center:1025 right:1807 #define SWA ch[4] //up:240 down: 1807 #define SWB ch[5] //up:240 center:1024 down: 1807 #define SWC ch[6] //up:240 center:1024 down: 1807 #define SWD ch[7] //up:240 down: 1807 #define VAA ch[8] //left:240 center:1024 right:1807 #define VAB ch[9] //left:1807 center:1024 right:240 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] //left:242 center:1033 right:1804

實際就是遙控器右邊搖桿水平撥動時對應的是ch[0]中的值的變化。不撥動時ch[0]值是1033,右搖桿撥到最左邊時ch[0]是242,最右邊時ch[0]是1804.不同的遙控器中間值和最大最小值會有小范圍的偏差,一般不會超過幾十。其他搖桿和按鍵的對應關系請自行體會。也可以去B站看實際控制遙控器對應的CH[]變化,不過是16進制的看著不是很方便:
https://www.bilibili.com/video/BV1Kv411k7fQ
最后附一張剛買的一個遙控器的初始值調試結果的截圖:

總結

以上是生活随笔為你收集整理的基于stm32和富斯遥控器的SBUS波形分析和通讯实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。