文章目录
一、遥控器DT7&DR16介绍 1.DT7&DR16的使用2.DT7&DR16的连接3.DBUS协议二、DR16与C型开发板通信 1.DR16数据帧解析2.cubemx配置以及串口DMA接收初始化3.数据帧解码三、效果展示一、遥控器DT7&DR16介绍
1.DT7&DR16的使用
需要注意DT7遥控器很不耐摔,天线和拨杆都容易断,切记要保护好
DR16接收机输出的信号为标准的DBUS协议数据,当遥控器与接收机建立连接 后,接收机每隔14ms通过DBUS发送一帧18字节数据。数据内容包括左右两个摇杆的数据,左右上角的两个开关拨杆,以及左侧上方的云台俯仰控制拨轮。
2.DT7&DR16的连接
使用DT7&DR16套件前需要保证接收机和遥控器之间已经成功配对。
DR16上的LED指示灯一般有三种状态:红灯常亮,绿灯闪烁,绿灯常亮,对应的状态如下:
指示灯状态 | 对应状态 |
---|---|
红灯常亮 | 未检测到遥控器 |
绿灯闪烁 | 检测到遥控器,但未配对 |
绿灯常亮 | 已与遥控器配对 |
配对方法为开启需要配对的遥控器,靠近正常工作的DR16,长按对频按键5s左右,然后松开,当指示灯常亮绿灯时便代表连接成功。(连接时确保周围没有开启的遥控器,不然会误连)
3.DBUS协议
接收机与接收机之间采用DBUS协议进行通信,信号电平为TTL电平,但是和UART是反相的,需要过一个反相器再输入到单片机的串口上进行接收。但是可以直接连接Robomaster的开发板上专门留给DR16接收机的接口,A板一般是串口1,C板是串口3,直接将接收机上引出的线对应连接即可,接口内置了反相器。DBUS与UART之间电平标准为反相关系,所以不能随意连接到其他串口。
接收时对照着DBUS参数表进行串口的参数设置。
一、DR16与C型开发板通信
1.DR16数据帧解析
我们日常使用中一般使用通道0到3的摇杆和S1,S2两个拨杆开关,摇杆数据为11字节,范围在1684到364,拨杆数据为2字节,范围在3到1。
开关拨杆S1和S2分别位于遥控器左右上角,但摇杆相反,例如通道0位于右摇杆的x轴,通道1位于右摇杆y轴。
2.cubemx配置以及串口DMA接收初始化
根据C板用户手册可知,C板的DBUS接口为UART3,并且波特率为100Kbps。
配置串口3并将波特率配置为100000并开启串口3NVIC.
为串口3添加一个DMA接收通道,并将模式改为轮询(Circular)。
在.h文件中定义DBUS串口,预定义的数据缓冲区长度以及控制帧数据长度,以便后面使用。
#define DBUS_MAX_LEN (50)#define DBUS_BUFLEN (18)#define DBUS_HUART huart3
定义一个长度为18字节的数组用于存储接收到的遥控器数据。
uint8_t dbus_buf[DBUS_BUFLEN];
配置UART以通过DMA接收数据
static int uart_receive_dma_no_it(UART_HandleTypeDef* huart, uint8_t* pData, uint32_t Size){//用于接收数据的函数,使用DMA方式来接收UART数据 uint32_t tmp1 = 0; tmp1 = huart->RxState;//创建一个临时变量tmp1,并将UART的接收状态赋值给它if (tmp1 == HAL_UART_STATE_READY)//判断UART是否处于就绪状态{if ((pData == NULL) || (Size == 0)){return HAL_ERROR;}huart->pRxBuffPtr = pData;huart->RxXferSize = Size;huart->ErrorCode = HAL_UART_ERROR_NONE;HAL_DMA_Start(huart->hdmarx, (uint32_t)&huart->Instance->DR, (uint32_t)pData, Size);//启动DMA接收,源地址为UART数据寄存器,目标地址为接收缓冲区SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);//使能UART的DMA接收功能return HAL_OK;}else{return HAL_BUSY;}}
void dbus_uart_init(void)//DBUS串口初始化{__HAL_UART_CLEAR_IDLEFLAG(&DBUS_HUART);//用于清除UART的空闲标志,空闲标志指示UART在接收数据时处于空闲状态,通常在接收完成后设置//清除这个标志是为了确保后续的接收操作能够正确检测到新的空闲状态__HAL_UART_ENABLE_IT(&DBUS_HUART, UART_IT_IDLE);//使能UART的空闲中断,当UART处于空闲状态并且接收缓冲区没有数据时,会触发这个中断//使能这个中断后,可以在中断服务例程中处理空闲状态uart_receive_dma_no_it(&DBUS_HUART, dbus_buf, DBUS_MAX_LEN);//调用之前的函数,用DMA来接收串口数据}
配置好DBUS串口初始化函数后,在main函数中调用并初始化。
3.数据帧解码
在.h文件中定义名为rc_info_t的结构体,成员为遥控器数据解码后的内容。
typedef __packed struct{ int16_t ch0; int16_t ch1; int16_t ch2; int16_t ch3; int16_t roll; uint8_t sw1; uint8_t sw2;} rc_info_t;#define rc_Init \{ \0, \0, \0, \0, \0, \0, \0, \}
定义以上面结构体为数据类型的变量rc,并赋初值0.
rc_info_t rc = rc_Init;
遥控器解码数据,并将解码后的真正遥控器值存储到变量rc的结构体成员中。
void rc_callback_handler(rc_info_t *rc, uint8_t *buff){//用于处理接收到的遥控器(rc)数据,将接收到的字节解码 rc->ch0 = (buff[0] | buff[1] << 8) & 0x07FF;//将buff[0]和buff[1]的值组合为ch0通道的值,并将其限制在11位(通过与0x07FF按位与) rc->ch0 -= 1024;//由于数据在364到1684,将解码后的数据减去1024,使其中心值为0 rc->ch1 = (buff[1] >> 3 | buff[2] << 5) & 0x07FF; rc->ch1 -= 1024; rc->ch2 = (buff[2] >> 6 | buff[3] << 2 | buff[4] << 10) & 0x07FF; rc->ch2 -= 1024; rc->ch3 = (buff[4] >> 1 | buff[5] << 7) & 0x07FF; rc->ch3 -= 1024; rc->roll = (buff[16] | (buff[17] << 8)) & 0x07FF; //左上角滚轮 rc->roll -= 1024; rc->sw1 = ((buff[5] >> 4) & 0x000C) >> 2; rc->sw2 = (buff[5] >> 4) & 0x0003;//sw1和sw2的值分别由相应的位计算得出 if ((abs(rc->ch0) > 660) || \ (abs(rc->ch1) > 660) || \ (abs(rc->ch2) > 660) || \ (abs(rc->ch3) > 660)) { memset(rc, 0, sizeof(rc_info_t));//如果任一通道的绝对值超过660,表示接收到异常数据,则将rc结构体的所有内容清零 }}
处理DBUS串口接收的数据,并通过空闲中断来解决接收随机长度数据的问题。
uint16_t dma_current_data_counter(DMA_Stream_TypeDef *dma_stream){//返回DMA预定义的缓冲区剩余的长度,方便了解传输过程中还有多少数据尚未传输 return ((uint16_t)(dma_stream->NDTR));}static void uart_rx_idle_callback(UART_HandleTypeDef* huart){__HAL_UART_CLEAR_IDLEFLAG(huart);//清除UART的空闲标志,以便下一次接收时能够正确检测到空闲状态if (huart == &DBUS_HUART)//确保只处理DBUS串口{__HAL_DMA_DISABLE(huart->hdmarx);//失能DMA接收,防止下一次接收的数据在上一次数据的尾部,而不是全新的数据if ((DBUS_MAX_LEN - dma_current_data_counter(huart->hdmarx->Instance)) == DBUS_BUFLEN){//计算当前接收的数据长度,如果接收到的数据长度等于18字节,则调用处理数据函数rc_callback_handler(&rc, dbus_buf);//处理接收的数据并解码}__HAL_DMA_SET_COUNTER(huart->hdmarx, DBUS_MAX_LEN);//设置DMA接收预定义的缓冲区的长度,以便为下一次接收做好准备__HAL_DMA_ENABLE(huart->hdmarx);//重新启用DMA接收,以便继续接收数据}}
使用回调函数来实现串口DMA空闲中断接收。
void uart_receive_handler(UART_HandleTypeDef *huart){//用于检查UART接收状态并在接收到空闲状态时调用相应的回调函数if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) && //检查UART是否设置了空闲标志,表示UART接收完成并进入空闲状态__HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE))//检查UART空闲中断是否被使能,只有在中断使能的情况下,才会处理空闲状态{uart_rx_idle_callback(huart);//调用之前定义的函数,处理接收到的数据}}
最后在cubemx生成的stm32f4xx_it.c中找到串口3中断服务函数,并调用之前写的回调函数来处理接收的数据。
void USART3_IRQHandler(void){ /* USER CODE BEGIN USART3_IRQn 0 */uart_receive_handler(&huart3);//调用之前定义的函数,传入DBUS串口的地址,以处理接收事件 /* USER CODE END USART3_IRQn 0 */ HAL_UART_IRQHandler(&huart3); /* USER CODE BEGIN USART3_IRQn 1 */ /* USER CODE END USART3_IRQn 1 */}
3、效果展示
当我将遥控器配置为左拨杆中间,右拨杆下,可以看到S1值为3,S2值为2.将左摇杆摇到最左边时可以看到ch2为-660.