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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

STM32实现四驱小车(二)通信任务——遥控器SBUS通信

發布時間:2023/12/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32实现四驱小车(二)通信任务——遥控器SBUS通信 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 一. 遙控器通信原理簡介
  • 二. SBUS信號解析
    • 1. SBUS信號簡介
    • 2. STM32F7解析SBUS信號
  • 三. 通信任務實現

一. 遙控器通信原理簡介

要實現一個遙控小車當然要有一個遙控器了,目前市面上常用的航模遙控器基本都是2.4G無線信號,從遙控器到小車子的鏈路是這樣子的。

接收機出來的信號一般有SBUS信號、PPM信號、PWM信號,SBUS信號就是一路串行信號,連接到控制板的串口按協議解析就行了。PWM信號就不說了,多路PWM接口表示多個信號,PPM信號也是一路信號,只不過它是一串波,它把多路PWM值混合到一幀信號里了,一幀數據的高電平時間按順序解析出來就是各路PWM的值。這里不講后兩種,解析起來麻煩還浪費控制板資源,后面所有機器人系列都用SBUS信號,也推薦大家用SBUS信號。

二. SBUS信號解析

關于STM32解析SBUS信號我之前寫過一篇文章,有源碼,大家可以直接去讀那一篇,傳送門:STM32解析SBUS信號例程詳解. 在這里我簡單再介紹一下。

1. SBUS信號簡介

