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位自动装载计数器构成. 通用定时器的很多 ...
随机推荐
- 前端Tips#6 - 在 async iterator 上使用 for-await-of 语法糖
视频讲解 前往原文 前端Tips 专栏#6,点击观看 文字讲解 本期主要是讲解如何使用 for-await-of 语法糖进行异步操作迭代,让组织异步操作的代码更加简洁易读. 1.场景简述 以下代码中的 ...
- 7种你应该知道的JavaScript常见的错误
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://blog.bitsrc.io/types-of-native-errors-in- ...
- .Net Core 依赖注入手记
.Net Core自身提供了一套简单的DI框架,能满足我们DI基本的需求.它依赖以下组件,需要从Nuget包下拉取. Microsoft.Extensions.DependencyInjection. ...
- LinkedHashMap源码解读
1. 前言 还是从面试中来,到面试中去.面试官在面试 Redis 的时候经常会问到,Redis 的 LRU 是如何实现的?如果让你实现 LRU 算法,你会怎么实现呢?除了用现有的结构 LinkedHa ...
- 如何从普通程序员晋升为架构师 面向过程编程OP和面向编程OO
引言 计算机科学是一门应用科学,它的知识体系是典型的倒三角结构,所用的基础知识并不多,只是随着应用领域和方向的不同,产生了很多的分支,所以说编程并不是一件很困难的事情,一个高中生经过特定的训练就可以做 ...
- Markdown中插入复杂的合并表格方法
由于Markdown自身的语法限制,不能直接插入有合并单元格的复杂表格. 姓名 学号 专业 张三 2018123456 计算机 赵四 2018222356 自动化 李六 2018666666 信息工程 ...
- django 验证码图片生成视图函数
def verify_code(request): import random # 定义验证码图片背景颜色 宽和高 bgcolor = (random.randrange(20,180),random ...
- MATLAB神经网络(5) 基于BP_Adaboost的强分类器设计——公司财务预警建模
5.1 案例背景 5.1.1 BP_Adaboost模型 Adaboost算法的思想是合并多个“弱”分类器的输出以产生有效分类.其主要步骤为:首先给出弱学习算法和样本空间($X$,$Y$),从样本空间 ...
- CAS无锁模式
一.java内存模型:JMM 在内存模型当中定义一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一个块工作内存,工作内存当中主内存数据的副本当更新数据时,会 ...
- 微信小程序校历组件
微信小程序校历组件 校历组件,可以作为校园小程序的插件,如果觉得不错,点个star吧