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的更多相关文章

  1. 关于普通定时器与高级定时器的 PWM输出的初始化的区别

    不管是普通定时器还是高级定时器,你用哪个通道,就在程序里用OC多少.比如CH3对应OC3 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  TIM_ ...

  2. STM32F103定时器输出PWM波控制直流电机

    这个暑假没有回家,在学校准备九月份的电子设计竞赛.今天想给大家分享一下STM32高级定时器输出PWM波驱动直流电机的问题.. 要想用定时器输出的PWM控制直流电机,,首先要理解“通道”的概念..一个定 ...

  3. STM32定时器输出PWM频率和步进电机控制速度计算

    1.STM32F4系列定时器输出PWM频率计算 第一步,了解定时器的时钟多少: 我们知道AHP总线是168Mhz的频率,而APB1和APB2都是挂在AHP总线上的. (1)高级定时器timer1, t ...

  4. stm32cube--通用定时器--产生pwm波

    看了通用定时器的资料,发现内容挺多,挺难看懂,现在还是先掌握使用方法,以后再多看几遍吧. ① ② ③生成mdk工程后,在main.c的while(1)前面加上HAL_TIM_PWM_Start(&am ...

  5. STM32 基于定时器的PWM发生器

    脉冲宽度调制(PWM),是英文"Pulse Width Modulation" 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术.简单一点,就 ...

  6. STM32 HAL库学习系列第4篇 定时器TIM----- 开始定时器与PWM输出配置

    基本流程: 1.配置定时器 2.开启定时器 3.动态改变pwm输出,改变值  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); 函数总结: __HAL_TIM ...

  7. AVR 定时器快速PWM模式使用

    PWM很常用,AVR自带内部PWM功能,分为快速PWM模式和相位修正PWM模式.   我们这里选择方式15 ,由OCR1A保存上限值,由OCR1B保存匹配值,所以输出管脚 OCR1A不能输PWM,只能 ...

  8. 案例 高级定时器和通用定时器产生pwm的区别 gd32和stm32

  9. (五)转载:通用定时器PWM输出

    1.     TIMER输出PWM基本概念 脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有 ...

随机推荐

  1. 清晰易懂的RxJava入门实践

    导入 我相信大家肯定对ReactiveX 和 RxJava 都不陌生,因为现在只要是和技术相关的网站,博客都会随处见到介绍ReactiveX和RxJava的文章. ReactiveX Reactive ...

  2. TestNG注释@BeforeGroups与@AfterGroups不执行的处理

    在学习TestNG框架注解时发现在执行以下的代码 package com.groups; import org.testng.annotations.AfterGroups; import org.t ...

  3. 算法竞赛中的常用JAVA API:PriorityQueue(优先队列)(转载)

    算法竞赛中的常用JAVA API:PriorityQueue(优先队列) PriorityQueue 翻译过来就是优先队列,本质是一个堆, 默认情况下堆顶每次都保留最小值,每插入一个元素,仍动态维护堆 ...

  4. JavaSE-常用类

    JavaSE-常用类 Obeject Java Object类是所有类的父类,也就是说Java的所有类都继承了Object,子类可以使用Object的所有方法. Object类位于java.lang包 ...

  5. MATLAB—数组运算及数组化编程

    文章目录 前言 一.数组的结构和创建 1.数组及其结构 2.行数组的创建 3.对数组构造的操作 二.数组元素编址及寻访 1.数组元素的编址 2.二维数组元素的寻访 三.数组运算 非数的问题 前言 编程 ...

  6. 一文搞懂B树、B-树、B+树

    前言 B树和B-树是同一种数据结构,如果不清楚的话,会被面试官忽悠,所以本文介绍两种数据结构,B树和B+树,废话不多数咱们开干. B树 介绍 在计算机科学中,B树是一种自平衡的树,能够保持数据有序.这 ...

  7. Java Web JSTL实现登陆页面重定向 jstl标签

    <form action="js/loginResult.jsp" class="loginForm" id="loginForm" ...

  8. redis中使用SCAN代替KEYS

    前言 由于redis的keys命令是线上禁用,所以就有了SCAN.SSCAN.HSCAN和ZSCAN四个命令. 但是这四个命令也不是每次返回全部匹配结果,因此需要一遍遍执行下去,而且每次返回的curs ...

  9. SpringCloud之Config

    1.背景 在前的学习中,我们几乎解决了springCloud的所有常规应用,但是大家有没有想过这样一个问题: 是使用微服务后,有非常多的application.yml文件,每个模块都有一个,实际开发中 ...

  10. mzy对于枚举的理解

    关于enum,其实就是简化了的class,功能就是提供一个个独立的.特定含义的常量! 在JDK5.0之前我们想模拟enum的功能,只能使用自定义类的形式: 1.首先私有化构造方法,让外部不能new对象 ...