A8:任务骨架与调度(5 个任务、两把锁)
需求 §10.3 落地。四件事: 1. 五任务表(tasks.c):周期与优先级写在一张 const 表里,开机自检查的是 相对顺序(safety 唯一最高、log 唯一最低、thermal 与 actuator 同级), 外加「巡检周期必须长于 INA226 一帧转换 8.8 ms」。不过就 abort。 2. 节拍器(sched.c):记下一次该醒的绝对时刻,回绕安全(int32_t 差判先后)。 过载时跳过错过的整周期、保住相位,不补跑。 3. 两把锁(hal_lock):I²C 与 74HC595,禁止嵌套持有 —— 这块板上两者没有交集, 「拿着 A 去拿 B」永远是写错了。ESP 侧用 mutex 取优先级继承。 4. t_safety 的一个周期:拿锁 → 6 片 INA226 的 V+I + PCA9555 两端口 → 放锁。 不死等锁(5 ms 超时就跳过这圈),一片读失败不中断整圈、保留上一圈的值。 I²C 占用由 HAL 按线上真实字节数记账(两个目标同一份代码),静态预算与实测 必须相等:一次寄存器读 48 位,一圈 13 次事务 = 1560 µs / 20 ms = 7.8 %, 与 §10.3 手算的「约 1.5 ms / 约 8 %」相符(本节的 46 位没数 STOP 和重复 START)。 sim 侧配套两样:假 I²C 事务真的把虚拟时钟花掉(否则时序测试是空话), 以及可强制的加锁纪律检查(配一条反向测试,防止检查本身失灵)。 11 条 ctest(161 例)全绿,idf.py build 通过;16 个注错实验全部变红。 Co-Authored-By: Claude Opus 5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,107 @@
|
||||
/* 两把互斥锁的假实现。宿主机测试是**单线程**的,所以这里模拟的不是并发本身,
|
||||
而是「加锁纪律」—— 谁拿了、放没放、有没有嵌套、该拿的时候拿了没有。
|
||||
|
||||
⚠️ 单线程 sim 里「等一把别人持有的锁」永远等不到,所以:
|
||||
· 有限超时 → 把虚拟时钟往前拨满 timeout_ms,计一次超时,返回 HAL_ERR_TIMEOUT
|
||||
(这正是真板上等不到锁时该发生的事,调用方的处理路径因此测得到);
|
||||
· HAL_LOCK_FOREVER 且被别人持有 → 直接 abort。那在真板上是死锁,
|
||||
在这里悄悄返回一个错误码只会把它藏起来。
|
||||
|
||||
「谁」是一个假任务号(`sim_lock_set_task()`),默认 0。测试用它扮演第二个任务。 */
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hal_lock.h"
|
||||
#include "sim_hal.h"
|
||||
|
||||
#define NO_OWNER (-1)
|
||||
|
||||
static int s_owner[HAL_LOCK_COUNT];
|
||||
static uint32_t s_takes[HAL_LOCK_COUNT];
|
||||
static uint32_t s_timeouts[HAL_LOCK_COUNT];
|
||||
static uint32_t s_violations[HAL_LOCK_COUNT];
|
||||
static bool s_require[HAL_LOCK_COUNT];
|
||||
static int s_task;
|
||||
static bool s_inited;
|
||||
|
||||
void sim_lock_reset_internal(void); /* 只给 sim_hal_reset() 用 */
|
||||
|
||||
void sim_lock_reset_internal(void)
|
||||
{
|
||||
for (int i = 0; i < HAL_LOCK_COUNT; i++) {
|
||||
s_owner[i] = NO_OWNER;
|
||||
}
|
||||
memset(s_takes, 0, sizeof(s_takes));
|
||||
memset(s_timeouts, 0, sizeof(s_timeouts));
|
||||
memset(s_violations, 0, sizeof(s_violations));
|
||||
memset(s_require, 0, sizeof(s_require));
|
||||
s_task = 0;
|
||||
s_inited = false;
|
||||
}
|
||||
|
||||
static bool valid(hal_lock_id_t id) { return id >= 0 && id < HAL_LOCK_COUNT; }
|
||||
|
||||
hal_err_t hal_lock_init(void)
|
||||
{
|
||||
s_inited = true;
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
bool hal_lock_held(hal_lock_id_t id)
|
||||
{
|
||||
return valid(id) && s_owner[id] == s_task;
|
||||
}
|
||||
|
||||
hal_err_t hal_lock_take(hal_lock_id_t id, uint32_t timeout_ms)
|
||||
{
|
||||
if (!valid(id) || !s_inited) {
|
||||
return HAL_ERR_STATE;
|
||||
}
|
||||
/* 已经持有 = 重复加锁(这两把都不是递归锁);持有另一把 = 嵌套,见 hal_lock.h。 */
|
||||
for (int i = 0; i < HAL_LOCK_COUNT; i++) {
|
||||
if (s_owner[i] == s_task) {
|
||||
return HAL_ERR_STATE;
|
||||
}
|
||||
}
|
||||
if (s_owner[id] != NO_OWNER) {
|
||||
assert(timeout_ms != HAL_LOCK_FOREVER); /* 单线程里死等 = 死锁 */
|
||||
s_timeouts[id]++;
|
||||
hal_time_delay_ms(timeout_ms); /* 真板上这段时间是等掉的,虚拟时钟也要花掉 */
|
||||
return HAL_ERR_TIMEOUT;
|
||||
}
|
||||
s_owner[id] = s_task;
|
||||
s_takes[id]++;
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
void hal_lock_give(hal_lock_id_t id)
|
||||
{
|
||||
assert(valid(id));
|
||||
assert(s_owner[id] == s_task); /* 不是自己拿的 / 根本没人拿 */
|
||||
s_owner[id] = NO_OWNER;
|
||||
}
|
||||
|
||||
uint32_t hal_lock_timeouts(hal_lock_id_t id) { return valid(id) ? s_timeouts[id] : 0; }
|
||||
|
||||
/* ---- 假板子这一侧的控制面 ------------------------------------------------ */
|
||||
|
||||
void sim_lock_set_task(int task) { s_task = task; }
|
||||
int sim_lock_task(void) { return s_task; }
|
||||
int sim_lock_owner(hal_lock_id_t id) { return valid(id) ? s_owner[id] : NO_OWNER; }
|
||||
uint32_t sim_lock_takes(hal_lock_id_t id) { return valid(id) ? s_takes[id] : 0; }
|
||||
|
||||
void sim_lock_require(hal_lock_id_t id, bool on)
|
||||
{
|
||||
if (valid(id)) {
|
||||
s_require[id] = on;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sim_lock_violations(hal_lock_id_t id) { return valid(id) ? s_violations[id] : 0; }
|
||||
|
||||
void sim_lock_check(hal_lock_id_t id)
|
||||
{
|
||||
if (valid(id) && s_require[id] && !hal_lock_held(id)) {
|
||||
s_violations[id]++;
|
||||
}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户