发帖
2 1 0

安信可雷达Rd-03_V2 基础功能测试 之 STM32代码编写

xiaofei213
中级会员

7

主题

4

回帖

287

积分

中级会员

积分
287
Rd-03系列 76 2 4 天前

写在前面

(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的测评就是这样了,稍后会看一下能不能做个目录索引,方便大家查找每一篇帖子。

──── 1人觉得很赞 ────

使用道具 举报

稳定性跟环境还有安装角度确实有很大关系
非常棒
您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 29855 个
  • 话题数: 43656 篇