STM32F103ZET6的基本定时器
1、定时器的分类
STM32F103ZET6总共有8个定时器,它们是:TIM1~TIM8。STM32的定时器分为基本定时器、通用定时器和高等定时器。
TIM6、TIM7是基本定时器。基本定时器是只能向上计数的16位定时器,基本定时器只能有定时的功能,没有外部IO口,所以没有捕获和比较通道。
TIM2、TIM3、TIM4、TIM5是通用定时器。通用定时器是可以向上计数,也可以向下计数的16位定时器。通用定时器可以定时、输出比较、输入捕捉,每个通用定时器具有4个外部IO口。
TIM1、TIM8是高等定时器。高等定时器是是可以向上计数,也可以向下计数的16位定时器。高等定时器可以定时、输出比较、输入捕捉、还可以输出三相电机互补信号,每个高等定时器有8个外部IO口。
定时器分类图如下:
2、基本定时器
基本定时器没有外部IO口,所以它只有定时的功能。
基本定时器只能向上计数,也就是说基本定时器只能递增计数。
基本定时器功能框图如下:
从功能图的1中可以看到,基本定时器的时钟TIMxCLK来自内部时钟,该内部时钟为经过APB1预分频器分频后提供的。基本定时器跟APB1总线时钟的关系如下:
- 如果APB1预分频系数为1,则基本定时器的时钟等于APB1总线时钟。
- 如果APB1预分频系数不为1,则基本定时器的时钟等于APB1总线时钟经过分频后的2倍。
比如APB1总线经过2分频后的时钟为36MHZ,那么基本定时器的时钟就是72MHZ3(36*2)。
功能图中的2是一个预分频器,来自内部的时钟经过预分器分频后的时钟,用来驱动基本定时器的计数器计数。基本定时器的预分频器是一个16位的预分频器,预分频器可以对定时器时钟进行1~65536之间的任何一个数进行分频。计算方式如下:
定时器工作时钟 = 来自APB1的时钟/(预分频系数+1)
功能图中的3是一个16位的计数器,该计数器能能向上计数,最大计数值位65535。基本定时器的计数器从0开始向上计数,当计数器的值与自动重装载寄存器相等时产生更新事件,并清零从头开始计数。
功能图中的4是一个16位的自动重装载寄存器。该寄存器装着计数器能计数的最大数值。当基本定时器的计数器计数到这个值的时候,如果使能了中断。定时器就会产生溢出中断。
在定时器的参考资料中,很多地方都提到更新事件,其实更新事件就是计数器溢出。
3、定时器的时间计算
基本定时器每计数一次所经过的时间为:
Time = (PSC + 1)/ TIMxCLK(us)
PSC是定时器的分频系数,TIMxCLK是内部时钟。
基本定时器的计数次数由自动重装载寄存器决定的,基本定时器的计数器从0开始向上计数,当计数器的值与自动重装载寄存器相等时,产生溢出。所以基本定时器的溢出时间计算公式如下:
Time = (PSC+1)*(ARR)/ TIMxCLK(us)
ARR是自动重装载寄存器的值。
假设基本定时器TIMxCLK = 72MHZ,PSC = 71,ARR = 1000,那么定时器的溢出时间为:
Time = (71+1)*1000/72 = 1000(us) = 1(ms)
4、定时器的寄存器
TIMx_CR1控制寄存器1
Bit0位CEN是定时器的使能位,CEN=0时,计数器不工作;CEN=1是计数器开始计数。想要定时器工作,就要将CEN置1。
Bit1位UDIS是禁止更新位,当UDIS=0时,定时器溢出后会把TIMx_SR寄存器的UIF置1;当UDIS=1时,定时器溢出后并不会置位UIF位,即不会产生中断。
Bit2位URS是更新请求源,当URS=0时,计数器溢出、设置UG位、通过从模式控制器产生的更新都会产生中断;当URS=1时,只有在计数器溢出的情况下才会产生中断。
Bit3位OPM可以设置定时器是工作一次还是重复工作,当OPM=0时,定时器产生中断后继续工作;当OPM=1时,定时器溢出后会清除CEN位,即定时器停止计数。
Bit7位ARPE是自动重装载预装载使能,当ARPE=0时,TIMx_ARR寄存器没有缓冲;当ARPE=1时,TIMx_ARR寄存器具有缓冲。
TIMx_DIER中断使能寄存器
Bit0位UIE是更新中断使能位,当UIE=0时,定时器禁止溢出中断;当UIE=1时,定时器可以产生溢出中断。
Bit8位UDE是更新DMA请求使能位,当UDE=0时,禁止更新DMA请求;当UDE=1时,使能更新DMA请求。
TIMx_SR状态寄存器
Bit1位UIF是溢出中断标志。当TIMX_CR1的UDIS=0,如果定时器产生溢出,UIF就会被置1,UIF不会被硬件自动清除,只能软件清除。当产生中断时,可以由该位判断是否是定时器的溢出中断。
TIMx_EGR事件产生寄存器
Bit0位UG是产生更新事件,UG位由软件置1,但由硬件自动清除。当UG写0时无作用;当UG写1时,如果TIMx_CR1寄存器的UDIS = 0,则会重新初始化定时器的计数器并产生对寄存器的更新,需要注意的是预分频器也被清除(但预分频系数不变)。
TIMx_CNT计数器寄存器
TIMx_CNT只有低16位有效,可以从0到65535之间计数。
自动重装载寄存器TIMx_ARR
如果TIMx_ARR的数值为0,则TIMx则会停止工作。
自动重装载寄存器是预加载的,每次读写TIMx_ARR寄存器时,实际上是通过读写预加载寄存器实现的。TIMx_ARR寄存器根据TIMx_CR1控制寄存器的bit7位ARPE来决定是带缓冲的或是不带缓冲的。
当ARPE=0时,TIMx_ARR寄存器是不具有缓冲功能的,当改变TIMx_ARR寄存器的值时,立马就更新定时器的自动重装载寄存器,如下图。
从上图可以看到,在没改变TIMx_ARR寄存器的值之前自动重装载的寄存器的是FF,当在计数器跑到32的时候突然改变TIMx_ARR寄存器的值为36,那么自动重装载的寄存器的值也立马从FF变为了36,改变TIMx_ARR寄存器的值并不会影响计数器寄存器的值,计数器寄存器继续从32计数到36后溢出并产生更新中断。
当ARPE=1时,TIMx_ARR寄存器是具有缓冲功能的,当改变TIMx_ARR寄存器的值时,并不会马上更新定时器的自动重装载寄存器,而是要等到下一次更新事件发生时,才会更新定时器的自动重装载寄存器的值,如下图。
从上图可以看到,在没改变TIMx_ARR寄存器的值之前自动重装载的寄存器的是F5,当在计数器跑到F1的时候突然改变TIMx_ARR寄存器的值为36时,虽然自动重装载的寄存器的值也变为了36,但是自动重载影子寄存器的值还是F5,并没有被改变,而且计数器寄存器的值是跟自动重载影子寄存器的值比较,只有当产生更新事件的时候,自动重载影子寄存器的值才变为了36。
TIMx_PSC预分频寄存器
TIMx_PSC只有低16位有效,用来设置定时器时钟的分频系数。
TIMx_PSC预分频寄存器具有缓冲功能,如果在定时器运行的过程中更改TIMx_PSC寄存器的值,定时器的分频系数并不会立马发生更改,而是会等到下一个计数器溢出时新的预分频数值才会更改。如下图。
从图中可以看到,当计数器寄存器递增到F8的时候,TIMx_PSC的值被改写成了1,但是实际的预分频系数还没有被改变,还是0,只有当计数器寄存器溢出变为0时,实际的预分频系数才变为1。从图中还可以看出,当预分频系数为0的时候,CK_PSC = CK_CNT,当预分频系数为1的时候,CK_CNT = 2CK_PSC,也就是通过波形可以看出计数频率变了。
5、基本定时器的配置流程
使能基本定时的时钟,不然无法使用基本定时器。
设置基本定时器的预分频系数和自动重装载寄存器值,通用定时器和高等定时器还要设置计数方向,因为基本定时器只能向上计数,所以不用设置。
开启基本定时器。
通过NVIC配置基本定时器的中断。
编写基本定时器的中断服务函数,在中断服务函数中,可以通过基本定时器的状态寄存器的值来判断此次产生的中断属于什么类型。
6、HAL库操作基本定时器
在使用HAL操作基本定时器之前,需要将stm32f1xx_hal_tim.c和stm32f1xx_hal_tim_ex.c文件添加到功能目录,还要在stm32f1xx_hal_conf.h文件中使能HAL_TIM_MODULE_ENABLED宏。如下图。
以操作基本定时器TIM6为例,初始化代码如下:
TIM_HandleTypeDef TIM6_Handler_Init; void TIM6_Init(void)
{
__HAL_RCC_TIM6_CLK_ENABLE(); TIM6_Handler_Init.Instance = TIM6;
TIM6_Handler_Init.Init.Prescaler = -;
TIM6_Handler_Init.Init.Period = ;
HAL_TIM_Base_Init(&TIM6_Handler_Init); HAL_TIM_Base_Start_IT(&TIM6_Handler_Init); HAL_NVIC_SetPriority(TIM6_IRQn,,);
HAL_NVIC_EnableIRQ(TIM6_IRQn); }
第1行首先定义一个全局变量TIM6_Handler_Init,这是一个句柄变量,用来操作TIM6。
第5行通过__HAL_RCC_TIM6_CLK_ENABLE()宏使能TIM6的时钟,如果不是能时钟,是无法操作TIM6的。
第7行是将TIM6_Handler_Init句柄变量指向TIM6。
第8行是将TIM6的分频系数设为36000,如果TIM6的时钟为72M,那么分频后的值为72000000/36000 = 2000HZ。
第9行是将TIM6的自动重载在的值设为1000,即TIM6的计数器计数1000次后溢出,根据分频系数可以得出溢出时间为1000 * 36000 /72000000 = 1000 / 2000 = 0.5s,即TIM6的计数器每0.5秒溢出一次。
第10行是通过HAL_TIM_Base_Init函数配置TIM6定时器。
第12行是通过HAL_TIM_Base_Start_IT函数使能溢出中断,并启动定时器从0开始向上计数。
第14行是通过HAL_NVIC_SetPriority函数设置TIM6的中断优先级。
第15行是通过HAL_NVIC_EnableIRQ函数使能TIM6中断,只有使能了中断,才能进入中断服务函数。
初始化完TIM6后,编写中断服务函数,代码如下:
void TIM6_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM6_Handler_Init);
} void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &TIM6_Handler_Init)
{
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_6);
}
}
第1行是TIM6的中断服务函数。
第2行是调用HAL库编写的定时器中断处理函数,将定时器的句柄变量通过参数传入其中。
第6行是定时器的中断回调函数,通过这个函数实现定时器的中断处理。
第8行是通过句柄判断需要处理的是什么类型的中断。
STM32F103ZET6的基本定时器的更多相关文章
- STM32F103ZET6 用定时器级联方式输出特定数目的PWM(转载)
STM32F103ZET6里共有8个定时器,其中高级定时器有TIM1-TIM5.TIM8,共6个.这里需要使用定时器的级联功能,ST的RM0008 REV12的P388和P399页上有说明对于特定的定 ...
- STM32F103ZET6 用定时器级联方式输出特定数目的PWM
STM32F103ZET6 用定时器级联方式输出特定数目的PWM STM32F103ZET6里共有8个定时器,其中高级定时器有TIM1-TIM5.TIM8,共6个. 这里需要使用定时器的级联功能,ST ...
- STM32F103ZET6通用定时器
1.通用定时器简介 通用定时器是由一个可编程预分频器驱动的16位自动装载计数器构成.通用定时器可以应用于多种场合,如测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM).使用通用定时 ...
- STM32F103ZET6通用定时器的输入捕获
1.通用定时器输入捕获功能简介 通用定时器的输入捕获模式可以用来测量脉冲宽度或者测量频率. STM32的每个通用定时器都有4个输入捕获的通道,分别是TIMx_CH1.TIMx_CH2.TIMx_CH3 ...
- STM32F103ZET6系统定时器SysTick
1.系统定时器SysTick的简介 系统定时器SysTick属于内核外设,内嵌在NVIC中.SysTick是一个24位的向下递减的计数器,计数器根据SysTick的时钟源计数,当SysTick的计数器 ...
- 基于霸道秉火的STM32F103ZET6嵌入式开发之------定时器中断3
1 #include "time.h" 2 #include "led.h" 3 #include "beep.h" 4 //¶¨Ê±Æ÷Ö ...
- STM32学习笔记(一)时钟和定时器
由于近期在准备海洋航行器比赛,正好趁此机会学习一下ARM,看到周围很多同学都在使用32,所以我也买了一块STM32F103ZET6,准备好好地学习一下. STM32的时钟系统相当的复杂,包含了5个时钟 ...
- stm32定时器学习二——PWM设置
/* STM32 嵌入式学习入门(5)——PWM的实现 上一篇博文介绍了定时器和PWM的基本的原理,本篇博文从代码层面来介绍PWM的具体实现.同样,还是以博主所用的开发板——正点原子开发板STM32F ...
- STM32F103ZET6 PWM输出
1.通用定时器的PWM功能 STM32F103ZET6有4个通用定时器,分别是TIM2.TIM3.TIM4.TIM5. 通用定时器由一个可编程预分频器驱动的16位自动装载计数器构成. 通用定时器的很多 ...
随机推荐
- 304 Not Modified
304 Not Modified,不是服务器发出的错误,是服务器所承载的业务系统在开发时为了节省带宽和提升浏览器的体验,对GET/js,css,image等执行了缓存机制.客户端第一次对服务器发出GE ...
- JavaScript对象(三)
序列化对象: 对象序列化:对象的状态转化为字符串,也可以将字符串还原为对象.方法:JSON.stringify(),用来序列化,JSON.parse(),用来还原对象. JSON(JavaScript ...
- RAC修改VIP地址
目录 当前环境 1.通过[srvctl config]确认当前VIP地址. 2.关闭dbconsole[对应的em] 3.关闭数据库实例 4.关闭asm实例 5.关闭结点服务 6.修改两个节点的/et ...
- JavaMail(一):利用JavaMail发送简单邮件
JavaMail,提供给开发者处理电子邮件相关的编程接口.它是Sun发布的用来处理email的API.它可以方便地执行一些常用的邮件传输.但它并没有包含在JDK中,要使用JavaMail首先要下载ja ...
- 使用VS2017进行Python代码的编写并打印出九九乘法表
我们来盘一盘怎么使用VS2017进行python代码的编写并打印出九九乘法表. 使用Visual Studio 2017进行Python编程不需要太复杂的工作,只需要vs2017安装好对Python的 ...
- 题解 NOI1999【生日蛋糕】—— 洛谷
自己想出这题的大佬蒟蒻在这儿%您了 我实在是太弱了,搜索这种辣鸡算法都不会(逃 这题真的是想了好久,每次都会T三个点,我以为我的剪枝已经堆了够多了,结果后来才知道是一个关键剪枝没想到OTZ 先贴代码 ...
- JVM 参数及各部分含义(转)
转自:https://www.jianshu.com/p/1c6b5c2e95f9 JVM参数分类 JVM参数分为标准参数和非标准参数: 标准参数: "-"开头的参数,如-clie ...
- 通过xshell远程部署
Xshell 和 Xftp5 操作linux系统的机器 ------------------------------------------------------------------------ ...
- 从零开始学习R语言(三)——数据结构之“矩阵(Matrix)”
本文首发于知乎专栏:https://zhuanlan.zhihu.com/p/60140022 也同步更新于我的个人博客:https://www.nickwu.cn/blog/id=129 3. [二 ...
- 安装SQL Server 2008R2 报错“此计算机上安装了 Microsoft Visual Studio 2008 的早期版本”解决方法
安装SQL Server 2008 R2报错“此计算机上安装了 Microsoft Visual Studio 2008 的早期版本,请在安装 SQL Server 2008 前将 VS2008 升级 ...