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. 自学vue第二天,从入门到放弃(生命周期的理解)

    生命周期的理解 beforeCreate 创建前 数据还没有监听,没有绑定到vue对象实例,同时也没有挂载对象 没有数据也没有方法 created 在创建后 数据和方法已经 data数据已经绑定好了 ...

  2. Android NDK 直播推流与引流

    本篇介绍一下直播技术中推流与引流的简单实现. 1.流媒体服务器测试 首先利用快直播 app (其他支持 RTMP 推流与引流的 app 亦可)和 ffplay.exe 对流媒体服务器进行测试. 快直播 ...

  3. 代码重构与单元测试——使用“以查询取代临时变量”再次对Statement()方法进行重构(七)

    代码重构与单元测试(一) 代码重构与单元测试--测试项目(二) 代码重构与单元测试--"提取方法"重构(三) 代码重构与单元测试--重构1的单元测试(四) 代码重构与单元测试--对 ...

  4. MongoDB-02-复制集

    复制集(ReplicationSet) 基本原理 基本构成是1主2从的结构,自带互相监控投票机制(Raft(MongoDB) Paxos(mysql MGR 用的是变种)) 如果发生主库宕机,复制集内 ...

  5. CTF-favorite_number

    想到自己越来越菜就越发兴奋 这是一道CTF,PHP审计的一道: 其实我也知道,兄弟们看见也都头皮有点凉,我就直接看wp了. 也不是没提示,php5.5.9,和全等数组. PHP数组的key溢出问题 其 ...

  6. noip模拟6(T2更新

    由于蒟弱目前还没调出T1和T2,所以先写T3和T4.(T1T2更完辣! update in 6.12 07:19 T3 大佬 题目描述: 他发现katarina大佬真是太强了,于是就学习了一下kata ...

  7. 【阿菜Writeup】Security Innovation Smart Contract CTF

    赛题地址:https://blockchain-ctf.securityinnovation.com/#/dashboard Donation 源码解析 我们只需要用外部账户调用 withdrawDo ...

  8. C#的6种常用集合类

    一.先来说说数组的不足(也可以说集合与数组的区别): 1.数组是固定大小的,不能伸缩.虽然System.Array.Resize这个泛型方法可以重置数组大小,但是该方法是重新创建新设置大小的数组,用的 ...

  9. 防止XSS 攻击集成springboot

    1.配置相关数据 在配置文件中配置 # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice/* # 匹 ...

  10. LeetCoded第2题题解--两数相加

    2.两数相加 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表 ...