STM32和STM32CubeMX实现遥控器控制, 保姆级教程

【背景】

各位,现在无人车和机器人大发展,但是,大家也都看到了,在无人车和机器人能够自动控制之前,都是用遥控器来控制的。那么,实现一个遥控器的控制,就变得非常有必要。然而,实际情况是,业内的人,都觉得用遥控器控制是基本的,和理所当然的;业外的人,都知道遥控器控制,要做遥控器的设计,又觉得无从下手。那么,今天我就来写一写用STM32和STM32CubeMX实现遥控器控制的保姆级教程,让大家都能实现遥控器控制自由。

【遥控器控制的方式】

市面上的遥控器有很多,甚至于游戏手柄,也是遥控器的一种。那现在比较流行的遥控器,都是用SBUS的协议来控制的。我这里就来实现SBUS协议的遥控器控制。这协议应该是能适用90%的遥控器了。如果不明白什么是SBUS协议,可以自行去网上学习。

【硬件设计】

我这是用的STM32的uart3来作为遥控器的接收电路。实际上只用了接收端RXD3, TXD3空着没用。H3是个3端子插头,接遥控器的接收端。 SBUS信号是个反的,所以要用上面的电路反一下。

【STM32CubeMX的设置】

stm32cubemx的配置就这么多,要注意的是波特率是100kbps,跟一般的串口不一样。照着我上面配就行了。

【软件编写】

sbus.h

复制代码
#ifndef _RC_SBUS_H_
#define _RC_SBUS_H_

#include "main.h"

// RC遥控器相关 开始
#define SBUS_NUM_CHANNELS 16
#define SBUS_PACKET_SIZE 25
#define SBUS_HEADER 0x0F
#define SBUS_END 0x00
#define SBUS_BUFFER_SIZE 25

#define SBUS_OPT_C17    0x01
#define SBUS_OPT_C18    0x02
#define SBUS_OPT_FS     0x08
#define SBUS_OPT_FL     0x04

typedef struct {
    uint16_t channels[SBUS_NUM_CHANNELS];
    uint8_t ch17, ch18;
    uint8_t failsafe;
    uint8_t frameLost;
    uint8_t sbus_flag;  // 1: valid, 0: invalid
} sbus_packet_t;

enum sbus_err_t
{
    SBUS_OK = 0,
    SBUS_FAIL = -1,
};

typedef struct {
    sbus_packet_t sbus_packet;
    uint8_t sbus_Buffer[SBUS_BUFFER_SIZE];

    /// flag for RC transimtter is existing or not
    /// 0: not exist, 1: exist but not using, 2: exist and using
    uint8_t isExist;

    /// counter for RC transmitter existing judgement
    uint8_t exist_cnt1;
    uint8_t exist_cnt2;

} sbus_handle_t;


extern sbus_handle_t sbusHandle; // SBUS handle

extern UART_HandleTypeDef huart3;
void RC_init(sbus_handle_t sbusHandle);
void RC_run(sbus_handle_t sbusHandle);

// RC遥控器相关 结束

#endif /* _RC_SBUS_H_ */

sbus.c

复制代码
#include "rc_sbus.h"
#include "uart.h"

// RC遥控器相关 开始

void RC_init(sbus_handle_t sbusHandle)
{
    // Initialize SBUS packet structure
    for (int i = 0; i < SBUS_NUM_CHANNELS; i++)
    {
        sbusHandle.sbus_packet.channels[i] = 0;
    }
    sbusHandle.sbus_packet.ch17 = 0;
    sbusHandle.sbus_packet.ch18 = 0;
    sbusHandle.sbus_packet.failsafe = 0;
    sbusHandle.sbus_packet.frameLost = 0;
    sbusHandle.sbus_packet.sbus_flag = 0; // Initially invalid

    // Initialize SBUS buffer
    for (int i = 0; i < SBUS_BUFFER_SIZE; i++)
    {
        sbusHandle.sbus_Buffer[i] = 0;
    }

    sbusHandle.isExist=1;
    sbusHandle.exist_cnt1=0;
    sbusHandle.exist_cnt2=0;


    // Start receiving data

    HAL_UARTEx_ReceiveToIdle_IT(&huart3, sbusHandle.sbus_Buffer, SBUS_BUFFER_SIZE);

    // sbusHandle.isExist=1;

    printf("RC Initialized\r\n");
    // HAL_Delay(5); // Delay for stability
}

