CEM5861G-M11 是萤火工场(中电港)推出的 24GHz 毫米波雷达,在传统人体感应雷达的功能基础上增加了通过微小运动判断人体存在的功能
模块特性
| 关键指标 | 典型值 | 备注 | 
|---|---|---|
| 频段 | 24.00 – 24.25 GHz | |
| 工作电压 | 3.6 – 5.5 V | 5 V 时平均电流 22 mA | 
| 检测半径(挂高 3 m) | 静止 3 m / 移动 6 m | 可调 | 
| 数据接口 | UART | TTL-3.3 V | 
| 天线 FoV | 110° × 110° | 水平 & 垂直 | 
上手试玩
帧结构
一般结构
| 字段 | 长度 | 含义 | 
|---|---|---|
| Head | 2 B | 上位机发送,雷达接收: 0x55 0x5A雷达发送,上位机接收: 0x55 0xA5 | 
| Len | 2 B | 后续数据长度: 功能码 + 命令码 + 数据 + 校验和 | 
| Func | 1 B | 读: 0x0写: 0x1被动上报: 0x2主动上报:0x3 | 
| CMD | 2 B | 命令码1 功能分类 命令码2 具体功能 | 
| DATA | N B | 数据 | 
| SUM | 1 B | 校验和 | 
所有多字节参数,均采用大端格式传输
雷达输出格式
| Head | Len | Func | CMD | DATA | SUM | 
|---|---|---|---|---|---|
| 0x55 0xA5 | 0x00 0x0E | 0x03 | 0x81 0x00 | Data[0...9] | 校验和 | 
| DATA 段 | DATA 段 | 说明 | 描述 | ||
| Data[0] | 目标ID号 | ||||
| Data[1] | 目标状态 | 0:无目标 1:移动 2:存在 | |||
| Data[2] | Data[3] | 距离 | 单位 cm | ||
| Data[4] | Data[5] | 速度 | 单位 cm/s | ||
| Data[6] | 方位角度 | 单位 度 | |||
| Data[7] | 俯仰角度 | 单位 度 | |||
| Data[8] | Data[9] | 信号强度 | 
例如:
55 A5 00 0E 03 81 00 00 01 00 5E 00 00 00 00 01 78 64目标ID:第8 byte 0x00 
运动状态:第9 byte 0x01,检测到运动目标 
目标距离:第1017 bytes 0x01 0x78, 转换成十进制为 376
命令
上位机 CEM5861G-Dashboard
本项目使用 Apache License 2.0 许可协议开源,你可以自由使用、修改和分发本项目的代码,但需要遵守该协议的条款
使用 Vue3 开发,本上位机软件可实时显示雷达数据、实时绘图,并提供配置面板快速设置参数,欢迎使用


技术框架
- 前端框架:Vue3
- 数据可视化:内嵌图表组件(如距离、速度图),实时渲染雷达回传数据
- 串口通信:通过 Web 串口 API 实现与毫米波雷达模块的数据交互
- 事件通信:组件之间通过事件系统通信,实现数据流转与界面更新解耦
- 项目结构:
- src/App.vue:主界面入口
- src/components/ChartComponent.vue:实时数据图表面板组件
- src/components/ConfigPanel.vue:雷达配置面板组件
- src/components/DataDisplay.vue:实时数据面板组件
- src/components/LogDisplay.vue:日志面板组件
- src/components/SerialPortControl.vue:串口通信与数据解析核心组件
- src/components/SidebarDrawer.vue:侧边抽屉组件
- public/index.html:网页入口,支持离线运行
主要组件介绍
SerialPortControl
负责与雷达模块的串口通信,收发数据帧,实时解析雷达回传的数据,区分并处理主动上报帧、命令响应帧等
ConfigPanel
提供用户友好的参数配置界面,支持读取和写入各项雷达参数
点灯
本次点灯的开发板是 RA 生态工作室的 RA-ECO-RA6E2-64PIN-V1.0,板载入门级 RA6E2 MCU,基于带有 TrustZone® 的 200MHz Arm® Cortex®-M33 内核
硬件连接
将雷达模块的 T 引脚连接到开发板的 P110(UART9_RXD),模块使用 5V 供电与开发板共地
点灯程序
/*******************************************************************************************************************//**
 * 阻塞式精确读取指定字节数的串口数据
 *
 * @note  内含阻塞等待,不建议在中断或高优先级任务中调用
 *
 * @param[in]  buf                       目标缓冲区首地址
 * @param[in]  len                       欲要读取的字节数
 *
 * @retval  FSP_SUCCESS                  成功读取 len 字节
 * @retval  FSP_ERR_*                    错误码,详细参考 R_SCI_UART_Read 返回值
 *
 **********************************************************************************************************************/
