/* PWM 的假实现:只记住配置与占空比。宿主机专用。 */ #include #include "sim_hal.h" typedef struct { bool configured; int pin; uint32_t freq_hz; uint16_t permille; } ch_t; static ch_t s_ch[HAL_PWM_CH_MAX]; /* ---- 假风扇(模型说明见 sim_hal.h)--------------------------------------- */ typedef struct { bool attached; int pwm_ch; int en_pin; uint32_t rpm_full; uint32_t rpm_min; uint8_t ppr; bool gated; /* 关断期不吐脉冲 */ bool stalled; uint64_t last_us; /* 上一次结算的时刻 */ uint64_t frac; /* 边沿的小数部分,按 1e6 缩放(整数积分,不引浮点) */ uint32_t edges; } fan_t; static fan_t s_fan[SIM_FAN_MAX]; static void fan_sync(void); void sim_pwm_reset_internal(void); /* 只给 sim_hal_reset() 用 */ void sim_pwm_reset_internal(void) { memset(s_ch, 0, sizeof(s_ch)); memset(s_fan, 0, sizeof(s_fan)); /* 换了一块新板子 ⇒ 风扇也是新的 */ } static bool valid(int ch) { return ch >= 0 && ch < HAL_PWM_CH_MAX; } static bool fan_valid(int idx) { return idx >= 0 && idx < SIM_FAN_MAX && s_fan[idx].attached; } /* 这一刻这只风扇的转速。三个把它变成 0 的条件各对应一条真实故障。 */ static uint32_t fan_rpm_now(const fan_t *f) { if (f->stalled) { return 0; /* 堵转 / 风扇线没插 */ } if (f->en_pin >= 0 && sim_gpio_level(f->en_pin) != 1) { return 0; /* 12V 总开关没开 —— 复位态就是这个样子 */ } int g = sim_pwm_permille(f->pwm_ch); if (g < 0) { /* PWM 还没配:板上 R734 把 Q6 栅极拉低 ⇒ 风扇 PWM 线被风扇自己拉高 ⇒ **满速**。这是安全侧,也是固件还没接管时的真实行为。 */ g = 0; } const uint32_t fan_duty = 1000u - (uint32_t)g; /* Q6 反相 */ return f->rpm_min + (f->rpm_full - f->rpm_min) * fan_duty / 1000u; } /* 把「上次结算到现在」这段时间的边沿积出来。 */ static void fan_sync(void) { const uint64_t now = hal_time_now_us(); for (int i = 0; i < SIM_FAN_MAX; i++) { fan_t *f = &s_fan[i]; if (!f->attached) { continue; } if (now <= f->last_us) { f->last_us = now; continue; } uint64_t dt = now - f->last_us; f->last_us = now; const uint32_t rpm = fan_rpm_now(f); if (rpm == 0) { continue; } if (f->gated) { /* 关断期没有回流 ⇒ 脉冲只出现在导通的那一部分时间里。 ⚠️ 这一行就是 TR-6 那条误报的来源:占空比越低,同样一秒里 吐出来的脉冲越少,而风扇转得好好的。 */ int g = sim_pwm_permille(f->pwm_ch); const uint32_t fan_duty = (g < 0) ? 1000u : (1000u - (uint32_t)g); dt = dt * fan_duty / 1000u; } /* edges = rpm/60 × ppr × dt[s],按 1e6 缩放累加。 */ f->frac += (uint64_t)rpm * f->ppr * dt / 60u; f->edges += (uint32_t)(f->frac / 1000000u); f->frac %= 1000000u; } } void sim_fan_attach(int idx, int pwm_ch, int en_pin, uint32_t rpm_full, uint32_t rpm_min, uint8_t ppr) { if (idx < 0 || idx >= SIM_FAN_MAX || ppr == 0) { return; } s_fan[idx] = (fan_t){ .attached = true, .pwm_ch = pwm_ch, .en_pin = en_pin, .rpm_full = rpm_full, .rpm_min = rpm_min, .ppr = ppr, .gated = true, .last_us = hal_time_now_us(), }; } void sim_fan_set_gated(int idx, bool on) { fan_sync(); if (fan_valid(idx)) { s_fan[idx].gated = on; } } void sim_fan_set_stalled(int idx, bool on) { fan_sync(); if (fan_valid(idx)) { s_fan[idx].stalled = on; } } uint32_t sim_fan_edges(int idx) { fan_sync(); return fan_valid(idx) ? s_fan[idx].edges : 0; } uint32_t sim_fan_rpm(int idx) { fan_sync(); return fan_valid(idx) ? fan_rpm_now(&s_fan[idx]) : 0; } hal_err_t hal_pwm_config(int ch, int pin, uint32_t freq_hz) { if (!valid(ch) || pin < 0 || pin >= SIM_GPIO_PINS || freq_hz == 0) { return HAL_ERR_ARG; } fan_sync(); /* 配置之前那段时间按「还没配 ⇒ 满速」结算(见 fan_rpm_now) */ s_ch[ch] = (ch_t){ .configured = true, .pin = pin, .freq_hz = freq_hz, .permille = 0 }; return HAL_OK; } hal_err_t hal_pwm_set_permille(int ch, uint16_t permille) { if (!valid(ch) || !s_ch[ch].configured || permille > 1000) { return HAL_ERR_ARG; } /* 先按**旧**占空比把已经过去的那段时间结算掉,再换新值 —— 不然改占空比的那一刻会把过去一整秒的脉冲按新占空比重算一遍。 */ fan_sync(); s_ch[ch].permille = permille; return HAL_OK; } hal_err_t hal_pwm_get_permille(int ch, uint16_t *permille) { if (!valid(ch) || !s_ch[ch].configured || permille == NULL) { return HAL_ERR_ARG; } *permille = s_ch[ch].permille; return HAL_OK; } int sim_pwm_pin(int ch) { return valid(ch) && s_ch[ch].configured ? s_ch[ch].pin : -1; } uint32_t sim_pwm_freq(int ch) { return valid(ch) && s_ch[ch].configured ? s_ch[ch].freq_hz : 0; } int sim_pwm_permille(int ch) { return valid(ch) && s_ch[ch].configured ? (int)s_ch[ch].permille : -1; }