1. 项目概述
作为一名嵌入式开发初学者,使用正点原子STM32F407探索者开发板进行定时器实验是掌握STM32核心功能的重要一步。过去五天里,我系统性地完成了从基础定时器到高级定时器的全系列实验,这个过程让我对STM32的定时器体系有了更深入的理解。
定时器在嵌入式系统中扮演着关键角色,从简单的延时功能到复杂的PWM波形生成,都需要依赖定时器模块。STM32F407系列微控制器提供了多达17个定时器,包括基本定时器(TIM6/TIM7)、通用定时器(TIM2-TIM5/TIM9-TIM14)和高级定时器(TIM1/TIM8),每种定时器都有其特定的应用场景。
2. 硬件准备与环境搭建
2.1 开发板介绍
正点原子STM32F407探索者开发板是一款功能强大的学习平台,核心采用STM32F407ZGT6微控制器,具有以下关键特性:
- 168MHz主频的Cortex-M4内核
- 1MB Flash存储器
- 192KB SRAM
- 丰富的外设接口
开发板已经集成了ST-Link调试器,方便我们直接通过USB连接电脑进行程序下载和调试。
2.2 开发环境配置
进行定时器实验需要准备以下软件环境:
- Keil MDK-ARM开发环境(建议使用5.25以上版本)
- STM32CubeMX图形化配置工具
- 正点原子提供的标准库或HAL库
- 串口调试助手(如SecureCRT或Putty)
提示:初次使用时,务必安装正确的设备支持包(Device Family Pack),确保编译器能够识别STM32F407系列芯片。
2.3 工程模板创建
建议按照以下步骤创建基础工程:
- 使用STM32CubeMX生成基础代码框架
- 选择正确的芯片型号(STM32F407ZGTx)
- 配置系统时钟(通常设置为168MHz)
- 启用SWD调试接口
- 生成MDK-ARM工程
3. 定时器基础实验
3.1 基本定时器配置
基本定时器(TIM6/TIM7)是STM32中最简单的定时器,主要用于产生基础的时间基准。以下是配置TIM6的典型步骤:
c复制// TIM6初始化函数
void TIM6_Init(uint16_t arr, uint16_t psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_TimeBaseInitStructure.TIM_Period = arr; // 自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; // 预分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStructure);
TIM_Cmd(TIM6, ENABLE); // 使能定时器
}
定时器溢出时间计算公式:
溢出时间 = (ARR + 1) * (PSC + 1) / TIMx时钟频率
例如,要配置一个1ms的定时中断:
- 假设APB1总线时钟为84MHz
- 设置PSC=8399,ARR=9
- 溢出时间 = (9+1)*(8399+1)/84MHz = 1ms
3.2 定时器中断实现
定时器中断是嵌入式系统中常用的技术,以下是实现步骤:
- 在定时器初始化后配置中断:
c复制TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
- 配置NVIC(嵌套向量中断控制器):
c复制NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM6_DAC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
- 编写中断服务函数:
c复制void TIM6_DAC_IRQHandler(void)
{
if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
// 用户代码区
}
}
注意:中断服务函数中必须清除中断标志位,否则会不断进入中断。
3.3 定时器PWM输出实验
通用定时器(TIM2-TIM5)支持PWM输出功能,以下是配置TIM3_CH2(PA7)输出PWM的步骤:
- GPIO初始化:
c复制GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_TIM3);
- 定时器PWM模式配置:
c复制TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 时基单元配置
TIM_TimeBaseStructure.TIM_Period = 999; // ARR值
TIM_TimeBaseStructure.TIM_Prescaler = 83; // PSC值
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500; // 初始占空比50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_Cmd(TIM3, ENABLE);
PWM频率计算公式:
PWM频率 = TIMx时钟频率 / [(ARR + 1) * (PSC + 1)]
占空比计算公式:
占空比 = CCRx / (ARR + 1)
4. 高级定时器应用
4.1 互补PWM输出
高级定时器(TIM1/TIM8)支持互补PWM输出,常用于电机控制等场景。以下是TIM1_CH1和CH1N的配置示例:
c复制// GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_TIM1);
// 定时器配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 999;
TIM_TimeBaseStructure.TIM_Prescaler = 83;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 10; // 死区时间
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
重要:高级定时器必须调用TIM_CtrlPWMOutputs()函数才能使能PWM输出。
4.2 输入捕获功能
定时器的输入捕获功能可用于测量脉冲宽度或频率。以下是配置TIM5_CH1(PA0)进行输入捕获的步骤:
- GPIO和定时器初始化:
c复制GPIO_InitTypeDef GPIO_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// GPIO配置
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5);
// 定时器时基配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF;
TIM_TimeBaseStructure.TIM_Prescaler = 83;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
// 输入捕获配置
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM5, &TIM_ICInitStructure);
TIM_ITConfig(TIM5, TIM_IT_CC1, ENABLE);
TIM_Cmd(TIM5, ENABLE);
- 中断服务函数实现:
c复制volatile uint32_t IC_Value1 = 0, IC_Value2 = 0;
volatile uint8_t Capture_Flag = 0;
volatile float DutyCycle = 0, Frequency = 0;
void TIM5_IRQHandler(void)
{
if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)
{
if(Capture_Flag == 0)
{
IC_Value1 = TIM_GetCapture1(TIM5);
TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Falling);
Capture_Flag = 1;
}
else if(Capture_Flag == 1)
{
IC_Value2 = TIM_GetCapture1(TIM5);
if(IC_Value2 > IC_Value1)
{
DutyCycle = (IC_Value2 - IC_Value1) * 100.0 / TIM5->ARR;
Frequency = 84000000.0 / (TIM5->PSC + 1) / (IC_Value2 - IC_Value1);
}
TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Rising);
Capture_Flag = 0;
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1);
}
}
5. 定时器级联应用
5.1 主从定时器配置
STM32的定时器支持级联工作模式,可以实现更长的定时周期。以下是TIM2作为主定时器、TIM3作为从定时器的配置示例:
c复制// 主定时器TIM2配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_TimeBaseStructure.TIM_Period = 9999;
TIM_TimeBaseStructure.TIM_Prescaler = 8399;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE);
// 从定时器TIM3配置
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1); // TIM2作为TIM3的触发源
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);
TIM_Cmd(TIM3, ENABLE);
这种配置下,TIM2的每次溢出都会触发TIM3计数一次,可以实现超长周期的定时功能。
5.2 编码器接口模式
STM32的定时器还支持编码器接口模式,可用于读取正交编码器的信号。以下是TIM4配置为编码器接口模式的示例:
c复制GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
// GPIO配置
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_TIM4);
// 定时器编码器模式配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 6;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_SetCounter(TIM4, 32768); // 初始值设为中间值
TIM_Cmd(TIM4, ENABLE);
读取编码器位置:
c复制int16_t encoder_value = TIM_GetCounter(TIM4);
6. 常见问题与调试技巧
6.1 定时器不工作的排查步骤
-
时钟检查:
- 确认定时器所在总线时钟已使能(APB1或APB2)
- 使用示波器测量定时器时钟输入引脚
-
GPIO配置检查:
- 确认GPIO模式设置为复用功能(AF)
- 检查GPIO复用功能映射是否正确
-
定时器配置检查:
- 确认TIM_Cmd()已调用使能定时器
- 对于高级定时器PWM输出,确认调用了TIM_CtrlPWMOutputs()
-
中断配置检查:
- 确认NVIC中断已使能
- 检查中断优先级配置
- 确保中断服务函数名称正确
6.2 PWM输出异常问题
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无PWM输出 | GPIO配置错误 | 检查GPIO模式和复用功能 |
| PWM频率不对 | 预分频或ARR值计算错误 | 重新计算并设置PSC和ARR |
| 占空比不准确 | CCRx值超出ARR范围 | 确保CCRx ≤ ARR |
| PWM波形抖动 | 中断干扰 | 优化中断优先级或减少中断处理时间 |
6.3 输入捕获测量误差
-
高频信号测量:
- 增加定时器时钟频率(减小预分频)
- 使用更高性能的定时器(如TIM2/TIM5是32位)
-
低频信号测量:
- 增大预分频值
- 使用定时器溢出中断辅助计数
-
信号抖动问题:
- 配置输入捕获滤波器(TIM_ICInitStructure.TIM_ICFilter)
- 硬件上增加RC滤波电路
6.4 高级定时器特殊注意事项
-
死区时间设置:
- 根据驱动器件特性设置合适的死区时间
- 典型值在100ns-1μs之间
-
刹车功能:
- 合理配置刹车输入引脚
- 设置正确的刹车极性
-
互补输出同步:
- 检查CHx和CHxN的输出极性配置
- 确保自动输出使能(TIM_AutomaticOutput_Enable)
7. 性能优化技巧
7.1 减少定时器中断延迟
- 设置合理的中断优先级:
c复制NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
-
简化中断服务函数:
- 避免在中断中进行复杂计算
- 使用标志位+主循环处理模式
-
使用DMA减轻CPU负担:
- 配置定时器触发DMA传输
- 适用于ADC采样等场景
7.2 精确延时实现
不使用阻塞延时,而是利用定时器实现精确延时:
c复制volatile uint32_t TimingDelay = 0;
void Delay_ms(uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
// 在1ms定时中断中
void TIM6_DAC_IRQHandler(void)
{
if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
if(TimingDelay > 0)
TimingDelay--;
}
}
7.3 低功耗定时器应用
在低功耗应用中,可以使用基本定时器(TIM6/TIM7)配合停机模式:
- 配置定时器唤醒:
c复制RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_WakeUpPinCmd(ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 9999;
TIM_TimeBaseStructure.TIM_Prescaler = 41999; // 约1秒唤醒
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM6, ENABLE);
- 进入停机模式:
c复制PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
- 唤醒后恢复系统时钟:
c复制SystemInit(); // 重新初始化系统时钟
8. 项目扩展与进阶应用
8.1 多定时器协同工作
通过合理配置多个定时器,可以实现复杂的时序控制。例如,使用TIM2控制整体节奏,TIM3和TIM4分别控制两个独立的PWM输出:
c复制// TIM2配置为主定时器,控制整体时序
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 1999;
TIM_TimeBaseStructure.TIM_Prescaler = 8399;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_Cmd(TIM2, ENABLE);
// TIM3和TIM4配置为从定时器
TIM_TimeBaseStructure.TIM_Period = 999;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1);
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Trigger);
TIM_Cmd(TIM3, ENABLE);
TIM_SelectInputTrigger(TIM4, TIM_TS_ITR1);
TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Trigger);
TIM_Cmd(TIM4, ENABLE);
8.2 定时器与DMA结合
定时器触发DMA传输可以实现高效的数据采集或输出,以下是使用TIM1触发DMA传输DAC数据的示例:
c复制DAC_InitTypeDef DAC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
// DAC配置
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T1_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
DAC_Cmd(DAC_Channel_1, ENABLE);
// DMA配置
DMA_DeInit(DMA1_Stream5);
DMA_InitStructure.DMA_Channel = DMA_Channel_7;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&DAC->DHR12R1;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)wave_data;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = WAVE_DATA_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream5, &DMA_InitStructure);
DMA_Cmd(DMA1_Stream5, ENABLE);
// TIM1配置
TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
8.3 使用定时器实现软件看门狗
除了硬件看门狗,还可以使用定时器实现软件看门狗功能:
c复制// 看门狗定时器初始化
void SW_Watchdog_Init(uint16_t timeout_ms)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
TIM_TimeBaseStructure.TIM_Period = (timeout_ms * 84) / 1000 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 999;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM7, ENABLE);
}
// 喂狗函数
void SW_Watchdog_Feed(void)
{
TIM7->CNT = 0;
}
// 看门狗中断处理
void TIM7_IRQHandler(void)
{
if(TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
// 看门狗超时处理
System_Reset(); // 系统复位
}
}