fsp_err_t uart_read_exact(uint8_t *buf, uint32_t len)
{
    uint32_t got = 0;
    while (got < len)
    {
        fsp_err_t err = R_SCI_UART_Read(&g_uart9_ctrl, &buf[got], len - got);
        if (err != FSP_SUCCESS)
            return err;
        got += (len - got);
    }
    return FSP_SUCCESS;
}
/*******************************************************************************************************************//**
 * 主程序入口
 **********************************************************************************************************************/
void hal_entry(void)
{
    /* TODO: add your own code here */
    uint32_t idx = 0;
    UART9_Init();
    R_IOPORT_Open(&g_ioport_ctrl, g_ioport.p_cfg);
    printf("Hello, CEM5861G!\r\n");
    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_13, BSP_IO_LEVEL_LOW);
    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_07, BSP_IO_LEVEL_LOW);
    while (1)
    {
        switch (cem_state)
        {
            case STATE_FIND_HEAD1:    // 找第帧头第一位 0x55
            {
                if (R_SCI_UART_Read(&g_uart9_ctrl, &cem_rx_byte, 1) == FSP_SUCCESS)
                {
                    if (cem_rx_byte == 0x55)
                        cem_state = STATE_FIND_HEAD2;
                }
                break;
            }
            case STATE_FIND_HEAD2:    //找第帧头第二位 0xA5
            {
                if (R_SCI_UART_Read(&g_uart9_ctrl, &cem_rx_byte, 1) == FSP_SUCCESS)
                {
                    if (cem_rx_byte == 0xA5)       // 帧头 OK,接下来接收余下 16 字节
                    {
                        cem_frame[0] = 0x55;
                        cem_frame[1] = 0xA5;
                        idx = 2;
                        cem_state = STATE_RECV_PAYLOAD;
                    }
                    else if (cem_rx_byte == 0x55)  // 还是 0x55,继续找 0xA5
                    {
                        ;
                    }
                    else                           // 异常,重新找 0x55
                    {
                        cem_state = STATE_FIND_HEAD1;
                    }
                }
                break;
            }
            case STATE_RECV_PAYLOAD:  // 接收剩余 16 字节完成一帧
            {
                fsp_err_t err = uart_read_exact(&cem_frame[idx], CEM_FRAME_LEN - idx);
                if (err != FSP_SUCCESS)  //读取出错,重新开始
                {
                    cem_state = STATE_FIND_HEAD1;
                    break;
                }
                // 开始校验
                uint8_t sum = 0;
                for (uint32_t i = 0; i < CEM_FRAME_LEN - 1; i++)
                    sum += cem_frame[i];
                sum &= 0xFF;
                if (sum != cem_frame[CEM_FRAME_LEN - 1])  // 校验失败,丢掉这一帧
                {
                    // 校验失败,丢掉这一帧
                    printf("Checksum Error: sum=%02X, frame[17]=%02X\r\n", sum, cem_frame[CEM_FRAME_LEN - 1]);
                    cem_state = STATE_FIND_HEAD1;
                    break;
                }
                // 校验通过,使用第 9 个数据字节 DATA[1] 做控制
                uint8_t data1 = cem_frame[3 + 5];
                // P113[LED2]: 目标存在否
                if (data1 == 0x00)       // 目标不存在 - 灭
                    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_13, BSP_IO_LEVEL_LOW);
                else                     // 目标存在   - 亮
                    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_13, BSP_IO_LEVEL_HIGH);
                // P207[LED1]: 目标状态
                if (data1 == 0x01)       // 目标移动   - 亮
                    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_07, BSP_IO_LEVEL_HIGH);
                else if (data1 == 0x02)  // 目标存在   - 灭
                    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_02_PIN_07, BSP_IO_LEVEL_LOW);
                // 输出数据帧
                for (int i = 0; i < CEM_FRAME_LEN; i++)
                    printf("%02X ", cem_frame[i]);
                printf("\r\n");
                cem_state = STATE_FIND_HEAD1;
                break;
            }
        }
    }
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}uart_read_exact
利用 R_SCI_UART_Read 逐字节阻塞读取雷达发来的数据,直到收满 len 字节
hal_entry
核心逻辑为使用状态机拆帧对齐数据帧:

- 检查帧头 0x55 0xA5 保证帧同步
- 帧同步后读取余下 16 B 数据
- 整帧 18 B(含帧头)全部收完后计算校验和校验
- 校验通过后,直接取 DATA[1](第 9 字节) 做 LED 控制:
- 0x00 → LED2 灭
- 0x01 → LED2 亮,LED1 亮(移动存在)
- 0x02 → LED2 亮,LED1 灭(静止存在)

资源下载
使用本文所述资源需遵守对应开源许可协议
- [⇓] 离线版 CEM5861G-Dashboard
- 使用 Apache License 2.0 开源许可协议
- [⇓] RA6E2 Demo e² Studio 工程
- 使用 MIT License 开源许可协议




 
 