SBUS全稱serial-bus,是一種串口通信協議,廣泛應用于航模遙控器(接收機)中。只用一根信號線就能傳輸多達16通道的數據,比多路PWM捕獲高效且省資源。

  • 串口配置:
    100k波特率,8位數據位,2位停止位,偶校驗(EVEN),無控流,25個字節。
  • 協議格式:(每幀數據25字節)
    [startbyte] [data1][data2]…[data22][flags][endbyte]
    startbyte=0x0f;
    endbyte=0x00;
    data1…data22: LSB(低位在前),對應16個通道(ch1-ch16),每個通道11bit(22 × 8=16 × 11);
    flag位標志遙控器的通訊狀態,我使用的樂迪AT9S在遙控器通上的時候是0x00,斷開的時候是0xC0,可以通過查詢flag位來采取失控保護。
  • 數據范圍
    航模遙控器輸出的PWM值是1000~2000,中值為1500,sbus輸出的會不一樣,例如樂迪AT9S的范圍為300 ~ 1700,中值1000,我使用的另一套數傳設備的范圍是341-1707,中值1024。大家注意用串口調試助手把數據讀出來看看范圍是多少。不然后面會出問題。
  • sbus的負邏輯
    這個地方一定要萬分注意,接收機接板子必須加硬件反相器,因為SBUS的信號是采用的負邏輯,也就是電平相反,不要試圖在軟件里面取反,因為軟件里面只能操作數據位(記得串口配置里面的數據位8么),你是操作不了停止位、校驗位啥的!!
    如果是自己畫板子也很簡單,如圖所示
  • 2. STM32F7解析SBUS信號

    在上一章STM32實現四驅小車(一)硬件與軟件準備的工程模板基礎上,創建sbus.h,sbus.c兩個文件用于SBUS解析,再創建uart.h, uart.c兩個文件寫串口驅動。

    sbus.h的內容如下

    #ifndef __SBUS_H #define __SBUS_H #include "sys.h"#define SBUS_FRAME_SIZE 25 #define SBUS_INPUT_CHANNELS 16//定義subs信號的最小值 最大值 中值 死區 以及希望轉換成PWM值的范圍(1000-2000) #define SBUS_RANGE_MIN 300.0f #define SBUS_RANGE_MAX 1700.0f #define SBUS_TARGET_MIN 1000.0f #define SBUS_TARGET_MAX 2000.0f #define DEAD_RANGE_MIN 960 //死區 #define DEAD_RANGE_MAX 1040 #define SBUS_RANGE_MIDDLE 1000.0f #define SBUS_CONNECT_FLAG 0x00//低速與高速模式,這里用一個二段開關控制速度檔位 #define LOW_SPEED 0 #define HIGH_SPEED 1// 定義四個搖桿與撥動開關的功能 #define YAW 1 #define THROTTLE 2 #define PITCH 3 #define ROLL 4 #define SPEED_MODE 6extern int command[20]; //遙控器數據typedef struct {uint16_t signal[25];uint16_t CH1;//通道1數值uint16_t CH2;//通道2數值uint16_t CH3;//通道3數值uint16_t CH4;//通道4數值uint16_t CH5;//通道5數值uint16_t CH6;//通道6數值uint16_t CH7;//通道7數值uint16_t CH8;//通道8數值uint16_t CH9;//通道9數值uint16_t CH10;//通道10數值uint16_t CH11;//通道11數值uint16_t CH12;//通道12數值uint16_t CH13;//通道13數值uint16_t CH14;//通道14數值uint16_t CH15;//通道15數值uint16_t CH16;//通道16數值uint8_t ConnectState;//遙控器與接收器連接狀態 0=未連接,1=正常連接 }SBUS_CH_Struct;extern SBUS_CH_Struct SBUS_CH; //SBUS信號解析相關函數 u8 update_sbus(u8 *buf); u16 sbus_to_pwm(u16 sbus_value); float sbus_to_Range(u16 sbus_value, float p_min, float p_max); #endif

    這里稍作解釋,SBUS信號讀出來是300-1700的數據,我們需要把它轉換成1000-2000的PWM值(不轉其實也沒關系,只是為了方便)。遙控器上兩個搖桿對應的是1-4四路通道,范圍是連續變化的,其他的撥碼開關是離散值,二段開關對應通道值為300和1700,三段開關對應的通道值為300,1000,1700.根據數值就可以判斷撥碼開關是在什么狀態,就可以進行換擋。本文只使用了一個二段開關,用于低速與高速模式。在比較復雜的系統里面所有的撥碼開關可能都會用到甚至復合作用。比如PX4飛控里面有定高/手動模式,有頭/無頭模式,一鍵起飛/降落模式,定點、特技模式等,都需要用撥碼開關來進行切換和換擋。在這里定義了一個全局變量command[20],用來存儲遙控器各個通道的值,用PWM值表示,范圍1000-2000. 知道了各個通道的值就可以去映射到不同的功能。

    sbus.c的內容如下

    #include "sbus.h"SBUS_CH_Struct SBUS_CH; int command[20]; //遙控器數據//將sbus信號轉化為通道值 u8 update_sbus(u8 *buf) {int i;for (i=0;i<25;i++)SBUS_CH.signal[i] = buf[i];if (buf[23] == SBUS_CONNECT_FLAG){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;}} //將sbus信號通道值轉化為PWM的數值 [1000,2000] 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信號通道值轉化為特定區間的數值 [p_min,p_max] float sbus_to_Range(u16 sbus_value, float p_min, float p_max) {float p;p = p_min + (float)(sbus_value - SBUS_RANGE_MIN) * (p_max-p_min)/(float)(SBUS_RANGE_MAX - SBUS_RANGE_MIN); if (p > p_max) p = p_max;if (p < p_min) p = p_min;return p; }

    這個文件的核心是實現update_sbus()函數,在串口中斷函數里面會調用這個函數,用于更新SBUS信號值,后面的sbus_to_pwm()函數在main.c中的通信任務里會調用,用于將SBUS信號轉換為PWM值。

    uart.h的內容如下

    #ifndef _USART_H #define _USART_H #include "sys.h" #include "stdio.h" #include "pid.h"#define USART_REC_LEN 100 //定義最大接收字節數 200 #define RXBUFFERSIZE 1 //緩存大小//串口1 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收 extern u8 USART1_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節.末字節為換行符 extern u16 USART1_RX_STA; //接收狀態標記 extern UART_HandleTypeDef UART1_Handler; //UART句柄 extern u8 aRxBuffer1[RXBUFFERSIZE];//HAL庫USART接收Buffer//串口2 #define EN_USART2_RX 1 //使能(1)/禁止(0)串口1接收 extern u8 USART2_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節.末字節為換行符 extern u16 USART2_RX_STA; //接收狀態標記 extern UART_HandleTypeDef UART2_Handler; //UART句柄 extern u8 aRxBuffer2[RXBUFFERSIZE];//HAL庫USART接收Buffer//串口3 #define EN_USART3_RX 1 //使能(1)/禁止(0)串口1接收 extern u8 USART3_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節.末字節為換行符 extern u16 USART3_RX_STA; //接收狀態標記 extern UART_HandleTypeDef UART3_Handler; //UART句柄 extern u8 aRxBuffer3[RXBUFFERSIZE];//HAL庫USART接收Buffer//如果想串口中斷接收,請不要注釋以下宏定義 void uart1_init(u32 bound); //如果想串口中斷接收,請不要注釋以下宏定義 void uart2_init(u32 bound); void uart3_init(u32 bound);#endif

    這里我們將會使用到三個串口,串口1用于解析SBUS信號,串口2用于讀取姿態傳感器數據,串口3在不久的將來用于地面站通信(小車系列不涉及)。

    uart.c的內容如下

    #include "usart.h" #include "sys.h" #include <iwdg.h> #include "pid.h" #include "HT905.h" #include "string.h" #include "sbus.h" // //如果使用os,則包括下面的頭文件即可. #if SYSTEM_SUPPORT_OS #include "includes.h" //os 使用 #endif//加入以下代碼,支持printf函數,而不需要選擇use MicroLIB //#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #if 1 #pragma import(__use_no_semihosting) //標準庫需要的支持函數 struct __FILE {int handle; };FILE __stdout; //定義_sys_exit()以避免使用半主機模式 void _sys_exit(int x) {x = x; } //重定義fputc函數 int fputc(int ch, FILE *f) {while ((USART1->ISR & 0X40) == 0); //循環發送,直到發送完畢USART1->TDR = (u8)ch;return ch; } #endif//串口1中斷服務程序 //注意,讀取USARTx->SR能避免莫名其妙的錯誤 u8 USART1_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節. //接收狀態 //bit15, 接收完成標志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字節數目 u16 USART1_RX_STA = 0; //接收狀態標記 u8 aRxBuffer1[RXBUFFERSIZE]; //HAL庫使用的串口接收緩沖 UART_HandleTypeDef UART1_Handler; //UART句柄//串口2中斷服務程序 u8 USART2_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節. u16 USART2_RX_STA=0; //接收狀態標記 u8 aRxBuffer2[RXBUFFERSIZE];//HAL庫使用的串口接收緩沖 UART_HandleTypeDef UART2_Handler; //UART句柄//串口3 u8 USART3_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節.末字節為換行符 u16 USART3_RX_STA; //接收狀態標記 UART_HandleTypeDef UART3_Handler; //UART句柄 u8 aRxBuffer3[RXBUFFERSIZE];//HAL庫USART接收Buffer//初始化IO 串口1 //bound:波特率 void uart1_init(u32 bound) {//UART 初始化設置UART1_Handler.Instance = USART1; //USART1UART1_Handler.Init.BaudRate = bound; //波特率UART1_Handler.Init.WordLength = UART_WORDLENGTH_9B; //字長為9位數據格式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; //收發模式HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()會使能UART1HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer1, RXBUFFERSIZE); //該函數會開啟接收中斷:標志位UART_IT_RXNE,并且設置接收緩沖以及接收緩沖接收最大數據量 }void uart2_init(u32 bound) {//UART3 初始化設置UART2_Handler.Instance=USART2; //USART1UART2_Handler.Init.BaudRate=bound; //波特率UART2_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字長為8位數據格式UART2_Handler.Init.StopBits=UART_STOPBITS_1; //一個停止位UART2_Handler.Init.Parity=UART_PARITY_NONE; //無奇偶校驗位UART2_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //無硬件流控UART2_Handler.Init.Mode=UART_MODE_TX_RX; //收發模式HAL_UART_Init(&UART2_Handler); //HAL_UART_Init()會使能UART2HAL_UART_Receive_IT(&UART2_Handler, (u8 *)aRxBuffer2, RXBUFFERSIZE);//該函數會開啟接收中斷:標志位UART_IT_RXNE,并且設置接收緩沖以及接收緩沖接收最大數據量 }//串口3解析SBUS信號,100k波特率,2個停止位,偶校驗 void uart3_init(u32 bound) {//UART3 初始化設置UART3_Handler.Instance=USART3; //USART1UART3_Handler.Init.BaudRate=bound; //波特率UART3_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字長為8位數據格式UART3_Handler.Init.StopBits=UART_STOPBITS_1; //2個停止位UART3_Handler.Init.Parity=UART_PARITY_NONE; //偶校驗位UART3_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //無硬件流控UART3_Handler.Init.Mode=UART_MODE_TX_RX; //收發模式HAL_UART_Init(&UART3_Handler); //HAL_UART_Init()會使能UART1HAL_UART_Receive_IT(&UART3_Handler, (u8 *)aRxBuffer3, RXBUFFERSIZE);//該函數會開啟接收中斷:標志位UART_IT_RXNE,并且設置接收緩沖以及接收緩沖接收最大數據量 }//UART底層初始化,時鐘使能,引腳配置,中斷配置 //此函數會被HAL_UART_Init()調用 //huart:串口句柄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); //搶占優先級3,子優先級3 #endif}if(huart->Instance==USART2)//如果是串口3,進行串口3 MSP初始化{__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOB時鐘__HAL_RCC_USART2_CLK_ENABLE(); //使能USART1時鐘GPIO_Initure.Pin=GPIO_PIN_2; //PA2 TXGPIO_Initure.Mode=GPIO_MODE_AF_PP; //復用推挽輸出GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速GPIO_Initure.Alternate=GPIO_AF7_USART2; //復用為USART3HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9GPIO_Initure.Pin=GPIO_PIN_3; //PA3 RXHAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10#if EN_USART2_RXHAL_NVIC_EnableIRQ(USART2_IRQn); //使能USART1中斷通道HAL_NVIC_SetPriority(USART2_IRQn,3,3); //搶占優先級3,子優先級3 #endif }if(huart->Instance==USART3)//如果是串口3,進行串口3 MSP初始化{__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB時鐘__HAL_RCC_USART3_CLK_ENABLE(); //使能USART1時鐘GPIO_Initure.Pin=GPIO_PIN_10; //PB10 TXGPIO_Initure.Mode=GPIO_MODE_AF_PP; //復用推挽輸出GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速GPIO_Initure.Alternate=GPIO_AF7_USART3; //復用為USART3HAL_GPIO_Init(GPIOB,&GPIO_Initure); //初始化PA9GPIO_Initure.Pin=GPIO_PIN_11; //PB11 RXHAL_GPIO_Init(GPIOB,&GPIO_Initure); //初始化PA10#if EN_USART3_RXHAL_NVIC_EnableIRQ(USART3_IRQn); //使能USART1中斷通道HAL_NVIC_SetPriority(USART3_IRQn,2,1); //搶占優先級3,子優先級3 #endif } }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; ///接收數據錯誤,重新開始接收// if (USART1_RX_BUF[0] == 0x0F && USART1_RX_BUF[24] == 0x00 && USART1_RX_STA == 25) //接受完一幀數據if (USART1_RX_BUF[0] == 0x0F && USART1_RX_STA == 25) //接受完一幀數據{update_sbus(USART1_RX_BUF);for (i = 0; i<25; i++){USART1_RX_BUF[i] = 0;}USART1_RX_STA = 0;#ifdef ENABLE_IWDGIWDG_Feed(); //喂狗 //超過時間沒有收到遙控器的數據會復位#endif}break;}if (huart->Instance==USART2)//如果是串口2{}if (huart->Instance==USART3)//如果是串口3{} }//串口1中斷服務程序 void USART1_IRQHandler(void) {u32 timeout = 0;u32 maxDelay = 0x1FFFF; #if SYSTEM_SUPPORT_OS //使用OSOSIntEnter(); #endifHAL_UART_IRQHandler(&UART1_Handler); //調用HAL庫中斷處理公用函數timeout = 0;while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY) //等待就緒{timeout++; 超時處理if (timeout > maxDelay)break;}timeout = 0;while (HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer1, RXBUFFERSIZE) != HAL_OK) //一次處理完成之后,重新開啟中斷并設置RxXferCount為1{timeout++; //超時處理if (timeout > maxDelay)break;} #if SYSTEM_SUPPORT_OS //使用OSOSIntExit(); #endif }//串口2中斷服務程序 void USART2_IRQHandler(void) { u32 timeout=0;u32 maxDelay=0x1FFFF; #if SYSTEM_SUPPORT_OS //使用OSOSIntEnter(); #endifHAL_UART_IRQHandler(&UART2_Handler); //調用HAL庫中斷處理公用函數timeout=0;while (HAL_UART_GetState(&UART2_Handler)!=HAL_UART_STATE_READY)//等待就緒{timeout++;超時處理if(timeout>maxDelay) break; }timeout=0;while(HAL_UART_Receive_IT(&UART2_Handler,(u8 *)aRxBuffer2, RXBUFFERSIZE)!=HAL_OK)//一次處理完成之后,重新開啟中斷并設置RxXferCount為1{timeout++; //超時處理if(timeout>maxDelay) break; } #if SYSTEM_SUPPORT_OS //使用OSOSIntExit(); #endif } //串口3中斷服務程序 void USART3_IRQHandler(void) { u32 timeout=0;u32 maxDelay=0x1FFFF; #if SYSTEM_SUPPORT_OS //使用OSOSIntEnter(); #endifHAL_UART_IRQHandler(&UART3_Handler); //調用HAL庫中斷處理公用函數timeout=0;while (HAL_UART_GetState(&UART3_Handler)!=HAL_UART_STATE_READY)//等待就緒{timeout++;超時處理if(timeout>maxDelay) break; }timeout=0;while(HAL_UART_Receive_IT(&UART3_Handler,(u8 *)aRxBuffer3, RXBUFFERSIZE)!=HAL_OK)//一次處理完成之后,重新開啟中斷并設置RxXferCount為1{timeout++; //超時處理if(timeout>maxDelay) break; } #if SYSTEM_SUPPORT_OS //使用OSOSIntExit(); #endif }

    uart.c里面就是實現串口的驅動和串口中斷函數的編寫。這里串口2和串口3中斷函數都是空的,后續章節會補上,咱們一步步來。看串口一的中斷函數,這段不多講了,先進行數據校驗,再進行數據接收和解析,核心如前所述,調用update_sbus()函數更新信號值,這個值在主函數里面會周期性調用。

    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; ///接收數據錯誤,重新開始接收// if (USART1_RX_BUF[0] == 0x0F && USART1_RX_BUF[24] == 0x00 && USART1_RX_STA == 25) //接受完一幀數據if (USART1_RX_BUF[0] == 0x0F && USART1_RX_STA == 25) //接受完一幀數據{update_sbus(USART1_RX_BUF);for (i = 0; i<25; i++){USART1_RX_BUF[i] = 0;}USART1_RX_STA = 0;#ifdef ENABLE_IWDGIWDG_Feed(); //喂狗 //超過時間沒有收到遙控器的數據會復位#endif}break;}

    還要記得在main函數里面調用串口初始化函數,這里串口1用于解析SBUS,波特率是100k。

    uart1_init(100000); //串口1初始化uart2_init(115200); //串口2初始化uart3_init(115200); //串口3初始化

    三. 通信任務實現

    在上一章實現了SBUS的驅動部分,下面我們寫應用部分。還記得上一篇文章里的communicate_task函數嗎,現在我們來補充它。

    void communicate_task(void *p_arg) {OS_ERR err;CPU_SR_ALLOC();char remotor_ready = 0; //遙控器通訊是否準備好char motor_ready = 0; //電機通訊是否正常u8 sbus_count = 0;u8 can_count = 0;SBUS_CH.ConnectState = 0;//等待遙控器通訊正常,否則一直等待。遙控器校正,初值初始化while (sbus_count < 10){if (SBUS_CH.ConnectState == 1){sbus_count++;SBUS_CH.ConnectState = 0;}LED0_Toggle;LED1_Toggle;delay_ms(100);}remotor_ready = 1; //遙控器通訊成功HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); //遙控器就緒LED1常亮//等待CAN通訊正常,否則一直等待while (can_count < 10){if (get_moto_measure(&moto_info, &CAN1_Handler))can_count++;delay_ms(100);}motor_ready = 1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); //電機就緒LED0常亮//讀遙控器數據while (1){if (SBUS_CH.CH6 < 400) //速度檔位command[SPEED_MODE] = LOW_SPEED;elsecommand[SPEED_MODE] = HIGH_SPEED;// 左手油門command[THROTTLE] = sbus_to_pwm(SBUS_CH.CH3);command[YAW] = sbus_to_pwm(SBUS_CH.CH4); command[ROLL] = sbus_to_pwm(SBUS_CH.CH1);command[PITCH] = sbus_to_pwm(SBUS_CH.CH2);delay_ms(10);} }

    這段代碼很簡單,開始時等待遙控器狀態正常,然后等待CAN總線通信正常(也就是電機通信正常,因為用的總線電機)。在等待的時候用指示燈指示狀態,當然這里可以用多個指示燈,設計酷炫的效果用于顯示各種運行狀態。等所有的連接都是正常的之后,進入while死循環,每隔10ms讀一次遙控器的數據,并轉換為PWM值和檔位值。這些數據在StabilizationTask姿態控制中會用到,具體如何應用,請聽下回分解。

    這里做下說明,本系列文章筆者重在分享思想、算法,在講解上會弱化一些基本知識(比如單片機各個外設的原理、單片機編程的基本知識、操作系統的具體原理等),在代碼的粘貼上會忽視一些底層的驅動代碼和無關緊要的部分,事實上上面的代碼我都經過刪減了,只留下了干貨。所以可以說面向的是中高級選手,拿來主義者可以打道回府了,本系列文章不開源,不提供源碼,請見諒。

    總結

    以上是生活随笔為你收集整理的STM32实现四驱小车(二)通信任务——遥控器SBUS通信的全部內容,希望文章能夠幫你解決所遇到的問題。

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