可以看到,遥控器是用的uart3的空闲中断,25bytes。每个中断会接收25bytes数据,然后进行解码。

解码是在uart3的中断里进行的。代码如下:

uart.c

复制代码
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{

    // Handle idle line detection for sbus
    if(huart->Instance == USART3)
    {
        if(Size==25)        // Expected packet size is 25bytes
        {
           
            if(sbusHandle.sbus_Buffer[0]==SBUS_HEADER && sbusHandle.sbus_Buffer[SBUS_BUFFER_SIZE-1]==SBUS_END)
            {
                sbusHandle.sbus_packet.channels[0]  = (uint16_t)((sbusHandle.sbus_Buffer[1] >> 0 | sbusHandle.sbus_Buffer[2] << 8)                          & 0x07FF);
                sbusHandle.sbus_packet.channels[1]  = (uint16_t)((sbusHandle.sbus_Buffer[2] >> 3 | sbusHandle.sbus_Buffer[3] << 5)                        & 0x07FF);
                sbusHandle.sbus_packet.channels[2]  = (uint16_t)((sbusHandle.sbus_Buffer[3] >> 6 | sbusHandle.sbus_Buffer[4] << 2 | sbusHandle.sbus_Buffer[5] << 10)     & 0x07FF);
                sbusHandle.sbus_packet.channels[3]  = (uint16_t)((sbusHandle.sbus_Buffer[5] >> 1 | sbusHandle.sbus_Buffer[6] << 7)                        & 0x07FF);
                sbusHandle.sbus_packet.channels[4]  = (uint16_t)((sbusHandle.sbus_Buffer[6] >> 4 | sbusHandle.sbus_Buffer[7] << 4)                        & 0x07FF);
                sbusHandle.sbus_packet.channels[5]  = (uint16_t)((sbusHandle.sbus_Buffer[7] >> 7 | sbusHandle.sbus_Buffer[8] << 1 | sbusHandle.sbus_Buffer[9] << 9)      & 0x07FF);
                sbusHandle.sbus_packet.channels[6]  = (uint16_t)((sbusHandle.sbus_Buffer[9] >> 2 | sbusHandle.sbus_Buffer[10] << 6)                        & 0x07FF);
                sbusHandle.sbus_packet.channels[7]  = (uint16_t)((sbusHandle.sbus_Buffer[10] >> 5 | sbusHandle.sbus_Buffer[11] << 3)                       & 0x07FF);
                sbusHandle.sbus_packet.channels[8]  = (uint16_t)((sbusHandle.sbus_Buffer[12] << 0 | sbusHandle.sbus_Buffer[13] << 8)                         & 0x07FF);
                sbusHandle.sbus_packet.channels[9]  = (uint16_t)((sbusHandle.sbus_Buffer[13] >> 3 | sbusHandle.sbus_Buffer[14] << 5)                      & 0x07FF);
                sbusHandle.sbus_packet.channels[10] = (uint16_t)((sbusHandle.sbus_Buffer[14] >> 6 | sbusHandle.sbus_Buffer[15] << 2 | sbusHandle.sbus_Buffer[16] << 10)  & 0x07FF);
                sbusHandle.sbus_packet.channels[11] = (uint16_t)((sbusHandle.sbus_Buffer[16] >> 1 | sbusHandle.sbus_Buffer[17] << 7)                      & 0x07FF);
                sbusHandle.sbus_packet.channels[12] = (uint16_t)((sbusHandle.sbus_Buffer[17] >> 4 | sbusHandle.sbus_Buffer[18] << 4)                      & 0x07FF);
                sbusHandle.sbus_packet.channels[13] = (uint16_t)((sbusHandle.sbus_Buffer[18] >> 7 | sbusHandle.sbus_Buffer[19] << 1 | sbusHandle.sbus_Buffer[20] << 9)   & 0x07FF);
                sbusHandle.sbus_packet.channels[14] = (uint16_t)((sbusHandle.sbus_Buffer[20] >> 2 | sbusHandle.sbus_Buffer[21] << 6)                      & 0x07FF);
                sbusHandle.sbus_packet.channels[15] = (uint16_t)((sbusHandle.sbus_Buffer[21] >> 5 | sbusHandle.sbus_Buffer[22] << 3)                      & 0x07FF);
                sbusHandle.sbus_packet.ch17 = (sbusHandle.sbus_Buffer[23] & 0x0f) & SBUS_OPT_C17;
                sbusHandle.sbus_packet.ch18 = (sbusHandle.sbus_Buffer[23] & 0x0f) & SBUS_OPT_C18;
                sbusHandle.sbus_packet.failsafe = (sbusHandle.sbus_Buffer[23] & 0x0f) & SBUS_OPT_FS;
                sbusHandle.sbus_packet.frameLost = (sbusHandle.sbus_Buffer[23] & 0x0f) & SBUS_OPT_FL;
                
                sbusHandle.sbus_packet.sbus_flag = 1;
            }
            else
            {
                sbusHandle.sbus_packet.sbus_flag = 0;
            }
            // printf("sbus has been received successfully! sbus_flag = %d\n", sbusHandle.sbus_packet.sbus_flag);
        }
        else
        {
            sbusHandle.sbus_packet.sbus_flag = 0;
            printf("sbus received invalid length: %d\n", Size);
        }

        HAL_UARTEx_ReceiveToIdle_IT(&huart3, sbusHandle.sbus_Buffer, SBUS_BUFFER_SIZE); // Restart receiving data
    }
}

