1、通信协议解析说明
常见的官方遥控器大概如下所示:
常用的搭配接收机:
这里需要注意的是:i6是可以刷十通道固件的,但是十通道的性能要发挥出来需要用IA10B的接收机才行,IA6B的接收机解码IBUS(富斯官方的协议)最多仅支持到8通道,所以你看到下面我们解码最后两个通道无论怎么动都没有反应是正常的:
然后官方也公开了通信协议
从官网我们可以获取到的协议原文如下:
这里我也用逻辑分析仪,抓了一段数据,可以看到基本上和上面说的没什么区别,基本上也就是一帧32个字节的数据,然后开头0x20,0x40这样的,后面我们只需要按照要求进行解码就可以了。
2、驱动程序设计
首先是配置时钟,这里是时钟一定要拉到最高,不然通信的时候波特率会出问题(起因是我第一次忘了配然后一直通信失败,读不出准确的数据)
之后配置串口并开启串口中断,当然如果就是使用串口DMA也是可以的,我之前有一篇文章系统总结了一些串口中设备的处理办法:串口通信中一些常用的小工具
配置完成之后就可以生成代码了,这里我们首先配置下需要的串口号还有一些宏参数:
查看解析函数:
之后我们就可以在主函数中来测试接收函数了
3、实测
首先是在打开接收中断
在中断回调函数不断处理解析函数,获取解析的数据。
这样将程序下载到开发板并进入仿真就可以看到数据了,前面四个通道对应摇杆的四个位,中位的时候都是1500,对应PWM高电平时间都是1.5ms,最低的时候是1000,拉满是2000.
4、使用串口空闲中断+DMA接收
我们知道,这里其实就是处理个数据而已所以我们改为数据包即可,代码如下:
#define USART_DMA_RX_BUFFER_MAXIMUM 64 // DMA缓冲区大小
extern DMA_HandleTypeDef hdma_usart2_rx;
uint8_t FUSI_rx_buffer[USART_DMA_RX_BUFFER_MAXIMUM]; //串口1的DMA接收缓冲区
uint16_t FUSI_rx_len; // DMA一次空闲中断接收到的数据长度
uint8_t FUSI_data[USART_DMA_RX_BUFFER_MAXIMUM]; // DMA接收数据缓存区
uint16_t channel[IBUS_USER_CHANNELS];
void UART_DMA_start(void)
{
for(uint8_t i = 0;i<IBUS_MAX_CHANNLES;i++)
{
channel[i] = 1500;
}
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart2,(uint8_t *)FUSI_rx_buffer, USART_DMA_RX_BUFFER_MAXIMUM);
}
uint16_t checksum_cal, checksum_ibus;
void IBUS_READ_CHANNEL(uint8_t user_channels)
{
uint16_t channel_buffer[IBUS_MAX_CHANNLES] = {0};
if(FUSI_data[0] == IBUS_LENGTH && FUSI_data[1] == IBUS_COMMAND40)
{
checksum_cal = 0xffff - FUSI_data[0] - FUSI_data[1];
for(int i = 0; i < IBUS_MAX_CHANNLES; i++)
{
channel_buffer[i] = (uint16_t)(FUSI_data[i * 2 + 3] << 8 | FUSI_data[i * 2 + 2]);
checksum_cal = checksum_cal - FUSI_data[i * 2 + 3] - FUSI_data[i * 2 + 2];
}
checksum_ibus = FUSI_data[31] << 8 | FUSI_data[30];
if(checksum_cal == checksum_ibus)
{
for(int j = 0; j < user_channels; j++)
{
channel[j] = channel_buffer[j];
}
}
}
}
void HAL_UART_ReceiveIdle(UART_HandleTypeDef *huart)
{
//当触发了串口空闲中断
if ((__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET))
{
if (huart->Instance == USART2)
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
HAL_UART_DMAStop(huart);
FUSI_rx_len = USART_DMA_RX_BUFFER_MAXIMUM - (__HAL_DMA_GET_COUNTER(&hdma_usart2_rx));
memcpy(FUSI_data, FUSI_rx_buffer, USART_DMA_RX_BUFFER_MAXIMUM);
memset(FUSI_rx_buffer, 0, USART_DMA_RX_BUFFER_MAXIMUM);
while (HAL_UART_Receive_DMA(&huart2, (uint8_t *)FUSI_rx_buffer, USART_DMA_RX_BUFFER_MAXIMUM) != HAL_OK)
;
IBUS_READ_CHANNEL(IBUS_USER_CHANNELS);
}
//下面添加其他串口的处理函数
}
}
之后在初始话部分开启DMA,并在中断处调用即可!
5、源码
fly_ibus.c
/*
* fly_ibus.c
*
* Created on: Feb 13, 2022
* Author: LX
*/
#include "fly_ibus.h"
uint8_t rx_buffer[32] = {0};
uint16_t channel[IBUS_USER_CHANNELS] = {0};
uint16_t checksum_cal, checksum_ibus;
void IBUS_INIT()
{
HAL_UART_Receive_IT(IBUS_UART, rx_buffer, 32);
}
void IBUS_READ_CHANNEL(uint8_t user_channels)
{
uint16_t channel_buffer[IBUS_MAX_CHANNLES] = {0};
if(rx_buffer[0] == IBUS_LENGTH && rx_buffer[1] == IBUS_COMMAND40)
{
checksum_cal = 0xffff - rx_buffer[0] - rx_buffer[1];
for(int i = 0; i < IBUS_MAX_CHANNLES; i++)
{
channel_buffer[i] = (uint16_t)(rx_buffer[i * 2 + 3] << 8 | rx_buffer[i * 2 + 2]);
checksum_cal = checksum_cal - rx_buffer[i * 2 + 3] - rx_buffer[i * 2 + 2];
}
checksum_ibus = rx_buffer[31] << 8 | rx_buffer[30];
if(checksum_cal == checksum_ibus)
{
for(int j = 0; j < user_channels; j++)
{
channel[j] = channel_buffer[j];
}
}
}
HAL_UART_Receive_IT(IBUS_UART, rx_buffer, 32);
}
fly_ibus.h
/*
* fly_ibus.h
*
* Created on: Feb 13, 2022
* Author: LX
*/
#ifndef FLY_IBUS_H_
#define FLY_IBUS_H_
#include "main.h"
#include "usart.h"
/* User configuration */
#define IBUS_UART (&huart1)
#define IBUS_UART_INSTANCE (USART1)
#define IBUS_USER_CHANNELS 10 // Use 6 channels
/* User configuration */
#define IBUS_LENGTH 0x20 // 32 bytes
#define IBUS_COMMAND40 0x40 // Command to set servo or motor speed is always 0x40
#define IBUS_MAX_CHANNLES 14
void IBUS_INIT();
void IBUS_READ_CHANNEL(uint8_t user_channels);
#endif /* FLY_IBUS_H_ */