赞
踩
本文分两大部分:
一、定时器中断
这部主要是简单的介绍了一下定时器,给出了定时器中断的配置步骤及实例代码。如果只是想实现定时器中断代码,则只需参考本部分即可。
二、补充&深入
在这一部分中,简单介绍了一下定时器的框图、影子寄存器、时钟、时钟树。另外还讲解了使用固件库编写定时器相关代码时,需要对固件库中默认参数进行更改的地方
—————————————————————————————
定时器是 单片机中用来 “定时” 的器件
定时器可以根据计数到来的时钟脉冲周期的个数,来实现定时功能 =》 计数值 * 时钟周期 = 定时时间
在STM32F103C8T6芯片中
当采用RCC内部时钟作为定时器时钟源输入时,三者配置代码步骤基本相同
递增计数:从0计数到设定值ARR后,再来一个脉冲就溢出。
递减计数:从ARR计数到0后,再来一个脉冲就溢出。
中心对齐计数模式1:交替计数,在向下计数溢出时会产生中断。
中心对齐计数模式2:交替计数,在向上计数溢出时会产生中断。
中心对齐计数模式3:交替计数,在向下和向上计数溢出时均会产生中断。
注:
所有定时器都支持递增计数。
TIM6/TIM7/TIM9/TIM10/TIM11/TIM12/TIM13/TIM14只支持递增 (参考芯片中文手册)
时基单元分为3个部分:
① 预分频器TIMx_PSC
② 计数器TIMx_CNT
③ 自动重装器TIMx_ARR
工作原理:
通过PSC预分频器将定时器从总线上获取的输入频率CK_PSC分频为一个合适的计数频率CK_CNT。每来一个脉冲周期,定时器中的计数CNT加1,当计数器计数溢出时,就可以向NVIC上报一个定时器溢出中断请求,以此来实现定时。
计数器计数频率:
CK_CNT = CK_PSC / (PSC + 1)计数器溢出频率:
CK_CNT_OV = CK_CNT / (ARR + 1)
= CK_PSC / (PSC + 1) / (ARR + 1)
注意事项:
1.预分频器的设定值与实际预分频系数是有一个1的差值:
因为预分频器的取值是从0开始的,当设置预分频值为0时,对应1分频,即:CK_PSC / 1
2.自动重装载的设定值与实际溢出值也有一个1的差值:
当计数到设定值ARR时,并不会产生溢出中断,还需要再来一个脉冲后才产生中断
这就是为什么上面公式中PSC、ARR都要加1的原因
例子:
TIM9位于APB2总线上,则其输入频率CK_PSC = 84M*2 = 168M。
设置:
PSC = 168 - 1;
ARR = 1000 - 1;
定时器的时钟来源有4个:
①RCC内部时钟:即定时器所在总线的定时器时钟。
②ETR外部时钟:即通过GPIO引脚输入的脉冲时钟。
③ITRx其他定时器时钟:即以其他定时器溢出更新事件作为时钟源,用于实现定时器的级联。
④TIx捕获通道:定时器输入捕获功能从GPIO口捕获的脉冲作为时钟源。
一般情况下都采用RCC内部时钟作为定时器的时钟源。
使用RCC内部时钟时,CK_PSC 的频率由定时器所在总线决定。
例如:定时器TIM9位于APB2总线上,则CK_PSC = 168M
注:基本定时器只能使用RCC内部时钟
定时中断的基本结构框图
要实现定时中断,则只需 “打通” 上面这个框图的线路即可:
使能TIMx定时器时钟
选择RCC内部时钟
选择计数模式
计数重装值ARR
预分频PSC
...
允许更新中断输出到NVIC
① 配置优先级分组
② 配置NVIC
中断请求线
抢占优先级
响应优先级
使能
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
STM32F407芯片
使用定时器TIM4、内部时钟RCC 实现定时器中断
//配置定时器中断 void TIM4_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; NVIC_InitTypeDef NVIC_InitStruct; //1.使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //2.选择时钟源 TIM_InternalClockConfig(TIM4); //选择内部时钟源作为TIM4时基单元驱动 //3.配置时基单元 TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //递增计数 TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1; //计数重装值ARR TIM_TimeBaseInitStruct.TIM_Prescaler = 8400 - 1; //预分频PSC //TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //选择外部时钟时,时钟源处的预分频 //TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; //重复计数器的值,高级定时器才用到,这里给0即可 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStruct); //清除中断标志(不清除则会在启动定时器前先产生一次更新中断事件) //(自动重装载器机制是通过产生一次更新中断事件来装载的,所以初次给ARR值时,即便定时器还没有启动,也会产生一次更新中断事件) TIM_ClearFlag(TIM4, TIM_FLAG_Update); //4.配置输出中断控制,允许更新中断输出到NVIC TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //5.配置NVIC(嵌套中断向量控制器) // ① NVIC优先级分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // ② NVIC NVIC_InitStruct.NVIC_IRQChannel = TIM4_IRQn; //选择NVIC通道 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; NVIC_Init(&NVIC_InitStruct); //启动定时器 TIM_Cmd(TIM4, ENABLE); } //定时器4中断处理函数(函数位置任意,但函数名必须以中断向量表中的函数名为准,可在启动文件中找到) void TIM4_IRQHandler(void) { //1.先判断TIM4中断标志位 if(TIM_GetITStatus(TIM4, TIM_IT_Update) == SET) { //2.处理中断的代码 //3.软件清除中断标志 TIM_ClearFlag(TIM4, TIM_FLAG_Update); } } //主函数 int main() { TIM4_Init(); while(1); }
高级定时器框图与基本定时器差不多,且通用定时器更常用,故不再列出
由上面两个框图可以看出来
基本定时器的定时器时钟源只有RCC内部时钟
而通用定时器有四个定时器时钟源
在上面框图中,预分频器、自动重装载器的框图后都有一个 “影子” ,这些寄存器都有一个影子寄存器。
这些寄存器实际上是有两个的:
一个供我们读写用的,他并不直接决定分频系数
另外一个是缓冲寄存器(影子寄存器),这才是真正起作用的寄存器
参考上面这个时序图,红线②对应的分频器值为0(即分频系数为1),之后在①处改变分频系数,但②-③的定时器时钟并没有立即改变,且此时缓冲寄存器中的值仍然是0。直到红线③处,计数器计数溢出产生更新事件后,预分频控制寄存器的值才传递到缓冲寄存器中,此时定时器的时钟才变成输入时钟的2分频
影子寄存器的作用:同步。同步更新ARR、PSC、CCR。
防止在一个计数周期(从开始计数到溢出)内更改ARR、PSC、RCC而导致波形频率变化/计数出错等问题。
例如:
如果上图更改的重装值ARR立即生效,由于当前计数值计数已经大于新的重装值,则计数器就会一直计数,直到达到计数上限65535。导致计数错误。
时钟是数字电路的灵魂,是用来使系统相关的电子组件实现 同步运行
时钟本质上是一个周期性变化的矩形波(方波)
两个与时钟息息相关的概念:
- 时钟周期T:指 时钟信号的最小重复单位,即一个低电平+一个高电平,基本单位 秒s。
- 时钟频率f:指 时钟信号在1s内重复的周期个数,基本单位 Hz。
f = 1 / T f = 1/T f=1/T
拓展讲解:
晶振:石英晶体振荡器,石英晶体是自然存在的一种能够通过压电效应产生周期性振荡波形的物质,只需要给石英晶体通电,就能够得到一个周期性变化的信号,在当下的电子产品中,通常会采用晶振来为系统提供基础时钟频率
RC振荡电路:通过电阻和电容按特定电路结构连接后,通过电容的充放电也可以产生周期性变化的信号,在数字电路中经常也会被用来作为 基础时钟使用,只不过 RC振荡电路产生的时钟频率较低,稳定性比晶振要差
PLL:锁相环电路,可以通过电路设计,对输入时钟信号进行 倍频/分频处理
倍频:放大时钟频率
分频:减小时钟频率
DIVIDER:分频器 Prescale:预分频器
时钟树的完整分析用文字描述太过冗长,不适合食用
详解可以参考B站《江协科技的STM32入门教程》的[6-1TIM定时中断]定位进度条40:53
HSI :High Speed Internal 高速内部RC时钟,频率16MHz
HSE :High Speed External 高速外部晶振时钟,频率范围为4MHz~26MHz,频率取决于PH0和PH1连接的外部晶振电路
GEC-M4采用的是8MHz晶振。LSI :Low Speed Internal 低速内部RC时钟,频率32KHz
LSE:Low Speed External 低速外部晶振时钟,频率32.768KHz,连接在PC14和PC15引脚的晶振电路提供
参考 STM32F4xx中文参考手册.pdf 107页
① SYSCLK 系统时钟,STM32F407最高是168M,来自于 PLL锁相环电路提供的时钟信号
② PLLCLK 经过锁相环电路倍频/分频后得到的 时钟信号,通常是选择 HSE作为锁相环的输入时钟 =》 外部晶振时钟比较稳定
P
L
L
C
L
K
=
H
S
E
÷
M
×
N
÷
P
PLLCLK = HSE ÷ M × N ÷ P
PLLCLK=HSE÷M×N÷P
③ AHB总线时钟:由 SYSCLK系统时钟经过 AHB_PRESC预分频器分频后得到,主要提供给内核、存储器、DMA、系统时钟定时器等使用
④ APBx总线时钟:APB外设时钟总线,用来给外设控制器提供时钟信号,又可以分为 低速外设时钟总线APB1(最高42MHz) 和 高速外设时钟总线 APB2(最高84MHz)
注意:对于APBx总线上的外设 和 定时器来讲,虽然 APBx总线上的时钟是相同的,但作用在外设和定时器时频率会不相同!
对于外设来说,APBx提供的时钟频率就是 总线上的 频率。
对于定时器来说,APBx提供的时钟频率还与 APBx_PRESC预分频器的系数有关。
APBx_外设时钟 = APBx_CLK
APBx_定时器时钟 = APBx_CLK*1 ,当APBx_PRESC == 1 时
APBx_定时器时钟 = APBx_CLK*2 ,当APBx_PRESC != 1 时
关于 APBx_PRESC 系数的配置 见下面第4部分
比如:
TIM9和SYSCFG都是位于 APB2总线(频率84M)上,则 SYSCFG外设得到的时钟频率为84MHz,
而TIM9定时器得到的时钟频率是 84M*2=168M
在固件库system_stm32xx.c文件的 SetSysClock() 函数中为我们默认配置了时钟树中的一些参数
图一
图二
①处:HCLK = SYSCLK,参考图二可知APB_PRESC的系数为1
②处:PCLK2 = HCLK/2 =》PCLK2 = SYSCLK/2,PCLK2即APB2的外设时钟。
参考图二可知,APB2_PRESC的系数为2,故
APB2外设时钟为 SYSCLK/2 = 168M / 2 = 84M
APB2定时器时钟为 (SYSCLK/2) * 2 = SYSCLK = 168M
③处:同理可得
APB1外设时钟为 SYSCLK/4 = 168M / 4 = 42M
APB1定时器时钟为 (SYSCLK/4) * 2 = 84M
由于固件库代码在出厂时无法预知用户的外部晶振会接多大,因此只是给了个默认值,用户需要根据自己的硬件电路,来修改时钟配置。
主要需要修改的值有4个:
HSE_VALUE : GEC-M4采用8M
PLL_M、PLL_N、PLL_P 这三个要确保PLLCLK能够达到最大值168M
上述内容,必须根据实物电路上的晶振大小进行修改,否则 后续定时器的定时时间 、串口的波特率、SPI的通信速率、实时操作系统的系统时钟,都会是错误的!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。