基于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系列或不同串口。实际应用时请务必参考模块最新数据手册,核对协议细节(如校验和、帧长度等)。若模块支持主动上报模式,也可按类似思路调整解析逻辑。

参考资料

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

相关推荐
森G38 分钟前
STM32F103C8T6工程---标准库版usart2写回显
stm32·单片机
EVERSPIN1 小时前
基于MCU CH32X035 Type-C PD显示器方案
单片机·mcu·计算机外设
Joseph Cooper2 小时前
STM32MP157 Linux驱动学习笔记(一):驱动基础与设备模型入门(同步互斥/LCD/I2C/Input)
linux·stm32·学习
Joseph Cooper2 小时前
STM32MP157 Linux驱动学习笔记(二):硬件资源地基(Pinctrl/GPIO/Interrupt)
linux·stm32·学习
Z文的博客2 小时前
FLASHDB实战详解 - 嵌入式KV/TSD数据库开发全攻略
stm32·单片机·嵌入式·flash·flashdb·w25q256
SUNNYSPY0012 小时前
120R016-ASEMI解锁电力电子的效率革命
单片机
芯希望2 小时前
芯伯乐XOPA340/XOPA2340/XOPA4340系列11MHz低噪声CMOS运放,高性能与低功耗的理想平衡
单片机·嵌入式硬件·dc-dc·工业控制·国产替代·电源管理·xblw芯伯乐
LCMICRO-133108477463 小时前
长芯微LCMDC8588完全P2P替代ADS8588,是一款16位、8通道同步采样的逐次逼近型(SAR)模数转换器
stm32·单片机·嵌入式硬件·fpga开发·硬件工程·模数转换器
誰能久伴不乏3 小时前
SPI总线通信协议基础与ICM20607传感器驱动开发指南
arm开发·c++·驱动开发·嵌入式硬件·arm
VBsemi-专注于MOSFET研发定制3 小时前
面向车载冰箱高效可靠需求的功率器件选型策略与器件适配手册
单片机