写在前面
(1)之前和官方交流过,说RD-03_V1已经准备下架了,所以本来是不应该写V1版本的代码的。但是巧合的是,V1版本的代码我曾经写过,所以就把两个版本进行了整合,以满足手头有V1版本的小伙伴的使用。
(2)代码在写的时候,串口的接收策略可以根据需要进行选择,我的代码仅仅是用来测试的,所以没有用复杂的接收策略,直接用的串口中断+延时检测的方式进行的设计,用来处理分包的情况。当然,使用其他接收策略(如串口+DMA、串口+空闲中断、环形缓冲区等)也都可以。
(3)不同版本的代码根据选择的宏进行配置,大家应该都懂,我就不班门弄斧了。
代码部分
大家现在可能更多的用的Hal库,本人才疏学浅,相对习惯使用标准STD库,但整体逻辑大差不差,还请见谅,希望没有在库的选择上引战。硬件平台选择的是野火小智系列的STM32F103C8T6最小系统板,其他型号的芯片同理。
头文件
本人写代码习惯性地在头文件里把各种相关参数定义好,方便后续使用和移植。本代码选用了串口1进行配置,其他串口同理。【头部注释部分是当时自己做库地时候乱写的,不太专业,大家可以忽略,顺带说一句:感谢官方没有改淘宝链接,所以头部注释几乎不用改】代码中通过宏定义RD03_V2选择了V2版本的代码。
/* ----------------------------------------------------------------------
*
* Copyright (C) 2025 XiaoFei. All rights reserved.
*
* $file: AiThinker_RD03.h
* $Date: 31-March-2025
* $Revision: V1.2
* $Project: XiaoFei BSP Library
* $Taobao: https://item.taobao.com/item.htm?abbucket=17&id=719248225132
*
* -------------------------------------------------------------------- */
#ifndef __AiThinker_RD03_H
#define __AiThinker_RD03_H
#define RD03_V2
#define RD03_RCC_Usart(x) RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, (FunctionalState)x)
#define RD03_RCC_TX(x) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, (FunctionalState)x)
#define RD03_RCC_RX(x) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, (FunctionalState)x)
#define RD03_GPIO_TX GPIOA
#define RD03_Pin_TX GPIO_Pin_9
#define RD03_GPIO_RX GPIOA
#define RD03_Pin_RX GPIO_Pin_10
#define RD03_Usart USART1
#define RD03_BaudRate 115200
#define RD03_Group NVIC_PriorityGroup_2
#define RD03_ITPre 1
#define RD03_ITSub 1
#define RD03_IRQn USART1_IRQn
#define RD03_IRQHandler USART1_IRQHandler
#ifndef RD03_BufDef_def
#define RD03_BufDef_def
typedef struct
{
#if defined(RD03_V1)
unsigned char buf[90]; /*!< The receved RD03 Data buffer */
#elif defined(RD03_V2)
unsigned char buf[300]; /*!< The receved RD03 Data buffer */
#endif
unsigned int len_max; /*!< The max length of RD03 data */
unsigned int len; /*!< The current length of RD03 data */
}RD03_BufDef;
#endif
#ifndef RD03_MoodDef_def
#define RD03_MoodDef_def
typedef enum
{
Report_Mood = 0x04 , /*!< The RD03 report mood */
Running_Mood = 0x64 /*!< The RD03 running mood */
}RD03_MoodDef;
#endif
#ifndef RD03_RunDataDef_def
#define RD03_RunDataDef_def
typedef struct
{
unsigned char state; /*!< The data is 0x01 if the decoding is correct, otherwise it is 0x00 */
unsigned int range; /*!< The RD03 range data */
}RD03_RunDataDef;
#endif
#ifndef RD03_RepDataDef_def
#define RD03_RepDataDef_def
typedef struct
{
unsigned char state; /*!< The data is 0x01 if the decoding is correct, otherwise it is 0x00 */
unsigned int range; /*!< The RD03 range data */
#if defined(RD03_V1)
unsigned int energy[16]; /*!< The RD03 energy data */
#elif defined(RD03_V2)
unsigned int energy[32]; /*!< The RD03 energy data */
#endif
}RD03_RepDataDef;
#endif
/*
* @function: RD03_BufClear
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] ClThis Input 0x01 to clear all buffers, otherwise input 0x00.
* @return: None.
*/
void RD03_BufClear(RD03_BufDef* RD03, unsigned char ClThis);
/*
* @function: RD03_BufGet
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] us The interval for obtaining two adjacent pointers.
* @return: Return 0x01 if the data has get successful, otherwise return 0x00.
*/
unsigned char RD03_BufGet(RD03_BufDef* RD03, unsigned int us);
/*
* @function: RD03_Init
* @parameter: [in] RD03 The handle of RD03.
* @return: None.
*/
void RD03_Init(RD03_BufDef* RD03);
/*
* @function: RD03_SetMood
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] Mood The mode will be set to RD03.
* @return: None.
*/
void RD03_SetMood(RD03_BufDef* RD03, RD03_MoodDef Mood);
/*
* @function: RD03_RunningData
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] Data The data decoding result of RD03 in running mode.
* @return: None.
*/
void RD03_RunningData(RD03_BufDef* RD03, RD03_RunDataDef* Data);
/*
* @function: RD03_ReportData
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] Data The data decoding result of RD03 in report mode.
* @return: None.
*/
void RD03_ReportData(RD03_BufDef* RD03, RD03_RepDataDef* Data);
/*
* @function: RD03_IRQHandler
* @callback: The function will be callback when get the data from RD03.
*/
void RD03_IRQHandler(void);
#endif
串口逻辑
对于串口的接收部分,选择使用中断接收,数据获取通过指针的方式进行,若在一段延时时间前后,未触发串口接收中断,即串口的数据长度未改变,即认为一包数据接收完成。【注意:这是一种不太高效的数据接收方式,对于简单的测试还好,在实际应用中应该尽量避免】
RD03_BufDef RD03_This;
/*
* @function: RD03_BufClear
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] ClThis Input 0x01 to clear all buffers, otherwise input 0x00.
*/
void RD03_BufClear(RD03_BufDef* RD03, unsigned char ClThis)
{
RD03->len = 0;
for (unsigned int i = 0; i < RD03->len_max; i++)
RD03->buf[i] = 0;
if (!(!ClThis))
{
RD03_This.len = 0;
for (unsigned int i = 0; i < RD03_This.len_max; i++)
RD03_This.buf[i] = 0;
}
}
/*
* @function: RD03_BufGet
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] us The interval for obtaining two adjacent pointers.
* @return: Return 0x01 if the data has get successful, otherwise return 0x00.
*/
unsigned char RD03_BufGet(RD03_BufDef* RD03, unsigned int us)
{
unsigned int len = RD03_This.len;
XiaoFei_Delay_us(us);
if ((len == RD03_This.len) && (RD03_This.len != 0))
{
RD03->len = RD03_This.len;
RD03->len_max = RD03_This.len_max;
for (unsigned int i = 0; i < RD03->len_max; i++)
RD03->buf[i] = RD03_This.buf[i];
return 0x01;
}
return 0x00;
}
/*
* @function: RD03_Init
* @parameter: [in] RD03 The handle of RD03.
*/
void RD03_Init(RD03_BufDef* RD03)
{
RD03_RCC_Usart(1);
RD03_RCC_TX(1);
RD03_RCC_RX(1);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = RD03_Pin_TX;
GPIO_Init(RD03_GPIO_TX, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = RD03_Pin_RX;
GPIO_Init(RD03_GPIO_RX, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = RD03_BaudRate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(RD03_Usart, &USART_InitStructure);
USART_ITConfig(RD03_Usart, USART_IT_RXNE, ENABLE);
NVIC_PriorityGroupConfig(RD03_Group);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = RD03_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = RD03_ITPre;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = RD03_ITSub;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(RD03_Usart, ENABLE);
RD03->len = 0;
RD03_This.len = 0;
#if defined(RD03_V1)
RD03->len_max = 90;
RD03_This.len_max = 90;
#elif defined(RD03_V2)
RD03->len_max = 300;
RD03_This.len_max = 300;
#endif
for(unsigned int i=0; i < RD03->len_max; i++)
{
RD03->buf[i] = 0x00;
RD03_This.buf[i] = 0x00;
}
}
/*
* @function: RD03_Send
* @parameter: [in] str The data will be send to RD03.
* @parameter: [in] num The size of the data.
*/
static void RD03_Send(unsigned char* str, unsigned int num)
{
for (unsigned int i = 0; i < num; i++)
{
USART_SendData(RD03_Usart, str[i]);
while (USART_GetFlagStatus(RD03_Usart, USART_FLAG_TXE) == RESET);
}
while (USART_GetFlagStatus(RD03_Usart, USART_FLAG_TC) == RESET);
}
/*
* @function: RD03_IRQHandler
* @callback: The function will be callback when get the data from RD03.
*/
void RD03_IRQHandler(void)
{
if (USART_GetFlagStatus(RD03_Usart, USART_FLAG_RXNE) == SET)
{
USART_ClearITPendingBit(RD03_Usart, USART_IT_RXNE);
RD03_This.buf[RD03_This.len] = USART_ReceiveData(RD03_Usart);
if (RD03_This.len < RD03_This.len_max)
RD03_This.len++;
}
}
模块配置与数据处理
模块的配置逻辑实际应该进行接收数据的校验的,此处为了方便使用,默认数据均成功发送,仅作为配置流程示例使用【虽然实际配置过程中,数据都成功配置了,但最好还是进行校验】。配置过程中,涉及到需要计算的部分,也选用了全局的数组进行配置,实际使用过程中需要自己进行计算。
unsigned char Cmd_WriCmd[14] = {0xfd, 0xfc, 0xfb, 0xfa, 0x04, 0x00, 0xff, 0x00, 0x01, 0x00, 0x04, 0x03, 0x02, 0x01};
unsigned char Cmd_MinDis[18] = {0xfd, 0xfc, 0xfb, 0xfa, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01};
unsigned char Cmd_MaxDis[18] = {0xfd, 0xfc, 0xfb, 0xfa, 0x08, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01};
unsigned char Cmd_DelTim[18] = {0xfd, 0xfc, 0xfb, 0xfa, 0x08, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01};
#if defined(RD03_V2)
unsigned char Cmd_KeepDa[12] = {0xfd, 0xfc, 0xfb, 0xfa, 0x02, 0x00, 0xfd, 0x00, 0x04, 0x03, 0x02, 0x01};
#endif
unsigned char Cmd_ReadDa[13] = {0xfd, 0xfc, 0xfb, 0xfa, 0x02, 0x00, 0xfe, 0x00, 0x04, 0x03, 0x02, 0x01, 0x00};
/*
* @function: RD03_SetMood
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] Mood The mode will be set to RD03.
*/
void RD03_SetMood(RD03_BufDef* RD03, RD03_MoodDef Mood)
{
unsigned char Cmd_SetMod[18] = {0xfd, 0xfc, 0xfb, 0xfa, 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, (unsigned char)Mood, 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01};
RD03_BufClear(RD03, 1);
RD03_Send(Cmd_WriCmd, sizeof(Cmd_WriCmd));
XiaoFei_Delay_ms(150);
RD03_BufClear(RD03, 1);
RD03_Send(Cmd_WriCmd, sizeof(Cmd_WriCmd));
RD03_BufClear(RD03, 1);
RD03_Send(Cmd_MinDis, sizeof(Cmd_MinDis));
RD03_BufClear(RD03, 1);
RD03_Send(Cmd_MaxDis, sizeof(Cmd_MaxDis));
RD03_BufClear(RD03, 1);
RD03_Send(Cmd_DelTim, sizeof(Cmd_DelTim));
RD03_BufClear(RD03, 1);
RD03_Send(Cmd_SetMod, sizeof(Cmd_SetMod));
RD03_BufClear(RD03, 1);
#if defined(RD03_V2)
RD03_Send(Cmd_KeepDa, sizeof(Cmd_KeepDa));
RD03_BufClear(RD03, 1);
#endif
RD03_Send(Cmd_ReadDa, sizeof(Cmd_ReadDa));
RD03_BufClear(RD03, 1);
}
数据处理包括运行模式部分和上报模式部分,处理方式均是包头校验->包长校验->包尾校验->数据解析,校验过程中如遇到错误会提前退出。此部分的数据校验适配了V1版本和V2版本,小伙伴们可以用宏定义的方式直接配置【见头文件部分】。我的代码在上报部分处理时使用的是乘法和加法运算,大家也可以用移位的方法进行处理。
unsigned char Data_Hander[4] = {0xf4, 0xf3, 0xf2, 0xf1};
/*
* @function: RD03_RunningData
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] Data The data decoding result of RD03 in running mode.
*/
void RD03_RunningData(RD03_BufDef* RD03, RD03_RunDataDef* Data)
{
if (RD03->len < strlen("OFF"))
{
Data->state = 0x00;
Data->range = 0;
return;
}
char *temp = NULL;
#if defined(RD03_V1)
temp = strstr((char*)RD03->buf, "Range");
if (temp != NULL)
{
Data->state = 0x01;
Data->range = atoi(temp + strlen("Range"));
RD03_BufClear(RD03, 1);
return;
}
#elif defined(RD03_V2)
temp = strstr((char*)RD03->buf, "distance");
if (temp != NULL)
{
Data->state = 0x01;
Data->range = atoi(temp + strlen("distance"));
RD03_BufClear(RD03, 1);
return;
}
#endif
else
{
Data->state = 0x00;
Data->range = 0;
RD03_BufClear(RD03, 1);
return;
}
}
/*
* @function: RD03_ReportData
* @parameter: [in] RD03 The handle of RD03.
* @parameter: [in] Data The data decoding result of RD03 in report mode.
*/
void RD03_ReportData(RD03_BufDef* RD03, RD03_RepDataDef* Data)
{
#if defined(RD03_V1)
#define RD03_Debug_len 80
#define RD03_Enerage_Size 16
#define RD03_Debug2_flu 41
#elif defined(RD03_V2)
#define RD03_Debug_len 200
#define RD03_Enerage_Size 32
#define RD03_Debug2_flu 137
#endif
if (RD03->len < RD03_Debug_len)
{
Data->state = 0x00;
Data->range = 0;
for (unsigned char i = 0; i < RD03_Enerage_Size; i++)
Data->energy[i] = 0;
return;
}
char *temp = NULL;
temp = strstr((char*)RD03->buf, (char*)Data_Hander);
if (temp == NULL)
{
Data->state = 0x00;
Data->range = 0;
for (unsigned char i = 0; i < RD03_Enerage_Size; i++)
Data->energy[i] = 0;
goto RD03_Err_Deal;
}
unsigned int header_offset = (unsigned int)(temp - (char *)RD03->buf);
if (header_offset + RD03_Debug2_flu + 4 > RD03->len)
goto RD03_Err_Deal;
if (*((unsigned char*)temp + RD03_Debug2_flu) != 0xF8)
goto RD03_Err_Deal;
if (*((unsigned char*)temp + RD03_Debug2_flu + 1) != 0xF7)
goto RD03_Err_Deal;
if (*((unsigned char*)temp + RD03_Debug2_flu + 2) != 0xF6)
goto RD03_Err_Deal;
if (*((unsigned char*)temp + RD03_Debug2_flu + 3) != 0xF5)
goto RD03_Err_Deal;
Data->state = *((unsigned char*)temp + 6);
Data->range = (unsigned int)(*((unsigned char*)temp + 7)) + 256 * (*((unsigned char*)temp + 8));
for (unsigned char i = 0; i < RD03_Enerage_Size; i++)
{
#if defined(RD03_V1)
Data->energy[i] = (unsigned int)(*((unsigned char*)temp + 9 + 2 * i))
+ 256 * (*((unsigned char*)temp + 10 + 2 * i));
#elif defined(RD03_V2)
Data->energy[i] = (unsigned int)(*((unsigned char*)temp + 9 + 4 * i))
+ 256 * (*((unsigned char*)temp + 10 + 4 * i))
+ 65536* (unsigned int)(*((unsigned char*)temp + 11 + 4 * i))
+ 16777216 * (*((unsigned char*)temp + 12 + 4 * i));
#endif
}
RD03_BufClear(RD03, 1);
return;
RD03_Err_Deal:
RD03_BufClear(RD03, 1);
return;
#undef RD03_Debug_len
#undef RD03_Enerage_Size
#undef RD03_Debug2_flu
}
如何使用代码?
这段代码的使用还是非常简单的,所有参数配置完成后,直接获取数据并解析数据即可。简单写个流程就是:
RD03_Init->RD03_SetMood->
RD03_BufGet->RD03_RunningData(RD03_ReportData)->
RD03_BufGet->RD03_RunningData(RD03_ReportData)->(...)
大家应该明白了吧。当然,如果代码有什么bug或者改进措施,欢迎大家提出来。
写在后面
本来应该是根据这个模组来做个应用的,我的初步想法是用在公司的一个设备中,由于设备具有一定危险性,所以初步想的是检测到一定范围内有人就停止设备运行,否则就允许设备启动。但是就在后来测试的时候,问题出现了,主要是两个问题:
(1)雷达信号在整个过程中被频繁干扰,频繁地检测到"有人"(可能是磁场导致的能量扰动吧)。
(2)用串口进行工作的时候,TTL的串口信号也被频繁干扰,导致大量的数据错误(说不出来的那种大量)。
我这边初步猜测是强电流环境或者强磁场环境的干扰,但是如果要想办法屏蔽掉这些干扰,或者想办法增强信号的功率,就又要增加很多成本,所以非常的可惜,最后没能成功地应用在实际地项目里,只完成了驱动和简单的demo(因为太简单了,具体的代码就不贴,其实就是运行模式查询检测数据,然后控制开关的通断)。
我对RD-03_V2的测评就是这样了,稍后会看一下能不能做个目录索引,方便大家查找每一篇帖子。