看到没有,在uart中断里面解码之后,会置一个sbusHandle.sbus_packet.sbus_flag=1; 那么在应用程序里面,进行查询,看到了sbusHandle.sbus_packet.sbus_flag=1,就可以对各个channels的数据进行处理了。

这里做一个简单的例子,就是将数据打印出来。

复制代码
void RC_run(sbus_handle_t sbusHandle)
{
    if (sbusHandle.sbus_packet.sbus_flag == 1)
    {
        sbusHandle.sbus_packet.sbus_flag = 0; // Reset flag after processing

        // usb_printf("RC Data Received: ");
        printf("RC Data Received: ");
        for (int i = 0; i < SBUS_NUM_CHANNELS; i++)
        {
             printf("Channel %d: %d ", i + 1, sbusHandle.sbus_packet.channels[i]);
        }
        printf("Ch17: %d, Ch18: %d, Failsafe: %d, Frame Lost: %d\r\n",
                       sbusHandle.sbus_packet.ch17, sbusHandle.sbus_packet.ch18, sbusHandle.sbus_packet.failsafe, sbusHandle.sbus_packet.frameLost);
     }
}

需要说明的是,遥控器的数据来得很快,每帧25bytes,间隔是14ms。应用程序进行处理的时候,要在14ms里面处理完一帧,并接着处理下一帧。通常来说,放在一个while(1)大循环里,是来不及的。需要用另外一个并发的任务来处理。这就牵涉到RTOS了,这是另外一个话题,这里就不展开了。

在系统上,还要能够检测到遥控器开和关,进行plug&play, 这是跟每个具体的遥控器和接收机相关的,这里也不展开了。各位自己去探索吧。

【结果检验】

这套代码用在了我实际的无人车的控制中,已经经过长时间的检验,没有问题。

【好了大功告成,亲个嘴儿。】

相关推荐
-Springer-2 小时前
STM32 学习 —— 个人学习笔记11-2(SPI 通信外设 & 硬件 SPI 读写 W25Q64)
笔记·stm32·学习
llilian_162 小时前
晶振测量仪 晶振频率测试仪器的多领域应用解析 晶振频率测试仪器
功能测试·单片机·嵌入式硬件·测试工具·51单片机
kaikaile19952 小时前
基于STM32F103的BMS通信控制
stm32·单片机·嵌入式硬件
天天爱吃肉82182 小时前
笔记:同步电机调试时电角度校正方法说明
大数据·人工智能·笔记·功能测试·嵌入式硬件·汽车
国科安芯2 小时前
空间激光通信系统中抗辐射 MCU 芯片应用研究
单片机·嵌入式硬件·架构·risc-v·安全性测试
Deitymoon2 小时前
STM32——外部中断
stm32·单片机·嵌入式硬件
踏着七彩祥云的小丑2 小时前
嵌入式——认识电子元器件——继电器系列
单片机·嵌入式硬件
智塑未来2 小时前
GJB电磁兼容标准对加固SSD有哪些要求?测试项目与合格指标
单片机·嵌入式硬件
IT_阿水3 小时前
基于STM32河流水质检测软件设计
stm32·单片机·嵌入式硬件