本帖最后由 jkernet 于 2024-3-18 09:32 编辑
本帖最后由 jkernet 于 2024-3-18 09:30 编辑
本帖最后由 jkernet 于 2024-3-18 09:29 编辑
本帖最后由 jkernet 于 2024-3-18 09:26 编辑
前言
本来是想用来控制WS2812B灯带的,后来觉得不太实用,而且两个活动用同一个项目不太好(估计园长会揍我),于是...想起来以前TB白嫖的USB小夜灯(它终于派上用场了)
展示
【雷达灯控】安信可 Rd-03E+灯+USB小夜灯
代码
主要核心代码并不复杂,而我写得复杂了一点点,原因是多年的开发养成了一个习惯,就是不要把代码"写死",多一点"包容",这样做可能会让以后省很多事.
就这个项目来说要注意的问题
// 雷达数据格式
// 帧头(2) 目标信息(1) 距离信息(2) 帧尾(2)
// AA AA 01 2F 00 55 55
1.帧头是两个"AA",可能为了图方便只判断了一个"AA",就开始填充帧数据了,没有考虑到距离信息的干扰,比如当距离是170厘米的时候,数据为"AA AA 01 AA 00 55 55",这样势必会导致填充到错误的数据,帧尾同理.
2.雷达模块可能不会给你"正确"的数据,比如刚发完帧头,它就宕机重启了,也就是说数据可能在任何时候中断,接收到的数据也许是这样的"AA AA AA 01 2F 00 55"或者这样的"AA AA 01 AA AA 01 2F"甚至这样的"01 2F 00 55 55 AA AA",要是没处理好,陷入死循环也是可能的.
总之一句话"不要太相信第三方获取到的数据",兼容校验都得做,这只是控制一个小夜灯,要是控制一些危险的设备,错误的数据可能会导致严重的后果,当然我给出的代码也不一定是最好方案,抛砖引玉而已.
#include "bflb_gpio.h"
#include "board.h"
#include "bflb_uart.h"
// 触发一次延时时长(毫秒)
#define LIGHT_TIMEOUT 5 * 1000
// 小夜灯控制引脚
#define LIGHT_CTRL_PIN GPIO_PIN_25
static struct bflb_device_s *uart1;
static struct bflb_device_s *gpio;
int main(void)
{
board_init();
printf("start\n");
// 初始化小夜灯控制引脚
gpio = bflb_device_get_by_name("gpio");
bflb_gpio_init(gpio, LIGHT_CTRL_PIN, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
// 默认关闭
bflb_gpio_set(gpio, LIGHT_CTRL_PIN);
// 初始化串口1(雷达模组通信)
bflb_gpio_uart_init(gpio, GPIO_PIN_31, GPIO_UART_FUNC_UART1_TX);
bflb_gpio_uart_init(gpio, GPIO_PIN_30, GPIO_UART_FUNC_UART1_RX);
struct bflb_uart_config_s cfg;
cfg.baudrate = 256000;
cfg.data_bits = UART_DATA_BITS_8;
cfg.stop_bits = UART_STOP_BITS_1;
cfg.parity = UART_PARITY_NONE;
cfg.flow_ctrl = 0;
cfg.tx_fifo_threshold = 7;
cfg.rx_fifo_threshold = 7;
uart1 = bflb_device_get_by_name("uart1");
bflb_uart_init(uart1, &cfg);
// 接收到的字节
int ch = -1;
// 上一次接收到的字节
char old_ch = 0;
// 有效帧数据长度
uint8_t data_len = 0;
// 目标数据
// 1字节 0x00 无目标 0x01 运动目标 0x02 微动目标
uint8_t dst_data = 0;
// 距离数据
// 2字节 小端格式(16位整数)
uint8_t dis_data[2] = {0};
// 触发时间
uint64_t trigger_time = 0;
// 雷达数据格式
// 帧头(2) 目标信息(1) 距离信息(2) 帧尾(2)
// AA AA 01 2F 00 55 55
while (1)
{
// 判断触发后是否超时(持续无人状态)
if (trigger_time != 0)
{
if (bflb_mtimer_get_time_ms() - trigger_time >= LIGHT_TIMEOUT)
{
// 超时后重置为未触发状态
trigger_time = 0;
// 关闭小夜灯
bflb_gpio_set(gpio, LIGHT_CTRL_PIN);
printf("timeout:%d\n", LIGHT_TIMEOUT);
}
}
// 判断接收缓冲区中是否有数据
if (bflb_uart_rxavailable(uart1))
{
// 获取1字节的数据
ch = bflb_uart_getchar(uart1);
if (ch != -1)
{
// 判断是否已经开始接收一帧的数据
if (data_len == 0)
{
// 帧头
if (ch == 0xAA)
{
// 判断上一次接收到的字节是否也是0xAA,如果是则确定为帧头,开始接收帧数据,否则记录等待下次判断
if (old_ch == ch)
{
// 帧头2个字节
data_len = 2;
}
}
}
else
{
data_len++;
// 帧尾
if (ch == 0x55)
{
// 判断上一次接收到的字节是否也是0x55,如果是则确定为帧尾,开始新的帧判断,否则记录等待下次判断
if (old_ch == ch)
{
// 校验数据,正确的一帧雷达数据应该是7个字节,不能多也不能少,否则视为无效数据,防止错误数据导致误触发,USB小夜灯还没事,要是其它的啥啥啥...
if (data_len == 7)
{
// 检测到运动目标则刷新触发时间
if (dst_data == 0x01)
{
trigger_time = bflb_mtimer_get_time_ms();
// 开启小夜灯
bflb_gpio_reset(gpio, LIGHT_CTRL_PIN);
printf("light on:%d\n");
}
// 距离 *((uint16_t)&dis_data[0])
printf("dst:%d dis:%d\n", dst_data, *((uint16_t *)&dis_data[0]));
}
}
}
// 无论是否接收到帧尾,只要数据长度大于等于了7字节,就开始新的帧判断
if (data_len >= 7)
{
// 重置数据位置,跳过本次循环,开始新的帧判断
data_len = 0;
continue;
}
// 第3字节 目标信息
if (data_len == 3)
{
dst_data = ch;
}
// 第4-5字节 距离信息
else if (data_len == 4 || data_len == 5)
{
dis_data[data_len - 4] = ch;
}
}
old_ch = ch;
}
}
}
}