定时器及PWM
1 定时器
1.1 定时器分类
对于STM32来说,定时器可分为基本定时器、通用定时器、高级定时器三类,后者包括前者的全部功能。以stm32f1系列为例,TIM6和TIM7为基本定时器,TIM2~TIM5为通用定时器,TIM和TIM8为高级控制定时器。
基本定时器(TIM6/TIM7)【精简型】
● 16位自动重装载累加计数器 
● 16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频 
● 触发DAC的同步电路,TIM6/7独有功能
● 在更新事件(计数器溢出)时产生中断/DMA请求
通用定时器(TIM2、TIM3、TIM4和TIM5)【通用型】
● 16位向上、向下、向上/向下自动装载计数器 
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值 
● 4个独立通道: 
─ 输入捕获 
─ 输出比较 
─ PWM生成(边缘或中间对齐模式) 
─ 单脉冲模式输出 
● 使用外部信号控制定时器和定时器互连的同步电路 
● 如下事件发生时产生中断/DMA: 
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) 
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) 
─ 输入捕获 
─ 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器电路 
● 触发输入作为外部时钟或者按周期的电流管理
高级定时器(TIM1和TIM8)【增强型】
● 16位向上、向下、向上/下自动装载计数器
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值
● 4个独立通道:
─ 输入捕获
 ─ 输出比较
 ─ PWM生成(边缘或中间对齐模式) 
─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互联的同步电路 
● 如下事件发生时产生中断/DMA: 
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) 
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) 
─ 输入捕获 
─ 输出比较
─ 刹车信号输入 
● 支持针对定位的增量(正交)编码器和霍尔传感器电路 
● 触发输入作为外部时钟或者按周期的电流管理
● 死区时间可编程的互补输出
● 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器
● 刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态
通用定时器挂载在APB1总线,高级定时器挂载在APB2总线。
1.2 计数模式
(1)向上计数模式
计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器向上溢出时间,每次溢出时可以产生更新事件。
(2)向下计数模式
计数器从自动加载值(TIMx_ARR计数器的内容)向下计数到0,然后从自动装载值重新开始并且产生一个计数器向下溢出时间,每次溢出时可以产生更新事件。
(3)中央对齐模式(向上/向下计数)
计数器从0开始计数到自动加载值(TIMx_ARR寄存器)-1,产生一个计数器向上溢出事件,最后向下计数到1并产生一个计数器向下溢出时间,最后再从0开始重新计数。
1.3 相关结构及函数
本节基于stm32f1系列基本定时器进行相关讲解,如下图所示为基本定时器结构框图

查阅参考手册RCC章节的时钟树可以知道,RCC的定时器时钟TIMxCLK,即内部时钟CK_INT是由APB1预分频器分频后提供。如下图所示,如果APB1预分频系数为1,则频率不变,否则频率为2倍。即此时用于分频的APB1的预分频系数为2,所以TIMxCLK = 36 * 2 = 72MHz。

看第一个圆圈内容,APB1的时钟,最大是36M,由分频系数决定,当分频系数是2的时候,APB1的时钟就是36MHz。
看第二个圆圈内容,当APB1的分频系数不为1的时候,TIMXCLK的时钟就是APB1的时钟乘以2,所以TIM2的时钟就是72MHz了。为什么可以乘以2?答:手册上就是这么说的,至于为什么,你得去问STM32芯片厂商的IC工程师了。
system_stm32f10x.c文件的SetSysClockTo72()函数,默认就是配置APB1位2分频,如下图所示:

(1)TIM_TimeBaseInitTypeDef结构体

