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. Android开发音视频方向学习路线及资源分享,学完还怕什么互联网寒冬?

    接触Android音视频这一块已经有一段时间了,跟普通的应用层开发相比,的确更花费精力.期间为了学习音视频的录制,编码,处理也看过大大小小的几十个项目.总体感觉就是知识比较零散,对刚入门的朋友比较不友 ...

  2. Git-06-远程仓库

    本地仓库推送到远程仓库 1 创建ssh key 用户主目录下运行如下命令,然后一路回车 ssh-keygen -t rsa -C "1029612787@qq.com" 2 找到公 ...

  3. LeetCode通关:栈和队列六连,匹配问题有绝招

    刷题路线参考: https://github.com/chefyuan/algorithm-base https://github.com/youngyangyang04/leetcode-maste ...

  4. ECC(Ellipse Curve Cryptography)+AES(Advanced Encryption Standard)前端通讯加密模拟(使用eccrypto-js)

    前置知识 不了解对称加密与非对称加密的小伙伴可以看看下面的文章,想详细学习与区块链有关的加密算法可以戳这里 对称与非对称加密 https://blog.csdn.net/u013320868/arti ...

  5. VLAN-4 单臂路由实现vlan之间路由

    一.实验拓扑图 二.实验编址 三.实验步骤 1.给对应的PC设置对应的IP和掩码还有接口,以及根据需要划分不同的vlan区域,再用文本标记出不同部门. 2. 启动设备(全选) 3.创建vlan并配置a ...

  6. kubernetes/k8s CNI分析-容器网络接口分析

    关联博客:kubernetes/k8s CSI分析-容器存储接口分析 kubernetes/k8s CRI分析-容器运行时接口分析 概述 kubernetes的设计初衷是支持可插拔架构,从而利于扩展k ...

  7. cpu设计实践1

    本栏目将实现一个简单cpu(8-32位)的设计,使用xinlink spatan6平台

  8. websocket在慕课网中的应用

    网上资料都是介绍概念,我们来看看实际网站怎么使用websocket的吧.限于自身水平解读并不深入,慕课网上的websocket某些字段不知何用. 是什么 是一种应用层协议,有html5而推出,是一种全 ...

  9. redis缓存穿透,缓存击穿,缓存雪崩

    缓存穿透 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有.这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询).这样请求就会绕过 ...

  10. docker配置cdn-容器内可以通过域名访问

    添加docker的cdn配置 # 没有这个文件创建 vim /etc/docker/daemon.json 添加内容如下 { "dns":["8.8.8.8", ...