除了显示驱动,其余全部测试通过
This commit is contained in:
+726
@@ -0,0 +1,726 @@
|
||||
#include "system/includes.h"
|
||||
#include "system/timer.h"
|
||||
#include "app_log.h"
|
||||
#include "r60abd1.h"
|
||||
#include "radar_uart.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*
|
||||
* R60ABD1协议格式:
|
||||
*
|
||||
* [0] 0x53
|
||||
* [1] 0x59
|
||||
* [2] CTRL
|
||||
* [3] CMD
|
||||
* [4] DATA_LEN_H
|
||||
* [5] DATA_LEN_L
|
||||
* [6..] DATA
|
||||
* [-3] CHECKSUM
|
||||
* [-2] 0x54
|
||||
* [-1] 0x43
|
||||
*
|
||||
* full_len = 9 + data_len
|
||||
*/
|
||||
|
||||
#define R60_FRAME_HEADER1 0x53
|
||||
#define R60_FRAME_HEADER2 0x59
|
||||
#define R60_FRAME_TAIL1 0x54
|
||||
#define R60_FRAME_TAIL2 0x43
|
||||
|
||||
#define R60_CTRL_PRESENCE 0x80
|
||||
#define R60_CTRL_BREATH 0x81
|
||||
#define R60_CTRL_SLEEP 0x84
|
||||
#define R60_CTRL_HEART 0x85
|
||||
|
||||
#define R60_PARSE_BUF_SIZE 128
|
||||
|
||||
|
||||
#ifndef R60ABD1_DEBUG_LOG
|
||||
#define R60ABD1_DEBUG_LOG 1
|
||||
#endif
|
||||
|
||||
|
||||
#if R60ABD1_DEBUG_LOG
|
||||
#define R60_LOG(...) APP_LOG(__VA_ARGS__)
|
||||
#else
|
||||
#define R60_LOG(...)
|
||||
#endif
|
||||
|
||||
|
||||
static u8 r60_parse_buf[R60_PARSE_BUF_SIZE];
|
||||
static u16 r60_parse_len;
|
||||
|
||||
static u32 r60_rx_bytes;
|
||||
static u32 r60_valid_frame_count;
|
||||
static u32 r60_data_update_count;
|
||||
static u32 r60_checksum_error_count;
|
||||
|
||||
static struct radar_r60abd1_data radar_data;
|
||||
|
||||
static OS_MUTEX radar_data_mutex;
|
||||
static u8 radar_data_mutex_ready;
|
||||
|
||||
|
||||
/*
|
||||
* R60坐标格式:
|
||||
*
|
||||
* bit15 = 符号位
|
||||
* bit14..0 = 数值绝对值
|
||||
*/
|
||||
static s16 r60_parse_signed_coordinate(u16 raw_value)
|
||||
{
|
||||
s16 value = (s16)(raw_value & 0x7fff);
|
||||
|
||||
if (raw_value & 0x8000) {
|
||||
value = -value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 协议层初始化。
|
||||
*
|
||||
* 必须在:
|
||||
* radar_r60abd1_input_bytes()
|
||||
* radar_r60abd1_get_data()
|
||||
*
|
||||
* 之前调用。
|
||||
*/
|
||||
int radar_r60abd1_init(void)
|
||||
{
|
||||
memset(r60_parse_buf, 0, sizeof(r60_parse_buf));
|
||||
memset(&radar_data, 0, sizeof(radar_data));
|
||||
|
||||
r60_parse_len = 0;
|
||||
r60_rx_bytes = 0;
|
||||
r60_valid_frame_count = 0;
|
||||
r60_data_update_count = 0;
|
||||
r60_checksum_error_count = 0;
|
||||
|
||||
if (!radar_data_mutex_ready) {
|
||||
os_mutex_create(&radar_data_mutex);
|
||||
radar_data_mutex_ready = 1;
|
||||
}
|
||||
|
||||
R60_LOG("[R60] init success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 完整帧合法性校验。
|
||||
*/
|
||||
static int r60_validate_frame(const u8 *frame, u16 frame_len)
|
||||
{
|
||||
u8 checksum = 0;
|
||||
u16 data_len;
|
||||
u16 i;
|
||||
|
||||
if (!frame || frame_len < 9) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frame[0] != R60_FRAME_HEADER1 ||
|
||||
frame[1] != R60_FRAME_HEADER2) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
data_len =
|
||||
((u16)frame[4] << 8) |
|
||||
frame[5];
|
||||
|
||||
if ((u16)(9 + data_len) != frame_len) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (frame[frame_len - 2] != R60_FRAME_TAIL1 ||
|
||||
frame[frame_len - 1] != R60_FRAME_TAIL2) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
/*
|
||||
* checksum:
|
||||
*
|
||||
* frame[0] 到 DATA最后一个字节之和。
|
||||
* 不包含checksum本身和帧尾。
|
||||
*/
|
||||
for (i = 0; i < frame_len - 3; i++) {
|
||||
checksum += frame[i];
|
||||
}
|
||||
|
||||
if (checksum != frame[frame_len - 3]) {
|
||||
|
||||
if (radar_data_mutex_ready) {
|
||||
|
||||
os_mutex_pend(&radar_data_mutex, 0);
|
||||
|
||||
r60_checksum_error_count++;
|
||||
radar_data.checksum_errors =
|
||||
r60_checksum_error_count;
|
||||
|
||||
os_mutex_post(&radar_data_mutex);
|
||||
|
||||
} else {
|
||||
|
||||
r60_checksum_error_count++;
|
||||
}
|
||||
|
||||
R60_LOG("[R60] checksum error: calc=%02X recv=%02X\n",
|
||||
checksum,
|
||||
frame[frame_len - 3]);
|
||||
|
||||
return -5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 业务解析。
|
||||
*
|
||||
* 只负责:
|
||||
* 1. CTRL/CMD解释
|
||||
* 2. 更新RAM中的最新状态
|
||||
*
|
||||
* 不做:
|
||||
* Flash
|
||||
* 网络上传
|
||||
*/
|
||||
static int r60_parse_frame(const u8 *frame, u16 frame_len)
|
||||
{
|
||||
u8 ctrl;
|
||||
u8 cmd;
|
||||
u16 data_len;
|
||||
int updated = 0;
|
||||
|
||||
if (!frame || frame_len < 9) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!radar_data_mutex_ready) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctrl = frame[2];
|
||||
cmd = frame[3];
|
||||
|
||||
data_len =
|
||||
((u16)frame[4] << 8) |
|
||||
frame[5];
|
||||
|
||||
|
||||
os_mutex_pend(&radar_data_mutex, 0);
|
||||
|
||||
|
||||
switch (ctrl) {
|
||||
|
||||
/*
|
||||
* 人体存在 / 运动 / 距离 / 坐标
|
||||
*/
|
||||
case R60_CTRL_PRESENCE:
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case 0x01:
|
||||
case 0x81:
|
||||
|
||||
if (data_len >= 1) {
|
||||
radar_data.presence = frame[6];
|
||||
updated = 1;
|
||||
|
||||
R60_LOG("[R60][PARSE] presence=%u\n",
|
||||
radar_data.presence);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 0x02:
|
||||
|
||||
if (data_len >= 1) {
|
||||
radar_data.motion = frame[6];
|
||||
updated = 1;
|
||||
|
||||
R60_LOG("[R60][PARSE] motion=%u\n",
|
||||
radar_data.motion);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 0x03:
|
||||
|
||||
if (data_len >= 1) {
|
||||
radar_data.body_movement = frame[6];
|
||||
updated = 1;
|
||||
|
||||
R60_LOG("[R60][PARSE] body_movement=%u\n",
|
||||
radar_data.body_movement);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 0x04:
|
||||
|
||||
if (data_len >= 2) {
|
||||
|
||||
radar_data.distance_cm =
|
||||
((u16)frame[6] << 8) |
|
||||
frame[7];
|
||||
updated = 1;
|
||||
|
||||
R60_LOG("[R60][PARSE] distance=%u cm\n",
|
||||
radar_data.distance_cm);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 0x05:
|
||||
|
||||
if (data_len >= 6) {
|
||||
|
||||
radar_data.pos_x_mm =
|
||||
r60_parse_signed_coordinate(
|
||||
((u16)frame[6] << 8) |
|
||||
frame[7]);
|
||||
|
||||
radar_data.pos_y_mm =
|
||||
r60_parse_signed_coordinate(
|
||||
((u16)frame[8] << 8) |
|
||||
frame[9]);
|
||||
|
||||
radar_data.pos_z_mm =
|
||||
r60_parse_signed_coordinate(
|
||||
((u16)frame[10] << 8) |
|
||||
frame[11]);
|
||||
updated = 1;
|
||||
|
||||
R60_LOG("[R60][PARSE] pos=(%d,%d,%d) mm\n",
|
||||
radar_data.pos_x_mm,
|
||||
radar_data.pos_y_mm,
|
||||
radar_data.pos_z_mm);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
/*
|
||||
* 呼吸
|
||||
*/
|
||||
case R60_CTRL_BREATH:
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case 0x02:
|
||||
case 0x82:
|
||||
|
||||
if (data_len >= 1) {
|
||||
|
||||
radar_data.breath_rate_x10 =
|
||||
(u16)frame[6] * 10;
|
||||
updated = 1;
|
||||
|
||||
R60_LOG("[R60][PARSE] breath=%u /min\n",
|
||||
radar_data.breath_rate_x10 / 10);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 0x05:
|
||||
|
||||
if (data_len >= 1) {
|
||||
radar_data.breath_waveform = frame[6];
|
||||
updated = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
/*
|
||||
* 心率
|
||||
*/
|
||||
case R60_CTRL_HEART:
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case 0x02:
|
||||
case 0x82:
|
||||
|
||||
if (data_len >= 1) {
|
||||
|
||||
radar_data.heart_rate_x10 =
|
||||
(u16)frame[6] * 10;
|
||||
updated = 1;
|
||||
|
||||
R60_LOG("[R60][PARSE] heart=%u bpm\n",
|
||||
radar_data.heart_rate_x10 / 10);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 0x05:
|
||||
|
||||
if (data_len >= 1) {
|
||||
radar_data.heart_waveform = frame[6];
|
||||
updated = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
/*
|
||||
* 睡眠
|
||||
*/
|
||||
case R60_CTRL_SLEEP:
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case 0x0c:
|
||||
case 0x8d:
|
||||
|
||||
if (data_len >= 2) {
|
||||
|
||||
radar_data.presence = frame[6];
|
||||
radar_data.sleep_state = frame[7];
|
||||
updated = 1;
|
||||
|
||||
R60_LOG("[R60][PARSE] sleep presence=%u state=%u\n",
|
||||
radar_data.presence,
|
||||
radar_data.sleep_state);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
/*
|
||||
* 手册已确认、但当前公共状态结构尚未承载的合法帧,例如:
|
||||
*
|
||||
* ctrl=07 cmd=07
|
||||
* ctrl=81 cmd=01
|
||||
*
|
||||
* 本阶段暂不映射,避免顺带扩展业务字段。
|
||||
*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 通信统计和业务新鲜度严格分开:
|
||||
*
|
||||
* frame_count:
|
||||
* 所有checksum正确的合法帧都会增加。
|
||||
*
|
||||
* data_update_count / update_ms:
|
||||
* 只有当前parser真正识别并更新业务字段时才推进。
|
||||
*
|
||||
* 因此07/07、01/01等未知合法帧不会把旧数据“续鲜”。
|
||||
*/
|
||||
radar_data.rx_bytes = r60_rx_bytes;
|
||||
radar_data.frame_count = r60_valid_frame_count;
|
||||
radar_data.checksum_errors =
|
||||
r60_checksum_error_count;
|
||||
|
||||
if (updated) {
|
||||
r60_data_update_count++;
|
||||
radar_data.data_update_count =
|
||||
r60_data_update_count;
|
||||
radar_data.update_ms = timer_get_ms();
|
||||
} else {
|
||||
R60_LOG(
|
||||
"[R60] ignored business frame ctrl=%02X cmd=%02X "
|
||||
"frame=%u data=%u\n",
|
||||
ctrl,
|
||||
cmd,
|
||||
r60_valid_frame_count,
|
||||
r60_data_update_count
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
os_mutex_post(&radar_data_mutex);
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 收到一帧完整数据。
|
||||
*/
|
||||
static void r60_frame_received(const u8 *frame, u16 frame_len)
|
||||
{
|
||||
int ret;
|
||||
u32 frame_count;
|
||||
|
||||
ret = r60_validate_frame(frame, frame_len);
|
||||
|
||||
if (ret != 0) {
|
||||
|
||||
R60_LOG("[R60] invalid frame, ret=%d\n",
|
||||
ret);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!radar_data_mutex_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_data_mutex, 0);
|
||||
|
||||
r60_valid_frame_count++;
|
||||
|
||||
radar_data.frame_count =
|
||||
r60_valid_frame_count;
|
||||
|
||||
frame_count =
|
||||
r60_valid_frame_count;
|
||||
|
||||
os_mutex_post(&radar_data_mutex);
|
||||
|
||||
|
||||
R60_LOG("[R60] valid frame=%u len=%u ctrl=%02X cmd=%02X\n",
|
||||
frame_count,
|
||||
frame_len,
|
||||
frame[2],
|
||||
frame[3]);
|
||||
|
||||
|
||||
r60_parse_frame(frame, frame_len);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 逐字节组帧。
|
||||
*/
|
||||
static void r60_feed_byte(u8 byte)
|
||||
{
|
||||
u16 data_len;
|
||||
u16 full_len;
|
||||
|
||||
|
||||
/*
|
||||
* 找0x53。
|
||||
*/
|
||||
if (r60_parse_len == 0) {
|
||||
|
||||
if (byte != R60_FRAME_HEADER1) {
|
||||
return;
|
||||
}
|
||||
|
||||
r60_parse_buf[0] = byte;
|
||||
r60_parse_len = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 已收到0x53,找0x59。
|
||||
*/
|
||||
if (r60_parse_len == 1) {
|
||||
|
||||
if (byte == R60_FRAME_HEADER2) {
|
||||
|
||||
r60_parse_buf[1] = byte;
|
||||
r60_parse_len = 2;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 53 53:
|
||||
* 后一个53仍可能是新帧头。
|
||||
*/
|
||||
if (byte == R60_FRAME_HEADER1) {
|
||||
return;
|
||||
}
|
||||
|
||||
r60_parse_len = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* parser buffer保护。
|
||||
*/
|
||||
if (r60_parse_len >= sizeof(r60_parse_buf)) {
|
||||
|
||||
R60_LOG("[R60] frame buffer overflow\n");
|
||||
|
||||
r60_parse_len = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
r60_parse_buf[r60_parse_len++] = byte;
|
||||
|
||||
|
||||
/*
|
||||
* 前6字节收齐后才能读取DATA_LEN。
|
||||
*/
|
||||
if (r60_parse_len < 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
data_len =
|
||||
((u16)r60_parse_buf[4] << 8) |
|
||||
r60_parse_buf[5];
|
||||
|
||||
|
||||
full_len =
|
||||
(u16)(9 + data_len);
|
||||
|
||||
|
||||
if (full_len > sizeof(r60_parse_buf)) {
|
||||
|
||||
R60_LOG("[R60] invalid frame length: data=%u full=%u\n",
|
||||
data_len,
|
||||
full_len);
|
||||
|
||||
r60_parse_len = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (r60_parse_len < full_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
r60_frame_received(
|
||||
r60_parse_buf,
|
||||
full_len
|
||||
);
|
||||
|
||||
|
||||
r60_parse_len = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UART层收到数据后调用。
|
||||
*/
|
||||
int radar_r60abd1_input_bytes(const u8 *data, u16 len)
|
||||
{
|
||||
u16 i;
|
||||
|
||||
if (!data || !len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_data_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 统计UART输入字节数。
|
||||
*/
|
||||
os_mutex_pend(&radar_data_mutex, 0);
|
||||
|
||||
r60_rx_bytes += len;
|
||||
radar_data.rx_bytes = r60_rx_bytes;
|
||||
|
||||
os_mutex_post(&radar_data_mutex);
|
||||
|
||||
|
||||
/*
|
||||
* 逐字节送入组帧器。
|
||||
*/
|
||||
for (i = 0; i < len; i++) {
|
||||
r60_feed_byte(data[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 获取当前最新雷达状态快照。
|
||||
*
|
||||
* 可供其他任务调用。
|
||||
*/
|
||||
int radar_r60abd1_get_data(struct radar_r60abd1_data *out)
|
||||
{
|
||||
if (!out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_data_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_data_mutex, 0);
|
||||
|
||||
memcpy(out,
|
||||
&radar_data,
|
||||
sizeof(*out));
|
||||
|
||||
os_mutex_post(&radar_data_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 向R60ABD1发送原始协议命令。
|
||||
*
|
||||
* 协议层只负责把完整命令交给UART层发送。
|
||||
*/
|
||||
int radar_r60abd1_send_command(const u8 *cmd, u16 len)
|
||||
{
|
||||
if (!cmd || !len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return radar_uart_write(
|
||||
cmd,
|
||||
len
|
||||
);
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
#ifndef R60ABD1_H
|
||||
#define R60ABD1_H
|
||||
|
||||
#include "typedef.h"
|
||||
|
||||
|
||||
/*
|
||||
* R60ABD1最新状态快照。
|
||||
*
|
||||
* 该结构只表示“当前最新值”,
|
||||
* 不负责历史数据存储。
|
||||
*/
|
||||
struct radar_r60abd1_data {
|
||||
|
||||
/* 人体存在/运动 */
|
||||
u8 presence;
|
||||
u8 motion;
|
||||
u8 body_movement;
|
||||
|
||||
/* 距离 */
|
||||
u16 distance_cm;
|
||||
|
||||
/*
|
||||
* 心率/呼吸率。
|
||||
*
|
||||
* x10保存:
|
||||
* 例如 72.0 bpm -> 720
|
||||
*/
|
||||
u16 heart_rate_x10;
|
||||
u16 breath_rate_x10;
|
||||
|
||||
/* 睡眠状态 */
|
||||
u8 sleep_state;
|
||||
|
||||
/* XYZ坐标 */
|
||||
s16 pos_x_mm;
|
||||
s16 pos_y_mm;
|
||||
s16 pos_z_mm;
|
||||
|
||||
/* 最新波形采样值 */
|
||||
u8 heart_waveform;
|
||||
u8 breath_waveform;
|
||||
|
||||
/*
|
||||
* 最近一次“已识别业务数据更新”的系统时间。
|
||||
*
|
||||
* 未知但checksum正确的合法帧不会刷新该时间。
|
||||
* 单位:ms。
|
||||
*/
|
||||
u32 update_ms;
|
||||
|
||||
/* 运行统计 */
|
||||
u32 rx_bytes;
|
||||
|
||||
/*
|
||||
* checksum正确的合法帧总数。
|
||||
* 包含当前没有解析业务含义的帧。
|
||||
*/
|
||||
u32 frame_count;
|
||||
|
||||
/*
|
||||
* 当前parser真正识别并更新业务字段的帧数。
|
||||
*/
|
||||
u32 data_update_count;
|
||||
|
||||
u32 checksum_errors;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 初始化R60ABD1协议层。
|
||||
*
|
||||
* 必须在input/get_data之前调用。
|
||||
*/
|
||||
int radar_r60abd1_init(void);
|
||||
|
||||
|
||||
/*
|
||||
* 输入UART收到的原始字节流。
|
||||
*/
|
||||
int radar_r60abd1_input_bytes(
|
||||
const u8 *data,
|
||||
u16 len
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* 获取当前最新状态快照。
|
||||
*
|
||||
* 内部使用mutex保护。
|
||||
*/
|
||||
int radar_r60abd1_get_data(
|
||||
struct radar_r60abd1_data *out
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* 发送一条已经组好的R60ABD1协议命令。
|
||||
*/
|
||||
int radar_r60abd1_send_command(
|
||||
const u8 *cmd,
|
||||
u16 len
|
||||
);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,449 @@
|
||||
#include "system/includes.h"
|
||||
#include "system/timer.h"
|
||||
|
||||
#include "app_log.h"
|
||||
|
||||
#include "radar_command_service.h"
|
||||
#include "r60abd1.h"
|
||||
|
||||
|
||||
#define RADAR_COMMAND_FRAME_LEN 10
|
||||
|
||||
#define RADAR_COMMAND_STARTUP_INTERVAL_MS 50
|
||||
#define RADAR_COMMAND_PERIODIC_INTERVAL_MS 2000
|
||||
#define RADAR_COMMAND_SKIP_INTERVAL_MS 100
|
||||
#define RADAR_COMMAND_RETRY_INTERVAL_MS 100
|
||||
|
||||
#define RADAR_COMMAND_TASK_PRIO 20
|
||||
#define RADAR_COMMAND_TASK_STACK 1024
|
||||
|
||||
|
||||
enum radar_command_phase {
|
||||
RADAR_COMMAND_PHASE_STARTUP = 0,
|
||||
RADAR_COMMAND_PHASE_PERIODIC
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ESP32参考工程启动阶段:
|
||||
*
|
||||
* 1. queryPresenceCmd原始10字节
|
||||
* 2. 12条startupCommands
|
||||
*
|
||||
* 每条之间50ms。
|
||||
*/
|
||||
static const u8 radar_startup_commands[][RADAR_COMMAND_FRAME_LEN] = {
|
||||
{0x53, 0x59, 0x80, 0x81, 0x00, 0x01, 0x00, 0xAE, 0x54, 0x43},
|
||||
|
||||
{0x53, 0x59, 0x80, 0x00, 0x00, 0x01, 0x01, 0x2E, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x81, 0x00, 0x00, 0x01, 0x01, 0x2F, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x85, 0x00, 0x00, 0x01, 0x01, 0x33, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x00, 0x00, 0x01, 0x01, 0x32, 0x54, 0x43},
|
||||
|
||||
{0x53, 0x59, 0x81, 0x0C, 0x00, 0x01, 0x01, 0x3B, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x85, 0x0A, 0x00, 0x01, 0x01, 0x3D, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x13, 0x00, 0x01, 0x01, 0x45, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x14, 0x00, 0x01, 0x01, 0x46, 0x54, 0x43},
|
||||
|
||||
{0x53, 0x59, 0x80, 0x80, 0x00, 0x01, 0x0F, 0xBC, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x81, 0x80, 0x00, 0x01, 0x0F, 0xBD, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x85, 0x80, 0x00, 0x01, 0x0F, 0xC1, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x80, 0x00, 0x01, 0x0F, 0xC0, 0x54, 0x43},
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 周期查询命令:
|
||||
*
|
||||
* 84系列保持ESP32参考任务的查询顺序;
|
||||
* 心率/呼吸数值查询按R60ABD1 V3.4手册使用85/82、81/82。
|
||||
* 每条value固定为0x0F,正常每2秒发送一条。
|
||||
*/
|
||||
static const u8 radar_periodic_commands[][RADAR_COMMAND_FRAME_LEN] = {
|
||||
{0x53, 0x59, 0x84, 0x81, 0x00, 0x01, 0x0F, 0xC1, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x8D, 0x00, 0x01, 0x0F, 0xCD, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x8F, 0x00, 0x01, 0x0F, 0xCF, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x8E, 0x00, 0x01, 0x0F, 0xCE, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x91, 0x00, 0x01, 0x0F, 0xD1, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x92, 0x00, 0x01, 0x0F, 0xD2, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x83, 0x00, 0x01, 0x0F, 0xC3, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x84, 0x00, 0x01, 0x0F, 0xC4, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x85, 0x00, 0x01, 0x0F, 0xC5, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x86, 0x00, 0x01, 0x0F, 0xC6, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x84, 0x90, 0x00, 0x01, 0x0F, 0xD0, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x85, 0x82, 0x00, 0x01, 0x0F, 0xC3, 0x54, 0x43},
|
||||
{0x53, 0x59, 0x81, 0x82, 0x00, 0x01, 0x0F, 0xBF, 0x54, 0x43},
|
||||
};
|
||||
|
||||
|
||||
static enum radar_command_phase radar_command_phase;
|
||||
|
||||
static u8 radar_command_sleep_query_enabled;
|
||||
|
||||
static u16 radar_command_startup_cmd_index;
|
||||
static u16 radar_command_periodic_query_index;
|
||||
|
||||
static u32 radar_command_next_ms;
|
||||
|
||||
static int radar_command_task_pid;
|
||||
|
||||
|
||||
/*
|
||||
* ESP32中可关闭的睡眠查询:
|
||||
* periodic_query_index 1 -> 84/8D
|
||||
* periodic_query_index 10 -> 84/90
|
||||
*/
|
||||
static int radar_command_is_sleep_query(u16 query_index)
|
||||
{
|
||||
return (query_index == 1) ||
|
||||
(query_index == 10);
|
||||
}
|
||||
|
||||
|
||||
static int radar_command_time_reached(
|
||||
u32 now,
|
||||
u32 deadline
|
||||
)
|
||||
{
|
||||
/*
|
||||
* 兼容u32毫秒计数回绕。
|
||||
*/
|
||||
return ((s32)(now - deadline) >= 0);
|
||||
}
|
||||
|
||||
|
||||
static int radar_command_send_frame(
|
||||
const u8 *frame
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
||||
ret = radar_r60abd1_send_command(
|
||||
frame,
|
||||
RADAR_COMMAND_FRAME_LEN
|
||||
);
|
||||
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 当前UART写接口成功时应返回实际写入长度。
|
||||
*/
|
||||
if (ret != RADAR_COMMAND_FRAME_LEN) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 单次非阻塞调度。
|
||||
*
|
||||
* 只由本模块内部任务调用。
|
||||
*/
|
||||
static int radar_command_process_once(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
u32 now;
|
||||
|
||||
const u8 *frame;
|
||||
|
||||
|
||||
now = timer_get_ms();
|
||||
|
||||
|
||||
if (!radar_command_time_reached(
|
||||
now,
|
||||
radar_command_next_ms
|
||||
)) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 第一阶段:
|
||||
* 13条启动命令,50ms间隔。
|
||||
*/
|
||||
if (radar_command_phase ==
|
||||
RADAR_COMMAND_PHASE_STARTUP) {
|
||||
|
||||
frame =
|
||||
radar_startup_commands[
|
||||
radar_command_startup_cmd_index
|
||||
];
|
||||
|
||||
|
||||
ret = radar_command_send_frame(frame);
|
||||
|
||||
if (ret != 0) {
|
||||
|
||||
radar_command_next_ms =
|
||||
now +
|
||||
RADAR_COMMAND_RETRY_INTERVAL_MS;
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] startup send failed "
|
||||
"startup_cmd_index=%u ctrl=%02X cmd=%02X ret=%d\n",
|
||||
radar_command_startup_cmd_index,
|
||||
frame[2],
|
||||
frame[3],
|
||||
ret
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] startup sent "
|
||||
"startup_cmd_index=%u ctrl=%02X cmd=%02X value=%02X\n",
|
||||
radar_command_startup_cmd_index,
|
||||
frame[2],
|
||||
frame[3],
|
||||
frame[6]
|
||||
);
|
||||
|
||||
|
||||
radar_command_startup_cmd_index++;
|
||||
|
||||
|
||||
if (radar_command_startup_cmd_index >=
|
||||
(sizeof(radar_startup_commands) /
|
||||
sizeof(radar_startup_commands[0]))) {
|
||||
|
||||
radar_command_phase =
|
||||
RADAR_COMMAND_PHASE_PERIODIC;
|
||||
|
||||
radar_command_periodic_query_index = 0;
|
||||
|
||||
radar_command_next_ms =
|
||||
now +
|
||||
RADAR_COMMAND_PERIODIC_INTERVAL_MS;
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] startup complete\n"
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
radar_command_next_ms =
|
||||
now +
|
||||
RADAR_COMMAND_STARTUP_INTERVAL_MS;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 第二阶段:
|
||||
* 周期查询。
|
||||
*/
|
||||
if ((!radar_command_sleep_query_enabled) &&
|
||||
radar_command_is_sleep_query(
|
||||
radar_command_periodic_query_index
|
||||
)) {
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] periodic skip "
|
||||
"periodic_query_index=%u ctrl=%02X cmd=%02X\n",
|
||||
radar_command_periodic_query_index,
|
||||
radar_periodic_commands[
|
||||
radar_command_periodic_query_index
|
||||
][2],
|
||||
radar_periodic_commands[
|
||||
radar_command_periodic_query_index
|
||||
][3]
|
||||
);
|
||||
|
||||
|
||||
radar_command_periodic_query_index =
|
||||
(u16)(
|
||||
(radar_command_periodic_query_index + 1) %
|
||||
(
|
||||
sizeof(radar_periodic_commands) /
|
||||
sizeof(radar_periodic_commands[0])
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
radar_command_next_ms =
|
||||
now +
|
||||
RADAR_COMMAND_SKIP_INTERVAL_MS;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
frame =
|
||||
radar_periodic_commands[
|
||||
radar_command_periodic_query_index
|
||||
];
|
||||
|
||||
|
||||
ret = radar_command_send_frame(frame);
|
||||
|
||||
if (ret != 0) {
|
||||
|
||||
radar_command_next_ms =
|
||||
now +
|
||||
RADAR_COMMAND_RETRY_INTERVAL_MS;
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] periodic send failed "
|
||||
"periodic_query_index=%u ctrl=%02X cmd=%02X ret=%d\n",
|
||||
radar_command_periodic_query_index,
|
||||
frame[2],
|
||||
frame[3],
|
||||
ret
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] periodic sent "
|
||||
"periodic_query_index=%u ctrl=%02X cmd=%02X value=%02X\n",
|
||||
radar_command_periodic_query_index,
|
||||
frame[2],
|
||||
frame[3],
|
||||
frame[6]
|
||||
);
|
||||
|
||||
|
||||
radar_command_periodic_query_index =
|
||||
(u16)(
|
||||
(radar_command_periodic_query_index + 1) %
|
||||
(
|
||||
sizeof(radar_periodic_commands) /
|
||||
sizeof(radar_periodic_commands[0])
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
radar_command_next_ms =
|
||||
now +
|
||||
RADAR_COMMAND_PERIODIC_INTERVAL_MS;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void radar_command_task(void *priv)
|
||||
{
|
||||
(void)priv;
|
||||
|
||||
|
||||
while (1) {
|
||||
|
||||
/*
|
||||
* process_once自身按timer_get_ms()判断是否到期。
|
||||
* 这里仅短暂让出CPU,不依赖app_main的UART阻塞周期。
|
||||
*/
|
||||
radar_command_process_once();
|
||||
|
||||
os_time_dly(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int radar_command_service_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
||||
if (radar_command_task_pid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
radar_command_phase =
|
||||
RADAR_COMMAND_PHASE_STARTUP;
|
||||
|
||||
radar_command_sleep_query_enabled = 1;
|
||||
|
||||
radar_command_startup_cmd_index = 0;
|
||||
radar_command_periodic_query_index = 0;
|
||||
|
||||
/*
|
||||
* 任务启动后允许立即发送首条queryPresenceCmd。
|
||||
*/
|
||||
radar_command_next_ms =
|
||||
timer_get_ms();
|
||||
|
||||
|
||||
ret = thread_fork(
|
||||
"radar_cmd",
|
||||
RADAR_COMMAND_TASK_PRIO,
|
||||
RADAR_COMMAND_TASK_STACK,
|
||||
0,
|
||||
&radar_command_task_pid,
|
||||
radar_command_task,
|
||||
NULL
|
||||
);
|
||||
|
||||
|
||||
if (ret != OS_NO_ERR) {
|
||||
|
||||
radar_command_task_pid = 0;
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] task start failed ret=%d\n",
|
||||
ret
|
||||
);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] init success "
|
||||
"startup=%u periodic=%u\n",
|
||||
(u32)(
|
||||
sizeof(radar_startup_commands) /
|
||||
sizeof(radar_startup_commands[0])
|
||||
),
|
||||
(u32)(
|
||||
sizeof(radar_periodic_commands) /
|
||||
sizeof(radar_periodic_commands[0])
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void radar_command_service_set_sleep_query_enabled(
|
||||
u8 enabled
|
||||
)
|
||||
{
|
||||
radar_command_sleep_query_enabled =
|
||||
enabled ? 1 : 0;
|
||||
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_CMD] sleep query %s\n",
|
||||
radar_command_sleep_query_enabled ?
|
||||
"enabled" :
|
||||
"disabled"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
u8 radar_command_service_get_sleep_query_enabled(void)
|
||||
{
|
||||
return radar_command_sleep_query_enabled;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef RADAR_COMMAND_SERVICE_H
|
||||
#define RADAR_COMMAND_SERVICE_H
|
||||
|
||||
#include "typedef.h"
|
||||
|
||||
|
||||
/*
|
||||
* radar_command_service
|
||||
*
|
||||
* 职责:
|
||||
* - 按参考工程顺序发送R60ABD1启动命令
|
||||
* - 启动完成后按周期轮询查询命令
|
||||
* - 控制是否跳过睡眠相关查询
|
||||
*
|
||||
* 调度任务由本模块内部持有。
|
||||
*
|
||||
* 不负责:
|
||||
* - UART硬件初始化
|
||||
* - 雷达接收/协议解析
|
||||
* - radar_manager状态
|
||||
* - MQTT/WiFi
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* 初始化并启动命令调度任务。
|
||||
*
|
||||
* 调用前要求:
|
||||
* radar_uart_init() 已成功
|
||||
* radar_r60abd1_init() 已成功
|
||||
*
|
||||
* 重复调用安全:已经启动时直接返回0。
|
||||
*/
|
||||
int radar_command_service_init(void);
|
||||
|
||||
|
||||
/*
|
||||
* 是否发送睡眠相关周期查询。
|
||||
*
|
||||
* 默认开启,与ESP32参考工程一致。
|
||||
*
|
||||
* enabled:
|
||||
* 0 跳过84/8D和84/90
|
||||
* 非0 正常发送
|
||||
*/
|
||||
void radar_command_service_set_sleep_query_enabled(u8 enabled);
|
||||
|
||||
|
||||
/*
|
||||
* 获取当前睡眠查询开关。
|
||||
*/
|
||||
u8 radar_command_service_get_sleep_query_enabled(void);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
#include "system/includes.h"
|
||||
#include "app_log.h"
|
||||
|
||||
#include "radar_data_service.h"
|
||||
#include "r60abd1.h"
|
||||
#include "radar_manager.h"
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*
|
||||
* 已经成功同步到radar_manager的
|
||||
* R60 data_update_count。
|
||||
*
|
||||
* 不使用frame_count:
|
||||
* 未知但checksum正确的合法帧只增加frame_count,
|
||||
* 不应该触发manager业务状态更新。
|
||||
*/
|
||||
static u32 radar_data_service_last_update_count;
|
||||
|
||||
|
||||
/*
|
||||
* 把R60协议层的公开快照
|
||||
* 映射成manager通用输入结构。
|
||||
*
|
||||
* 这里不做:
|
||||
* - 合法性判断
|
||||
* - meaningful change判断
|
||||
* - freshness判断
|
||||
*
|
||||
* 这些都属于radar_manager职责。
|
||||
*/
|
||||
static void radar_data_service_build_manager_input(
|
||||
const struct radar_r60abd1_data *r60,
|
||||
struct radar_manager_input *input
|
||||
)
|
||||
{
|
||||
memset(input, 0, sizeof(*input));
|
||||
|
||||
|
||||
input->presence =
|
||||
r60->presence;
|
||||
|
||||
input->motion =
|
||||
r60->motion;
|
||||
|
||||
input->body_movement =
|
||||
r60->body_movement;
|
||||
|
||||
|
||||
input->distance_cm =
|
||||
r60->distance_cm;
|
||||
|
||||
|
||||
input->heart_rate_x10 =
|
||||
r60->heart_rate_x10;
|
||||
|
||||
input->breath_rate_x10 =
|
||||
r60->breath_rate_x10;
|
||||
|
||||
|
||||
input->sleep_state =
|
||||
r60->sleep_state;
|
||||
|
||||
|
||||
input->pos_x_mm =
|
||||
r60->pos_x_mm;
|
||||
|
||||
input->pos_y_mm =
|
||||
r60->pos_y_mm;
|
||||
|
||||
input->pos_z_mm =
|
||||
r60->pos_z_mm;
|
||||
|
||||
|
||||
/*
|
||||
* 当前稳定R60接口没有abnormal_state来源。
|
||||
* 不猜协议,保持0。
|
||||
*/
|
||||
input->abnormal_state = 0;
|
||||
|
||||
|
||||
/*
|
||||
* 使用协议层“真正业务数据更新”的时间。
|
||||
*
|
||||
* manager自身不依赖AC79 timer API。
|
||||
*/
|
||||
input->update_ms =
|
||||
r60->update_ms;
|
||||
}
|
||||
|
||||
|
||||
int radar_data_service_init(void)
|
||||
{
|
||||
radar_data_service_last_update_count = 0;
|
||||
|
||||
APP_LOG("[RADAR_DATA] init success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_data_service_process(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct radar_r60abd1_data r60;
|
||||
struct radar_manager_input input;
|
||||
|
||||
|
||||
ret = radar_r60abd1_get_data(&r60);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 没有新的“已识别业务更新”:
|
||||
* 不重复刷新manager。
|
||||
*
|
||||
* 这里直接比较计数即可。
|
||||
* 即使u32未来发生回绕,也仍能识别下一次变化。
|
||||
*/
|
||||
if (r60.data_update_count ==
|
||||
radar_data_service_last_update_count) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
radar_data_service_build_manager_input(
|
||||
&r60,
|
||||
&input
|
||||
);
|
||||
|
||||
|
||||
ret = radar_manager_update(&input);
|
||||
|
||||
if (ret != 0) {
|
||||
/*
|
||||
* manager更新失败时不要推进last_update_count,
|
||||
* 下次process还能重试同一份最新数据。
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 只有manager真正接收成功后,
|
||||
* 才认为这次R60更新已被消费。
|
||||
*/
|
||||
radar_data_service_last_update_count =
|
||||
r60.data_update_count;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef RADAR_DATA_SERVICE_H
|
||||
#define RADAR_DATA_SERVICE_H
|
||||
|
||||
|
||||
/*
|
||||
* radar_data_service 的职责:
|
||||
*
|
||||
* R60ABD1最新业务数据
|
||||
* ↓
|
||||
* radar_manager_input
|
||||
* ↓
|
||||
* radar_manager
|
||||
*
|
||||
* 它只负责协议层与业务状态层之间的数据适配。
|
||||
*
|
||||
* 它不知道:
|
||||
* - UART硬件
|
||||
* - R60 CTRL/CMD具体含义
|
||||
* - MQTT
|
||||
* - WiFi
|
||||
* - Flash
|
||||
* - 上报周期
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* 初始化数据适配服务。
|
||||
*
|
||||
* 注意:
|
||||
* radar_r60abd1_init() 和 radar_manager_init()
|
||||
* 仍由更上层负责。
|
||||
*/
|
||||
int radar_data_service_init(void);
|
||||
|
||||
|
||||
/*
|
||||
* 尝试把R60ABD1最新业务数据同步到radar_manager。
|
||||
*
|
||||
* 返回:
|
||||
* 1 本次发现新业务数据,并成功更新manager
|
||||
* 0 本次没有新的业务数据
|
||||
* <0 读取R60或更新manager失败
|
||||
*
|
||||
* 建议由单一任务/主循环调用。
|
||||
*/
|
||||
int radar_data_service_process(void);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,332 @@
|
||||
#include "system/includes.h"
|
||||
#include "radar_manager.h"
|
||||
#include <string.h>
|
||||
#include "app_log.h"
|
||||
|
||||
static struct radar_sensor_data current_data;
|
||||
static struct radar_sensor_data consumed_data;
|
||||
|
||||
static OS_MUTEX radar_manager_mutex;
|
||||
static u8 radar_manager_mutex_ready;
|
||||
static u8 has_consumed_snapshot;
|
||||
|
||||
|
||||
static u16 abs_diff_u16(u16 a, u16 b)
|
||||
{
|
||||
return (a >= b) ? (u16)(a - b) : (u16)(b - a);
|
||||
}
|
||||
|
||||
|
||||
static u8 abs_diff_u8(u8 a, u8 b)
|
||||
{
|
||||
return (a >= b) ? (u8)(a - b) : (u8)(b - a);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 保留 ESP32 当前最终业务判定:
|
||||
* heart: > 0 && < 200 bpm
|
||||
* breath: > 0 && <= 60 /min
|
||||
*
|
||||
* AC79 内部使用 x10 整数。
|
||||
*/
|
||||
static u8 heart_rate_valid(u16 value_x10)
|
||||
{
|
||||
return (value_x10 > 0 && value_x10 < 2000) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static u8 breath_rate_valid(u16 value_x10)
|
||||
{
|
||||
return (value_x10 > 0 && value_x10 <= 600) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 调用者必须持有 radar_manager_mutex。
|
||||
*/
|
||||
static void build_change_flags_locked(
|
||||
struct radar_change_flags *flags
|
||||
)
|
||||
{
|
||||
memset(flags, 0, sizeof(*flags));
|
||||
|
||||
if (!has_consumed_snapshot) {
|
||||
|
||||
if (current_data.last_update_ms != 0) {
|
||||
flags->any_changed = 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
flags->presence_changed =
|
||||
current_data.presence != consumed_data.presence;
|
||||
|
||||
flags->motion_changed =
|
||||
current_data.motion != consumed_data.motion;
|
||||
|
||||
flags->sleep_state_changed =
|
||||
current_data.sleep_state != consumed_data.sleep_state;
|
||||
|
||||
/*
|
||||
* ESP32 当前 meaningful-change 阈值:
|
||||
* body movement >= 5
|
||||
* distance >= 10 cm
|
||||
* heart >= 3 bpm
|
||||
* breath >= 2 /min
|
||||
*/
|
||||
flags->body_movement_changed =
|
||||
abs_diff_u8(
|
||||
current_data.body_movement,
|
||||
consumed_data.body_movement
|
||||
) >= 5;
|
||||
|
||||
flags->distance_changed =
|
||||
abs_diff_u16(
|
||||
current_data.distance_cm,
|
||||
consumed_data.distance_cm
|
||||
) >= 10;
|
||||
|
||||
flags->heart_rate_changed =
|
||||
abs_diff_u16(
|
||||
current_data.heart_rate_x10,
|
||||
consumed_data.heart_rate_x10
|
||||
) >= 30;
|
||||
|
||||
flags->breath_rate_changed =
|
||||
abs_diff_u16(
|
||||
current_data.breath_rate_x10,
|
||||
consumed_data.breath_rate_x10
|
||||
) >= 20;
|
||||
|
||||
flags->abnormal_state_changed =
|
||||
current_data.abnormal_state !=
|
||||
consumed_data.abnormal_state;
|
||||
|
||||
|
||||
flags->any_changed =
|
||||
flags->presence_changed ||
|
||||
flags->motion_changed ||
|
||||
flags->sleep_state_changed ||
|
||||
flags->body_movement_changed ||
|
||||
flags->distance_changed ||
|
||||
flags->heart_rate_changed ||
|
||||
flags->breath_rate_changed ||
|
||||
flags->abnormal_state_changed;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_init(void)
|
||||
{
|
||||
memset(¤t_data, 0, sizeof(current_data));
|
||||
memset(&consumed_data, 0, sizeof(consumed_data));
|
||||
|
||||
has_consumed_snapshot = 0;
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
os_mutex_create(&radar_manager_mutex);
|
||||
radar_manager_mutex_ready = 1;
|
||||
}
|
||||
|
||||
APP_LOG("[RADAR_MGR] init success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_update(
|
||||
const struct radar_manager_input *input
|
||||
)
|
||||
{
|
||||
if (!input) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
|
||||
current_data.presence = input->presence;
|
||||
current_data.motion = input->motion;
|
||||
current_data.body_movement = input->body_movement;
|
||||
|
||||
current_data.distance_cm = input->distance_cm;
|
||||
|
||||
current_data.heart_rate_x10 = input->heart_rate_x10;
|
||||
current_data.breath_rate_x10 = input->breath_rate_x10;
|
||||
|
||||
current_data.heart_valid =
|
||||
heart_rate_valid(input->heart_rate_x10);
|
||||
|
||||
current_data.breath_valid =
|
||||
breath_rate_valid(input->breath_rate_x10);
|
||||
|
||||
current_data.sleep_state = input->sleep_state;
|
||||
|
||||
current_data.pos_x_mm = input->pos_x_mm;
|
||||
current_data.pos_y_mm = input->pos_y_mm;
|
||||
current_data.pos_z_mm = input->pos_z_mm;
|
||||
|
||||
current_data.abnormal_state = input->abnormal_state;
|
||||
|
||||
current_data.last_update_ms = input->update_ms;
|
||||
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_get_sensor_data(
|
||||
struct radar_sensor_data *out
|
||||
)
|
||||
{
|
||||
if (!out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
memcpy(out, ¤t_data, sizeof(*out));
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_get_change_flags(
|
||||
struct radar_change_flags *out
|
||||
)
|
||||
{
|
||||
if (!out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
build_change_flags_locked(out);
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_get_report_snapshot(
|
||||
u32 now_ms,
|
||||
u32 max_age_ms,
|
||||
struct radar_report_snapshot *out
|
||||
)
|
||||
{
|
||||
struct radar_change_flags flags;
|
||||
u32 age;
|
||||
|
||||
if (!out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
|
||||
memcpy(
|
||||
&out->sensor,
|
||||
¤t_data,
|
||||
sizeof(out->sensor)
|
||||
);
|
||||
|
||||
build_change_flags_locked(&flags);
|
||||
|
||||
|
||||
out->has_person =
|
||||
current_data.presence != 0;
|
||||
|
||||
/*
|
||||
* 先保持 ESP32 当前语义:
|
||||
* presence != 0 或 sleep_state > 0
|
||||
*/
|
||||
out->bed_occupied =
|
||||
(current_data.presence != 0) ||
|
||||
(current_data.sleep_state > 0);
|
||||
|
||||
out->has_valid_vitals =
|
||||
current_data.heart_valid ||
|
||||
current_data.breath_valid;
|
||||
|
||||
out->has_meaningful_change =
|
||||
flags.any_changed;
|
||||
|
||||
|
||||
if (current_data.last_update_ms != 0) {
|
||||
|
||||
age =
|
||||
(u32)(
|
||||
now_ms -
|
||||
current_data.last_update_ms
|
||||
);
|
||||
|
||||
out->sample_age_ms = age;
|
||||
|
||||
if (age <= max_age_ms) {
|
||||
out->has_fresh_sample = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_mark_snapshot_consumed(void)
|
||||
{
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
memcpy(
|
||||
&consumed_data,
|
||||
¤t_data,
|
||||
sizeof(consumed_data)
|
||||
);
|
||||
|
||||
has_consumed_snapshot = 1;
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
#ifndef RADAR_MANAGER_H
|
||||
#define RADAR_MANAGER_H
|
||||
|
||||
#include "typedef.h"
|
||||
/*
|
||||
* radar_manager 的职责:
|
||||
* 1. 保存“当前最新雷达状态”
|
||||
* 2. 对上层提供一致快照
|
||||
* 3. 判断 fresh / valid / meaningful change
|
||||
*
|
||||
* 它不知道:
|
||||
* - UART
|
||||
* - R60 CTRL/CMD
|
||||
* - MQTT
|
||||
* - WiFi
|
||||
* - Flash
|
||||
*/
|
||||
|
||||
struct radar_manager_input {
|
||||
u8 presence;
|
||||
u8 motion;
|
||||
u8 body_movement;
|
||||
|
||||
u16 distance_cm;
|
||||
|
||||
u16 heart_rate_x10;
|
||||
u16 breath_rate_x10;
|
||||
|
||||
u8 sleep_state;
|
||||
|
||||
s16 pos_x_mm;
|
||||
s16 pos_y_mm;
|
||||
s16 pos_z_mm;
|
||||
|
||||
u8 abnormal_state;
|
||||
|
||||
/*
|
||||
* 由调用者传入本次数据真正更新的系统时间。
|
||||
* manager 自己不依赖 AC79 timer API,
|
||||
* 保持模块纯净。
|
||||
*/
|
||||
u32 update_ms;
|
||||
};
|
||||
|
||||
struct radar_sensor_data {
|
||||
u8 presence;
|
||||
u8 motion;
|
||||
u8 body_movement;
|
||||
|
||||
u16 distance_cm;
|
||||
|
||||
u16 heart_rate_x10;
|
||||
u16 breath_rate_x10;
|
||||
|
||||
u8 heart_valid;
|
||||
u8 breath_valid;
|
||||
|
||||
u8 sleep_state;
|
||||
|
||||
s16 pos_x_mm;
|
||||
s16 pos_y_mm;
|
||||
s16 pos_z_mm;
|
||||
|
||||
u8 abnormal_state;
|
||||
|
||||
u32 last_update_ms;
|
||||
};
|
||||
|
||||
struct radar_change_flags {
|
||||
u8 presence_changed;
|
||||
u8 motion_changed;
|
||||
u8 sleep_state_changed;
|
||||
u8 body_movement_changed;
|
||||
u8 distance_changed;
|
||||
u8 heart_rate_changed;
|
||||
u8 breath_rate_changed;
|
||||
u8 abnormal_state_changed;
|
||||
u8 any_changed;
|
||||
};
|
||||
|
||||
struct radar_report_snapshot {
|
||||
struct radar_sensor_data sensor;
|
||||
|
||||
u8 has_person;
|
||||
u8 bed_occupied;
|
||||
u8 has_valid_vitals;
|
||||
u8 has_fresh_sample;
|
||||
u8 has_meaningful_change;
|
||||
|
||||
u32 sample_age_ms;
|
||||
};
|
||||
|
||||
int radar_manager_init(void);
|
||||
|
||||
int radar_manager_update(
|
||||
const struct radar_manager_input *input
|
||||
);
|
||||
|
||||
int radar_manager_get_sensor_data(
|
||||
struct radar_sensor_data *out
|
||||
);
|
||||
|
||||
int radar_manager_get_change_flags(
|
||||
struct radar_change_flags *out
|
||||
);
|
||||
|
||||
int radar_manager_get_report_snapshot(
|
||||
u32 now_ms,
|
||||
u32 max_age_ms,
|
||||
struct radar_report_snapshot *out
|
||||
);
|
||||
|
||||
int radar_manager_mark_snapshot_consumed(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,307 @@
|
||||
#include "system/includes.h"
|
||||
|
||||
#include "app_log.h"
|
||||
#include "radar_report_service.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static struct radar_report_service_config report_config;
|
||||
|
||||
static struct radar_report_snapshot pending_snapshot;
|
||||
|
||||
static u8 report_service_initialized;
|
||||
static u8 report_pending;
|
||||
static u8 pending_reason;
|
||||
|
||||
static u32 last_report_ms;
|
||||
|
||||
|
||||
/*
|
||||
* 判断两个快照在“manager meaningful-change 关注的字段”上
|
||||
* 是否仍然一致。
|
||||
*
|
||||
* 不比较 position:
|
||||
* 当前 manager 的 meaningful-change 本来就不以 XYZ 作为
|
||||
* 上报变化条件,位置的持续抖动不应阻止 consumed baseline 推进。
|
||||
*
|
||||
* 不比较 last_update_ms/sample_age_ms:
|
||||
* 它们是时间信息,不是业务状态本身。
|
||||
*/
|
||||
static u8 radar_report_business_equal(
|
||||
const struct radar_report_snapshot *a,
|
||||
const struct radar_report_snapshot *b
|
||||
)
|
||||
{
|
||||
if (!a || !b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->sensor.presence != b->sensor.presence) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->sensor.motion != b->sensor.motion) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->sensor.sleep_state != b->sensor.sleep_state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->sensor.body_movement !=
|
||||
b->sensor.body_movement) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->sensor.distance_cm !=
|
||||
b->sensor.distance_cm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->sensor.heart_rate_x10 !=
|
||||
b->sensor.heart_rate_x10) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->sensor.breath_rate_x10 !=
|
||||
b->sensor.breath_rate_x10) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a->sensor.abnormal_state !=
|
||||
b->sensor.abnormal_state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int radar_report_service_init(
|
||||
const struct radar_report_service_config *config
|
||||
)
|
||||
{
|
||||
if (!config) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (config->max_age_ms == 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
memset(
|
||||
&pending_snapshot,
|
||||
0,
|
||||
sizeof(pending_snapshot)
|
||||
);
|
||||
|
||||
report_config = *config;
|
||||
|
||||
report_pending = 0;
|
||||
pending_reason = RADAR_REPORT_REASON_NONE;
|
||||
last_report_ms = 0;
|
||||
|
||||
report_service_initialized = 1;
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_REPORT] init success max_age=%u periodic=%u\n",
|
||||
report_config.max_age_ms,
|
||||
report_config.periodic_interval_ms
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_report_service_check(
|
||||
u32 now_ms,
|
||||
struct radar_report_snapshot *out,
|
||||
u8 *reason
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
u8 due_reason;
|
||||
struct radar_report_snapshot current;
|
||||
|
||||
if (!out || !reason) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!report_service_initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 已经有一份等待真正发送成功的快照:
|
||||
* 始终返回同一份,避免发送失败重试时内容漂移。
|
||||
*/
|
||||
if (report_pending) {
|
||||
|
||||
memcpy(
|
||||
out,
|
||||
&pending_snapshot,
|
||||
sizeof(*out)
|
||||
);
|
||||
|
||||
*reason = pending_reason;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ret = radar_manager_get_report_snapshot(
|
||||
now_ms,
|
||||
report_config.max_age_ms,
|
||||
¤t
|
||||
);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 没有真正业务样本,或者样本已经过期:
|
||||
* 不把旧数据交给上层发送。
|
||||
*/
|
||||
if (current.sensor.last_update_ms == 0 ||
|
||||
!current.has_fresh_sample) {
|
||||
|
||||
*reason = RADAR_REPORT_REASON_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
due_reason = RADAR_REPORT_REASON_NONE;
|
||||
|
||||
|
||||
/*
|
||||
* 第一次成功上报之前,
|
||||
* 只要已经有 fresh sample 就先产生一份初始快照。
|
||||
*/
|
||||
if (last_report_ms == 0) {
|
||||
|
||||
due_reason =
|
||||
RADAR_REPORT_REASON_INITIAL;
|
||||
|
||||
} else if (current.has_meaningful_change) {
|
||||
|
||||
due_reason =
|
||||
RADAR_REPORT_REASON_CHANGE;
|
||||
|
||||
} else if (
|
||||
report_config.periodic_interval_ms != 0 &&
|
||||
(u32)(now_ms - last_report_ms) >=
|
||||
report_config.periodic_interval_ms
|
||||
) {
|
||||
|
||||
due_reason =
|
||||
RADAR_REPORT_REASON_PERIODIC;
|
||||
}
|
||||
|
||||
|
||||
if (due_reason == RADAR_REPORT_REASON_NONE) {
|
||||
|
||||
*reason = RADAR_REPORT_REASON_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
memcpy(
|
||||
&pending_snapshot,
|
||||
¤t,
|
||||
sizeof(pending_snapshot)
|
||||
);
|
||||
|
||||
report_pending = 1;
|
||||
pending_reason = due_reason;
|
||||
|
||||
|
||||
memcpy(
|
||||
out,
|
||||
&pending_snapshot,
|
||||
sizeof(*out)
|
||||
);
|
||||
|
||||
*reason = pending_reason;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int radar_report_service_mark_reported(
|
||||
u32 now_ms
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
struct radar_report_snapshot current;
|
||||
|
||||
if (!report_service_initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!report_pending) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 发送成功后重新看一次 manager 当前状态。
|
||||
*
|
||||
* 如果从生成 pending 到发送成功之间,
|
||||
* meaningful-change 相关业务字段没有变化,
|
||||
* 才安全推进 manager consumed baseline。
|
||||
*
|
||||
* 如果已经变化,则不消费:
|
||||
* 下一次 check 会继续看到 CHANGE,
|
||||
* 防止“发送旧快照却把新状态标记为已消费”。
|
||||
*/
|
||||
ret = radar_manager_get_report_snapshot(
|
||||
now_ms,
|
||||
report_config.max_age_ms,
|
||||
¤t
|
||||
);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
if (radar_report_business_equal(
|
||||
&pending_snapshot,
|
||||
¤t
|
||||
)) {
|
||||
|
||||
ret =
|
||||
radar_manager_mark_snapshot_consumed();
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
APP_LOG(
|
||||
"[RADAR_REPORT] state changed while pending, "
|
||||
"keep manager change for next report\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
last_report_ms = now_ms;
|
||||
|
||||
report_pending = 0;
|
||||
pending_reason = RADAR_REPORT_REASON_NONE;
|
||||
|
||||
memset(
|
||||
&pending_snapshot,
|
||||
0,
|
||||
sizeof(pending_snapshot)
|
||||
);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#ifndef RADAR_REPORT_SERVICE_H
|
||||
#define RADAR_REPORT_SERVICE_H
|
||||
|
||||
#include "typedef.h"
|
||||
#include "radar_manager.h"
|
||||
|
||||
/*
|
||||
* radar_report_service 的职责:
|
||||
*
|
||||
* 1. 从 radar_manager 获取统一业务快照
|
||||
* 2. 判断当前是否需要上报
|
||||
* 3. 在真正上报成功后推进“已上报”状态
|
||||
*
|
||||
* 它不知道:
|
||||
* - UART
|
||||
* - R60 CTRL/CMD
|
||||
* - WiFi
|
||||
* - MQTT
|
||||
* - JSON
|
||||
* - Flash
|
||||
*
|
||||
* 本模块不创建任务。
|
||||
* 由未来的上报/网络执行流主动调用。
|
||||
*/
|
||||
|
||||
enum radar_report_reason {
|
||||
RADAR_REPORT_REASON_NONE = 0,
|
||||
RADAR_REPORT_REASON_INITIAL,
|
||||
RADAR_REPORT_REASON_CHANGE,
|
||||
RADAR_REPORT_REASON_PERIODIC,
|
||||
};
|
||||
|
||||
struct radar_report_service_config {
|
||||
/*
|
||||
* manager 快照允许的最大数据年龄。
|
||||
* 超过这个时间的数据不提交给上层上报。
|
||||
*/
|
||||
u32 max_age_ms;
|
||||
|
||||
/*
|
||||
* 即使没有 meaningful change,
|
||||
* 也允许按周期强制产生一次快照。
|
||||
*
|
||||
* 设为 0 表示关闭周期强制上报。
|
||||
*/
|
||||
u32 periodic_interval_ms;
|
||||
};
|
||||
|
||||
/*
|
||||
* 初始化上报决策层。
|
||||
*/
|
||||
int radar_report_service_init(
|
||||
const struct radar_report_service_config *config
|
||||
);
|
||||
|
||||
/*
|
||||
* 检查当前是否存在待上报快照。
|
||||
*
|
||||
* return:
|
||||
* 1 有快照需要上报,out/reason 有效
|
||||
* 0 当前不需要上报
|
||||
* <0 错误
|
||||
*
|
||||
* 注意:
|
||||
* 一旦产生 pending snapshot,
|
||||
* 在 radar_report_service_mark_reported() 成功调用前,
|
||||
* 后续 check 会返回同一份 pending snapshot。
|
||||
*
|
||||
* 这样上层发送失败时可以重试,
|
||||
* 不会因为 manager 又更新而偷偷替换发送内容。
|
||||
*/
|
||||
int radar_report_service_check(
|
||||
u32 now_ms,
|
||||
struct radar_report_snapshot *out,
|
||||
u8 *reason
|
||||
);
|
||||
|
||||
/*
|
||||
* 只有“上层已经真正发送成功”后才调用。
|
||||
*
|
||||
* 该函数会:
|
||||
* - 清除 pending snapshot
|
||||
* - 记录成功上报时间
|
||||
* - 在安全情况下推进 manager 的 consumed baseline
|
||||
*
|
||||
* 如果 pending 产生以后 manager 又发生了有意义字段变化,
|
||||
* 本函数不会把那个新变化错误地消费掉;
|
||||
* 下一次 check 仍会重新产生 CHANGE 上报。
|
||||
*/
|
||||
int radar_report_service_mark_reported(
|
||||
u32 now_ms
|
||||
);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "radar_uart.h"
|
||||
|
||||
static struct device *radar_uart_hdl = NULL;//串口操作句柄
|
||||
static u8 radar_uart_rx_buf[RADAR_UART_RX_BUF_SIZE]//串口驱动环形缓冲区
|
||||
__attribute__((aligned(32)));
|
||||
|
||||
/* UART1初始化 PC6 = TX PC7 = RX */
|
||||
UART1_PLATFORM_DATA_BEGIN(radar_uart_data)
|
||||
.baudrate = RADAR_UART_BAUDRATE,
|
||||
.port = PORT_REMAP,
|
||||
|
||||
.tx_pin = IO_PORTC_06,
|
||||
.rx_pin = IO_PORTC_07,
|
||||
|
||||
.output_channel = OUTPUT_CHANNEL3,
|
||||
.input_channel = INPUT_CHANNEL3,
|
||||
|
||||
.max_continue_recv_cnt = RADAR_UART_RX_BUF_SIZE,
|
||||
.idle_sys_clk_cnt = 500000,
|
||||
.clk_src = PLL_48M,
|
||||
UART1_PLATFORM_DATA_END();
|
||||
|
||||
/* 注册串口设备 */
|
||||
REGISTER_DEVICES(radar_uart_devices) = {
|
||||
{
|
||||
RADAR_UART_NAME,
|
||||
&uart_dev_ops,
|
||||
(void *)&radar_uart_data
|
||||
},
|
||||
};
|
||||
int radar_uart_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (radar_uart_hdl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
radar_uart_hdl = dev_open(RADAR_UART_NAME, NULL);
|
||||
if (!radar_uart_hdl) {
|
||||
return -1;
|
||||
}//句柄写入失败跳出返回-1
|
||||
|
||||
ret = dev_ioctl(
|
||||
radar_uart_hdl,
|
||||
UART_SET_CIRCULAR_BUFF_ADDR,//配置驱动缓冲区地址
|
||||
(u32)radar_uart_rx_buf
|
||||
);
|
||||
if (ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = dev_ioctl(
|
||||
radar_uart_hdl,
|
||||
UART_SET_CIRCULAR_BUFF_LENTH,//配置驱动缓冲区长度
|
||||
sizeof(radar_uart_rx_buf)
|
||||
);
|
||||
if (ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = dev_ioctl(
|
||||
radar_uart_hdl,
|
||||
UART_SET_RECV_BLOCK,//配置为阻塞接收模式
|
||||
1
|
||||
);
|
||||
if (ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = dev_ioctl(
|
||||
radar_uart_hdl,
|
||||
UART_SET_RECV_TIMEOUT,//配置接收超时时间
|
||||
RADAR_UART_RX_TIMEOUT_MS
|
||||
);
|
||||
if (ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = dev_ioctl(
|
||||
radar_uart_hdl,
|
||||
UART_START,//启动串口
|
||||
0
|
||||
);
|
||||
if (ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
//goto error时会跳转至此,释放资源返回ret
|
||||
error:
|
||||
dev_close(radar_uart_hdl);
|
||||
radar_uart_hdl = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int radar_uart_read(u8 *buf, u32 len)//从驱动缓冲区读取数据并存到buf中,len为读取的最大长度
|
||||
{
|
||||
if (!radar_uart_hdl || !buf || !len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dev_read(radar_uart_hdl, buf, len);
|
||||
}
|
||||
|
||||
|
||||
int radar_uart_write(const u8 *data, u32 len)//给雷达写命令,data为写入数据,len为实际写入长度
|
||||
{
|
||||
if (!radar_uart_hdl || !data || !len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dev_write(
|
||||
radar_uart_hdl,
|
||||
(void *)data,
|
||||
len
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef RADAR_UART_H
|
||||
#define RADAR_UART_H
|
||||
|
||||
#include "device/uart.h" //串口驱动头文件
|
||||
#include "asm/includes.h" //芯片引脚定义
|
||||
#include "typedef.h"
|
||||
|
||||
#define RADAR_UART_NAME "uart1"
|
||||
#define RADAR_UART_BAUDRATE 115200
|
||||
#define RADAR_UART_RX_BUF_SIZE 1024
|
||||
#define RADAR_UART_RX_TIMEOUT_MS 500
|
||||
|
||||
int radar_uart_init(void);
|
||||
int radar_uart_read(u8 *buf, u32 len);
|
||||
int radar_uart_write(const u8 *data, u32 len);
|
||||
int radar_uart_flush(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user