1、定时器定时中断
#include "stm32f10x.h" // 设备头文件
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器2时钟
TIM_InternalClockConfig(TIM2); // 配置定时器2为内部时钟模式
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频系数设置为1
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 定时器计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; // 定时器溢出计数值为10000-1
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; // 定时器预分频值为7200-1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器设置为0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 初始化定时器2的时间基准参数
TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除定时器2的更新标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能定时器2的更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级分组配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 中断通道选择为TIM2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级设置为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子优先级设置为1
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE); // 使能定时器2
}
/*
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
// 在此处添加定时器中断处理代码
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除定时器2的更新中断标志位
}
}
*/
TIM_InternalClockConfig(TIM2); // 配置定时器2为内部时钟模式
什么时候更新中断标志位被设置为SET?
1. 当定时器溢出并重新开始计数时,更新中断标志位会被设置。这发生在定时器计数器的值从最大值(比如设定的`TIM_Period`)重新回到0开始计数时。
2. 在某些情况下,您可能手动设置(SET)更新中断标志位,例如通过调用`TIM_SetFlag(TIM2, TIM_FLAG_Update)`函数。
当更新中断标志位被设置后,如果使能了该中断并且中断优先级较高,系统将会触发更新中断,并跳转到相应的中断处理函数执行相关的操作。在中断处理函数中,可以使用`TIM_ClearITPendingBit(TIM2, TIM_IT_Update)`函数清除更新中断标志位,以标记已经处理了该中断。这样可以确保在下一次定时器溢出时再次触发中断。
中断优先级分组配置的内部可供选择的参数的详细解释
中断通道如何对应选择
2、定时器外部时钟
#include "stm32f10x.h" // 包含STM32F10x系列的设备头文件
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // GPIOA的引脚工作在上拉输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // GPIOA的引脚0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // GPIOA的引脚速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA的引脚配置
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F); // 配置TIM2为外部时钟模式2
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频因子为1
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; // 定时器溢出值为10-1
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; // 定时器预分频值为1-1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器值为0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 初始化TIM2的时间基准配置
TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除TIM2的更新标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能TIM2的更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置中断优先级分组为组2
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // TIM2中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 中断抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 中断子优先级为1
NVIC_Init(&NVIC_InitStructure); // 初始化NVIC的中断配置
TIM_Cmd(TIM2, ENABLE); // 使能TIM2
}
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2); // 返回TIM2的计数器值
}
外部时钟模式2
// 配置TIM2为外部时钟模式2
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
TIM2
: 指定要配置的定时器是TIM2。TIM_ExtTRGPSC_OFF
: 配置外部时钟的预分频器,此处设置为TIM_ExtTRGPSC_OFF
表示预分频器关闭,不对外部时钟进行预分频。TIM_ExtTRGPolarity_NonInverted
: 配置外部时钟的极性,此处设置为TIM_ExtTRGPolarity_NonInverted
表示外部时钟信号极性为非反转(即正极性)。0x0F
: 配置外部时钟的滤波器值,此处设置为0x0F,表示滤波器值为15。滤波器用于对外部时钟信号进行滤波,以消除可能的干扰。
通过以上配置,TIM2定时器可以接收外部时钟信号,并以该信号作为定时器的时钟源,用于计数和定时操作。
外部时钟的极性
在配置外部时钟模式时,可以指定外部时钟信号的极性,即信号的电平变化方式。"非反转"表示外部时钟信号的电平与输入的信号保持一致,即在输入信号上升沿或下降沿时,定时器将进行计数或触发相应的事件。
具体解释如下:
- "非反转"极性意味着当外部时钟信号发生上升沿时,定时器开始计数或触发中断,而当外部时钟信号发生下降沿时,定时器停止计数或触发中断。这意味着外部时钟信号的上升沿触发了定时器的计数或中断操作。
- 相反,如果选择了"反转"极性,则外部时钟信号的下降沿将触发定时器的计数或中断,而上升沿将停止计数或触发中断。这意味着外部时钟信号的下降沿触发了定时器的计数或中断操作。
因此,在这种情况下,选择"非反转"极性表示外部时钟信号的上升沿触发定时器的计数或中断操作。这是根据特定应用需求选择的一种配置,根据外部时钟信号的特点和需求来确定使用上升沿还是下降沿触发定时器的操作。
3、PWM驱动呼吸灯
#include "stm32f10x.h" // 引入设备头文件
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟
// 配置GPIOA引脚为AF_PP模式(复用推挽输出模式)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // GPIOA引脚0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // GPIO速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置TIM2为内部时钟源
TIM_InternalClockConfig(TIM2);
// 配置TIM2时间基准
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // 设置周期值(ARR)
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; // 设置预分频值(PSC)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 设置重复计数值
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 配置TIM2通道1为PWM输出模式
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure); // 使用默认值初始化结构体
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 设置PWM模式1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 设置输出极性为高电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
TIM_OCInitStructure.TIM_Pulse = 0; // 设置初始脉冲值(CCR)
TIM_OC1Init(TIM2, &TIM_OCInitStructure); // 配置TIM2通道1
TIM_Cmd(TIM2, ENABLE); // 启动TIM2计数器
}
// 设置比较值并调整PWM占空比的函数
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare); // 设置比较值来调整PWM占空比
}
TIM_OCInitStructure.TIM_OCMode.TIM_PWM1是什么模式?
TIM_PWM1
是 TIM(定时器/计数器)的 PWM(脉冲宽度调制)模式之一。在这种模式下,定时器将产生一个 PWM 信号,其脉冲宽度可以通过比较寄存器(比如 CCR1)的值来调节。
在 PWM 模式 1 下,当定时器的计数器值小于等于比较寄存器的值时,输出为高电平;当计数器值大于比较寄存器的值时,输出为低电平。这样可以实现通过调节比较寄存器的值来控制 PWM 的占空比。
注意,具体的极性和输出使能状态还需要根据具体的需求进行配置,例如在代码中使用的 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
将输出极性设置为高电平,TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
使能输出。
PWM 模式 1 适用于许多应用场景,例如控制电机速度、调光、音频输出等/.
TIM_Pulse的含义
`TIM_Pulse` 是 TIM(定时器/计数器)的脉冲值,用于设置 PWM(脉冲宽度调制)信号的脉冲宽度。
在 PWM 模式下,定时器的计数器从 0 开始递增,当计数器的值小于等于脉冲值(`TIM_Pulse`)时,PWM 输出为高电平;当计数器的值大于脉冲值时,PWM 输出为低电平。通过调节脉冲值,可以实现对 PWM 信号的占空比进行调节。
脉冲值的范围取决于定时器的位数,例如对于 16 位定时器,脉冲值的范围是 0 到 65535(2^16-1)。通过改变脉冲值,可以调节 PWM 的占空比,即高电平的持续时间与周期的比例。
在代码中的 `TIM_OCInitStructure.TIM_Pulse` 可以设置 PWM 的初始脉冲值,根据具体的应用需求进行调节,以实现所需的占空比。
占空比 = (OCMode.TIM_Pulse = 0)/(TIM_TimeBaseInitStructure.TIM_Period = 100 - 1)
4、关于代码中函数的解读(以PWM驱动呼吸灯为例子)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
对于端口复用的解释:
例如我想用TIM2的CH1通道输出PWM波对LED灯的亮灭进行调节。通过查找上表发现应该是PA0,假如PA0已经用于ADC12_IN0。此时我们可以通过
1、RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
打开AFIO复用功能
2、GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
开启复用功能,参数1:GPIO_PartialRemap1_TIM2
可以参考STM32F103C8T6中文手册,在8.3 复用功能I/O和调试配置(AFIO)(116页)中找到定时器复用功能重映射,并找到TIM2复用功能重映像。由于我们要将PA0复用到PA15(只能是PA15,看表中定义)选择TIM2_REMAP[1:0]=01或者TIM2_REMAP[1:0]=11都可以。区别是前者不改变TIM2_CH3\TIM2_CH4的原始端口,而后者的话也一同把TIM2_CH3\TIM2_CH4由原来的PA2\PA3一同改到了PB10\PB11。注意不是一个一个改的没那么高级!!!
GPIO_PartialRemap1_TIM2可以跳转到定义,刚好对应第二列第三列和第四列,由于第一列就是初始配置所以没有参数。
3、一般到这里就可以了,但由于PA15比较特殊,他原本是JDTI端口(JTAG调试端口),故需要再把JDTI失能打开。
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
这里就是TIM内部配置
TIM_CKD_DIV1:代表不分频,故时钟频率为72MHz
TIM_CounterMode_Up:代表向上计数
TIM_Period = 100 - 1; //ARR 自动重装值
TIM_Prescaler = 7200 - 1; //PSC 预分频值
TIM_RepetitionCounter = 0; 重复计数器,0表示重复0次
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //使用TIM_OCMode_PWM1模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //极性选择
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //CCR 的值 占空比
TIM_OC1Init(TIM2, &TIM_OCInitStructure); //OC1表示通道1,OC2表示通道2
TIM_Cmd(TIM2, ENABLE);
void PWM_SetCompare1(uint16_t Compare)//通过使用SetCompare1改变占空比的值,setCompare2表示改变通道2的值,以此类推
{
TIM_SetCompare1(TIM2, Compare);
}
注意TIM定时器共用一个自动重装寄存器,故无法对TIM2的每一个通道单独配置时钟,也就是说TIM2的每一个通道时钟频率都一样,但可以改变每个通道单独的占空比。使用IM_SetComparex(TIM2, Compare);
x可以是1、2、3、4。
5、电平标准