feat(canfd): 切换 CANFD 接收为中断驱动和环形缓冲机制
- 将 CANFD 接收机制由轮询改为 FIFO0 的中断事件驱动处理 - 实现循环取空硬件 FIFO0,提高接收效率和响应速度 - 引入环形缓冲区缓存接收帧,支持多路通道多帧队列管理 - 置位解析任务标志位,实现接收数据的异步解析 - 增加接收中断统计,包括 FIFO 满和溢出计数 - 重构临界区代码,使用自定义函数保护中断状态 - 修改接收过滤器配置,支持按掩码匹配标准帧进入 FIFO0 - 使能 CAN FIFO0 多种中断标志,包括非空、满和溢出中断
This commit is contained in:
@@ -35,12 +35,37 @@
|
||||
/** @brief ADC 空输入保护时默认 VDDA,单位 V。 */
|
||||
#define CONFIG_ADC_DEFAULT_VDDA 3.3f
|
||||
|
||||
/** @brief ADC 中值滤波窗口长度(最终样本数为该值 + 1)。 */
|
||||
#define CONFIG_ADC_FILTER_SIZE 6U
|
||||
|
||||
/** @brief CAN 外设实例。 */
|
||||
#define CONFIG_CAN_INSTANCE CAN0
|
||||
/** @brief CAN 外设时钟使能枚举。 */
|
||||
#define CONFIG_CAN_RCU RCU_CAN0
|
||||
/** @brief CAN 状态报文标准帧 ID。 */
|
||||
#define CONFIG_CAN_STATUS_ID 0x321U
|
||||
/** @brief ESP32 请求 DCU_APP 进入 Bootloader OTA 窗口的扩展帧 ID(备用 CAN 入口)。 */
|
||||
#define CONFIG_CAN_OTA_BOOT_REQ_ID 0x00000320U
|
||||
/** @brief 进入 Bootloader OTA 请求魔术字节 0。 */
|
||||
#define CONFIG_CAN_OTA_BOOT_MAGIC0 0x4FU
|
||||
/** @brief 进入 Bootloader OTA 请求魔术字节 1。 */
|
||||
#define CONFIG_CAN_OTA_BOOT_MAGIC1 0x54U
|
||||
/** @brief 进入 Bootloader OTA 请求魔术字节 2。 */
|
||||
#define CONFIG_CAN_OTA_BOOT_MAGIC2 0x41U
|
||||
/** @brief 进入 Bootloader OTA 请求魔术字节 3。 */
|
||||
#define CONFIG_CAN_OTA_BOOT_MAGIC3 0x21U
|
||||
/** @brief ESP32 在 APP 运行期通过 SPI 发送的进入 Bootloader OTA 命令。 */
|
||||
#define CONFIG_SPI_APP_OTA_BOOT_CMD 0xB0U
|
||||
/** @brief SPI 进入 Bootloader OTA 命令确认状态:成功。 */
|
||||
#define CONFIG_SPI_APP_OTA_BOOT_ACK_OK 0xA5U
|
||||
/** @brief SPI 进入 Bootloader OTA 命令确认状态:失败。 */
|
||||
#define CONFIG_SPI_APP_OTA_BOOT_ACK_ERROR 0xE2U
|
||||
/** @brief CAN 接收过滤器目标标准帧 ID。 */
|
||||
#define CONFIG_CAN_RX_FILTER_ID CONFIG_CAN_STATUS_ID
|
||||
/** @brief CAN 接收过滤器标准帧掩码;`0x7FFU` 表示精确匹配 11-bit ID。 */
|
||||
#define CONFIG_CAN_RX_FILTER_MASK 0x7FFU
|
||||
/** @brief 单次 CAN 接收解析任务最多消费的软件队列帧数,避免主循环长期被接收流量占用。 */
|
||||
#define CONFIG_CAN_RX_PARSE_BUDGET 48U
|
||||
/** @brief CAN 状态载荷中调试标识字符 0。 */
|
||||
#define CONFIG_CAN_STATUS_TAG0 0x43U
|
||||
/** @brief CAN 状态载荷中调试标识字符 1。 */
|
||||
@@ -69,14 +94,26 @@
|
||||
#define CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS 5U
|
||||
/** @brief 调度基准节拍:ADC 温度采样/处理周期,单位 100us tick。 */
|
||||
#define CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS 10U
|
||||
/** @brief 调度基准节拍:CAN 接收轮询/解析周期,单位 100us tick。 */
|
||||
/** @brief 调度基准节拍:10ms 计数周期,单位 100us tick。 */
|
||||
#define CONFIG_TASK_PERIOD_CAN_RX_TICKS 100U
|
||||
/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */
|
||||
#define CONFIG_TASK_PERIOD_CAN_TX_TICKS 1000U
|
||||
/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */
|
||||
#define CONFIG_TASK_PERIOD_HEARTBEAT_TICKS 10000U
|
||||
|
||||
/** @brief UART 诊断打印级别:0=仅温度/电压,1=温度/电压+CAN 统计。 */
|
||||
/** @brief 周期温度/电压 UART 打印开关:0=关闭,1=开启。 */
|
||||
#define CONFIG_UART_TEMP_PERIODIC_PRINT 1
|
||||
/** @brief UART 诊断打印级别:0=仅温度/电压,1=温度/电压+CAN 统计。 */
|
||||
#define CONFIG_UART_DIAG_LEVEL 1
|
||||
|
||||
|
||||
/** @brief FWDGT 独立看门狗预分频值。 */
|
||||
#define CONFIG_FWDGT_PSC FWDGT_PSC_DIV64
|
||||
/** @brief FWDGT 独立看门狗重载值。
|
||||
* 超时 = (64 / 40000) * (RLD + 1) = 1.6ms * (RLD+1)
|
||||
* RLD=1874 → 1.6ms * 1875 ≈ 3.0s
|
||||
*/
|
||||
#define CONFIG_FWDGT_RLD 1874U
|
||||
/** @brief FWDGT 超时近似值(毫秒),仅供文档参考。 */
|
||||
#define CONFIG_FWDGT_TIMEOUT_MS 3000U
|
||||
#endif /* SYS_CONFIG_H */
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
#include "device/usart0.h"
|
||||
#include "device/Timer.h"
|
||||
#include "device/adc.h"
|
||||
#include "device/watchdog.h"
|
||||
#include "device/app_spi_command.h"
|
||||
#include "sys/systick.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/task_manager.h"
|
||||
|
||||
/**
|
||||
@@ -27,6 +30,9 @@ void AppInitializer::init()
|
||||
Usart0::init();
|
||||
TempSensor::init();
|
||||
CanFd::init();
|
||||
OtaBootRequest::init();
|
||||
AppSpiCommand::init();
|
||||
TaskManager::init();
|
||||
SchedulerTimer::init();
|
||||
}
|
||||
Watchdog::init();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* @file ota_boot_request.cpp
|
||||
* @brief DCU_APP 进入 Bootloader OTA 窗口控制实现。
|
||||
*/
|
||||
|
||||
#include "sys/ota_boot_request.h"
|
||||
|
||||
#include "gd32e50x.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/systick.h"
|
||||
|
||||
namespace {
|
||||
/** Bootloader OTA 配置页地址,与 BOOT 工程 sys/config.h 保持一致。 */
|
||||
constexpr uint32_t kOtaConfigPageAddr = 0x0807E000U;
|
||||
constexpr uint32_t kAppLatestAvalAddr = kOtaConfigPageAddr + 0x00U;
|
||||
constexpr uint32_t kAppLatestEnAddr = kOtaConfigPageAddr + 0x08U;
|
||||
constexpr uint32_t kAppLatestCheckAddr = kOtaConfigPageAddr + 0x10U;
|
||||
constexpr uint32_t kOtaAval = 0x00000000U;
|
||||
constexpr uint32_t kOtaNotAval = 0xFFFFFFFFU;
|
||||
constexpr uint32_t kFmcPageSize = 0x00000800U;
|
||||
constexpr uint32_t kRebootDelayTicks = 2U;
|
||||
|
||||
static bool writeWord(uint32_t addr, uint32_t value)
|
||||
{
|
||||
fmc_unlock();
|
||||
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
||||
fmc_state_enum status = fmc_word_program(addr, value);
|
||||
fmc_lock();
|
||||
return status == FMC_READY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按 Bootloader OTA_SET 语义更新配置页中单个 word。
|
||||
* @note Flash 只能 1->0;需要写回 0xFFFFFFFF 时必须擦整页再恢复其他字段。
|
||||
*/
|
||||
static bool otaConfigSet(uint32_t targetAddr, uint32_t value)
|
||||
{
|
||||
if(targetAddr < kOtaConfigPageAddr || targetAddr >= (kOtaConfigPageAddr + kFmcPageSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t current = *((volatile uint32_t *)targetAddr);
|
||||
if(current == value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(value != kOtaNotAval) {
|
||||
return writeWord(targetAddr, value);
|
||||
}
|
||||
|
||||
uint32_t words[6];
|
||||
for(uint32_t i = 0U; i < 6U; ++i) {
|
||||
words[i] = *((volatile uint32_t *)(kOtaConfigPageAddr + i * 4U));
|
||||
}
|
||||
words[(targetAddr - kOtaConfigPageAddr) / 4U] = value;
|
||||
|
||||
fmc_unlock();
|
||||
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);
|
||||
fmc_state_enum status = fmc_page_erase(kOtaConfigPageAddr);
|
||||
if(status != FMC_READY) {
|
||||
fmc_lock();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
for(uint32_t i = 0U; i < 6U; ++i) {
|
||||
if(words[i] == kOtaNotAval) {
|
||||
continue;
|
||||
}
|
||||
status = fmc_word_program(kOtaConfigPageAddr + i * 4U, words[i]);
|
||||
if(status != FMC_READY) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fmc_lock();
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool requestMagicValid(const uint8_t *data, uint8_t length)
|
||||
{
|
||||
return data != nullptr && length >= 4U &&
|
||||
data[0] == CONFIG_CAN_OTA_BOOT_MAGIC0 &&
|
||||
data[1] == CONFIG_CAN_OTA_BOOT_MAGIC1 &&
|
||||
data[2] == CONFIG_CAN_OTA_BOOT_MAGIC2 &&
|
||||
data[3] == CONFIG_CAN_OTA_BOOT_MAGIC3;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/** @brief 已写入 OTA 标志并等待软复位的挂起标志。 */
|
||||
volatile bool OtaBootRequest::s_rebootPending = false;
|
||||
/** @brief 复位前的调度延迟计数,给 SPI/CAN 应答留出发送时间。 */
|
||||
uint32_t OtaBootRequest::s_rebootCountdown = 0U;
|
||||
|
||||
/**
|
||||
* @brief 初始化进入 Bootloader 请求模块的运行态。
|
||||
*/
|
||||
void OtaBootRequest::init()
|
||||
{
|
||||
s_rebootPending = false;
|
||||
s_rebootCountdown = 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写入 Bootloader OTA 等待标志,并安排稍后软复位。
|
||||
* @return true 表示标志写入成功;false 表示 Flash 配置页写入失败。
|
||||
*/
|
||||
bool OtaBootRequest::requestEnterBoot()
|
||||
{
|
||||
bool ok = true;
|
||||
ok = otaConfigSet(kAppLatestCheckAddr, kOtaAval) && ok;
|
||||
ok = otaConfigSet(kAppLatestAvalAddr, kOtaAval) && ok;
|
||||
if(ok) {
|
||||
ok = otaConfigSet(kAppLatestEnAddr, kOtaNotAval);
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
s_rebootCountdown = kRebootDelayTicks;
|
||||
s_rebootPending = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理备用 CAN 入口的进入 Bootloader 请求。
|
||||
* @note 当前主路径是 SPI 命令;CAN 入口只负责识别魔术字并触发同一套标志写入逻辑。
|
||||
*/
|
||||
bool OtaBootRequest::handleCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t length)
|
||||
{
|
||||
if(id != CONFIG_CAN_OTA_BOOT_REQ_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!requestMagicValid(data, length)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
(void)channel;
|
||||
const bool ok = requestEnterBoot();
|
||||
(void)ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在主循环任务中执行延迟软复位。
|
||||
* @note 不在 SPI/CAN 中断里直接复位,避免应答还没发出就重启。
|
||||
*/
|
||||
void OtaBootRequest::process()
|
||||
{
|
||||
if(!s_rebootPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(s_rebootCountdown > 0U) {
|
||||
--s_rebootCountdown;
|
||||
return;
|
||||
}
|
||||
|
||||
__disable_irq();
|
||||
NVIC_SystemReset();
|
||||
while(1) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef SYS_OTA_BOOT_REQUEST_H
|
||||
#define SYS_OTA_BOOT_REQUEST_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief DCU_APP 进入 Bootloader OTA 窗口控制模块。
|
||||
* @note ESP32 通过 CAN-FD 发送受保护命令,APP 写 OTA 标志后软复位,
|
||||
* Bootloader 下次启动会停留在 OTA 等待窗口。
|
||||
*/
|
||||
class OtaBootRequest {
|
||||
public:
|
||||
/** @brief 初始化内部状态。 */
|
||||
static void init();
|
||||
|
||||
/**
|
||||
* @brief 尝试处理一帧 CAN 接收报文。
|
||||
* @param[in] channel 报文来源 CAN 通道。
|
||||
* @param[in] id 报文 ID。
|
||||
* @param[in] data 报文负载。
|
||||
* @param[in] length 报文负载长度。
|
||||
* @retval true 该报文已被 OTA/BOOT 控制模块消费。
|
||||
* @retval false 该报文不是 OTA/BOOT 控制报文。
|
||||
*/
|
||||
static bool handleCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t length);
|
||||
|
||||
/**
|
||||
* @brief 设置 Bootloader OTA 等待标志并安排软复位。
|
||||
* @retval true 标志写入成功,稍后会复位进入 Bootloader。
|
||||
* @retval false Flash 标志写入失败。
|
||||
*/
|
||||
static bool requestEnterBoot();
|
||||
|
||||
/** @brief 若已确认进入 BOOT 请求,则延迟片刻后复位。 */
|
||||
static void process();
|
||||
|
||||
private:
|
||||
static volatile bool s_rebootPending;
|
||||
static uint32_t s_rebootCountdown;
|
||||
};
|
||||
|
||||
#endif /* SYS_OTA_BOOT_REQUEST_H */
|
||||
@@ -54,4 +54,4 @@ void SysTickTimer::initMs()
|
||||
* @retval 无返回值。
|
||||
* @note C 向量包装函数,内部转调 `SysTickTimer::onInterrupt()`。
|
||||
*/
|
||||
void SysTick_Handler(void) { SysTickTimer::onInterrupt(); }
|
||||
extern "C" void SysTick_Handler(void) { SysTickTimer::onInterrupt(); }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @file task_manager.cpp
|
||||
* @brief 任务调度管理器实现。
|
||||
*/
|
||||
@@ -7,8 +7,36 @@
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/config.h"
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* @brief Enter a critical section and preserve the previous interrupt mask.
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval uint32_t Previous PRIMASK value.
|
||||
*/
|
||||
static uint32_t enterCriticalSection()
|
||||
{
|
||||
const uint32_t primask = __get_PRIMASK();
|
||||
__disable_irq();
|
||||
return primask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exit a critical section by restoring the saved interrupt mask.
|
||||
* @param[in] primask Previous PRIMASK value returned by `enterCriticalSection()`.
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
*/
|
||||
static void exitCriticalSection(uint32_t primask)
|
||||
{
|
||||
__set_PRIMASK(primask);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
task_scheduler_t g_scheduler = {0};
|
||||
|
||||
volatile uint32_t g_app_uptime_sec = 0U;
|
||||
|
||||
/**
|
||||
* @brief 初始化任务调度器内部状态。
|
||||
* @param[in] 无。
|
||||
@@ -40,7 +68,9 @@ void TaskManager::init()
|
||||
*/
|
||||
void TaskManager::set(uint32_t taskMask)
|
||||
{
|
||||
const uint32_t primask = enterCriticalSection();
|
||||
g_scheduler.task_flags |= taskMask;
|
||||
exitCriticalSection(primask);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,10 +83,10 @@ void TaskManager::set(uint32_t taskMask)
|
||||
uint32_t TaskManager::takePending()
|
||||
{
|
||||
uint32_t pending;
|
||||
__disable_irq();
|
||||
const uint32_t primask = enterCriticalSection();
|
||||
pending = g_scheduler.task_flags;
|
||||
g_scheduler.task_flags &= ~pending;
|
||||
__enable_irq();
|
||||
exitCriticalSection(primask);
|
||||
return pending;
|
||||
}
|
||||
|
||||
@@ -65,7 +95,7 @@ uint32_t TaskManager::takePending()
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 按预设周期置位 ADC、CAN 接收、状态灯、心跳与诊断打印等任务。
|
||||
* @note 按预设周期推进节拍并置位 ADC、状态灯、心跳与诊断打印等任务;CAN 接收改由 RX 中断事件驱动。
|
||||
*/
|
||||
void TaskManager::tick100us()
|
||||
{
|
||||
@@ -85,7 +115,6 @@ void TaskManager::tick100us()
|
||||
if(++g_scheduler.div_can_rx >= CONFIG_TASK_PERIOD_CAN_RX_TICKS) {
|
||||
g_scheduler.div_can_rx = 0U;
|
||||
g_scheduler.tick_10ms++;
|
||||
set((1UL << TASK_CAN_RX_POLL) | (1UL << TASK_CAN_RX_PARSE));
|
||||
}
|
||||
|
||||
if(++g_scheduler.div_can_tx >= CONFIG_TASK_PERIOD_CAN_TX_TICKS) {
|
||||
@@ -97,7 +126,12 @@ void TaskManager::tick100us()
|
||||
if(++g_scheduler.div_heartbeat >= CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) {
|
||||
g_scheduler.div_heartbeat = 0U;
|
||||
g_scheduler.tick_1s++;
|
||||
g_app_uptime_sec++;
|
||||
#if (CONFIG_UART_TEMP_PERIODIC_PRINT != 0)
|
||||
set((1UL << TASK_HEARTBEAT) | (1UL << TASK_DIAGNOSTIC_PRINT));
|
||||
#else
|
||||
set(1UL << TASK_HEARTBEAT);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,4 +145,4 @@ void TaskManager::tick100us()
|
||||
task_scheduler_t &TaskManager::state()
|
||||
{
|
||||
return g_scheduler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
* @param[in] taskMask 待置位的任务位图掩码。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 内部通过保持当前 PRIMASK 状态的临界区保证可由主循环与 ISR 共享调用。
|
||||
*/
|
||||
static void set(uint32_t taskMask);
|
||||
|
||||
@@ -55,4 +56,4 @@ public:
|
||||
static task_scheduler_t &state();
|
||||
};
|
||||
|
||||
#endif /* SYS_TASK_MANAGER_H */
|
||||
#endif /* SYS_TASK_MANAGER_H */
|
||||
|
||||
@@ -5,14 +5,66 @@
|
||||
|
||||
#include "main.h"
|
||||
#include "device/adc.h"
|
||||
#include "device/watchdog.h"
|
||||
#include "device/app_spi_command.h"
|
||||
#include "device/canfd.h"
|
||||
#include "device/temp_sensor.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/task_runner.h"
|
||||
|
||||
namespace {
|
||||
constexpr uint32_t kAdcFilterWindow = CONFIG_ADC_FILTER_SIZE + 1U;
|
||||
|
||||
/**
|
||||
* @brief Push one sample into a fixed-size sliding window buffer.
|
||||
* @param[in,out] buffer Sample buffer with `kAdcFilterWindow` elements.
|
||||
* @param[in] value New sample value.
|
||||
*/
|
||||
static void pushSample(float *buffer, float value)
|
||||
{
|
||||
for(uint32_t i = 0U; i < CONFIG_ADC_FILTER_SIZE; ++i) {
|
||||
buffer[i] = buffer[i + 1U];
|
||||
}
|
||||
buffer[CONFIG_ADC_FILTER_SIZE] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Apply median filtering to a fixed-size sample window.
|
||||
* @param[in] buffer Sample buffer with `kAdcFilterWindow` elements.
|
||||
* @return Median value in the current window.
|
||||
*/
|
||||
static float medianFilter(const float *buffer)
|
||||
{
|
||||
float sorted[kAdcFilterWindow];
|
||||
|
||||
for(uint32_t i = 0U; i < kAdcFilterWindow; ++i) {
|
||||
sorted[i] = buffer[i];
|
||||
}
|
||||
|
||||
for(uint32_t i = 1U; i < kAdcFilterWindow; ++i) {
|
||||
const float key = sorted[i];
|
||||
uint32_t j = i;
|
||||
while((j > 0U) && (sorted[j - 1U] > key)) {
|
||||
sorted[j] = sorted[j - 1U];
|
||||
--j;
|
||||
}
|
||||
sorted[j] = key;
|
||||
}
|
||||
|
||||
return sorted[kAdcFilterWindow / 2U];
|
||||
}
|
||||
} // namespace
|
||||
|
||||
struct TaskRunner::Runtime {
|
||||
temp_sensor_data_t sample{};
|
||||
float temperature_samples[kAdcFilterWindow] = {0.0f};
|
||||
float vdda_samples[kAdcFilterWindow] = {0.0f};
|
||||
float vin_adc_samples[kAdcFilterWindow] = {0.0f};
|
||||
uint16_t last_vin_raw = 0U;
|
||||
uint16_t last_vref_raw = 0U;
|
||||
uint16_t last_temp_raw = 0U;
|
||||
uint8_t last_rx_channel = 0U;
|
||||
uint32_t last_rx_id = 0U;
|
||||
uint8_t last_rx_len = 0U;
|
||||
@@ -30,7 +82,8 @@ TaskRunner::Runtime TaskRunner::s_runtime;
|
||||
*/
|
||||
void TaskRunner::taskAdcVrefSample()
|
||||
{
|
||||
Adc::collectSample(&s_runtime.sample);
|
||||
s_runtime.last_vin_raw = Adc::readVinRaw();
|
||||
s_runtime.last_vref_raw = Adc::readVrefRaw();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +95,7 @@ void TaskRunner::taskAdcVrefSample()
|
||||
*/
|
||||
void TaskRunner::taskAdcTempSample()
|
||||
{
|
||||
Adc::collectSample(&s_runtime.sample);
|
||||
s_runtime.last_temp_raw = Adc::readTempRaw();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,9 +107,30 @@ void TaskRunner::taskAdcTempSample()
|
||||
*/
|
||||
void TaskRunner::taskAdcProcess()
|
||||
{
|
||||
/* 当前采样任务已直接调用 Adc::collectSample() 填充工程值。
|
||||
* 该处理任务保留为调度兼容入口,后续若重新拆分采样/换算阶段,可在此恢复。 */
|
||||
(void)s_runtime.sample;
|
||||
auto &s = s_runtime.sample;
|
||||
const float vdda = Adc::calculateVdda(s_runtime.last_vref_raw);
|
||||
const float vin_adc_v = Adc::calculateVinAdcVoltage(s_runtime.last_vin_raw, vdda);
|
||||
const float temperature = Adc::calculateTemperature(s_runtime.last_temp_raw, vdda);
|
||||
const bool sampleValid = (s_runtime.last_vref_raw > 0U) &&
|
||||
(s_runtime.last_vref_raw < 4095U) &&
|
||||
(s_runtime.last_temp_raw > 0U) &&
|
||||
(s_runtime.last_temp_raw < 4095U) &&
|
||||
(vdda >= 2.7f) && (vdda <= 3.6f) &&
|
||||
(temperature >= -40.0f) && (temperature <= 125.0f);
|
||||
|
||||
if(sampleValid) {
|
||||
pushSample(s_runtime.vdda_samples, vdda);
|
||||
pushSample(s_runtime.vin_adc_samples, vin_adc_v);
|
||||
pushSample(s_runtime.temperature_samples, temperature);
|
||||
}
|
||||
|
||||
s.vin_raw = s_runtime.last_vin_raw;
|
||||
s.vref_raw = s_runtime.last_vref_raw;
|
||||
s.temp_raw = s_runtime.last_temp_raw;
|
||||
s.vdda = medianFilter(s_runtime.vdda_samples);
|
||||
s.vin_adc_v = medianFilter(s_runtime.vin_adc_samples);
|
||||
s.temperature = medianFilter(s_runtime.temperature_samples);
|
||||
s.valid = sampleValid ? 1U : 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,21 +151,36 @@ void TaskRunner::taskCanRxPoll() { CanFd::poll(); }
|
||||
*/
|
||||
void TaskRunner::taskCanTxSend()
|
||||
{
|
||||
/* 当前阶段不做周期性发送调度。
|
||||
* 若后续需要显式验证某一路发送能力,可由调用方先 prepare,再直接调用 CanFd::sendPrepared()。 */
|
||||
/* 当前阶段不做周期性发送调度。 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析并消费一帧待处理的 CAN 接收报文。
|
||||
* @brief 批量解析并消费待处理的 CAN 接收报文。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 若当前存在待消费报文,则更新最近一次接收的 ID、数据长度、数据内容与来源通道号。
|
||||
* @note 一次任务按预算尽量清空软件接收队列;若仍有积压,则重新置位解析任务。
|
||||
*/
|
||||
void TaskRunner::taskCanRxParse()
|
||||
{
|
||||
if(CanFd::hasRxFrame())
|
||||
CanFd::consumeRxFrame(&s_runtime.last_rx_id, s_runtime.last_rx_data, &s_runtime.last_rx_len, &s_runtime.last_rx_channel);
|
||||
uint32_t consumed = 0U;
|
||||
while(consumed < CONFIG_CAN_RX_PARSE_BUDGET) {
|
||||
if(!CanFd::consumeRxFrame(&s_runtime.last_rx_id, s_runtime.last_rx_data, &s_runtime.last_rx_len, &s_runtime.last_rx_channel)) {
|
||||
break;
|
||||
}
|
||||
if(OtaBootRequest::handleCanFrame(s_runtime.last_rx_channel,
|
||||
s_runtime.last_rx_id,
|
||||
s_runtime.last_rx_data,
|
||||
s_runtime.last_rx_len)) {
|
||||
OtaBootRequest::process();
|
||||
return;
|
||||
}
|
||||
consumed++;
|
||||
}
|
||||
|
||||
if(CanFd::hasRxFrame()) {
|
||||
TaskManager::set(1UL << TASK_CAN_RX_PARSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,16 +188,11 @@ void TaskRunner::taskCanRxParse()
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 当前阶段三路 CANFD 仅要求底层收发能力可用,不做周期性业务状态广播;后续若需要恢复按通道发送,可在此处显式调用 `CanFd::prepareStatusForChannel()` 或 `CanFd::prepareStatus()`。
|
||||
* @note 当前阶段三路 CANFD 仅要求底层收发能力可用,不做周期性业务状态广播。
|
||||
*/
|
||||
void TaskRunner::taskCanTxPrepare()
|
||||
{
|
||||
/* 当前阶段三路 CANFD 只要求底层收发能力可用,不做周期性业务状态广播。
|
||||
* 保留该任务入口,后续若需要恢复按通道发送,可在此处显式调用:
|
||||
* CanFd::prepareStatusForChannel(channel, &status);
|
||||
* 或:
|
||||
* CanFd::prepareStatus(&status);
|
||||
*/
|
||||
/* 当前阶段不做周期性业务状态广播。 */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +200,7 @@ void TaskRunner::taskCanTxPrepare()
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 当前仅保留任务入口,具体状态灯策略尚未实现。
|
||||
* @note 当前仅保留任务入口。
|
||||
*/
|
||||
void TaskRunner::taskStatusLed() { /* 预留 */ }
|
||||
|
||||
@@ -125,32 +209,47 @@ void TaskRunner::taskStatusLed() { /* 预留 */ }
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 当前仅保留任务入口,后续可在此加入周期健康检查或轻量保活逻辑。
|
||||
* @note 当前仅保留任务入口。
|
||||
*/
|
||||
void TaskRunner::taskHeartbeat() { /* 预留 */ }
|
||||
void TaskRunner::taskHeartbeat() { OtaBootRequest::process(); }
|
||||
|
||||
/**
|
||||
* @brief 输出当前温度、电压与 CAN 诊断信息。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 由 1 秒节拍触发,通过 UART 打印当前温度、电压,以及最近一次 CAN 接收与统计信息。
|
||||
* @note 由 1 秒节拍触发,通过 UART 打印当前温度、电压;采用定点整数格式,避免依赖浮点 `printf` 支持。
|
||||
*/
|
||||
void TaskRunner::taskDiagnosticPrint()
|
||||
{
|
||||
const auto *diag = CanFd::diag();
|
||||
const auto &s = s_runtime.sample;
|
||||
const int32_t temp_x100 = static_cast<int32_t>(s.temperature * 100.0f);
|
||||
const uint32_t vdda_mV = static_cast<uint32_t>(s.vdda * 1000.0f);
|
||||
const uint32_t vin_adc_mV = static_cast<uint32_t>(s.vin_adc_v * 1000.0f);
|
||||
|
||||
#if (CONFIG_UART_DIAG_LEVEL == 0)
|
||||
printf("temp=%.2f C, vdda=%.3f V, vin_adc=%.3f V\r\n",
|
||||
s.temperature,
|
||||
s.vdda,
|
||||
s.vin_adc_v);
|
||||
printf("[APP] uptime=%lus valid=%u, temp_raw=%u, vref_raw=%u, temp=%ld.%02ld C, vdda=%lu.%03lu V, vin_adc=%lu.%03lu V\r\n",
|
||||
(unsigned long)g_app_uptime_sec,
|
||||
static_cast<unsigned int>(s.valid),
|
||||
static_cast<unsigned int>(s.temp_raw),
|
||||
static_cast<unsigned int>(s.vref_raw),
|
||||
static_cast<long>(temp_x100 / 100),
|
||||
static_cast<long>((temp_x100 >= 0 ? temp_x100 : -temp_x100) % 100),
|
||||
static_cast<unsigned long>(vdda_mV / 1000U),
|
||||
static_cast<unsigned long>(vdda_mV % 1000U),
|
||||
static_cast<unsigned long>(vin_adc_mV / 1000U),
|
||||
static_cast<unsigned long>(vin_adc_mV % 1000U));
|
||||
#else
|
||||
printf("temp=%.2f C, vdda=%.3f V, vin_adc=%.3f V, rx_ch=%u, rx_id=0x%08lX, rx_len=%u, tx_ch=%u, can_tx=%lu, can_rx=%lu, can_drop=%lu, tx_drop=%lu, can_err=%lu\r\n",
|
||||
s.temperature,
|
||||
s.vdda,
|
||||
s.vin_adc_v,
|
||||
printf("[APP] uptime=%lus valid=%u, temp=%ld.%02ld C, vdda=%lu.%03lu V, vin_adc=%lu.%03lu V, rx_ch=%u, rx_id=0x%08lX, rx_len=%u, tx_ch=%u, can_tx=%lu, can_rx=%lu, sw_drop=%lu, hw_full=%lu, hw_over=%lu, tx_drop=%lu, can_err=%lu\r\n",
|
||||
(unsigned long)g_app_uptime_sec,
|
||||
static_cast<unsigned int>(s.valid),
|
||||
static_cast<long>(temp_x100 / 100),
|
||||
static_cast<long>((temp_x100 >= 0 ? temp_x100 : -temp_x100) % 100),
|
||||
static_cast<unsigned long>(vdda_mV / 1000U),
|
||||
static_cast<unsigned long>(vdda_mV % 1000U),
|
||||
static_cast<unsigned long>(vin_adc_mV / 1000U),
|
||||
static_cast<unsigned long>(vin_adc_mV % 1000U),
|
||||
static_cast<unsigned int>(s_runtime.last_rx_channel),
|
||||
static_cast<unsigned long>(s_runtime.last_rx_id),
|
||||
static_cast<unsigned int>(s_runtime.last_rx_len),
|
||||
@@ -158,6 +257,8 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
static_cast<unsigned long>(diag->tx_count),
|
||||
static_cast<unsigned long>(diag->rx_count),
|
||||
static_cast<unsigned long>(diag->rx_drop_count),
|
||||
static_cast<unsigned long>(diag->rx_fifo_full_count),
|
||||
static_cast<unsigned long>(diag->rx_fifo_overfull_count),
|
||||
static_cast<unsigned long>(diag->tx_drop_count),
|
||||
static_cast<unsigned long>(diag->error_count));
|
||||
#endif
|
||||
@@ -173,6 +274,15 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
void TaskRunner::init()
|
||||
{
|
||||
Adc::collectSample(&s_runtime.sample);
|
||||
s_runtime.last_vin_raw = s_runtime.sample.vin_raw;
|
||||
s_runtime.last_vref_raw = s_runtime.sample.vref_raw;
|
||||
s_runtime.last_temp_raw = s_runtime.sample.temp_raw;
|
||||
|
||||
for(uint32_t i = 0U; i < kAdcFilterWindow; ++i) {
|
||||
s_runtime.temperature_samples[i] = s_runtime.sample.temperature;
|
||||
s_runtime.vdda_samples[i] = s_runtime.sample.vdda;
|
||||
s_runtime.vin_adc_samples[i] = s_runtime.sample.vin_adc_v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,6 +294,8 @@ void TaskRunner::init()
|
||||
*/
|
||||
void TaskRunner::runPending()
|
||||
{
|
||||
Watchdog::feed();
|
||||
|
||||
const uint32_t pending = TaskManager::takePending();
|
||||
if(pending == 0U) return;
|
||||
|
||||
@@ -195,4 +307,5 @@ void TaskRunner::runPending()
|
||||
if(pending & (1UL << TASK_STATUS_LED)) taskStatusLed();
|
||||
if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat();
|
||||
if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint();
|
||||
}
|
||||
if(pending & (1UL << TASK_SPI_LOG)) AppSpiCommand::poll();
|
||||
}
|
||||
|
||||
@@ -51,4 +51,4 @@ private:
|
||||
static void taskDiagnosticPrint();
|
||||
};
|
||||
|
||||
#endif /* SYS_TASK_RUNNER_H */
|
||||
#endif /* SYS_TASK_RUNNER_H */
|
||||
|
||||
Reference in New Issue
Block a user