CEM5861G 初体验:自开发上位机、RA6E2 点灯
分享作者:Mr_Fang
作者昵称:Mr_Fang
评测品牌:萤火工场
评测型号:CEM5861G-M11
发布时间:2025-07-16 14:06:31
0
前言
为 CEM5861G 开发了上位机,并使用 RA6E2 开发板点灯
开源口碑分享内容

CEM5861G-M11 是萤火工场(中电港)推出的 24GHz 毫米波雷达,在传统人体感应雷达的功能基础上增加了通过微小运动判断人体存在的功能

模块特性

关键指标典型值备注
频段24.00 – 24.25 GHz
工作电压3.6 – 5.5 V5 V 时平均电流 22 mA
检测半径(挂高 3 m)静止 3 m / 移动 6 m可调
数据接口UARTTTL-3.3 V
天线 FoV110° × 110°水平 & 垂直

上手试玩

帧结构

一般结构

字段长度含义
Head2 B上位机发送,雷达接收: 0x55 0x5A
雷达发送,上位机接收: 0x55 0xA5
Len2 B后续数据长度:功能码 + 命令码 + 数据 + 校验和
Func1 B读:0x0
写: 0x1
被动上报: 0x2
主动上报:0x3
CMD2 B命令码1 功能分类
命令码2 具体功能
DATAN B数据
SUM1 B校验和

所有多字节参数,均采用大端格式传输

雷达输出格式

HeadLenFuncCMDDATASUM
0x55 0xA50x00 0x0E0x030x81 0x00Data[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 开发,本上位机软件可实时显示雷达数据、实时绘图,并提供配置面板快速设置参数,欢迎使用

cem.babyfang.cn

技术框架

  • 前端框架: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

核心逻辑为使用状态机拆帧对齐数据帧:

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


资源下载

使用本文所述资源需遵守对应开源许可协议

全部评论
暂无评论
0/144