TIM_Prescaler:指定定时器预分频器数值,由TIMx_PSC寄存器配置,可设置范围为0x0000~0xFFFF,即0~65535;
TIM_CounterMode:计数模式,可分为向上计数、向下计数以及三种中心对齐模式。而基本定时器只能向上计数;
TIM_Period:计数器周期,即自动重装载寄存器TIMx_ARR的值,在事件生成时更新到影子寄存器,由TIMx_CR1寄存器的ARPE位配置是否使能缓冲;
TIM_ClockDivision:时钟分频,配置定时器时钟CK_INT频率与数字滤波器采样时钟频率分频比,基本定时器没有这个功能,不用设置;
TIM_RepetitionCounter:重复计数器,属于高级控制寄存器专用寄存器位,利用它可以很容易控制输出PWM个数,这里不用设置。
计一个数的时间是1/CK_CNT,产生一次中断的时间为(ARR+1)/CK_CNT。如果在中断服务程序里设置一个变量time用于记录中断次数,则time定时时间为:(ARR+1)/CK_CNT*time。
(2)定时1s实验
例如,需要做一个1s的定时,CK_PSC=72MHz,则PSC=71,那么CK_CNT=1MHz,
计一个数时间:1/CK_CNT = 1/1MHz = 1us,
中断一次的时间:(ARR+1)/CK_CNT = (999+1)/1MHz = 1ms,
则定时时间:(ARR+1)/CK_CNT*time = 1ms*1000 = 1s
初始化TIM_TimeBaseInitTypeDef

1 /**
2 * @brief 基本定时器配置
3 * @param 无
4 * @retval 无
5 */
6 static void BASIC_TIM_Mode_Config(void)
7 {
8 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
9
10 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); // 内部时钟72MHz
11
12 TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载寄存器的值
13 TIM_TimeBaseStructure.TIM_Prescaler= 71; // 预分频器数值
14 TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
15
16 TIM_ClearFlag(TIM6, TIM_FLAG_Update); // 清除计数器中断标志位
17 TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
18
19 TIM_Cmd(TIM6, ENABLE);
20 }
基本定时器配置
中断优先级配置

/**
* @brief 中断优先级配置
* @param 无
* @retval 无
*/
static void BASIC_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
中断优先级配置
中断函数

1 extern volatile uint32_t time; // 该变量定义在main()函数里
2
3 void TIM6_IRQHandler(void)
4 {
5 if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
6 {
7 time++; // 每中断一次,time值加1,中断一次时间为1ms,需要中断1000次才可定时1s,即time值为1000
8 TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);
9 }
10 }
中断函数
在main()函数里调用led和定时器的初始化配置函数,在一个循环里判断time变量的值是否为1000,如果已经达到1000,则led灯状态变化(亮或灭)一次,并且time变量值重赋为0,以便继续判断及定时。
2 PWM
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
2.1 PWM工作过程
将寄存器值和计数器值比较,通过比较结果输出高低电平,实现PWM信号

如图为向上计数:
     定时器重装载值为ARR,比较值CCRx
     t时刻对计数器值和比较值进行比较
     如果计数器值小于CCRx值,输出低电平
     如果计数器值大于CCRx值,输出高电平
PWM的一个周期
    定时器从0开始向上计数
0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平
    t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平
    当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数...循环此过程
    至此一个PWM周期完成
影响因素
    ARR:决定PWM周期(在时钟频率一定的情况下,当前为默认内部时钟CK_INT)
    CCRx:决定PWM占空比(高低电平所占整个周期比例)

1 TIMx_CCMR1寄存器的OC1M[2:0]位,设置输出模式控制器
110:TIM_OCMode_PWM1(向上计数时,CNT<CCR为有效电平,oc1ref=1,否则为无效电平;向下计数时,CNT>CCR为无效电平,oc1ref=1,否则为有效电平)
    111:TIM_OCMode_PWM2(向上计数时,CNT<CCR为无效电平,oc1ref=1,否则为有效电平;向下计数时,CNT>CCR为有效电平,oc1ref=1,否则为无效电平)
两种模式的有效无效正好相反。
 
