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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

STM32实现水下四旋翼(三)通信任务——遥控器SBUS通信

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

目錄

  • 一. 遙控器SBUS通信原理
    • 1. 遙控器通信原理
    • 2. SBUS通信協(xié)議
  • 二. 基于UCOS-III操作系統(tǒng)創(chuàng)建通信任務
  • 三. 實現(xiàn)SBUS通信驅動程序
  • 四. 實現(xiàn)遙控器SBUS通信的應用程序

一. 遙控器SBUS通信原理

1. 遙控器通信原理

無人系統(tǒng)的遠程控制包括遙手持遙控器控制和地面站控制。遙控器是實現(xiàn)無人車、無人機、無人潛航器運動控制的基本部件,用一個高大上點的詞叫人在回路控制,或者人機協(xié)同控制。也就是將人的意志通過遙控器發(fā)送給機器人端,讓機器人按照我們想要的動作去執(zhí)行任務,例如前進后退、調(diào)速、調(diào)姿、調(diào)檔等。小型無人系統(tǒng)我們都可以用航模遙控器,它的通信鏈路如圖所示

其實遙控器端的原理很簡單,主要有左右兩組搖桿(四個通道)以及若干撥碼開關。搖桿連接的是電位器,搖桿的不同位置輸出不同的電壓值,遙控器的控制芯片進行AD采樣獲取搖桿位置,撥碼開關類似,只不過更簡單,狀態(tài)就是0和1,三段開關就是兩個電位器組合用就行了。遙控器將獲取到的各個通道值通過無線通信發(fā)送給接收機。接收機通過三種方式等值輸出這些通道值,一是多路PWM,一是PPM信號,一是SBUS信號。PPM信號是調(diào)制的PWM信號,就是將多個PWM信號調(diào)成一串信號,用同樣的方式去解析就能得到各個通道值,SBUS信號是串口通信信號,按照SBUS的通信協(xié)議進行解析即可。我們首選SBUS信號,因為它最方便、最省資源。

2. SBUS通信協(xié)議

