基于STM32F103C8T6的R60AFD1毫米波雷达模块驱动设计

若该文为原创文章,转载请注明原文出处。

1. 引言

R60AFD1 是一款60GHz毫米波雷达传感器模块,常用于人体存在检测、跌倒报警及运动状态识别等场景。该模块采用UART串口通信,支持两种工作模式:主动上报模式查询-应答模式。本文针对查询-应答模式,提供一套完整的STM32F103C8T6驱动代码,使用标准外设库开发,通过USART2(PA2 TX,PA3 RX)实现与模块的交互,可获取人体存在、活跃度及跌倒状态。

2. 硬件连接

STM32F103C8T6 R60AFD1模块
3.3V / 5V VCC
GND GND
PA2 (USART2_TX) RX
PA3 (USART2_RX) TX

注意事项:

  • 模块典型供电电压为3.3V或5V,请以手册为准。

  • 确保共地,且电源能提供足够电流(峰值约93mA)。

  • 模块建议顶装(天花板),安装高度2.2~3m,探测半径约2m(跌倒有效范围)。

3. 通信协议(查询-应答)

本驱动使用的命令帧格式固定为10字节,结构如下:

Byte0 Byte1 Byte2 Byte3 Byte4 Byte5 Byte6 Byte7 Byte8 Byte9
0x53 0x59 CMD1 CMD2 0x00 0x01 0x0F SUM 0x54 0x43
  • 帧头53 59

  • 命令字CMD1CMD2 决定查询类型

  • 固定字段00 01 0F

  • 校验和:前7个字节(Byte0~Byte6)的累加和

  • 帧尾54 43

3.1 查询命令定义

功能 CMD1 CMD2 命令帧(十六进制)
人体存在查询 0x80 0x81 53 59 80 81 00 01 0F BD 54 43
人体活跃查询 0x80 0x82 53 59 80 82 00 01 0F BE 54 43
跌倒状态查询 0x83 0x81 53 59 83 81 00 01 0F C0 54 43

3.2 应答帧格式

模块收到查询命令后返回相同结构的数据帧,其中 Byte6 为有效数据:

查询类型 应答数据 Byte6 含义
人体存在(0x80,0x81) 0x00 无人
0x01 有人
人体活跃(0x80,0x82) 0x01 静止
0x02 活跃
跌倒状态(0x83,0x81) 0x00 未跌倒
0x01 跌倒

应答示例:
53 59 80 81 00 01 01 AF 54 43 表示检测到有人。

4. 驱动代码实现

4.1 文件结构

  • R60AFD1.h:函数声明与状态宏定义

  • R60AFD1.c:串口初始化、命令发送、接收解析与状态管理

  • 中断服务函数:添加到 stm32f10x_it.c

4.2 完整代码

R60AFD1.h
复制代码
#ifndef __R60AFD1_H
#define __R60AFD1_H

#include "stm32f10x.h"
#include <stdbool.h>

// 状态位掩码
#define R60_HUM_EXT  0x01   // 人体存在
#define R60_HUM_ACT  0x02   // 人体活跃
#define R60_HUM_FAL  0x04   // 跌倒状态

void R60AFD1_Init(uint32_t baudrate);          // 初始化串口及模块配置
void R60AFD1_QueryHumanExist(void);            // 查询人体存在
void R60AFD1_QueryHumanActive(void);           // 查询活跃度
void R60AFD1_QueryFall(void);                  // 查询跌倒状态
uint8_t R60AFD1_GetState(void);                // 获取状态掩码
bool R60AFD1_IsPersonExist(void);              // 是否有人
bool R60AFD1_IsActive(void);                   // 是否活跃(true:活跃 false:静止)
bool R60AFD1_IsFallDetected(void);             // 是否跌倒
void R60AFD1_ClearFallFlag(void);              // 清除跌倒标志
void R60AFD1_Process(void);                    // 后台处理(超时复位)
void R60AFD1_IRQHandler(void);                 // 串口中断处理函数

#endif
R60AFD1.c
复制代码
#include "R60AFD1.h"

