文章目录
- [(一)硬件定时器开启前 停止操作](#(一)硬件定时器开启前 停止操作)
- (二)定时器重复启动,为何会失败
- (三)定时器更新参数,常规流程
- (四)虚拟定时器,启动函数
- [(五)IAR DEBUG模式 Call Stack(函数回溯)](#(五)IAR DEBUG模式 Call Stack(函数回溯))
- (六)STM32复位类型查看
(一)硬件定时器开启前 停止操作
- 初始化定时器,重置参数,开启定时器,运行正常
- 打了两个断点,看到正常运行通过

- 定时器开启后,重复开启定时器,再来一遍操作
- 看到第二次启动定时器就进入错误中断中

- 定时器开启之前 先停止操作
- 看到第二轮启动通过,不进入错误中断

- 使用定时器17再验证一下结论,现象相同
- 为什么会关注这个?因为发现在某项目中在某些用户操作逻辑下会有概率进入错误中断Error_Handler,然后回溯发现定时器启动失败导致的,
- 结论【定时器开启前,先保护停止操作】
(二)定时器重复启动,为何会失败

- 官方库函数中-启动的时候直接判断状态非就绪状态就错误
- 那么对于调用,启动之前一定要是就绪状态
- 顺便看看停止函数干了什么

- 直接重置定时器状态为 就绪态
(三)定时器更新参数,常规流程
- 停止定时器:通过库函数(如 HAL_TIM_Base_Stop(&htimx) 或直接操作寄存器)禁用定时器计数器。
- 更新参数:安全地修改自动重载值(ARR)、预分频器(PSC)、比较/捕获值(CCR)等关键参数。
- 重新启动定时器:再次使能定时器计数器,使其从新参数开始运行。
(四)虚拟定时器,启动函数
c
//枚举列出-虚拟定时器运行状态
typedef enum
{
TimerID_Free,
TimerID_Created,
TimerID_Running
}TimerIDStatus_t;
c
void HW_TS_Start(uint8_t timer_id, uint32_t timeout_ticks)
{
uint16_t time_elapsed;
uint8_t localcurrentrunningtimerid;
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
uint32_t primask_bit;
#endif
//看看 启动的时候会判断定时器的状态
//如果是运行状态 会调用停止函数 停止定时器
if(aTimerContext[timer_id].TimerIDStatus == TimerID_Running)
{
HW_TS_Stop( timer_id );
}
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */
__disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/
#endif
HAL_NVIC_DisableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Disable NVIC */
/* Disable the write protection for RTC registers */
__HAL_RTC_WRITEPROTECTION_DISABLE( &hrtc );
aTimerContext[timer_id].TimerIDStatus = TimerID_Running;
aTimerContext[timer_id].CountLeft = timeout_ticks;
aTimerContext[timer_id].CounterInit = timeout_ticks;
time_elapsed = linkTimer(timer_id);
localcurrentrunningtimerid = CurrentRunningTimerID;
if(PreviousRunningTimerID != localcurrentrunningtimerid)
{
RescheduleTimerList();
}
else
{
aTimerContext[timer_id].CountLeft -= time_elapsed;
}
/* Enable the write protection for RTC registers */
__HAL_RTC_WRITEPROTECTION_ENABLE( &hrtc );
HAL_NVIC_EnableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Enable NVIC */
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
__set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif
return;
}
(五)IAR DEBUG模式 Call Stack(函数回溯)
-
IAR Call Stack窗口的功能详解
在IAR的DEBUG模式下,Call Stack窗口为你提供了这个"调用堆栈"的可视化视图。它的核心作用是让你清晰地了解:当前程序执行到断点或暂停时,是经过怎样的函数调用链才到达当前位置的。
通过菜单 View > Call Stack 可以打开该窗口。它通常显示以下关键信息:
-
调用链:窗口以列表形式显示从 main 函数开始,到当前暂停位置(如断点处)的所有函数调用。
-
最上面的一行是当前正在执行的函数,往下则是调用它的父函数,依此类推,直到最底层的 main 函数。
-
位置信息:每一行通常会显示函数名以及该函数所在的源文件及行号。
-
使用上面的Error_Handler();测试一下
-
在Error_Handler中打一个断点,等运行到此处,使用Call Stack 回退到上一个函数
-
通过黄色的箭头可以看到从哪里进入的,看到上一个函数,如下图所示



(六)STM32复位类型查看
| 复位类型 | 触发原因 | CSR寄存器标志位 | 典型场景 |
|---|---|---|---|
| 上电/掉电复位 | 电源上电/掉电POR/PDR | RCC_CSR_PORRSTF | 开机、断电重启 |
| 外部复位 | NRST引脚低电平(硬件复位) | RCC_CSR_PINRSTF | 按下复位按钮、外部电路触发 |
| 独立看门狗复位 | IWDG超时(硬件看门狗) | RCC_CSR_IWDGRSTF | 程序卡死、看门狗未喂狗 |
| 窗口看门狗复位 | WWDG超时(窗口看门狗) | RCC_CSR_WWDGRSTF | 低功耗模式下看门狗超时 |
| 软件复位 | 软件触发(如NVIC_SystemReset() | RCC_CSR_SFTRSTF | 程序主动复位(如错误处理) |
| 低功耗复位 | 从待机/停止模式唤醒(LPWR) | RCC_CSR_LPWRRSTF | 从低功耗模式唤醒后复位 |
| 欠压复位 | 电源电压低于阈值(BOR) | RCC_CSR_BORRSTF | 电源不稳定、电池电压过低 |
关键点
所有复位标志位存储在 RCC_CSR寄存器低8位(位0-7)
复位后标志位自动置位,需手动清除(设置RCC_CSR_RMVF位)
SRAM数据:系统复位后可能保留,但不建议依赖此特性
- 每次调试模式点击复位按钮,用示波器连接芯片复位脚观察到低电平,
- 说明此复位是硬件引脚复位


- 测试一下设置看门狗不喂狗,然后在调试模式运行,在看门狗复位中断处打断点,发现进入,
- 说明看门狗复位已经发生,且获取到看门狗复位标志
c
/**
* @brief 判断并打印当前的系统复位原因
*/
void CheckResetSource(void)
{
int reset_count = 0;
// 选项字节加载器复位
if (__HAL_RCC_GET_FLAG(RCC_FLAG_OBLRST) == SET)
{
reset_count++;
}
// 引脚复位
if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST) == SET)
{
reset_count++;
}
// 欠压复位(BOR)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST) == SET)
{
reset_count++;
}
// 软件复位
if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST) == SET)
{
reset_count++;
}
// 独立看门狗复位
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) == SET)
{
reset_count++;
}
// 窗口看门狗复位
if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) == SET)
{
reset_count++;
}
// 低功耗复位
if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST) == SET)
{
reset_count++;
}
// 在判断完复位原因后,清除所有标志
__HAL_RCC_CLEAR_RESET_FLAGS();
}