STM32实现水下四旋翼(三)通信任务——遥控器SBUS通信
目錄
- 一. 遙控器SBUS通信原理
- 1. 遙控器通信原理
- 2. SBUS通信協議
- 二. 基于UCOS-III操作系統創建通信任務
- 三. 實現SBUS通信驅動程序
- 四. 實現遙控器SBUS通信的應用程序
一. 遙控器SBUS通信原理
1. 遙控器通信原理
無人系統的遠程控制包括遙手持遙控器控制和地面站控制。遙控器是實現無人車、無人機、無人潛航器運動控制的基本部件,用一個高大上點的詞叫人在回路控制,或者人機協同控制。也就是將人的意志通過遙控器發送給機器人端,讓機器人按照我們想要的動作去執行任務,例如前進后退、調速、調姿、調檔等。小型無人系統我們都可以用航模遙控器,它的通信鏈路如圖所示
其實遙控器端的原理很簡單,主要有左右兩組搖桿(四個通道)以及若干撥碼開關。搖桿連接的是電位器,搖桿的不同位置輸出不同的電壓值,遙控器的控制芯片進行AD采樣獲取搖桿位置,撥碼開關類似,只不過更簡單,狀態就是0和1,三段開關就是兩個電位器組合用就行了。遙控器將獲取到的各個通道值通過無線通信發送給接收機。接收機通過三種方式等值輸出這些通道值,一是多路PWM,一是PPM信號,一是SBUS信號。PPM信號是調制的PWM信號,就是將多個PWM信號調成一串信號,用同樣的方式去解析就能得到各個通道值,SBUS信號是串口通信信號,按照SBUS的通信協議進行解析即可。我們首選SBUS信號,因為它最方便、最省資源。
2. SBUS通信協議
SBUS全稱serial-bus,是一種串口通信協議,廣泛應用于航模遙控器(接收機)中。只用一根信號線就能傳輸多達16通道的數據。
100k波特率,8位數據位,2位停止位,偶校驗(EVEN),無控流,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的信號是采用的負邏輯,也就是電平相反,不要試圖在軟件里面取反,因為軟件里面只能操作數據位(記得串口配置里面的數據位8么),你是操作不了停止位、校驗位啥的!!
如果是自己畫板子也很簡單,如圖所示:
二. 基于UCOS-III操作系統創建通信任務
下面終于開始激動人心的寫代碼環節了,在上一章我提供的正點原子的UCOS-III操作系統工程模板基礎上,修改main.c函數,我們首先創建RadioLinkTask——無線通信任務,專門用來解析接收機SBUS信號。
//任務優先級 #define START_TASK_PRIO 3 //任務堆棧大小 #define START_STK_SIZE 128 //任務控制塊 OS_TCB StartTaskTCB; //任務堆棧 CPU_STK START_TASK_STK[START_STK_SIZE]; //任務函數 void start_task(void *p_arg);//communicate任務 //設置任務優先級 #define COMMUNICATE_TASK_PRIO 5 // SBUS 信號的更新是在串口中斷中進行的 //任務堆棧大小 #define COMMUNICATE_STK_SIZE 512 //任務控制塊 OS_TCB CommunicateTaskTCB; //任務堆棧 CPU_STK COMMUNICATE_TASK_STK[COMMUNICATE_STK_SIZE]; //led0任務 void communicate_task(void *p_arg);int main(void) {u8 res;OS_ERR err;CPU_SR_ALLOC();Write_Through(); //Cahce強制透寫MPU_Memory_Protection(); //保護相關存儲區域Cache_Enable(); //打開L1-CacheStm32_Clock_Init(432, 25, 2, 9); //設置時鐘,216MhzHAL_Init(); //初始化HAL庫delay_init(216); //延時初始化uart1_init(100000); //串口1初始化uart2_init(115200); //串口2初始化uart3_init(115200); //串口3初始化KEY_Init(); //按鍵初始化LED_Init(); //初始化LEDOSInit(&err); //初始化UCOSIIIOS_CRITICAL_ENTER(); //進入臨界區//創建開始任務OSTaskCreate((OS_TCB *)&StartTaskTCB, //任務控制塊(CPU_CHAR *)"start task", //任務名字(OS_TASK_PTR)start_task, //任務函數(void *)0, //傳遞給任務函數的參數(OS_PRIO)START_TASK_PRIO, //任務優先級(CPU_STK *)&START_TASK_STK[0], //任務堆棧基地址(CPU_STK_SIZE)START_STK_SIZE / 10, //任務堆棧深度限位(CPU_STK_SIZE)START_STK_SIZE, //任務堆棧大小(OS_MSG_QTY)0, //任務內部消息隊列能夠接收的最大消息數目,為0時禁止接收消息(OS_TICK)0, //當使能時間片輪轉時的時間片長度,為0時為默認長度,(void *)0, //用戶補充的存儲區(OS_OPT)OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR, //任務選項(OS_ERR *)&err); //存放該函數錯誤時的返回值OS_CRITICAL_EXIT(); //退出臨界區OSStart(&err); //開啟UCOSIIIwhile (1); }//開始任務函數 void start_task(void *p_arg) {OS_ERR err;CPU_SR_ALLOC();p_arg = p_arg;CPU_Init(); #if OS_CFG_STAT_TASK_EN > 0uOSStatTaskCPUUsageInit(&err); //統計任務 #endif#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了測量中斷關閉時間CPU_IntDisMeasMaxCurReset(); #endif#if OS_CFG_SCHED_ROUND_ROBIN_EN //當使用時間片輪轉的時候//使能時間片輪轉調度功能,設置默認的時間片長度OSSchedRoundRobinCfg(DEF_ENABLED, 1, &err); #endif__HAL_RCC_CRC_CLK_ENABLE(); //使能CRC時鐘GUI_Init(); //STemWin初始化WM_MULTIBUF_Enable(1); //開啟STemWin多緩沖,RGB屏可能會用到OS_CRITICAL_ENTER(); //進入臨界區//communicate任務 通信任務OSTaskCreate((OS_TCB *)&CommunicateTaskTCB,(CPU_CHAR *)"Communicate task",(OS_TASK_PTR)communicate_task,(void *)0,(OS_PRIO)COMMUNICATE_TASK_PRIO,(CPU_STK *)&COMMUNICATE_TASK_STK[0],(CPU_STK_SIZE)COMMUNICATE_STK_SIZE / 10,(CPU_STK_SIZE)COMMUNICATE_STK_SIZE,(OS_MSG_QTY)0,(OS_TICK)10,(void *)0,(OS_OPT)OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_SAVE_FP,(OS_ERR *)&err);OS_TaskSuspend((OS_TCB *)&StartTaskTCB, &err); //掛起開始任務OS_CRITICAL_EXIT(); //退出臨界區 }//通信任務 void communicate_task(void *p_arg) {OS_ERR err;CPU_SR_ALLOC();char remotor_ready = 0; //遙控器通訊是否準備好u8 sbus_count = 0;SBUS_CH.ConnectState = 0;//等待遙控器通訊正常,否則一直等待。遙控器校正,初值初始化while (sbus_count < 10){if (SBUS_CH.ConnectState == 1){sbus_count++;SBUS_CH.ConnectState = 0;}LED1_Toggle;LED0_Toggle;delay_ms(100);}remotor_ready = 1; //遙控器通訊成功HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); //遙控器就緒LED1常亮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); //LED0常亮//讀遙控器數據while (1){delay_ms(10);} }main文件當中在main函數之前定義任務優先級、任務堆棧、堆棧大小、任務函數等,目前來說我們只創建了StartTask和CommunicateTask(代碼里面我都寫的Communicate,后面不區分CommunicateTask和RadioLinkTask),StartTask是用于創建所有其他任務,它的任務優先級最高。main函數與裸機的main函數一樣,首先進行時鐘初始化、系統初始化、外設初始化等,不同的是操作系統框架下還要進行操作系統的初始化,之后創建開始任務。開始任務為void start_task(void *p_arg),它的任務是創建其他所有任務。后面我們想繼續增加新的任務,需要修改的有三處,一是main函數之前定義新任務優先級、任務堆棧、堆棧大小、任務函數等,一處是在void start_task(void *p_arg)當中調用OSTaskCreate函數來創建新任務,一是在后邊定義新的任務函數。
在通信任務(CommunicateTask)中,首先是通過判斷遙控器的標志位來判斷通信是否正常,連續一段時間通信正常則認為遙控器連接上了,LED燈常亮,進入While(1)循環,也就是我們需要實現的任務部分。暫時空著,后面我們補充。
三. 實現SBUS通信驅動程序
我們首先實現SBUS通信的驅動程序,主要是串口的驅動。后面我都按照這樣的模式寫,先實現驅動程序,再寫應用程序。 在uart.h和uart.c中增加串口1的相關宏定義與函數:
uart.h:
#ifndef _USART_H #define _USART_H #include "sys.h" #include "stdio.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接收Buffervoid uart1_init(u32 bound); #endifuart.c:
#include "usart.h" #include "sys.h" #include <iwdg.h> #include "string.h" #include "sbus.h"//如果使用os,則包括下面的頭文件即可. #if SYSTEM_SUPPORT_OS #include "includes.h" //os 使用 #endif #if 1 #pragma import(__use_no_semihosting) //標準庫需要的支持函數 struct __FILE {int handle; };FILE __stdout; //定義_sys_exit()以避免使用半主機模式 void _sys_exit(int x) {x = x; }int fputc(int ch, FILE *f) {while ((USART1->ISR & 0X40) == 0); //循環發送,直到發送完畢USART1->TDR = (u8)ch;return ch; } #endifu8 USART1_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節. u16 USART1_RX_STA = 0; //接收狀態標記 u8 aRxBuffer1[RXBUFFERSIZE]; //HAL庫使用的串口接收緩沖 UART_HandleTypeDef UART1_Handler; //UART句柄//初始化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; //字長為8位數據格式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,并且設置接收緩沖以及接收緩沖接收最大數據量 }//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} }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_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;} }//串口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 }到這里我們實現了串口1的驅動程序,按照SBUS通信的配置將串口1設置成數據位8,偶校驗,波特率100k(在main函數里面調用uart1_init(100000)),串口1 的中斷函數里面我們調用update_sbus(USART1_RX_BUF),以幀為單位(一幀25個字節)更新SBUS信號,它是自定義函數,原型為u8 update_sbus(u8 *buf),在sbus.c里面定義,sbus.h和sbus.c的內容如下:
sbus.h:
#ifndef __SBUS_H #define __SBUS_H#include "sys.h"#define RADIOLINK#define SBUS_FRAME_SIZE 25 #define SBUS_INPUT_CHANNELS 16#ifdef RADIOLINK#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 #endif#define SBUS_SCALE_FACTOR ((SBUS_TARGET_MAX - SBUS_TARGET_MIN) / (SBUS_RANGE_MAX - SBUS_RANGE_MIN)) #define SBUS_SCALE_OFFSET (int)(SBUS_TARGET_MIN - (SBUS_SCALE_FACTOR * SBUS_RANGE_MIN + 0.5f))//低速與高速模式 #define LOW_SPEED 0 #define HIGH_SPEED 1//定深與手動模式 #define HAND_MODE 0 #define DEPTH_MODE 1// IMU 的數據來源 #define JY901 0 #define HT905 1 #define JY901_HT905 2// 一鍵起飛、一鍵降落 #define ONEKEY_LANDING 0 #define ONEKEY_TAKEOFF 1// 定深模式使能、失能 #define HOLD_DISABLE 0 #define HOLD_ENABLE 1//右手X:方向 CH1 Y:油門 CH2 //左手X:滾轉 CH4 Y:深度 CH3 #define YAW 1 #define THROTTLE 2 #define Z_SPEED 2 #define PITCH 3 #define FORWARD_SPEED 3 #define ROLL 4 #define SPEED_MODE 6 #define PWM_LEFT 7 #define PWM_RIGHT 8 #define CARRY_MODE 9 #define IMU_MODE 10 #define ONEKEY_UP_AND_DOWN 11 // 一鍵起飛和著陸按鈕 #define ALTHOLD_ENABLE 12extern 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;//通道10數值uint16_t CH12;//通道10數值uint16_t CH13;//通道10數值uint16_t CH14;//通道10數值uint16_t CH15;//通道10數值uint16_t CH16;//通道10數值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); #endifsbus.c:
#include "sbus.h"SBUS_CH_Struct SBUS_CH; int command[20]; //遙控器數據/* [startbyte] [data1][data2]…[data22][flags][endbyte] startbyte=0x0f; endbyte=0x00; flags標志位是用來檢測控制器與遙控器是否斷開的標志位。 flags=1:控制器與接收器保持連接 flags=0:控制器與接收器斷開 *///將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;}}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; }在sbus.h中我們定義了一個SBUS_CH_Struct的結構體類型,并定義了一個該類型的結構體SBUS_CH,用來存放SBUS的各個通道數據值以及連接狀態,u8 update_sbus(u8 *buf)功能就是從編碼的sbus信號中解析出16個通道的數值以及遙控器連接標志。u16 sbus_to_pwm(u16 sbus_value)與float sbus_to_Range(u16 sbus_value, float p_min, float p_max)都是字面意思,將SBUS的信號值轉化為特定區間的值,就是一個函數映射關系。轉化為PWM的值就是1000-2000。
這里我們還定義了一個 數組 int command[20],這個數組非常重要,用它來存放遙控器發送的命令。注意是命令不是數據,SBUS_CH存放的是各個通道值,我們通過判斷各個通道的值就可以解析遙控器想要傳達的命令,請看下面的應用程序如何實現這種轉換。
四. 實現遙控器SBUS通信的應用程序
到這里我們已經實現了遙控器的SBUS信號解析,在前面的main函數中修改CommunicateTask函數:
//通信任務 void communicate_task(void *p_arg) {OS_ERR err;CPU_SR_ALLOC();char remotor_ready = 0; //遙控器通訊是否準備好u8 sbus_count = 0;SBUS_CH.ConnectState = 0;//等待遙控器通訊正常,否則一直等待。遙控器校正,初值初始化while (sbus_count < 10){if (SBUS_CH.ConnectState == 1){sbus_count++;SBUS_CH.ConnectState = 0;}LED1_Toggle;LED0_Toggle;delay_ms(100);}remotor_ready = 1; //遙控器通訊成功HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); //遙控器就緒LED1常亮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); //LED0常亮//讀遙控器數據while (1){printf("遙控器的通道值為: CH1 %d CH2 %d CH3 %d CH4 %d CH5 %d CH6 %d CH7 %d CH8 %d",SBUS_CH.CH1,SBUS_CH.CH2,SBUS_CH.CH3,SBUS_CH.CH4,SBUS_CH.CH5,SBUS_CH.CH6,SBUS_CH.CH7,SBUS_CH.CH8)delay_ms(10);} }增加了一行代碼printf("遙控器的通道值為: CH1 %d CH2 %d CH3 %d CH4 %d CH5 %d CH6 %d CH7 %d CH8 %d",SBUS_CH.CH1,SBUS_CH.CH2,SBUS_CH.CH3,SBUS_CH.CH4,SBUS_CH.CH5,SBUS_CH.CH6,SBUS_CH.CH7,SBUS_CH.CH8),用USB-TTL工具連接UART的TX口(RX口已經連接了接收機),電腦端注意將配置改為100k波特率與偶校驗。此時就可以看到打印出來的SBUS通道的數值了。但是我們需要二次處理這些數據,所以修改上面的while(1)函數:
//讀遙控器數據while (1){if (SBUS_CH.CH5 < 400) //選擇IMU數據來源command[IMU_MODE] = JY901;else if (SBUS_CH.CH5 < 1100)command[IMU_MODE] = HT905;elsecommand[IMU_MODE] = HT905;if (SBUS_CH.CH6 < 1000) //速度檔位command[SPEED_MODE] = LOW_SPEED;elsecommand[SPEED_MODE] = HIGH_SPEED;if (SBUS_CH.CH10 < 1000) //選擇定深模式(定高)與手動模式command[CARRY_MODE] = HAND_MODE;elsecommand[CARRY_MODE] = DEPTH_MODE;if (SBUS_CH.CH9 < 1000) //定高模式時 油門使能 按鈕,為disable油門不輸出,機器人不動command[ALTHOLD_ENABLE] = HOLD_DISABLE;elsecommand[ALTHOLD_ENABLE] = HOLD_ENABLE;// 左手油門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);// | 四軸油門(手動) | 四軸俯仰(手動)// | 上下速度(定高) | 前后速度(定高)// | |//——————————|————————— YAW ————————————|———————————— ROLL// | |// | |// | |// 手動模式用 旋鈕通道 控制油門command[PWM_LEFT] = sbus_to_pwm(SBUS_CH.CH7); // 控制舵機command[PWM_RIGHT] = sbus_to_pwm(SBUS_CH.CH8); // 控制波動鰭電機delay_ms(10);}這里我們將CH1-CH4四個遙感通道的值轉化為PWM值(1000-2000),這四個主要通道將用來操縱機器人的前后左右上下等機動運動。將其他二段開關或三段開關的值轉變為相應的命令模式,這些在后面都會用到,比如用來切換速度檔位、定深與手動模式等。當然你可以自定義各種模式,可以將各種模式對應到不同的通道,神奇嗎?想一想買過的一些玩具,包括智能車、無人機,是不是告訴過你遙控器按哪個按鈕可以實現什么功能,原理就是這樣的,很簡單。
總結
以上是生活随笔為你收集整理的STM32实现水下四旋翼(三)通信任务——遥控器SBUS通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: plsql存过声明游标_plsql--游
- 下一篇: 计算机excl知识题,2019职称计算机