// 串口接收缓冲区(底层驱动)
#define RX_BUF_SIZE    32
 uint8_t rxBuffer[RX_BUF_SIZE];
 uint8_t rxIndex = 0;
 volatile uint8_t rxComplete = 0;  // 1: 收到完整一帧
 volatile uint8_t rxTimeout = 0;   // 接收超时标志

// 雷达状态
 uint8_t radarState = 0;   // 使用 R60_HUM_EXT/ACT/FAL 位
 uint8_t lastFallState = 0;

// 查询命令帧模板
 const uint8_t CMD_FRAME_TEMPLATE[10] = {0x53,0x59,0xFF,0xFF,0x00,0x01,0x0F,0xFF,0x54,0x43};

// 计算校验和(前7个字节累加)
static uint8_t calcChecksum(const uint8_t *data) {
    uint8_t sum = 0;
    for (uint8_t i = 0; i < 7; i++) {
        sum += data[i];
    }
    return sum;
}

// 发送10字节命令帧
static void sendCommand(uint8_t cmd1, uint8_t cmd2) {
    uint8_t frame[10];
    memcpy(frame, CMD_FRAME_TEMPLATE, 10);
    frame[2] = cmd1;
    frame[3] = cmd2;
    frame[7] = calcChecksum(frame);
    
    // 通过USART2发送
    for (uint8_t i = 0; i < 10; i++) {
        while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
        USART_SendData(USART2, frame[i]);
    }
}

// 查询API
void R60AFD1_QueryHumanExist(void) {
    sendCommand(0x80, 0x81);
}

void R60AFD1_QueryHumanActive(void) {
    sendCommand(0x80, 0x82);
}

void R60AFD1_QueryFall(void) {
    sendCommand(0x83, 0x81);
}

// 解析接收到的帧(在串口中断中调用)
static void parseFrame(void) {
    if (rxComplete == 0) return;
    rxComplete = 0;
    
    // 长度、帧头、帧尾检查
    if (rxIndex != 10) goto clear;
    if (rxBuffer[0] != 0x53 || rxBuffer[1] != 0x59 ||
        rxBuffer[8] != 0x54 || rxBuffer[9] != 0x43) goto clear;
    
    // 校验和验证
    uint8_t calcSum = 0;
    for (uint8_t i = 0; i < 7; i++) calcSum += rxBuffer[i];
    if (calcSum != rxBuffer[7]) goto clear;
    
    // 根据命令字解析数据
    uint8_t cmd1 = rxBuffer[2];
    uint8_t cmd2 = rxBuffer[3];
    uint8_t data = rxBuffer[6];
    
    if (cmd1 == 0x83 && cmd2 == 0x81) { // 跌倒状态应答
        if (data == 0x01) {
            radarState |= R60_HUM_FAL;
        } else {
            radarState &= ~R60_HUM_FAL;
        }
    } else if (cmd1 == 0x80 && cmd2 == 0x81) { // 人体存在应答
        if (data == 0x01) {
            radarState |= R60_HUM_EXT;
        } else {
            radarState &= ~R60_HUM_EXT;
        }
    } else if (cmd1 == 0x80 && cmd2 == 0x82) { // 人体活跃应答
        if (data == 0x02) {
            radarState |= R60_HUM_ACT;
        } else {
            radarState &= ~R60_HUM_ACT;
        }
    }
    
clear:
    // 清空缓冲区,准备接收下一帧
    rxIndex = 0;
    rxTimeout = 0;
}

// 串口中断中接收单字节(在 stm32f10x_it.c 中调用)
void R60AFD1_IRQHandler(void) {
    if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
        uint8_t data = USART_ReceiveData(USART2);
        if (rxIndex < RX_BUF_SIZE) {
            rxBuffer[rxIndex++] = data;
        }
        // 简单超时机制:每次收到数据重置定时器,在Process中处理超时
        rxTimeout = 10;  // 约10ms超时(需配合主循环周期)
        // 如果收到10个字节,认为一帧完成
        if (rxIndex >= 10) {
            rxComplete = 1;
            parseFrame();  // 立即解析
        }
    }
}

