我们前面已经学过了滴答定时器,那么定时器的原理与它一样,只不过滴答定时器是在内核中的定时器,而定时器是片上外设。
定时器分为:基本定时器和通用定时器。
而基本定时器所拥有的功能,通用定时器都有。所以,通用定时器内集成了基本定时器。
定时器作用:产生一个精准的定时
stm32f03中基本定时器为TIM6和TIM7:
16位预分频器:将输入进来的72Mhz进行预分频,但是它是16位的,所以它的分配系数范围为1~65535。
16位自动重装载累加器:用来装载我们设置的计数值。当16位计数单元计数完后,它就将设置的计数值传给计数单元,让计数单元重新开始计数。
16位计数单元:它的内部就是定时器计数的过程,即从计数值开始计数。其内部的数值是变化的。
定时器的计数值设置:
我们用定时器来定时1s,那么这个计数值我们应该设置为多少?
先来设置16位预分频的分频系数,我们设置为7200。
所以 = 10000HZ,经过预分频后72Mhz变成了10000hz
所以,T = =,即:在10000hz频率下,计数10000次,需要花费1s的时间。换而言之就是,在10000hz频率下,想要延时1s就需要计数10000次。所以,此时计数值我们设置为10000。
所以,计算值设置为多少,需要先知道预分频的系数,才能计算出来。
库函数:
计数模式:
向上计数:从0计数到计数值
向下计数:从计数值计数到0
中央对模式1:从0计数到计数值 又 从计数值计数到0
中央对模式2:从计数值计数到0 又 从0计数到计数值
中央对模式3:与中央对模式2一样。
定时器中断源:
TIM_IT_Update 更新中断:即计数值计数完了,溢出了,此时产生中断。也就是计数溢出中断。注意:只有这个中断源是基本定时器的,其他的都是通用定时器的。
标志位:
TIM更新标志位:计数器计数溢出后,该标志位置SET,但是是否此时产生中断它是不会去管的。注意:只有这个标志位是基本定时器的,其他的都是通用定时器的。
清除标志位。
计数器计数溢出后,产生中断时,该标志位置SET。
清除标志位。
编程步骤:
基本定时器产生1s延时,编程步骤:
1》打开时钟-----TIM6
2》初始化定时器6
----自动重装载累加器的值
----预分频器的值
3》配置中断源-----计数器溢出时,产生定时器中断
4》配置中断优先级----NVIC初始化
----中断通道号----stm32f10x.h
----主优先级
----次优先级
----使能
5》编写中断服务函数
void TIM6_IRQHandler(void)
{
if(SET==TIM_GetITStatus(TIM6,TIM_IT_Update)){
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
ledflag=~ledflag;
}
}
6》使能定时器6
编写程序:
//定时器每定时1秒钟,就进入定时器中断
void TIM6_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 1》打开时钟-----TIM6
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM6, ENABLE);
// 2》初始化定时器6
TIM_TimeBaseInitStruct.TIM_Period =10000;
TIM_TimeBaseInitStruct.TIM_Prescaler =7200-1;
TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStruct);
// 3》配置中断源-----计数器溢出中断
TIM_ITConfig(TIM6, TIM_IT_Update,ENABLE);
// 4》配置中断优先级----NVIC初始化
NVIC_InitStruct.NVIC_IRQChannel =TIM6_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority =2;
NVIC_InitStruct.NVIC_IRQChannelCmd =ENABLE;
NVIC_Init(&NVIC_InitStruct);
// 6》使能定时器6
TIM_Cmd(TIM6,ENABLE);
}
中断服务函数:
void TIM6_IRQHandler(void)
{
if(SET==TIM_GetITStatus(TIM6,TIM_IT_Update)){
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
ledflag=~ledflag;
}
}
uint8_t ledflag=0;
int main(void)
{
LED_Config();
RCC_ConfigTo72M();//将系统时钟配置成72MHZ
Systick_Config(72);
TIM6_Config();
// 4,如果是程序的第一个中断,需要设置优先级分钟
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
while(1){
if(0==ledflag){
LED_CTRL(LEDR,LED_OPEN);
}else{
LED_CTRL(LEDR,LED_CLOSE);
}
}
}
在配置的过程中,出现的问题:
问题1:在配置TIM_TimeBaseInitTypeDef结构体时,我们发现该结构体里面多了一个成员。如下图红色框:
仔细一看注释才发现,该成员是用于TIM1和TIM8通用定时器的。所以,我们在配置的时候,又疑问,需要学会先查看注释。
该成员的作用:
还记得前面滴答定时器中1864的问题吗?这个就是解决类似1864问题的系数。当它为2的时候,计数器需要计数溢出两遍,才会产生中断。当它为3的时候,计数器需要计数溢出三遍,才会产生中断,以此类推。
问题2:为什么预分频器的分频系数设置需要减1?
这个是基本定时器中预分频器的寄存器,我们可以看到红框中有一个+1,也就是说我们给该寄存器设置一个16位的值放在PSC[15:1]中,但是硬件会自动再加1。其目的就是防止有人将PSC[15:1]中的值设置为0。因为,由前面可知:分配系数范围为1~65535(即1~0xffff)而不是0~65535。所以,一旦有人设置为0,那么PSC[15:1]中的值实际上是1。
所以我们设置PSC[15:1]中的值为7200,实际上PSC[15:1]中的值为7200+1 = 7201,但是我们想要的是7200,所以需要-1。所以,我们设置为7200-1 = 7199;此时PSC[15:1]中的值会自动加1,其值为7199+1 = 7200。
问题3:如果我们想再不改变预分频系数和计数值的情况下:每过5秒才改变小灯状态,而不是每过1s就改变小灯状态,那么怎么办?
我们可以修改中断服务函数:当i等于5时,再进入if中去改变小灯状态
中断服务函数:
void TIM6_IRQHandler(void)
{
static int i = 0;
if(i == 5){
if(SET==TIM_GetITStatus(TIM6,TIM_IT_Update)){
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
ledflag=~ledflag;
i = 0;
}
}
i++;
}
上一篇:stm32f103——通用定时器输出PWM
下一篇:stm32f103——外部中断和事件——检测按键按下点灯
推荐阅读最新更新时间:2024-11-13 14:50