目录
在我之前文章中说过一个系统定时器SysTick,它存在于内核,嵌套在NVIC中,所有的Cortex-M内核的单片机都具有这个定时器。而今天介绍的STM32定时器是相当于一个外设,功能更加丰富全面。
SysTick定时器文章链接:
STM32 系统定时器--SysTick_systick中断_Haohao fighting!的博客-CSDN博客
定时器分类:基本定时器(TIM6和TIM7)、通用定时器(TIM2、TIM3、TIM4和TIM5)、高级定时器(TIM1和TIM8)
功能:基本定时器:只能实现定时的功能
通用定时器:定时、输出比较、输入捕获、互补输出
高级定时器:包含了基本定时器和通用定时器的所有功能。
STM32F103系列有2个高级定时器TIM1和TIM8,4个通用定时器TIM2/3/4/5,2个基本定时器TIM6和TIM7。
计数器16bit,只能从0开始向上计数。没有外部的GPIO,是内部资源,只能用来定时。时钟来自PCLK1,为72M,可实现1~65536分频。
时钟源来自RCC的TIMx_CLK(属于内部的CK_INT)
基本定时器和通用定时器是挂载到APB1总线上,由于APB1的预分频系数是2,所以频率要×2,也就是72M。
控制器用于控制定时器的:复位、使能、计数、触发DAC。涉及到的寄存器为:CR1/2、DIER(DMA/中断使能寄存器)、EGR(事件产生寄存器)、SR(状态寄存器)
定时器最主要的就是时基部分:包括预分频器、计数器、自动重装载寄存器。这些所有定时器都有。
预分频器PSC (寄存器 TIMx_PSC控制):
结构框图中:CK_PSC就是从APB1得到的时钟(72M),预分频控制寄存器的值就是PSC里面的值,而CK_CNT就是分频最终得到的值。
16位的预分频器PSC对内部时钟CK_PSC进行分频之后,得到计数器时钟CK_CNT=CK_PSC(预分频器时钟)/(PSC(预分频因子)+1)
计数器CNT在计数器时钟的驱动下开始计数,计数一次的时间为1/CK_CNT。
计数器、自动重装载寄存器
定时器使能(CEN置1)后,计数器 CNT在CK_CNT 驱动下计数,当TCNT值(计数器的值)与ARR 的设定值(自动重装载寄存器的值,自动重装载寄存器是16位的,最大值是65535)相等时就自动生成事件(如果使能中断就产生中断)并 CNT 自动清零,然后自动重新开始计数,如此重复以上过程。
功能框图上有个影子,PSC和ARR都有影子寄存器。
什么是影子寄存器?
这表示在物理上这个寄存器对应2个寄存器:一个是我们可以可以写入或读出的寄存器,称为预装载寄存器,另一个是我们看不见的、无法真正对其读写操作的,但在使用中真正起作用的寄存器,称为影子寄存器。
每次发生更新事件(UEV)时,所有真正起作用的影子寄存器(shadowregister)可以同时被更新为预装载寄存器 (preloadregister)里面的值,这样可以保证多个通道的操作能够准确同步;如果没有影子寄存器(shadowregister),或者预装载寄存器 (preloadregister) 和影子寄存器 (shadowregister)是直通的,当软件更新了预装载寄存器 (preloadregister),也就立即更新了影子寄存器 (shadowregister),由于软件不能同时更新多个影子寄存器,这就会导致多个通道的时序不能同步。
原文链接:
https://blog.csdn.net/weixin_51703421/article/details/126500556
影子寄存器的存在起到一个缓冲的作用,用户值->寄存器->影子寄存器->起作用。例如:自动重装载的影子寄存器(ARR寄存器):自动重载寄存器是预装载的。对自动重载寄存器执行写入或读取操作时会访问预装载寄存器。预装载寄存器的内容既可以直接传送到影子寄存器,也可以在每次发生更新事件 (UEV) 时传送到影子寄存器。也就是说,在CNT计数器计数的过程中,自动重装载寄存器的值可以随时更改,更改的数值可以立刻起作用,也可以在本轮计数完成之后再起作用。是否立刻起作用可以通过软件来进行控制。由控制寄存器 1(TIMx_CR1)的ARPE位来控制。0:TIMx_ARR寄存器没有缓冲 1:TIMx_ARR寄存器具有缓冲。
而PSC的影子寄存器,只能在这一轮计数完成之后的下一轮才开始起作用。这个是不能通过软件来控制的
如果不使用影子寄存器则用户值在写到寄存器之后则里面起作用。
PSC = 72-1,定时器内部时钟为72MHz,定时器频率=72M/(PSC+1)=1MHz,近似时间为1微秒。
若我们设定ARR(自动重装载寄存器)= 1000-1,设定为向上计数模式,从0计数到999,则计了1000次。
中断周期 T = 1000 *1/1MHz = 1ms
时基初始化结构体 TIM_TimeBaseInitTypeDef 里面有5个成员,TIM6和TIM7的寄存器里面只有TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,另外三个成员是通用定时器和高级定时器才有。TIM_Prescaler对应预分频器(TIMx_PSC),TIM_Period对应自动重装载寄存器(TIMx_ARR)。
- typedef struct
- {
- TIM_Prescaler 都有
- TIM_CounterMode TIMx,x[6,7]没有,其他都有
- TIM_Period 都有
- TIM_ClockDivision TIMx,x[6,7]没有,其他都有
- TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有
- }TIM_TimeBaseInitTypeDef;
基本定时器产生500MS定时,在主函数里面让LED反转
设计思路:让基本定时器产生1ms的中断,到了1ms之后进入中断服务函数,中断服务函数里的变量自加,加到500之后代表到了500ms。我们可以在主函数内部判断变量值是否等于500,到了500让LED翻转一次。
这个就与51或者蓝桥杯的定时器0、1、2的定时功能差不多,我们通过定时的功能,完成一个固定周期的重复事件。
首先宏定义硬件,基本定时器的时钟,自动重装载的值,时钟分频因子,定时器的中断通道(在使用高级定时器的时候只更改这些即可)。
- /********************基本定时器TIM参数定义,只限TIM6、7************/
-
- #define BASIC_TIM6 // 如果使用TIM7,注释掉这个宏即可
-
- #ifdef BASIC_TIM6 // 使用基本定时器TIM6
- #define BASIC_TIM TIM6
- #define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
- #define BASIC_TIM_CLK RCC_APB1Periph_TIM6
- #define BASIC_TIM_Period 1000-1 //自动重装载的值 1000次计数进一次中断,也就是1毫秒进一次中断
- #define BASIC_TIM_Prescaler 71 //时钟分频因子 控制计数一次的时间 72/(71+1) = 1M 也就是1微秒
- #define BASIC_TIM_IRQ TIM6_IRQn //中断源 所有的中断源在 stm32f10x.h文件中
- #define BASIC_TIM_IRQHandler TIM6_IRQHandler
-
- #else // 使用基本定时器TIM7
- #define BASIC_TIM TIM7
- #define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
- #define BASIC_TIM_CLK RCC_APB1Periph_TIM7
- #define BASIC_TIM_Period 1000-1
- #define BASIC_TIM_Prescaler 71
- #define BASIC_TIM_IRQ TIM7_IRQn
- #define BASIC_TIM_IRQHandler TIM7_IRQHandler
-
- #endif
配置、初始化时基结构体,配置、初始化NVIC结构体。
- // 中断优先级配置
- static void BASIC_TIM_NVIC_Config(void)//涉及到中断就要配置、初始化 NVIC
- {
- NVIC_InitTypeDef NVIC_InitStructure;
- // 设置中断组为0
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- // 设置中断来源
- NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQ ;
- // 设置主优先级为 0
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- // 设置抢占优先级为3
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- }
-
-
- static void BASIC_TIM_Mode_Config(void)//初始化时基结构体
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
-
- // 开启定时器时钟,即内部时钟CK_INT=72M
- BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
-
- // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
- TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period;
-
- // 时钟预分频数为
- TIM_TimeBaseStructure.TIM_Prescaler= BASIC_TIM_Prescaler;
-
- // 时钟分频因子 ,基本定时器没有,不用管
- TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
-
- // 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
- TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
-
- // 重复计数器的值,基本定时器没有,不用管
- TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
-
- // 初始化定时器
- TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);
-
- // 清除计数器中断标志位
- TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
-
- // 开启计数器中断
- TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);
-
- // 使能计数器
- TIM_Cmd(BASIC_TIM, ENABLE);
- }
-
- void BASIC_TIM_Init(void)
- {
- BASIC_TIM_NVIC_Config();
- BASIC_TIM_Mode_Config();
- }
具体哪个定时器,具体哪种中断。这里用到的是更新中断,计数器从n计数到m为一轮产生的中断就叫做更新中断。
- extern volatile uint32_t time;
-
- void BASIC_TIM_IRQHandler (void)
- {
- if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET )//判断是否产生中断
- {
- time++;
- TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update);
- }
- }
- int main(void)
- {
- /*在程序来到main函数这里的时候,系统时钟已经配置成72M*/
- LED_GPIO_Config();//初始化函数
- BASIC_TIM_Init();
-
- while(1)
- {
- if(time == 500)
- {
- time = 0;
- LED1_TOGGLE;/* LED1 取反 实现LED1的亮灭反转*/
- }
- }
- }
完整程序下载链接:
链接:https://pan.baidu.com/s/13eb3qHm4YVtHELPXYBJXdQ
提取码:hjaw