// 后台处理(超时复位)
void R60AFD1_Process(void) {
    if (rxTimeout > 0) {
        rxTimeout--;
        if (rxTimeout == 0 && rxIndex > 0) {
            // 接收超时,清空缓冲区
            rxIndex = 0;
        }
    }
}

// 获取状态
uint8_t R60AFD1_GetState(void) {
    return radarState;
}

bool R60AFD1_IsPersonExist(void) {
    return (radarState & R60_HUM_EXT) != 0;
}

bool R60AFD1_IsActive(void) {
    return (radarState & R60_HUM_ACT) != 0;
}

bool R60AFD1_IsFallDetected(void) {
    return (radarState & R60_HUM_FAL) != 0;
}

void R60AFD1_ClearFallFlag(void) {
    radarState &= ~R60_HUM_FAL;
}

// 初始化USART2 (PA2 TX, PA3 RX)
void R60AFD1_Init(uint32_t baudrate) {
    	GPIO_InitTypeDef GPIO_InitStructure;
					USART_InitTypeDef usartInitStruct;
					NVIC_InitTypeDef nvicInitStruct;

				
				/*配置USART2和GPIO时钟*/
					RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
					RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
					
					/*GPIO配置*/
					GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
					GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
					GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
					GPIO_Init(GPIOA, &GPIO_InitStructure);
					
					GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
					GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
					GPIO_Init(GPIOA, &GPIO_InitStructure);
				
					usartInitStruct.USART_BaudRate = 115200;
					usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;     //无硬件控流
					usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                     //接收和发送	
					usartInitStruct.USART_Parity = USART_Parity_No;                                 //无校验
					usartInitStruct.USART_StopBits = USART_StopBits_1;                              //1位停止位
					usartInitStruct.USART_WordLength = USART_WordLength_8b;                         //8位数据位
					USART_Init(USART2,&usartInitStruct);
					
					USART_Cmd(USART2,ENABLE);                         //使能串口
				
					USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);									//使能接收中断
					
					nvicInitStruct.NVIC_IRQChannel = USART2_IRQn;
					nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
					nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
					nvicInitStruct.NVIC_IRQChannelSubPriority = 0;
					NVIC_Init(&nvicInitStruct);
    
    // 清空状态
    radarState = 0;
    rxIndex = 0;
    rxComplete = 0;
    rxTimeout = 0;
    
    // 可选:发送初始化配置命令(根据原代码中的 sti_R60aTxInit)
    // 发送配置帧:53 59 80 00 00 01 00 2D 54 43
    uint8_t initFrame[10] = {0x53,0x59,0x80,0x00,0x00,0x01,0x00,0x2D,0x54,0x43};
    for (uint8_t i = 0; i < 10; i++) {
        while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
        USART_SendData(USART2, initFrame[i]);
    }
}

void USART2_IRQHandler(void) {
    R60AFD1_IRQHandler();
}

5. 使用方法与示例

5.1 主函数示例

复制代码
#include "delay.h"
#include "sys.h"
#include "OLED.h"
#include "usart.h"

#include "ESP8266.h"
#include "Timer.h"
#include "led.h"
//网络协议层
#include "onenet.h"
//C库
#include <string.h>
#include "stdio.h"
#include "math.h"


#include "R60AFD1.h"


char Timer_IT=0;



u8 LIGHT = 3;

u16 Lsens_val = 0;

u8   Pir_Status = 0;
u16  Pir_Time_Cnt = 0;


// 定时查询(使用SysTick或简单延时)
uint32_t lastQuery = 0;
uint8_t queryStep = 0;