2 计数器值TIMx_CNT与通道1捕获比较寄存器CCR1进行比较,通过比较结果输出有效电平和无效电平
    OC1REF=0 无效电平
    OC1REF=1 有效电平
 
3 通过输出模式控制器产生的信号
TIMx_CCER寄存器的CC1P位,设置输入/捕获通道1输出极性
    0:高电平有效
    1:低电平有效
 
4 TIMx_CCER:CC1E位控制输出使能电路,信号由此输出到对应引脚
    0:关闭
    1:开启
计数器值TIMx_CNT与捕获比较寄存器值CCRx比较后,由TIMx_CCMR1:OC1M位和TIMx_CCER:CC1P位共同决定最终的输出结果。
下图中,通过配置TIMx_CCMR1可以配置相应通道为输入(捕获模式)还是输出(比较模式),OCxx描述了输出模式下的含义,ICxx描述了输入模式下的含义。

通过设置模式1或模式2,决定了比较结果输出为有效电平(OC1REF=1高电平)或无效电平(OC1REF=0低电平)

CC1P设置输入/捕获1极性,确定最终输出为高电平还是低电平,CC1P=0,则在OC1REF为高电平时输出高电平,CC1P=1,则在OC1REF为低电平时输出高电平,从图2中可以看出在CC1P=1时会有一个反相器,将OC1REF输入进行反相。
CC1E设置输入/捕获1输出使能,0:关闭-OC1禁止输出;1:开启-OC1信号输出到对应的输出引脚。
2.2 PWM相关库函数
在本人使用的板子上TIM3_CH1对应的GPIO是PB4,以此为例进行说明,以下为PWM相关的主要函数
1 使能定时器3和相关IO时钟(LED-PB4)
    使能定时器3时钟:RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    使能GPIOB时钟:RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
 
2 初始化IO口为复用功能输出GPIO_Init();
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
3 PB4输出PWM(定时器3通道1),需要对PB4进行映射
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);
 
4 初始化定时器 (重装载值ARR,与分频系数PSC等)
PrescalerValue = (uint16_t) (SystemCoreClock  / 1000000) - 1; // 100Mhz->1Mhz
TIM_TimeBaseStructure.TIM_Period = 100-1;
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  TIM_PrescalerConfig(TIM3, PrescalerValue, TIM_PSCReloadMode_Immediate);
5 初始化输出比较参数:
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
6 使能预装载寄存器
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
 
7 使能定时器
TIM_Cmd(TIM3, ENABLE);
8 不断改变比较值CCRx,达到不同的占空比效果
TIM_SetCompare1(TIM3, pule);
在main函数中实现如下
while (1)
  {
  Delay(10);
  if(i)
    pule++;
  else 
    pule--;
  if(pule==0)
    i=1;
  if(pule>100)
    i=0;
  TIM_SetCompare1(TIM3, pule);
}
定时器及PWM的更多相关文章
- 关于普通定时器与高级定时器的  PWM输出的初始化的区别
		
不管是普通定时器还是高级定时器,你用哪个通道,就在程序里用OC多少.比如CH3对应OC3 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_ ...
 - STM32F103定时器输出PWM波控制直流电机
		
这个暑假没有回家,在学校准备九月份的电子设计竞赛.今天想给大家分享一下STM32高级定时器输出PWM波驱动直流电机的问题.. 要想用定时器输出的PWM控制直流电机,,首先要理解“通道”的概念..一个定 ...
 - STM32定时器输出PWM频率和步进电机控制速度计算
		
1.STM32F4系列定时器输出PWM频率计算 第一步,了解定时器的时钟多少: 我们知道AHP总线是168Mhz的频率,而APB1和APB2都是挂在AHP总线上的. (1)高级定时器timer1, t ...
 - stm32cube--通用定时器--产生pwm波
		
