台架调试时 CAN 设备(尤其 Pitch)掉线会按 legacy 语义每 2s 响 n 下,连续 滴滴声很吵且影响听其它声音,故加一个开关: robot_param.h -> 台架调试开关 #define DEBUG_BUZZER_MELODY 1 // 1=警告音换旋律;0=legacy 报警(比赛用) 实现分三处: - robot_param.h: 新增开关,与 DEBUG_SHOOT_WITHOUT_REFEREE 放在同一分区 - modules/BUZZER: 新增 buzzer_play_melody(step) —— 按固定 100ms/步 推进, 内部对旋律总步数取模自动循环;旋律表 buzzer_melody[] 用"上行分解和弦 + 回落"共 14 步(1.4s),空拍 BUZZER_P 作断音,选音限定中音3~高音2 避免刺耳。 驱动层本身不判断开关,保持纯外设职责,只是多了一个音符序列接口。 - monitor_task.c: CAN 掉线分支按开关二选一。旋律模式下步进用本任务自身的 100ms 节拍(与文件里已有的遥控器连接音效同一写法),不用 HAL_GetTick 采样,避免周期抖动漏掉或重复音符。 设计取舍: 只替换"声音",不替换"灯语" —— 红灯常亮、遥控器掉线红灯双闪、 BMI088 失败绿灯双闪等 LED 语义完全不变,因此调试时仍能一眼看出是哪台 设备掉线;linkcheck_first_offline() 也保留调用,方便在调试器里直接看结果。 比赛前把 DEBUG_BUZZER_MELODY 改回 0 即恢复 legacy 报警。 两个工程同步修改,内容保持一致。
319 lines
11 KiB
C
319 lines
11 KiB
C
/* =============================================================================
|
|
* monitor_task.c - LED_Task 强定义:任务心跳 + LED/蜂鸣报警 + IWDG 看门狗
|
|
* 对应 legacy LinkCheck + Warming 模块,报警灯语:
|
|
* 蓝灯每 2s 短闪 = 系统健康心跳(区分"空闲"与"卡死")
|
|
* 红灯 1s 双闪 = 遥控器掉线 红灯常亮+蜂鸣 = CAN 设备掉线
|
|
* 绿灯连闪两下 = BMI088 失败 绿灯慢闪 = IST8310 失败
|
|
* 绿灯常亮 = IMU 过温(已关加热)/双传感器失败
|
|
* 连接音效 = 遥控器上线瞬间两声上扬(M3->H3)
|
|
* CAN 掉线蜂鸣(legacy 语义): 按设备序号 n 响 n 下,2s 一轮,每次只报
|
|
* 最先掉线的设备,设备序号表见 linkcheck_first_offline()
|
|
* 调试模式(DEBUG_BUZZER_MELODY=1,见 robot_param.h): 上述蜂鸣改为循环
|
|
* 播放旋律,避免台架调试时连续滴滴声;红灯常亮等灯语不受影响
|
|
* 看门狗: IWDG 约 2.5s;上电 5s 宽限期无条件喂,之后仅当全部任务心跳正常
|
|
* 才喂——任何任务卡死都会复位,不会整车失控
|
|
* ========================================================================== */
|
|
#include "app_tasks.h"
|
|
#include "cmsis_os.h"
|
|
#include "stm32f4xx_hal.h"
|
|
#include "led.h"
|
|
#include "buzzer.h"
|
|
#include "dr16.h"
|
|
#include "referee.h"
|
|
#include "c_comm.h"
|
|
#include "robot_param.h"
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
#include "gimbal.h"
|
|
#include "bmi088.h"
|
|
#include "ist8310.h"
|
|
#include "attitude.h"
|
|
#elif defined(ROBOT_TYPE_CHASSIS)
|
|
#include "chassis.h"
|
|
#endif
|
|
|
|
/* 监控任务(LED_Task 强定义):任务心跳检查 + LED/蜂鸣器报警 + 喂 IWDG
|
|
* 报警语义对应 legacy Warming 模块:
|
|
* 遥控器掉线 -> 1s 内红灯连闪两下
|
|
* CAN 设备掉线 -> 红灯常亮 + 2s 周期蜂鸣
|
|
* 图传链路掉线 -> 绿灯闪烁
|
|
* IMU 过温 -> 绿灯常亮
|
|
* 各任务心跳正常 -> 喂独立看门狗(约 1.6s 溢出) */
|
|
|
|
static volatile uint32_t heartbeat_tick[HB_COUNT];
|
|
|
|
void monitor_heartbeat(heartbeat_id_e id)
|
|
{
|
|
heartbeat_tick[id] = HAL_GetTick();
|
|
}
|
|
|
|
static uint8_t all_heartbeats_alive(void)
|
|
{
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
static const heartbeat_id_e expected[] = {HB_IMU, HB_CMD, HB_GIMBAL, HB_SHOOT, HB_CCOMM, HB_REFEREE};
|
|
#elif defined(ROBOT_TYPE_CHASSIS)
|
|
static const heartbeat_id_e expected[] = {HB_IMU, HB_CMD, HB_CHASSIS, HB_CCOMM, HB_REFEREE};
|
|
#endif
|
|
uint32_t now = HAL_GetTick();
|
|
for (uint8_t i = 0; i < sizeof(expected) / sizeof(expected[0]); i++)
|
|
{
|
|
if (heartbeat_tick[expected[i]] == 0 || now - heartbeat_tick[expected[i]] > 500)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* 启动后 5s 内无条件喂狗(宽限期),之后要求全部任务心跳正常 */
|
|
static uint8_t iwdg_should_feed(void)
|
|
{
|
|
if (HAL_GetTick() < 5000)
|
|
return 1;
|
|
return all_heartbeats_alive();
|
|
}
|
|
|
|
/* LinkCheck:返回第一个掉线 CAN 设备的序号(无掉线返回 -1)
|
|
* 序号即蜂鸣次数-1(legacy 语义:报警蜂鸣器以高音 6 响 n 下) */
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
#define LINKCHECK_DEVICES
|
|
#endif
|
|
#if defined(ROBOT_TYPE_CHASSIS)
|
|
#define LINKCHECK_DEVICES
|
|
#endif
|
|
|
|
#ifdef LINKCHECK_DEVICES
|
|
#define LINK_OFFLINE(tick) ((tick) == 0 || now - (tick) > DEVICE_OFFLINE_TIME_MS)
|
|
static int8_t linkcheck_first_offline(void)
|
|
{
|
|
uint32_t now = HAL_GetTick();
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
/* 设备序号:0 摩擦轮L(0x201) 1 摩擦轮R(0x202) 2 Pitch(0x206)
|
|
* 3 Yaw(0x205) 4 拨弹(0x207,安装时) 5 底盘回传(0x019) */
|
|
if (LINK_OFFLINE(motor_friction_l.fdb.last_online_tick))
|
|
return 0;
|
|
if (LINK_OFFLINE(motor_friction_r.fdb.last_online_tick))
|
|
return 1;
|
|
if (LINK_OFFLINE(motor_pitch_gimbal.fdb.last_online_tick))
|
|
return 2;
|
|
if (LINK_OFFLINE(motor_yaw_gimbal.fdb.last_online_tick))
|
|
return 3;
|
|
#if GIMBAL_RAMMER_INSTALLED
|
|
if (LINK_OFFLINE(motor_rammer.fdb.last_online_tick))
|
|
return 4;
|
|
#endif
|
|
if (LINK_OFFLINE(ctoc_reply_last_tick))
|
|
return 5;
|
|
#elif defined(ROBOT_TYPE_CHASSIS)
|
|
/* 设备序号:0~3 四轮(0x201~0x204) 4 云台数据(0x149/0x189/0x1A9) */
|
|
if (LINK_OFFLINE(motor_wheel_rf.fdb.last_online_tick))
|
|
return 0;
|
|
if (LINK_OFFLINE(motor_wheel_lf.fdb.last_online_tick))
|
|
return 1;
|
|
if (LINK_OFFLINE(motor_wheel_lr.fdb.last_online_tick))
|
|
return 2;
|
|
if (LINK_OFFLINE(motor_wheel_rr.fdb.last_online_tick))
|
|
return 3;
|
|
if (ctoc_remote.last_tick == 0 || now - ctoc_remote.last_tick > CTOC_OFFLINE_TIME_MS)
|
|
return 4;
|
|
#endif
|
|
return -1;
|
|
}
|
|
#undef LINK_OFFLINE
|
|
#endif /* LINKCHECK_DEVICES */
|
|
|
|
static void iwdg_init(void)
|
|
{
|
|
/* 直接写寄存器(HAL IWDG 模块未在 CubeMX 中启用)
|
|
* PR=4 -> LSI 32kHz / 64 分频 = 500Hz,重装载 1250 -> 约 2.5s 溢出
|
|
* (留足 IMU 零偏校准约 1s 的启动余量) */
|
|
IWDG->KR = 0x5555; // 解除写保护
|
|
IWDG->PR = 4; // 000:/4 001:/8 010:/16 011:/32 100:/64
|
|
IWDG->RLR = 1250;
|
|
IWDG->KR = 0xAAAA; // 重装载
|
|
IWDG->KR = 0xCCCC; // 启动
|
|
}
|
|
|
|
void LED_Task(void const * argument)
|
|
{
|
|
uint8_t counter = 0;
|
|
uint8_t imu_overheat = 0;
|
|
uint8_t can_alarm;
|
|
uint8_t first_ready = 1;
|
|
|
|
/* 看门狗在本任务入口立即启动(约 2.5s):
|
|
* 启动后前 5s 为宽限期无条件喂狗(容忍传感器初始化/校准耗时),
|
|
* 宽限期后仅在所有任务心跳正常时喂狗,任何任务卡死都会复位 */
|
|
iwdg_init();
|
|
|
|
/* 等待硬件初始化完成(CAN_Task 的蓝灯常亮即初始化中) */
|
|
while (!app_ready)
|
|
{
|
|
IWDG->KR = 0xAAAA;
|
|
osDelay(10);
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
counter++;
|
|
if (counter > 100)
|
|
counter = 1;
|
|
|
|
/* 初始化完成的第一拍:熄灭蓝灯,交由报警逻辑接管 */
|
|
if (first_ready)
|
|
{
|
|
first_ready = 0;
|
|
led_set_all_off();
|
|
}
|
|
|
|
/* ---- CAN 设备掉线:红灯常亮 + 2s 周期内响 n 下(n=掉线设备序号+1,legacy 语义) ---- */
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
can_alarm = gimbal_motor_offline();
|
|
#elif defined(ROBOT_TYPE_CHASSIS)
|
|
can_alarm = chassis_motor_offline();
|
|
#endif
|
|
if (can_alarm)
|
|
{
|
|
led_set_red(1);
|
|
|
|
/* 掉线设备序号(0 起)。旋律模式下不用它的返回值,仍保留调用:
|
|
* 一是调试时可直接在调试器里看 linkcheck 结果,二是避免该静态
|
|
* 函数在 DEBUG_BUZZER_MELODY=1 时被判为未使用而报警告 */
|
|
int8_t first_offline = linkcheck_first_offline();
|
|
|
|
#if DEBUG_BUZZER_MELODY
|
|
/* 调试模式:警告音替换为循环旋律(见 modules/BUZZER/buzzer.h)。
|
|
* 红灯常亮与其它灯语不变,"哪台设备掉线"这一信息不会丢。
|
|
* 步进用本任务自身的 100ms 节拍,不靠 HAL_GetTick 采样,避免周期
|
|
* 抖动漏掉或重复某个音符 */
|
|
static uint16_t melody_step = 0;
|
|
(void)first_offline;
|
|
buzzer_play_melody(melody_step++);
|
|
#else
|
|
if (first_offline >= 0)
|
|
{
|
|
uint8_t n = (uint8_t)(first_offline + 1);
|
|
uint8_t w = counter % 20; // 2s 窗口
|
|
/* 第 j 声:占 w=[2j,2j+1],响 100ms 停 100ms */
|
|
if ((w / 2) < n && (w % 2) == 0)
|
|
buzzer_on(BUZZER_L1);
|
|
else
|
|
buzzer_off();
|
|
}
|
|
else
|
|
buzzer_off();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
/* ---- 遥控器连接成功音效:离线->在线瞬间,两声上扬(M3 -> H3) ---- */
|
|
static uint8_t prev_remote_online = 0;
|
|
static uint8_t chime_step = 0;
|
|
uint8_t remote_online = !dr16_offline();
|
|
if (remote_online && !prev_remote_online)
|
|
chime_step = 4; // 触发音效序列(100ms/步)
|
|
prev_remote_online = remote_online;
|
|
|
|
if (chime_step > 0)
|
|
{
|
|
if (chime_step == 4)
|
|
buzzer_on(BUZZER_M3);
|
|
else if (chime_step == 3)
|
|
buzzer_off();
|
|
else if (chime_step == 2)
|
|
buzzer_on(BUZZER_H3);
|
|
else
|
|
buzzer_off();
|
|
chime_step--;
|
|
}
|
|
else
|
|
buzzer_off();
|
|
#else
|
|
buzzer_off();
|
|
#endif
|
|
#if !defined(ROBOT_TYPE_GIMBAL)
|
|
led_set_red(0);
|
|
#endif
|
|
}
|
|
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
/* ---- 遥控器掉线:1s 内红灯连闪两下 ---- */
|
|
if (dr16_offline())
|
|
{
|
|
if (counter % 20 == 1)
|
|
led_set_red(1);
|
|
else if (counter % 20 == 5)
|
|
led_set_red(0);
|
|
else if (counter % 20 == 13)
|
|
led_set_red(1);
|
|
else if (counter % 20 == 17)
|
|
led_set_red(0);
|
|
}
|
|
else if (!can_alarm)
|
|
led_set_red(0);
|
|
|
|
/* ---- 图传链路超时:绿灯 1s 周期闪烁 ---- */
|
|
if (referee.link_status &&
|
|
HAL_GetTick() - referee.last_update_tick > DEVICE_OFFLINE_TIME_MS * 10)
|
|
{
|
|
if (counter % 20 < 10)
|
|
led_set_green(1);
|
|
else
|
|
led_set_green(0);
|
|
}
|
|
|
|
/* ---- IMU 过温:绿灯常亮并关闭加热 ---- */
|
|
if (bmi088_temperature > IMU_TEMPERATURE_LIMIT)
|
|
{
|
|
imu_overheat = 1;
|
|
imu_heater_off();
|
|
}
|
|
if (imu_overheat)
|
|
led_set_green(1);
|
|
|
|
/* ---- 传感器检测失败(不同灯语便于区分) ----
|
|
* BMI088 失败:绿灯 1s 内连闪两下;IST8310 失败:绿灯 1s 周期慢闪;
|
|
* 两者皆失败:绿灯常亮 */
|
|
if (!bmi088_online && !ist8310_online)
|
|
led_set_green(1);
|
|
else if (!bmi088_online)
|
|
{
|
|
if (counter % 20 == 1)
|
|
led_set_green(1);
|
|
else if (counter % 20 == 5)
|
|
led_set_green(0);
|
|
else if (counter % 20 == 13)
|
|
led_set_green(1);
|
|
else if (counter % 20 == 17)
|
|
led_set_green(0);
|
|
}
|
|
else if (!ist8310_online)
|
|
{
|
|
if (counter % 20 < 10)
|
|
led_set_green(1);
|
|
else
|
|
led_set_green(0);
|
|
}
|
|
#elif defined(ROBOT_TYPE_CHASSIS)
|
|
led_set_green(0);
|
|
#endif
|
|
|
|
/* ---- 健康心跳:无报警时蓝灯每 2s 闪 100ms,用于区分"空闲"与"卡死" ---- */
|
|
uint8_t healthy = !can_alarm && !imu_overheat;
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
healthy = healthy && bmi088_online && ist8310_online;
|
|
#endif
|
|
if (healthy)
|
|
{
|
|
if (counter % 20 == 10)
|
|
led_set_blue(1);
|
|
else if (counter % 20 == 11)
|
|
led_set_blue(0);
|
|
}
|
|
|
|
/* ---- 心跳全部正常才喂狗(宽限期除外):任何任务卡死都会触发复位 ---- */
|
|
if (iwdg_should_feed())
|
|
IWDG->KR = 0xAAAA;
|
|
|
|
osDelay(100);
|
|
}
|
|
}
|