Files
Simba-OmniInfantry/云台/Application/Task/Src/monitor_task.c
T
LSMushui a4da5ad17c chore: 导入 Simba-OmniInfantry 基线(云台/底盘双工程)
- 云台(ROBOT_TYPE_GIMBAL)与底盘(ROBOT_TYPE_CHASSIS)完整固件源码
- 新增根级 .gitignore,排除 Keil MDK-ARM 构建产物(.o/.d/.crf/.axf/.lnp/.map/.htm/.sct/.dep/.iex/.lst 等)
  与 *.uvguix.* / DebugConfig/(与机器绑定的 UI 布局)
- core.autocrlf=false,避免固件源码在检出时被改动换行符
2026-09-12 10:28:21 +08:00

302 lines
9.9 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()
* 看门狗: 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);
int8_t id = linkcheck_first_offline();
if (id >= 0)
{
uint8_t n = (uint8_t)(id + 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();
}
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);
}
}