看了通用定时器的资料,发现内容挺多,挺难看懂,现在还是先掌握使用方法,以后再多看几遍吧. ① ② ③生成mdk工程后,在main.c的while(1)前面加上HAL_TIM_PWM_Start(&am ...
 - STM32 基于定时器的PWM发生器
		
脉冲宽度调制(PWM),是英文"Pulse Width Modulation" 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术.简单一点,就 ...
 - STM32 HAL库学习系列第4篇 定时器TIM----- 开始定时器与PWM输出配置
		
基本流程: 1.配置定时器 2.开启定时器 3.动态改变pwm输出,改变值 HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); 函数总结: __HAL_TIM ...
 - AVR 定时器快速PWM模式使用
		
PWM很常用,AVR自带内部PWM功能,分为快速PWM模式和相位修正PWM模式. 我们这里选择方式15 ,由OCR1A保存上限值,由OCR1B保存匹配值,所以输出管脚 OCR1A不能输PWM,只能 ...
 - 案例 高级定时器和通用定时器产生pwm的区别 gd32和stm32
 - (五)转载:通用定时器PWM输出
		
1. TIMER输出PWM基本概念 脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有 ...
 
随机推荐
- 有赞Android实习五面都挂了,复习半月再战,转拿腾讯offer!
			
缘起 为了有赞的面试准备了半个月的样子,当时还投了美团.字节.滴滴.京东,目的只有一个,就是要进大厂,但是只有有赞扛过了一面,其他都是一面就挂了. 前三面都自我感觉良好,以为能稳拿offer的,没想到 ...
 - “入职一年,那个被高薪挖来的Android开发被劝退了。”
			
其实,在很多小伙伴的想法中,是希望通过跳槽实现薪酬涨幅,可是跳槽不是冲动后决定,应该谨慎啊~ 01 我的学弟,最近向我吐槽,2020 年上半年入职一家公司,当时是高薪挖走的他,所谓钱到位,工作也是充满 ...
 - 并发编程——synchronized关键字的使用
			
前言 我们一般对共享数据操作的时候,为了达到线程安全我们会使用synchronized关键字去修饰方法或者代码块.那么今天我们就来讲一讲synchronized关键字的使用. 专栏推荐: 并发编程专栏 ...
 - CSS 奇思妙想 | 使用 resize 实现强大的图片拖拽切换预览功能
			
本文将介绍一个非常有意思的功能,使用纯 CSS 利用 resize 实现强大的图片切换预览功能.类似于这样: 思路 首先,要实现这样一个效果如果不要求可以拖拽,其实有非常多的办法. 将两张图片叠加在一 ...
 - 一次关于shiro反序列化漏洞的思考
			
0x01前言 之前在我反序列化的那篇文章中(https://www.cnblogs.com/lcxblogs/p/13539535.html),简单说了一下反序列化漏洞,也提了一嘴常见的几种Java框 ...
 - NOIP 模拟 $13\; \text{卡常题}$
			
题解 一道环套树的最小点覆盖题目,所谓环套树就是有在 \(n\) 个点 \(n\) 条边的无向联通图中存在一个环 我们可以发现其去掉一条环上的边后就是一棵树 那么对于此题,我们把所有 \(x\) 方点 ...
 - 查看linux系统是物理机还是虚拟机
			
物理机,返回机器型号 [root@laocalhost ~]# dmidecode -s system-product-name S910-X31E 虚拟机 [root@dev01-188 ~]# d ...
 - 配置之XML--读取XML文件 转存为Key-Value
			
将XML文件读取 绑定数据至Dictionary Eg: Xml文件 <?xml version="1.0" encoding="utf-8" ?> ...
 - 基于WindowsService的WebSocket编程Demo
			
一.什么是WebSocket WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工(full-duplex)通信--允许服务器主动发送信息给客户端.说了半天也就是说有了它 ...
 - 【java虚拟机】类加载机制
			
作者:平凡希 原文地址:https://www.cnblogs.com/xiaoxi/p/6959615.html 一.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中, ...