int main(void)
{
	   unsigned char *dataPtr = NULL;	
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//中断控制器分组设置
    delay_init();
    Usart1_Init(115200);
//   	Usart3_Init(115200);
	
	   R60AFD1_Init(115200);  // 根据雷达模块实际波特率设置(常见115200)
	
    OLED_Init();
    OLED_Clear();
	
//   	ESP8266_Init();
    Timer_Init();
    Timer_IT = 5;
	
	   
    while(1)
	   {
       
        
								
//        if(Timer_IT >= 5)//每隔3秒定时上传数据
//        {
//            Timer_IT=0;
//            OneNet_SendData();	//发送数据到云平台
//            ESP8266_Clear();    //情况串口接收区缓存
//        }
//        
//		      dataPtr = ESP8266_GetIPD(0);//获取云平台下发的数据
//		      if(dataPtr != NULL)//如果不为空,解析
//        {
//            OneNet_RevPro(dataPtr);	//平台返回数据检测
//        }
					
					// 每100ms轮询一次后台处理
        R60AFD1_Process();
        
        // 每500ms依次查询三种状态
            switch (queryStep) {
                case 0: R60AFD1_QueryHumanExist(); break;
                case 1: R60AFD1_QueryHumanActive(); break;
                case 2: R60AFD1_QueryFall(); break;
            }
            queryStep = (queryStep + 1) % 3;
        
        // 应用逻辑
        if (R60AFD1_IsPersonExist()) {
            // 有人
            if (R60AFD1_IsActive()) {
                // 活跃
            } else {
                // 静止
            }
            if (R60AFD1_IsFallDetected()) {
                // 触发跌倒报警
                // ... 执行动作
                R60AFD1_ClearFallFlag();  // 清除标志,避免重复报警
            }
        } else {
            // 无人
        }
       
								delay_ms(5000);
   	}
}


void TIM2_IRQHandler(void)
{
	 if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	 {
    Timer_IT++;
		  TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	 }
}

5.2 关键点说明

  • 查询周期:建议间隔≥200ms,避免串口拥塞。示例中使用500ms轮询一次,三种状态依次查询。

  • 跌倒标志 :跌倒状态是边沿触发,查询到后需调用 R60AFD1_ClearFallFlag() 清除,否则下次查询仍会认为跌倒。

  • 超时处理R60AFD1_Process() 必须在主循环中定期调用,用于清空接收不完整的数据帧。

6. 常见问题与调试

现象 可能原因 解决方法
串口无数据 波特率不匹配 / 硬件接线错误 核对模块手册波特率(常见115200),检查共地
接收帧校验失败 协议中校验和算法错误 / 命令帧格式错误 确认前7字节累加和是否正确,对比手册范例
跌倒状态无法清除 未调用 ClearFallFlag 在报警处理代码中调用清除函数
人体存在状态更新延迟 查询频率过低 / 模块响应时间较长 适当提高查询频率,但注意模块处理能力
模块无应答 未发送配置帧或模块处于主动上报模式 查阅手册,确保模块工作在查询模式

7. 总结

本文基于STM32F103C8T6标准外设库,实现了R60AFD1毫米波雷达模块的查询-应答驱动。通过USART2发送固定格式的命令帧,并解析返回数据,可稳定获取人体存在、活跃度及跌倒状态。代码结构清晰,便于移植到其他STM32系列或不同串口。实际应用时请务必参考模块最新数据手册,核对协议细节(如校验和、帧长度等)。若模块支持主动上报模式,也可按类似思路调整解析逻辑。

参考资料

如有侵权,或需要完整代码,请及时联系博主。

相关推荐
somi72 小时前
ARM-12-I.MX6U LCD
arm开发·单片机·嵌入式硬件·自用
bubiyoushang8883 小时前
基于STM32的心电采集系统设计
stm32·单片机·嵌入式硬件
笨笨饿3 小时前
26_为什么工程上必须使用拉普拉斯变换
c语言·开发语言·人工智能·嵌入式硬件·机器学习·编辑器·概率论
youcans_4 小时前
【STM32-MBD】(18)Clarke / Park 坐标变换链路
stm32·单片机·嵌入式硬件·matlab·代码生成
F137298015574 小时前
WD5208S 非隔离降压功率开关:集成650V MOSFET,220V降12V,5V,700MA
stm32·单片机·嵌入式硬件·51单片机
KOYUELEC光与电子努力加油4 小时前
JAE日本航空电子推出满足汽车市场小型防水最新需求的MX80系列连接器
服务器·科技·单片机·汽车
Zeku4 小时前
虚拟机网络设置
网络·stm32·freertos·linux驱动开发·linux应用开发
记录无知岁月4 小时前
【STM32】HAL库常用外设使用速查
stm32·单片机·cubemx
给点sun,就shine4 小时前
示波器入门
单片机·嵌入式硬件