Files
DLD154V4B/utilities/at32f421_freertos_demo/src/main.c
T
wangfq cd0fd90770 feat(V4B): V4.23 — 红灯呼吸对齐vd960Loop(≈2.6s) + 上电自检基准判稳
现场测试 DLD154V4 反馈两问题修复:

1. 红灯呼吸太快 → 借鉴 vd960Loop 效果 (main.c poll_red_pwm)
   - 参数按 665/6800 缩放取整: 大步 100→39(≈5.9%), 近顶 20→10(≈1.5%),
     分界 585(88%), 峰值保持 660×3 步
   - 周期 ≈1.6s → ≈2.6s (仿真 42 步×60ms=2520ms)

2. 绿灯上电自检闪到"稳定基准值"才停 (TaskLoop.c 稳定期判稳)
   - 根因: 固定 128 样本即判稳, 与基准是否稳定无关; static 计数不清零,
     二次稳定期(安全复位)1 样本瞬间判稳
   - 修复: 全局 g_stable_cnt/g_settle_cnt, 每窗(100样本≈1s)Origin 均值
     漂移 ≤0.1% → settle++, 连续 2 窗 + 最少 128 样本判稳; 硬兜底 500
     样本(5s); 计数判稳退出/INIT_VD 清零

验证: tests/test_power_on_stable.c (200/300/500/200 样本 PASS),
      tests/test_red_breath.c (周期 2520ms/峰值保持/写值≤ARR PASS),
      gcc -fsyntax-only 两文件 0 error (借 vd960Loop AT32F421 库头)
文档: devlog 置顶 + 修订表回填 V2.12~V2.20 + V2.21; product-manual §5.1/
      核心特性; technical-spec V2.2 (§5.2 参数表/§7 判稳语义/§15 修订记录)

待现场验证: 呼吸观感、绿灯自检时长 (无车 2~3s / 首窗尖峰 3s / 5s 兜底)
2026-09-02 16:26:03 +08:00

