STM32解析SBUS信号例程详解
文章目錄
- 1. SBUS信號簡介
- 2. STM32F7解析SBUS信號例程
- (1) 串口配置
- (2) 串口中斷接收
- (3) 信號解析
1. SBUS信號簡介
最近在搞一個項目的通信和控制,用到了SBUS,記錄一下心得。
SBUS全稱serial-bus,是一種串口通信協(xié)議,廣泛應用于航模遙控器(接收機)中。只用一根信號線就能傳輸多達16通道的數(shù)據(jù),比多路PWM捕獲高效且省資源。
100k波特率,8位數(shù)據(jù)位,2位停止位,偶校驗(EVEN),無控流,25個字節(jié)。
[startbyte] [data1][data2]…[data22][flags][endbyte]
startbyte=0x0f;
endbyte=0x00;
data1…data22: LSB(低位在前),對應16個通道(ch1-ch16),每個通道11bit(22 × 8=16 × 11);
flag位標志遙控器的通訊狀態(tài),我使用的樂迪AT9S在遙控器通上的時候是0x00,斷開的時候是0xC0,可以通過查詢flag位來采取失控保護。
航模遙控器輸出的PWM值是1000~2000,中值為1500,sbus輸出的會不一樣,例如樂迪AT9S的范圍為300 ~ 1700,中值1000,這個我估計跟遙控器廠家有關(guān)。
這個地方一定要萬分注意,必須加硬件反相器,因為SBUS的信號是采用的負邏輯,也就是電平相反,不要試圖在軟件里面取反,因為軟件里面只能操作數(shù)據(jù)位(記得串口配置里面的數(shù)據(jù)位8么),你是操作不了停止位、校驗位啥的!!
如果是自己畫板子也很簡單,如圖所示
5. 數(shù)據(jù)讀取
一般的串口調(diào)試助手可能沒有100K波特率的選項,推薦一個串口調(diào)試助手MicroLab,可以自定義串口波特率,還有其他好功能自己探索叭。
2. STM32F7解析SBUS信號例程
清楚了通信協(xié)議,解析就很簡單了。我使用的是正點原子的阿波羅F7開發(fā)板,其他的板子是一樣的。
(1) 串口配置
首先一些變量聲明,串口uart.c里用到的
#define USART_REC_LEN 100 //定義最大接收字節(jié)數(shù) 200 #define RXBUFFERSIZE 1 //緩存大小u8 USART1_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節(jié). u16 USART1_RX_STA = 0; //接收狀態(tài)標記 u8 aRxBuffer1[RXBUFFERSIZE]; //HAL庫使用的串口接收緩沖 UART_HandleTypeDef UART1_Handler; //UART句柄串口初始化函數(shù)
void uart1_init(u32 bound) {//UART 初始化設置UART1_Handler.Instance = USART1; //USART1UART1_Handler.Init.BaudRate = bound; //波特率UART1_Handler.Init.WordLength = UART_WORDLENGTH_9B; //字長為8位數(shù)據(jù)格式UART1_Handler.Init.StopBits = UART_STOPBITS_1; //一個停止位UART1_Handler.Init.Parity = UART_PARITY_EVEN; //無奇偶校驗位UART1_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; //無硬件流控UART1_Handler.Init.Mode = UART_MODE_TX_RX; //收發(fā)模式HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()會使能UART1HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer1, RXBUFFERSIZE); //該函數(shù)會開啟接收中斷:標志位UART_IT_RXNE,并且設置接收緩沖以及接收緩沖接收最大數(shù)據(jù)量 }void HAL_UART_MspInit(UART_HandleTypeDef *huart) {//GPIO端口設置GPIO_InitTypeDef GPIO_Initure;if (huart->Instance == USART1) //如果是串口1,進行串口1 MSP初始化{__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA時鐘__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1時鐘GPIO_Initure.Pin = GPIO_PIN_9; //PA9GPIO_Initure.Mode = GPIO_MODE_AF_PP; //復用推挽輸出GPIO_Initure.Pull = GPIO_PULLUP; //上拉GPIO_Initure.Speed = GPIO_SPEED_FAST; //高速GPIO_Initure.Alternate = GPIO_AF7_USART1; //復用為USART1HAL_GPIO_Init(GPIOA, &GPIO_Initure); //初始化PA9GPIO_Initure.Pin = GPIO_PIN_10; //PA10HAL_GPIO_Init(GPIOA, &GPIO_Initure); //初始化PA10#if EN_USART1_RXHAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中斷通道HAL_NVIC_SetPriority(USART1_IRQn, 3, 2); //搶占優(yōu)先級3,子優(yōu)先級3 #endif} }這里有個詭異的地方就是stm32要設置成9個數(shù)據(jù)位,一個停止位,我一開始按照8個數(shù)據(jù)位、兩個停止位讀出來的數(shù)據(jù)是錯的,后來改了之后才正常了。是不是和stm32內(nèi)部的串口配置有關(guān),哪位大神弄明白了告訴我哈。
(2) 串口中斷接收
串口中斷函數(shù),在中斷函數(shù)里面接收數(shù)據(jù),進行SBUS信號解析。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {int i;while (huart->Instance == USART1) //如果是串口1{USART1_RX_BUF[USART1_RX_STA] = aRxBuffer1[0];if (USART1_RX_STA == 0 && USART1_RX_BUF[USART1_RX_STA] != 0x0F) break; //幀頭不對,丟掉USART1_RX_STA++;if (USART1_RX_STA > USART_REC_LEN) USART1_RX_STA = 0; ///接收數(shù)據(jù)錯誤,重新開始接收if (USART1_RX_BUF[0] == 0x0F && USART1_RX_BUF[24] == 0x00 && USART1_RX_STA == 25) //接受完一幀數(shù)據(jù){update_sbus(USART1_RX_BUF);for (i = 0; i<25; i++) //清空緩存區(qū)USART1_RX_BUF[i] = 0;USART1_RX_STA = 0;}break;} }(3) 信號解析
上面中斷函數(shù)里面有一個update_sbus函數(shù),原型為u8 update_sbus(u8 *buf),解析subs信號全靠它了!!新建一個sbus.c文件,輸入如下代碼
#include "sbus.h"SBUS_CH_Struct SBUS_CH;//將sbus信號轉(zhuǎn)化為通道值 u8 update_sbus(u8 *buf) {int i;if (buf[23] == 0){SBUS_CH.ConnectState = 1;SBUS_CH.CH1 = ((int16_t)buf[ 1] >> 0 | ((int16_t)buf[ 2] << 8 )) & 0x07FF;SBUS_CH.CH2 = ((int16_t)buf[ 2] >> 3 | ((int16_t)buf[ 3] << 5 )) & 0x07FF;SBUS_CH.CH3 = ((int16_t)buf[ 3] >> 6 | ((int16_t)buf[ 4] << 2 ) | (int16_t)buf[ 5] << 10 ) & 0x07FF;SBUS_CH.CH4 = ((int16_t)buf[ 5] >> 1 | ((int16_t)buf[ 6] << 7 )) & 0x07FF;SBUS_CH.CH5 = ((int16_t)buf[ 6] >> 4 | ((int16_t)buf[ 7] << 4 )) & 0x07FF;SBUS_CH.CH6 = ((int16_t)buf[ 7] >> 7 | ((int16_t)buf[ 8] << 1 ) | (int16_t)buf[9] << 9 ) & 0x07FF;SBUS_CH.CH7 = ((int16_t)buf[ 9] >> 2 | ((int16_t)buf[10] << 6 )) & 0x07FF;SBUS_CH.CH8 = ((int16_t)buf[10] >> 5 | ((int16_t)buf[11] << 3 )) & 0x07FF;SBUS_CH.CH9 = ((int16_t)buf[12] << 0 | ((int16_t)buf[13] << 8 )) & 0x07FF;SBUS_CH.CH10 = ((int16_t)buf[13] >> 3 | ((int16_t)buf[14] << 5 )) & 0x07FF;SBUS_CH.CH11 = ((int16_t)buf[14] >> 6 | ((int16_t)buf[15] << 2 ) | (int16_t)buf[16] << 10 ) & 0x07FF;SBUS_CH.CH12 = ((int16_t)buf[16] >> 1 | ((int16_t)buf[17] << 7 )) & 0x07FF;SBUS_CH.CH13 = ((int16_t)buf[17] >> 4 | ((int16_t)buf[18] << 4 )) & 0x07FF;SBUS_CH.CH14 = ((int16_t)buf[18] >> 7 | ((int16_t)buf[19] << 1 ) | (int16_t)buf[20] << 9 ) & 0x07FF;SBUS_CH.CH15 = ((int16_t)buf[20] >> 2 | ((int16_t)buf[21] << 6 )) & 0x07FF;SBUS_CH.CH16 = ((int16_t)buf[21] >> 5 | ((int16_t)buf[22] << 3 )) & 0x07FF;return 1;}else {SBUS_CH.ConnectState = 0;return 0;} }u16 sbus_to_pwm(u16 sbus_value) {float pwm;pwm = (float)SBUS_TARGET_MIN + (float)(sbus_value - SBUS_RANGE_MIN) * SBUS_SCALE_FACTOR;// 1000 300 1000/1400if (pwm > 2000) pwm = 2000;if (pwm < 1000) pwm = 1000;return (u16)pwm; }上面定義了一個SBUS_CH_Struct 結(jié)構(gòu)體類型的變量SBUS_CH,該結(jié)構(gòu)體在sbus.h中定義
typedef struct {uint16_t CH1;//通道1數(shù)值uint16_t CH2;//通道2數(shù)值uint16_t CH3;//通道3數(shù)值uint16_t CH4;//通道4數(shù)值uint16_t CH5;//通道5數(shù)值uint16_t CH6;//通道6數(shù)值uint16_t CH7;//通道7數(shù)值uint16_t CH8;//通道8數(shù)值uint16_t CH9;//通道9數(shù)值uint16_t CH10;//通道10數(shù)值uint16_t CH11;//通道11數(shù)值uint16_t CH12;//通道12數(shù)值uint16_t CH13;//通道13數(shù)值uint16_t CH14;//通道14數(shù)值uint16_t CH15;//通道15數(shù)值uint16_t CH16;//通道16數(shù)值uint8_t ConnectState;//遙控器與接收器連接狀態(tài) 0=未連接,1=正常連接 }SBUS_CH_Struct;u16 sbus_to_pwm(u16 sbus_value)很好理解了,就是把sbus的值轉(zhuǎn)化為標準的1000-2000的pwm值,因為我用的遙控器sbus值是300-1700,大家用的時候具體數(shù)值到時候可以通過串口直接讀出來看看。
這樣就讀出了16個通道的數(shù)據(jù)啦。同時通過讀取ConnectState位判斷遙控器的狀態(tài),在主函數(shù)中采取失控保護。
上面這段解析數(shù)據(jù)的代碼是國際通用的,可以用在任何使用sbus協(xié)議的場合,可以很方便的移植到arduino、51、樹莓派上面。
最后main函數(shù)里面就很簡單了,只注意初始化串口設置為100K波特率。
void main() {/* 省略 */uart1_init(100000);/* 省略 */ }本文代碼已上傳至CSDN,獨樂樂不如眾樂樂,提供免費下載,點擊下載,歡迎交流討論。別忘了點個贊哦!
總結(jié)
以上是生活随笔為你收集整理的STM32解析SBUS信号例程详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TQ210 —— NandFlash
- 下一篇: Excel控制AutoCad进行坐标标注