SBUS全稱serial-bus,是一種串口通信協(xié)議,廣泛應用于航模遙控器(接收機)中。只用一根信號線就能傳輸多達16通道的數(shù)據(jù)。

  • 串口配置:
    100k波特率,8位數(shù)據(jù)位,2位停止位,偶校驗(EVEN),無控流,25個字節(jié)。
  • 協(xié)議格式:(每幀數(shù)據(jù)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位來采取失控保護。
  • 數(shù)據(jù)范圍
    航模遙控器輸出的PWM值是1000~2000,中值為1500,sbus輸出的會不一樣,例如樂迪AT9S的范圍為300 ~ 1700,中值1000,我使用的另一套數(shù)傳設備的范圍是341-1707,中值1024。大家注意用串口調(diào)試助手把數(shù)據(jù)讀出來看看范圍是多少。不然后面會出問題。
  • sbus的負邏輯
    這個地方一定要萬分注意,接收機接板子必須加硬件反相器,因為SBUS的信號是采用的負邏輯,也就是電平相反,不要試圖在軟件里面取反,因為軟件里面只能操作數(shù)據(jù)位(記得串口配置里面的數(shù)據(jù)位8么),你是操作不了停止位、校驗位啥的!!
    如果是自己畫板子也很簡單,如圖所示:
  • 二. 基于UCOS-III操作系統(tǒng)創(chuàng)建通信任務

    下面終于開始激動人心的寫代碼環(huán)節(jié)了,在上一章我提供的正點原子的UCOS-III操作系統(tǒng)工程模板基礎上,修改main.c函數(shù),我們首先創(chuàng)建RadioLinkTask——無線通信任務,專門用來解析接收機SBUS信號。

    //任務優(yōu)先級 #define START_TASK_PRIO 3 //任務堆棧大小 #define START_STK_SIZE 128 //任務控制塊 OS_TCB StartTaskTCB; //任務堆棧 CPU_STK START_TASK_STK[START_STK_SIZE]; //任務函數(shù) void start_task(void *p_arg);//communicate任務 //設置任務優(yōu)先級 #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(); //保護相關存儲區(qū)域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(); //進入臨界區(qū)//創(chuàng)建開始任務OSTaskCreate((OS_TCB *)&StartTaskTCB, //任務控制塊(CPU_CHAR *)"start task", //任務名字(OS_TASK_PTR)start_task, //任務函數(shù)(void *)0, //傳遞給任務函數(shù)的參數(shù)(OS_PRIO)START_TASK_PRIO, //任務優(yōu)先級(CPU_STK *)&START_TASK_STK[0], //任務堆棧基地址(CPU_STK_SIZE)START_STK_SIZE / 10, //任務堆棧深度限位(CPU_STK_SIZE)START_STK_SIZE, //任務堆棧大小(OS_MSG_QTY)0, //任務內(nèi)部消息隊列能夠接收的最大消息數(shù)目,為0時禁止接收消息(OS_TICK)0, //當使能時間片輪轉時的時間片長度,為0時為默認長度,(void *)0, //用戶補充的存儲區(qū)(OS_OPT)OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR, //任務選項(OS_ERR *)&err); //存放該函數(shù)錯誤時的返回值OS_CRITICAL_EXIT(); //退出臨界區(qū)OSStart(&err); //開啟UCOSIIIwhile (1); }//開始任務函數(shù) 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); //統(tǒng)計任務 #endif#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了測量中斷關閉時間CPU_IntDisMeasMaxCurReset(); #endif#if OS_CFG_SCHED_ROUND_ROBIN_EN //當使用時間片輪轉的時候//使能時間片輪轉調(diào)度功能,設置默認的時間片長度OSSchedRoundRobinCfg(DEF_ENABLED, 1, &err); #endif__HAL_RCC_CRC_CLK_ENABLE(); //使能CRC時鐘GUI_Init(); //STemWin初始化WM_MULTIBUF_Enable(1); //開啟STemWin多緩沖,RGB屏可能會用到OS_CRITICAL_ENTER(); //進入臨界區(qū)//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(); //退出臨界區(qū) }//通信任務 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常亮//讀遙控器數(shù)據(jù)while (1){delay_ms(10);} }

    main文件當中在main函數(shù)之前定義任務優(yōu)先級、任務堆棧、堆棧大小、任務函數(shù)等,目前來說我們只創(chuàng)建了StartTask和CommunicateTask(代碼里面我都寫的Communicate,后面不區(qū)分CommunicateTask和RadioLinkTask),StartTask是用于創(chuàng)建所有其他任務,它的任務優(yōu)先級最高。main函數(shù)與裸機的main函數(shù)一樣,首先進行時鐘初始化、系統(tǒng)初始化、外設初始化等,不同的是操作系統(tǒng)框架下還要進行操作系統(tǒng)的初始化,之后創(chuàng)建開始任務。開始任務為void start_task(void *p_arg),它的任務是創(chuàng)建其他所有任務。后面我們想繼續(xù)增加新的任務,需要修改的有三處,一是main函數(shù)之前定義新任務優(yōu)先級、任務堆棧、堆棧大小、任務函數(shù)等,一處是在void start_task(void *p_arg)當中調(diào)用OSTaskCreate函數(shù)來創(chuàng)建新任務,一是在后邊定義新的任務函數(shù)。

    在通信任務(CommunicateTask)中,首先是通過判斷遙控器的標志位來判斷通信是否正常,連續(xù)一段時間通信正常則認為遙控器連接上了,LED燈常亮,進入While(1)循環(huán),也就是我們需要實現(xiàn)的任務部分。暫時空著,后面我們補充。

    三. 實現(xiàn)SBUS通信驅動程序

    我們首先實現(xiàn)SBUS通信的驅動程序,主要是串口的驅動。后面我都按照這樣的模式寫,先實現(xiàn)驅動程序,再寫應用程序。 在uart.h和uart.c中增加串口1的相關宏定義與函數(shù):

    uart.h:

    #ifndef _USART_H #define _USART_H #include "sys.h" #include "stdio.h" #define USART_REC_LEN 100 //定義最大接收字節(jié)數(shù) 200 #define RXBUFFERSIZE 1 //緩存大小//串口1 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收 extern u8 USART1_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節(jié).末字節(jié)為換行符 extern u16 USART1_RX_STA; //接收狀態(tài)標記 extern UART_HandleTypeDef UART1_Handler; //UART句柄 extern u8 aRxBuffer1[RXBUFFERSIZE];//HAL庫USART接收Buffervoid uart1_init(u32 bound); #endif

    uart.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) //標準庫需要的支持函數(shù) 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); //循環(huán)發(fā)送,直到發(fā)送完畢USART1->TDR = (u8)ch;return ch; } #endifu8 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句柄//初始化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位數(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ù)量 }//UART底層初始化,時鐘使能,引腳配置,中斷配置 //此函數(shù)會被HAL_UART_Init()調(diào)用 //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); //搶占優(yōu)先級3,子優(yōu)先級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; ///接收數(shù)據(jù)錯誤,重新開始接收if (USART1_RX_BUF[0] == 0x0F && USART1_RX_STA == 25) //接受完一幀數(shù)據(jù){update_sbus(USART1_RX_BUF);for (i = 0; i<25; i++){USART1_RX_BUF[i] = 0;}USART1_RX_STA = 0;#ifdef ENABLE_IWDGIWDG_Feed(); //喂狗 //超過時間沒有收到遙控器的數(shù)據(jù)會復位#endif}break;} }//串口1中斷服務程序 void USART1_IRQHandler(void) {u32 timeout = 0;u32 maxDelay = 0x1FFFF; #if SYSTEM_SUPPORT_OS //使用OSOSIntEnter(); #endifHAL_UART_IRQHandler(&UART1_Handler); //調(diào)用HAL庫中斷處理公用函數(shù)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 }

    到這里我們實現(xiàn)了串口1的驅動程序,按照SBUS通信的配置將串口1設置成數(shù)據(jù)位8,偶校驗,波特率100k(在main函數(shù)里面調(diào)用uart1_init(100000)),串口1 的中斷函數(shù)里面我們調(diào)用update_sbus(USART1_RX_BUF),以幀為單位(一幀25個字節(jié))更新SBUS信號,它是自定義函數(shù),原型為u8 update_sbus(u8 *buf),在sbus.c里面定義,sbus.h和sbus.c的內(nèi)容如下:

    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 //死區(qū)#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 的數(shù)據(jù)來源 #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]; //遙控器數(shù)據(jù)typedef struct {uint16_t signal[25];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;//通道10數(shù)值uint16_t CH12;//通道10數(shù)值uint16_t CH13;//通道10數(shù)值uint16_t CH14;//通道10數(shù)值uint16_t CH15;//通道10數(shù)值uint16_t CH16;//通道10數(shù)值uint8_t ConnectState;//遙控器與接收器連接狀態(tài) 0=未連接,1=正常連接 }SBUS_CH_Struct;extern SBUS_CH_Struct SBUS_CH; //SBUS信號解析相關函數(shù) 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.c:

    #include "sbus.h"SBUS_CH_Struct SBUS_CH; int command[20]; //遙控器數(shù)據(jù)/* [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信號通道值轉化為特定區(qū)間的數(shù)值 [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的各個通道數(shù)據(jù)值以及連接狀態(tài),u8 update_sbus(u8 *buf)功能就是從編碼的sbus信號中解析出16個通道的數(shù)值以及遙控器連接標志。u16 sbus_to_pwm(u16 sbus_value)與float sbus_to_Range(u16 sbus_value, float p_min, float p_max)都是字面意思,將SBUS的信號值轉化為特定區(qū)間的值,就是一個函數(shù)映射關系。轉化為PWM的值就是1000-2000。

    這里我們還定義了一個 數(shù)組 int command[20],這個數(shù)組非常重要,用它來存放遙控器發(fā)送的命令。注意是命令不是數(shù)據(jù),SBUS_CH存放的是各個通道值,我們通過判斷各個通道的值就可以解析遙控器想要傳達的命令,請看下面的應用程序如何實現(xiàn)這種轉換。

    四. 實現(xiàn)遙控器SBUS通信的應用程序

    到這里我們已經(jīng)實現(xiàn)了遙控器的SBUS信號解析,在前面的main函數(shù)中修改CommunicateTask函數(shù):

    //通信任務 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常亮//讀遙控器數(shù)據(jù)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.CH4SBUS_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口已經(jīng)連接了接收機),電腦端注意將配置改為100k波特率與偶校驗。此時就可以看到打印出來的SBUS通道的數(shù)值了。但是我們需要二次處理這些數(shù)據(jù),所以修改上面的while(1)函數(shù):

    //讀遙控器數(shù)據(jù)while (1){if (SBUS_CH.CH5 < 400) //選擇IMU數(shù)據(jù)來源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),這四個主要通道將用來操縱機器人的前后左右上下等機動運動。將其他二段開關或三段開關的值轉變?yōu)橄鄳拿钅J?#xff0c;這些在后面都會用到,比如用來切換速度檔位、定深與手動模式等。當然你可以自定義各種模式,可以將各種模式對應到不同的通道,神奇嗎?想一想買過的一些玩具,包括智能車、無人機,是不是告訴過你遙控器按哪個按鈕可以實現(xiàn)什么功能,原理就是這樣的,很簡單。

    總結

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。