304 lines
9.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
**************************************************************************
* @file main.c
* @brief main program
**************************************************************************
* Copyright notice & Disclaimer
*
* The software Board Support Package (BSP) that is made available to
* download from Artery official website is the copyrighted work of Artery.
* Artery authorizes customers to use, copy, and distribute the BSP
* software and its related documentation for the purpose of design and
* development in conjunction with Artery microcontrollers. Use of the
* software is governed by this copyright notice and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
* GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
* TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
* STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
* INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
*
**************************************************************************
*/
#include "at32f421_board.h"
#include "at32f421_clock.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cmcng.h"
#include "TaskLoop.h"
/** @addtogroup UTILITIES_examples
* @{
*/
/** @addtogroup FreeRTOS_demo
* @{
*/
crm_clocks_freq_type g_crm_clocks_freq_struct = {0};
tmr_input_config_type g_tmr_input_config_struct;
__IO uint32_t sys_counter = 0;
//uint8_t g_input_div = 1;
TaskHandle_t loop_task_handler;
tmr_output_config_type tmr_oc_init_structure;
/*
pb1 output pwm waveform, use Tmr14.
tmr3 channel1 duty cycle = (tmr3_c1dt/ tmr3_pr)* 100 = 50%
tmr3 channel2 duty cycle = (tmr3_c2dt/ tmr3_pr)* 100 = 37.5%
tmr3 channel3 duty cycle = (tmr3_c3dt/ tmr3_pr)* 100 = 25%
tmr3 channel4 duty cycle = (tmr3_c4dt/ tmr3_pr)* 100 = 12.5%
*/
uint16_t c1dt_val = 333;
//uint16_t c2dt_val = 249;
//uint16_t c3dt_val = 166;
//uint16_t c4dt_val = 83;
uint16_t prescaler_value = 0;
__IO uint16_t pulse=0;//55; //脉冲宽度
uint8_t g_pulse_counter = 0;
uint8_t g_flag_pulse = 1;
/* Basic timr6 */
void Timr6_Init(void)
{
crm_periph_clock_enable(CRM_TMR6_PERIPH_CLOCK, TRUE);
//TODO: 定时的计算方法
// tmr_base_init(TMR6, 9999, (g_crm_clocks_freq_struct.ahb_freq / 10000) - 1); // 1s
tmr_base_init(TMR6, 999, (g_crm_clocks_freq_struct.ahb_freq / 1000000) - 1); // 1ms
tmr_cnt_dir_set(TMR6, TMR_COUNT_UP);
tmr_interrupt_enable(TMR6, TMR_OVF_INT, TRUE);
nvic_irq_enable(TMR6_GLOBAL_IRQn, 0, 0);
tmr_counter_enable(TMR6, TRUE);
// crm_periph_clock_enable(CRM_TMR14_PERIPH_CLOCK, TRUE);
//
// //TODO: 定时的计算方法
//// tmr_base_init(TMR6, 9999, (g_crm_clocks_freq_struct.ahb_freq / 10000) - 1); // 1s
// tmr_base_init(TMR14, (5000-1), (g_crm_clocks_freq_struct.ahb_freq / 1000000) - 1); // 5ms
//// tmr_base_init(TMR14, 9999, (g_crm_clocks_freq_struct.ahb_freq / 1000000) - 1); // 10ms
//
//
// tmr_cnt_dir_set(TMR14, TMR_COUNT_UP);
//
// tmr_interrupt_enable(TMR14, TMR_OVF_INT, TRUE);
//
// nvic_irq_enable(TMR14_GLOBAL_IRQn, 0, 0);
//
// tmr_counter_enable(TMR14, TRUE);
crm_periph_clock_enable(CRM_TMR15_PERIPH_CLOCK, TRUE);
//TODO: 定时的计算方法
// tmr_base_init(TMR6, 9999, (g_crm_clocks_freq_struct.ahb_freq / 10000) - 1); // 1s
tmr_base_init(TMR15, (5000-1), (g_crm_clocks_freq_struct.ahb_freq / 1000000) - 1); // 5ms
// tmr_base_init(TMR14, 9999, (g_crm_clocks_freq_struct.ahb_freq / 1000000) - 1); // 10ms
tmr_cnt_dir_set(TMR15, TMR_COUNT_UP);
tmr_interrupt_enable(TMR15, TMR_OVF_INT, TRUE);
nvic_irq_enable(TMR15_GLOBAL_IRQn, 0, 0);
tmr_counter_enable(TMR15, TRUE);
gpio_init_type gpio_init_struct;
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_pins = GPIO_PINS_1;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(GPIOB, &gpio_init_struct);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE1, GPIO_MUX_0);
crm_periph_clock_enable(CRM_TMR14_PERIPH_CLOCK, TRUE);
/* compute the prescaler value */
prescaler_value = (uint16_t)(system_core_clock / 24000000) - 1;
tmr_base_init(TMR14, 665, prescaler_value);
tmr_cnt_dir_set(TMR14, TMR_COUNT_UP);
tmr_clock_source_div_set(TMR14, TMR_CLOCK_DIV1);
tmr_output_default_para_init(&tmr_oc_init_structure);
tmr_oc_init_structure.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
tmr_oc_init_structure.oc_idle_state = FALSE;
tmr_oc_init_structure.oc_polarity = TMR_OUTPUT_ACTIVE_HIGH;
tmr_oc_init_structure.oc_output_state = TRUE;
c1dt_val = 670;
tmr_output_channel_config(TMR14, TMR_SELECT_CHANNEL_1, &tmr_oc_init_structure);
tmr_channel_value_set(TMR14, TMR_SELECT_CHANNEL_1, c1dt_val);
tmr_output_channel_buffer_enable(TMR14, TMR_SELECT_CHANNEL_1, TRUE);
tmr_period_buffer_enable(TMR14, TRUE);
/* tmr enable counter */
tmr_counter_enable(TMR14, TRUE);
}
/*===========================================================================
* 红灯呼吸 (PB1 / TMR14 PWM) — V4.23: 呼吸节奏对齐 vd960Loop (LED_PWR)
*
* vd960Loop 参考 (周期≈2.6s, 60ms/步): 满量程 6800
* 升: <6000 大步 +400(≈5.9%), ≥6000 小步 +100(≈1.5%)
* 峰值保持 3 步后回落; 降: -400(≈5.9%)
* V4B PWM 满量程 = TMR14 ARR 665, 参数按 665/6800 ≈ 0.0978 缩放取整:
* 大歩 39(≈5.9%) / 小步 10(≈1.5%) / 分界 585(≈88%) / 峰值保持 660(≈99%)
* 全周期 ≈ 2.6s, 与 vd960Loop 观感一致
*===========================================================================*/
#define RED_BREATH_ARR 665 /* 必须与 tmr_base_init(TMR14,665,...) 一致 */
#define RED_STEP_BIG 39 /* 低段升/全程降步长 ≈ 400×665/6800 */
#define RED_STEP_SMALL 10 /* 近顶小步 ≈ 100×665/6800 */
#define RED_SPLIT 585 /* 大步/小步分界 ≈ 6000×665/6800 (88%) */
#define RED_HOLD_LEVEL 660 /* 峰值保持占空比 (≈99%) */
#define RED_HOLD_TICKS 3 /* 峰值保持 3 步 × 60ms = 180ms */
uint8_t g_cnt_pwm_red_low_timeout = 0;
void poll_red_pwm(void)
{
g_pulse_counter++;
if (g_pulse_counter >= 12) { /* 12 × 5ms = 60ms/步 (同 vd960Loop) */
g_pulse_counter = 0;
if (g_flag_pulse) {
/* 渐亮: 低段大步, 近顶小步 */
if (pulse < RED_SPLIT)
pulse += RED_STEP_BIG;
else
pulse += RED_STEP_SMALL;
/* 到最亮: 保持 RED_HOLD_TICKS 步再回落 */
if (pulse >= RED_BREATH_ARR) {
g_cnt_pwm_red_low_timeout++;
if (g_cnt_pwm_red_low_timeout >= RED_HOLD_TICKS) {
g_flag_pulse = 0;
g_cnt_pwm_red_low_timeout = 0;
}
pulse = RED_HOLD_LEVEL;
}
} else {
/* 渐暗 */
if (pulse <= RED_STEP_BIG) {
pulse = 0;
g_flag_pulse = 1;
} else {
pulse -= RED_STEP_BIG;
}
}
tmr_channel_value_set(TMR14, TMR_SELECT_CHANNEL_1, pulse);
}
}
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
system_clock_config();
/* get system clock */
crm_clocks_freq_get(&g_crm_clocks_freq_struct);
at32_board_init();
/* init usart1 */
//2250000
// 115200
#ifdef DEBUG
uart_print_init(230400); //115200
PRINT("sys_clock:%d, ahb_freq:%d, sclk_freq:%d\n", system_core_clock, g_crm_clocks_freq_struct.ahb_freq, g_crm_clocks_freq_struct.sclk_freq);
#endif
loop_timer_io_init();
Timr6_Init();
wdt_init(); // IWDT 看门狗使能 (超时约1.64s), 主循环 poll_wdg() 喂狗
/* enter critical */
taskENTER_CRITICAL();
/* create loop task */
if(xTaskCreate((TaskFunction_t )loop_task_function,
(const char* )"Loop_task",
(uint16_t )512,
(void* )NULL,
(UBaseType_t )2,
(TaskHandle_t* )&loop_task_handler) != pdPASS)
{
PRINT("loop task could not be created as there was insufficient heap memory remaining.\r\n");
}
else
{
PRINT("loop task was created successfully.\r\n");
}
/* exit critical */
taskEXIT_CRITICAL();
/* start scheduler */
vTaskStartScheduler();
}
uint8_t g_flag_output = 0;
uint8_t g_flag_output2 = 0;
// 1ms interval
void TMR6_GLOBAL_IRQHandler(void)
{
static uint16_t _cnt_5ms = 0;
static uint32_t _cnt = 0;
if(tmr_interrupt_flag_get(TMR6, TMR_OVF_FLAG) != RESET)
{
#ifdef DEBUG
_cnt++;
if(_cnt >= 2000){
g_flag_output = 1;
_cnt = 0;
}
#endif
tmr_flag_clear(TMR6, TMR_OVF_FLAG);
}
}
/**
* @}
*/
/**
* @}
*/