1.定时器简介

(1)看门狗定时器(WWDG)
防止跑飞
独立看门狗
在定时期间,进行喂狗
窗口看门狗
在规定窗口期,进行喂狗

(2)Systick定时器
移植操作系统使用

(3)高级定时器(TIM1 & TIM8)
TIM1和TIM8定时器的功能包括:
● 16位向上、向下、向上/下自动装载计数器
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意
数值
● 多达4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
● 死区时间可编程的互补输出
● 使用外部信号控制定时器和定时器互联的同步电路
● 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器
● 刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态
● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
─ 刹车信号输入
● 支持针对定位的增量(正交)编码器和霍尔传感器电路
● 触发输入作为外部时钟或者按周期的电流管理

(4)通用定时器(TIMx)
● 16位向上、向下、向上/向下自动装载计数器 加法还是减法
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意
数值
● 4个独立通道:
─ 输入捕获 用引脚捕获脉冲
─ 输出比较 输出信号上升下降沿比较
─ PWM生成(边缘或中间对齐模式) PWM波
─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步电路
● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器电路 编码器
● 触发输入作为外部时钟或者按周期的电流管理

(5)基本定时器(TIM6 & TIM7)
● 16位自动重装载累加计数器(计数多少)2^16 65535 减法
● 16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频 时钟分频
● 触发DAC的同步电路 叠加功能
● 在更新事件(计数器溢出)时产生中断/DMA请求 中断
高级 >= 通用 >= 基本
往上会增加功能

2.SYSTICK定时器

2.1 作用

(1)专用于产生RTOS的系统滴答时钟
裸机 从开始到结束 就一个主线
操作系统不止有一个主任务和主线
CPU只能跑一个主任务
将任务分段,分段执行不同的任务
微观上的串行(一次只能执行一个任务),宏观上的并行(多个任务同时进行)
这个分段的一段时间就是滴答时钟
(2)可用于裸机程序中短时间精确延时函数 定时器用法
(3)可用作普通定时中断功能 定时器用法

2.2 SYSTICK定时器的数据手册

(1)24位定时器
(2)各个寄存器含义
STK_CTRL:
使能
是否产生中断
时钟源
标志位

2.3 标准库封装

SysTick_CLKSourceConfig
SysTick_Config

  1. static __INLINE uint32_t SysTick_Config(uint32_t ticks)
  2. {
  3. if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
  4. SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
  5. NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
  6. SysTick->VAL = 0; /* Load the SysTick Counter Value */
  7. SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | 时钟源 默认不分频
  8. SysTick_CTRL_TICKINT_Msk | 使能中断
  9. SysTick_CTRL_ENABLE_Msk; 开启 /* Enable SysTick IRQ and SysTick Timer */
  10. return (0); /* Function successful */
  11. }

默认使用AHB时钟,默认会产生中断,中断优先级是最低的,并且末尾开始了计时器
还可以8分频

2种工作模式
(1)中断方式 一般都是用中断方式
(2)查询方式 查询寄存器的值 是否为0

2.4 定时计算

(1)公式:重装载值 = systick时钟频率(Hz) 想要的定时时间(S)
(2)举个栗子(1ms) CNT = 72000000Hz
0.001S = 72000
(3)查询方式和中断方式都是这样计算

2.5 systick控制LED闪烁

image.png
选择PG7 LED

  1. #include "stm32f10x.h"
  2. #include "clock.h"
  3. void RCC_Configuration(void);
  4. void LED_Init(void);
  5. void Systick_Confguration(void);
  6. //选择GPIOG 7
  7. int main(void)
  8. {
  9. RCC_Configuration();
  10. LED_Init();
  11. Set_SysClockTo72M();
  12. Systick_Confguration();
  13. while(1)
  14. {
  15. };
  16. return 0;
  17. }
  18. void RCC_Configuration(void)
  19. {
  20. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
  21. //因为systick属于cpu部分,时钟是时钟打开的
  22. }
  23. void LED_Init(void)
  24. {
  25. GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
  26. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
  27. //配置第6个引脚为输出模式
  28. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  29. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
  30. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  31. GPIO_Init(GPIOG,&GPIO_InitStructure);
  32. GPIO_WriteBit(GPIOG, GPIO_Pin_7, Bit_SET); //默认为亮
  33. }
  34. //开启滴答时钟
  35. void Systick_Confguration(void)
  36. {
  37. // 主频是72MHz,定时时间500ms, ticks = 72000000 * 0.5 = 36000000
  38. SysTick_Config(4500000);
  39. SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
  40. }
  41. it
  42. //中断处理程序
  43. void SysTick_Handler(void)
  44. {
  45. //不需要清除中断 硬件清除
  46. GPIO_WriteBit(GPIOG, GPIO_Pin_7,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOG, GPIO_Pin_7)));
  47. }

2.6 查询方式实现微妙和毫秒级精确延时

查询和中断:
查询方式是阻塞的
中断方式是非阻塞的
主要操作以下的寄存器
SysTick->LOAD
SysTick->VAL
SysTick->CTRL

  1. #include "stm32f10x.h"
  2. #include "clock.h"
  3. void delay_us(unsigned int us);
  4. void delay_ms(unsigned int ms);
  5. void RCC_Configuration(void);
  6. void LED_Init(void);
  7. //选择GPIOG 7
  8. int main(void)
  9. {
  10. RCC_Configuration();
  11. LED_Init();
  12. Set_SysClockTo72M();
  13. while(1)
  14. {
  15. GPIO_WriteBit(GPIOG, GPIO_Pin_7, Bit_SET);
  16. delay_ms(500);
  17. GPIO_WriteBit(GPIOG, GPIO_Pin_7, Bit_RESET);
  18. delay_ms(500);
  19. };
  20. return 0;
  21. }
  22. void RCC_Configuration(void)
  23. {
  24. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
  25. //因为systick属于cpu部分,时钟是时钟打开的
  26. }
  27. void LED_Init(void)
  28. {
  29. GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
  30. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
  31. //配置第6个引脚为输出模式
  32. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  33. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
  34. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  35. GPIO_Init(GPIOG,&GPIO_InitStructure);
  36. }
  37. //systick计数器完成us级别的精确延时
  38. //有两个条件:1.主频必须是72MHZ,
  39. // 2.us要小于1864135
  40. void delay_us(unsigned int us)
  41. {
  42. unsigned int tmp;
  43. // 思路是先把systick的时钟源设置好,然后给一个正确的ticks
  44. // 然后使能systick,while循环等待countflag置位则时间到
  45. 1us 计数 9
  46. SysTick->LOAD = us * 9; //72MHz主频 9MHz
  47. SysTick->VAL = 0;
  48. SysTick->CTRL = 0x01; //时钟源是AHB/8 禁止中断 使能systick
  49. do
  50. {
  51. tmp = SysTick->CTRL;
  52. }while(!((tmp) & (1 << 16))); /判断CTRL寄存器的ENABLECOUNTFLAG的值
  53. //跳出do while循环时间到了,关闭定时器
  54. SysTick->VAL = 0;
  55. SysTick->CTRL = 0;
  56. }
  57. void delay_ms(unsigned int ms)
  58. {
  59. unsigned int tmp;
  60. // 思路是先把systick的时钟源设置好,然后给一个正确的ticks
  61. // 然后使能systick,while循环等待countflag置位则时间到
  62. SysTick->LOAD = ms * 9000; //72MHz主频 9MHz
  63. SysTick->VAL = 0;
  64. SysTick->CTRL = 0x01; //时钟源是AHB/8 禁止中断 使能systick
  65. do
  66. {
  67. tmp = SysTick->CTRL;
  68. }while(!((tmp) & (1 << 16)));
  69. //跳出do while循环时间到了,关闭定时器
  70. SysTick->VAL = 0;
  71. SysTick->CTRL = 0;
  72. }

3.通用定时器

设计的时候最多可以有17个定时器
F103ZET6 有8个
功能差别

学习要点:
(1)先学会定时器基本功能的使用
(2)高级功能用到时再去细看
(3)设计本身的复杂性导致学习难度大,要有耐心
(4)很多书面概念要搞清楚,需要前后不停对照,必要时要记笔记帮助理解
(5)学习三宝:数据手册+外设库源码+例程

TIMX 2、3、4、5

3.1 介绍

● 16位向上、向下、向上/向下自动装载计数器 加法还是减法
向上:从0 -> 最大
向下:从最大->0
向上向下:先从0->最大->0

● 16位可编程(可以实时修改)预分频器 改变定时器时钟基准,计数器时钟频率的分频系数为1~65536之间的任意
数值
TIM2 3 4 在APB1
TIM1 在APB2

● 4个独立通道:
─ 输入捕获 用引脚捕获脉冲
捕获外边脉冲
─ 输出比较 输出信号上升下降沿比较
输出值和某个值比较
─ PWM生成(边缘或中间对齐模式) PWM波
输出比较,产生PWM,驱动蜂鸣器
─ 单脉冲模式输出
就进行一次

● 使用外部信号控制定时器和定时器互连的同步电路
使用外部信号控制定时器
定时器互连的同步电路 两个定时器串联

● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) 定时时间到了,更新标志位
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) 如输入捕获低电平
─ 输入捕获
─ 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器电路 编码器
● 触发输入作为外部时钟或者按周期的电流管理

3.2 框图

转自:
https://zhuanlan.zhihu.com/p/336667027
image.png
10.定时器 - 图3

第一部分是时钟发生器。第二部分为时基单元。第三部分为输入捕获。第四部分为输出比较。第五部分包含了几个捕获/比较寄存器。

讲解如下:
第一部分:首先看到第二部分中psc预分频器需要接收时钟源,而时钟源共有四种来源,分别如下:
1、RCC寄存器中的APB1外设时钟使能寄存器,经过倍频之后输出时钟源,这是因为该寄存器的位0到位5分别表示的是定时器2到定时器7的使能位。(位于图上第一部分)
2、外部触发引脚TIMx_ETR的外部触发输入ETR,对应的引脚可以通过查数据手册得到。ETR经过分频得到ETRP,在经过滤波得到ETRF作为时钟信号。(位于图上第一部分)
3、内部触发输入(ITRx),来自其他的定时器的时钟,即将其他定时器产生的脉冲信号作为该定时器的时钟源,经过后面的选择器,进入到触发控制器。(位于图上第一部分)
4、外部输入引脚Tix,这个主要来自于TIMx_CHx (四个通道)。(位于图上第三部分)
第二部分:为时基单元,包括PSC预分频器、自动重装载寄存器和CNT计数器。首先由第一部分产生时钟源,进入PSC预分频器进行分频处理,得到新的时钟信号CK_CNT,使得CNT计数器加1或者减1,此时在自动重装载寄存器中有一个预先设定的装载值,当计数器的值达到装载值的时候,会产生溢出事件,然后触发中断。
第三部分为输入捕获,TIMx_CH1——TIMx_CH4 这四个通道,在芯片中都有对应的引脚,当脉冲从通道口进入时,经过输入滤波器(抗干扰的作用),然后经过边沿检测器检测到上升沿(下降沿),经过分频器,输入到第五部分中的捕获寄存器中,然后捕获寄存器记录此刻CNT计数器的值,当下一次下降沿(上升沿)过来时,也记录下CNT计数器的值,这样就可以计算出输入脉冲的宽度。
第四部分为输出比较(注意输入捕获和输出比较不可以同时进行),比如在比较寄存器中预先设定一个值,计数器从初始值到装载值之间计数时,当正好等于比较寄存器中的预设值时,控制TIMx_CH1——TIMx_CH4通道输出低电平或者高电平,这样随着计数器不断的计数,就可以获得一个脉冲,通过调整预设值,就可以调整脉冲宽度,调整初始值和装载值就可以调整周期。

注:PSC预分频器
目的是将定时器时钟源分频输出。它的值由TIMx_PSC决定,是一个16位正整数的值。为什么要分频输出呢?
例如当使用内部时钟时,它的频率一般比较高,导致时间体现在定时器上的效果就非常短,而如果我们需要更长的时间间隔,就需要对该时钟源分频处理,以降低定时器时钟(CK_CNT)的频率。
PSC预分频器工作的工作原理:定时器时钟源每tick一次,预分频器计数器值+1,直到达到预分频器的设定值,然后再tick一次后计数器归零,同时,CNT计数器值+1。

3.3 功能描述

3.3.1.时基单元

可编程通用定时器的主要部分是一个16位计数器和与其相关的自动装载寄存器。这个计数器可
以向上计数、向下计数或者向上向下双向计数。此计数器时钟由预分频器分频得到。
计数器、自动装载寄存器和预分频器寄存器可以由软件读写,在计数器运行时仍可以读写。
时基单元包含:
● 计数器寄存器(TIMx_CNT)
● 预分频器寄存器 (TIMx_PSC)
● 自动装载寄存器 (TIMx_ARR)

自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器。根据在
TIMx_CR1寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被立即或在
每次的更新事件UEV时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当
TIMx_CR1寄存器中的UDIS位等于’0’时,产生更新事件。更新事件也可以由软件产生。

分频寄存器PSC,自动重装载ARR,自动捕获CCRx
https://www.amobbs.com/thread-5588068-1-1.html
image.png
影子寄存器:存在,但是不能手动操作,是自动完成的
解释: 立即:意思是更改计数之后,立刻开始新的计数
更新事件:意思是更改计数之后,要完成原来的计数,然后开始新的计数

UDIS位等于’0’时,产生更新事件:即产生中断,而且该中断是可以软件产生的。类似中断软件产生

当预分频器的参数从1变到2时,计数器的时序图
image.png

3.3.2.计数器模式

(1)向上计数模式
从0 加到 给定的数字
每次计数器溢出时可以产生更新事件
设置TIMx_CR1寄存器中的UDIS位,可以禁止更新事件
在UDIS位被清’0’之前,将不产生更新事件。但是在应该产生更新事件时,计数器仍会被清’0’,同时预分频器的计数也被请0(但预分频系数不变)。

细节问题:捕获模式下 目前没有必要
如果设置了TIMx_CR1寄存器中的URS位(选择更新请求) 值1,设置UG位将产生一个更新事件UEV,但硬件不设置UIF标志(即不产生中断或DMA请求);这是为了避免在捕获模式下清除计数器时,同时产生更新和捕获中断。

计数器时序图,内部时钟分频因子为1
image.png
(2)向下计数模式
同理向上
减法中是默认缓存的
(3)中央对齐模式(向上/向下计数)
先向上、然后向下
加载到值-1
内部时钟分频因子为1,TIMx_ARR=0x6
image.png

3.3.4 时钟选择

计数器时钟可由下列时钟源提供:
● 内部时钟(CK_INT) 一般选择
● 外部时钟模式1:外部输入脚(TIx)
● 外部时钟模式2:外部触发输入(ETR)
● 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。 串联两个定时器

主要描述内部时钟源
如果禁止了从模式控制器(TIMx_SMCR寄存器的SMS=000),则CEN 打开定时器、DIR 方向(TIMx_CR1寄存器)
和UG位 软件更新(TIMx_EGR寄存器)是事实上的控制位,并且只能被软件修改(UG位仍被自动清除)。只
要CEN位被写成’1’,预分频器的时钟就由内部时钟CK_INT提供。

3.4标准库

1.结构体

  1. typedef struct
  2. {
  3. uint16_t TIM_Prescaler; /*!< Specifies the prescaler value used to divide the TIM clock.
  4. //设置分频 This parameter can be a number between 0x0000 and 0xFFFF */
  5. uint16_t TIM_CounterMode; /*!< Specifies the counter mode.
  6. //设置模式 向上 向下 This parameter can be a value of @ref TIM_Counter_Mode */
  7. uint16_t TIM_Period; /*!< Specifies the period value to be loaded into the active
  8. //计数值 Auto-Reload Register at the next update event.
  9. This parameter must be a number between 0x0000 and 0xFFFF. */
  10. uint16_t TIM_ClockDivision; /*!< Specifies the clock division.
  11. //时钟分频因子 数字滤波器 This parameter can be a value of @ref TIM_Clock_Division_CKD */
  12. uint8_t TIM_RepetitionCounter; /*!< Specifies the repetition counter value. Each time the RCR downcounter
  13. //高级定时器中的 reaches zero, an update event is generated and counting restarts
  14. from the RCR value (N).
  15. This means in PWM mode that (N+1) corresponds to:
  16. - the number of PWM periods in edge-aligned mode
  17. - the number of half PWM period in center-aligned mode
  18. This parameter must be a number between 0x00 and 0xFF.
  19. @note This parameter is valid only for TIM1 and TIM8. */
  20. } TIM_TimeBaseInitTypeDef;

函数:
TIM_TimeBaseInit 初始化
TIM_Cmd 使能
TIM_ITConfig 中断
TIM_ClearITPendingBit 清除中断
TIM_GetITStatus 判断是否中断

代码:
间隔1s闪烁一次
中断1000次,计数
中断时间是1ms

需要注意的点:
1.TIM_TimeBaseStructure.TIM_Period = 999; 设置的计数要减1
2.TIM_TimeBaseStructure.TIM_Prescaler= 71; 分频系数也要减1
3.这里我们设置的为AHB1,36MHZ,为什么计算是按照72MHZ呢?下面的图给你答案
image.png

  1. #include "stm32f10x.h"
  2. #include "clock.h"
  3. //设置全局变量
  4. __IO unsigned int short timer_count=0;
  5. //声明全局函数
  6. void LED_GPIO_Init(void);
  7. void GENERAL_TIMx_NVIC_Configuration(void);
  8. void GENERAL_TIMx_Configuration(void);
  9. //使用TIM2
  10. //使用LED 为B0
  11. int main(void)
  12. {
  13. //配置时钟到72MHZ
  14. Set_SysClockTo72M();
  15. //初始化LED引脚
  16. LED_GPIO_Init();
  17. //中断初始化
  18. GENERAL_TIMx_NVIC_Configuration();
  19. //时钟初始化
  20. GENERAL_TIMx_Configuration();
  21. while(1)
  22. {
  23. //设置1S LED闪烁
  24. if(timer_count==1000)
  25. {
  26. timer_count=0;
  27. GPIOB->ODR ^= GPIO_Pin_0;
  28. }
  29. };
  30. }
  31. //使用LED 为B0
  32. //初始化GPIO
  33. void LED_GPIO_Init(void)
  34. {
  35. /* 定义IO硬件初始化结构体变量 */
  36. GPIO_InitTypeDef GPIO_InitStructure;
  37. /* 使能(开启)LED1引脚对应IO端口时钟 */
  38. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  39. /* 设定LED1对应引脚IO编号 */
  40. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  41. /* 设定LED1对应引脚IO最大操作速度 :GPIO_Speed_50MHz */
  42. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  43. /* 设定LED1对应引脚IO为输出模式 */
  44. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  45. /* 初始化LED1对应引脚IO */
  46. GPIO_Init(GPIOB, &GPIO_InitStructure);
  47. /* 设置引脚输出为低电平,此时LED1灭 */
  48. GPIO_ResetBits(GPIOB, GPIO_Pin_0);
  49. }
  50. //中断配置
  51. void GENERAL_TIMx_NVIC_Configuration(void)
  52. {
  53. NVIC_InitTypeDef NVIC_InitStructure;
  54. /* 设置中断组为0 */
  55. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  56. /* 设置中断来源 */
  57. NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  58. /* 设置主优先级为 0 */
  59. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  60. /* 设置抢占优先级为3 */
  61. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
  62. /*定时器使能 */
  63. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  64. NVIC_Init(&NVIC_InitStructure);
  65. }
  66. //时钟配置
  67. void GENERAL_TIMx_Configuration(void)
  68. {
  69. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  70. /* 开启TIMx_CLK,x[2,3,4,5],即内部时钟CK_INT=72M */
  71. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  72. /* 通用定时器 TIMx,x[2,3,4,5]中断优先级配置 */
  73. GENERAL_TIMx_NVIC_Configuration();
  74. /* 自动重装载寄存器周的值(计数值) */
  75. TIM_TimeBaseStructure.TIM_Period = 999; //注意要减掉1
  76. /* 累计 TIM_Period个频率后产生一个更新或者中断
  77. 时钟预分频数为71,则驱动计数器的时钟CK_CNT = CK_INT / (71+1)=1M */
  78. TIM_TimeBaseStructure.TIM_Prescaler = 71;
  79. /* 时钟分频因子 ,没有用到,不用管 */
  80. //TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  81. /* 计数器计数模式,基本定时器TIM6和TIM7只能向上计数,没有计数模式的设置,不用管 */
  82. TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
  83. /* 重复计数器的值,通用定时器没有,不用管 */
  84. //TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
  85. /* 初始化定时器TIMx, x[2,3,4,5] */
  86. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  87. /* 清除计数器中断标志位 */
  88. TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
  89. /* 开启计数器中断 */
  90. TIM_ITConfig(TIM2,TIM_IT_Update, ENABLE);
  91. /* 使能计数器: */
  92. TIM_Cmd(TIM2, ENABLE);
  93. }
  94. //配置中断
  95. void TIM2_IRQHandler(void)
  96. {
  97. if ( TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET )
  98. {
  99. timer_count++;
  100. TIM_ClearITPendingBit(TIM2 , TIM_IT_Update);
  101. }
  102. }

3.4 PWM功能

3..4.1. 专用PWM输出的实现原理

(1)所谓的比较原理,涉及3个计数有关的寄存器
CMP:比较
CNT:计数
ARR:初始 =>这个就是初始值
计数值和CMP的值进行比较,若计数>CMP为高电平,否则为低电平

(2)在输入捕获/输出比较功能中

3.4.2 相关的寄存器

(1)TIMxCNT(计数器的值)、TIMxARR(自动装载)、TIMxCCRn(配置比较值)
(2)CCMR1(配置)、CCMR2(配置)、CCER(使能)
(3)CR1(控制)、CR2(控制)、PSC(预分频)

3.4.3 标准库中相关API

(1)TIM_TimeBaseInit
(2)TIM_OCnInit
计数:

  1. typedef struct
  2. {
  3. 1 uint16_t TIM_OCMode; /*!< Specifies the TIM mode.
  4. //模式设置 PWM This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
  5. 2 uint16_t TIM_OutputState; /*!< Specifies the TIM Output Compare state.
  6. //ENablele 输出状态 This parameter can be a value of @ref TIM_Output_Compare_state */
  7. uint16_t TIM_OutputNState; /*!< Specifies the TIM complementary Output Compare state.
  8. //TIM1 and TIM8 This parameter can be a value of @ref TIM_Output_Compare_N_state
  9. @note This parameter is valid only for TIM1 and TIM8. */
  10. 3 uint16_t TIM_Pulse; /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
  11. //用来比较的数值 This parameter can be a number between 0x0000 and 0xFFFF */
  12. uint16_t TIM_OCPolarity; /*!< Specifies the output polarity.
  13. //输出极性 设置比较的时候 输出是0还是1 反向波形 This parameter can be a value of @ref TIM_Output_Compare_Polarity */
  14. uint16_t TIM_OCNPolarity; /*!< Specifies the complementary output polarity.
  15. This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
  16. @note This parameter is valid only for TIM1 and TIM8. */
  17. uint16_t TIM_OCIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state.
  18. This parameter can be a value of @ref TIM_Output_Compare_Idle_State
  19. @note This parameter is valid only for TIM1 and TIM8. */
  20. uint16_t TIM_OCNIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state.
  21. This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
  22. @note This parameter is valid only for TIM1 and TIM8. */
  23. } TIM_OCInitTypeDef;

image.png
(3)TIM_OCnPreloadConfig 立即使能,还是完成一个周期后使能 放入的是比较值
是否在写入的时候立即更新值
(4)TIM_OCnFastConfig
输出比较1快速使能 只能用于PWM
(5)TIM_ClearOC1Ref
(6)TIM_OCnPolarityConfig
极性设置
(7)TIM_ARRPreloadConfig 允许或禁止在定时器工作时向ARR的缓冲器 计数值 TIM_Period中写入新值

TIM_ARRPreloadConfig和TIM_OCnPolarityConfig区别
https://blog.csdn.net/jpaekeo/article/details/64906477

PWM控制LED 呼吸灯
原理:
1、PWM是脉冲宽度调制,宽度——-就是脉冲的高电平的时间。PWM信号调节LED亮度时,信号频率是不变的,改变的是脉冲的高电平的时间,即LED的导通时间。

2.人眼的视觉残留效应。频率大于75HZ(周期小于13ms),人眼的闪烁感消失。当超过闪烁感消失频率,亮度感知等于亮度时间平均值(塔鲁伯法则)。所以周期一定(小于13ms),占空比高,即高电平时间多于低电平,根据塔鲁伯法则,亮度更高。

使用TIMER3 第三通道
image.png
因为我们PB0连接的是LED
所以可以直接输出PWM给LED实现 呼吸灯效果

  1. #include "stm32f10x.h"
  2. #include "clock.h"
  3. /* LED亮度等级 PWM表 */
  4. uint8_t indexWave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,107,143,191,255,
  5. 255,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};
  6. void NVIC_Config_PWM(void);
  7. void GENERAL_TIMx_PWM_Init(void);
  8. void GENERAL_TIMx_GPIO_Config(void);
  9. int main(void)
  10. {
  11. //设置时钟
  12. Set_SysClockTo72M();
  13. GENERAL_TIMx_PWM_Init();
  14. while(1)
  15. {
  16. };
  17. return 0;
  18. }
  19. //使用TIM3 第3通道
  20. void GENERAL_TIMx_GPIO_Config(void)
  21. {
  22. GPIO_InitTypeDef GPIO_InitStructure;
  23. /* 使能定时器始终:设置TIM3CLK 为 72MHZ */
  24. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  25. /* 使能定时器通道引脚GPIO时钟 */
  26. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  27. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
  28. /* 配置定时器通道3输出引脚模式:复用推挽输出模式 */
  29. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  30. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  31. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  32. GPIO_Init(GPIOB, &GPIO_InitStructure);
  33. //测试GPIO
  34. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  35. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  36. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  37. GPIO_Init(GPIOG, &GPIO_InitStructure);
  38. }
  39. //配置嵌套向量中断控制器NVIC
  40. void NVIC_Config_PWM(void)
  41. {
  42. NVIC_InitTypeDef NVIC_InitStructure;
  43. /* 选择中断优先级配置组为2个抢占式优先级和2个子优先级,可以参考misc.h文件了解相关设置 */
  44. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  45. /* 配置TIM3_IRQ中断为中断源 */
  46. NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  47. /* 设置抢占式优先级为0 */
  48. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  49. /* 设置子优先级为3 */
  50. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  51. /* 使能外部中断通道 */
  52. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  53. NVIC_Init(&NVIC_InitStructure);
  54. }
  55. /**
  56. * 函数功能: 配置TIMx输出的PWM信号的模式,如周期、极性、占空比
  57. * 输入参数: 无
  58. * 返 回 值: 无
  59. * 说 明:无
  60. */
  61. /*
  62. * TIMxCLK/CK_PSC --> TIMxCNT --> TIMx_ARR --> TIMxCNT 重新计数
  63. * TIMx_CCR(电平发生变化)
  64. * 信号周期=(TIMx_ARR +1 ) * 时钟周期
  65. * 占空比=TIMx_CCR/(TIMx_ARR +1)
  66. * _______ ______ _____ ____ ___ __ _
  67. * |_| |__| |___| |____| |_____| |______| |_______| |________|
  68. *
  69. */
  70. void GENERAL_TIMx_Configuration(void)
  71. {
  72. //定时器基本配置
  73. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  74. TIM_OCInitTypeDef TIM_OCInitStructure;
  75. /* 定时器基本参数始终 */
  76. /* 当定时器从0计数到255,即为256次,为一个定时周期 */
  77. TIM_TimeBaseStructure.TIM_Period = 255;
  78. /* 设置预分频输出脉冲频率:72MHz/(1999+1)/(255+1) */
  79. TIM_TimeBaseStructure.TIM_Prescaler = 1999;
  80. /* 设置时钟分频系数:不分频 默认72MHZ */
  81. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  82. /* 向上计数模式 */
  83. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  84. TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  85. /* 定时器输出通道1模式配置 */
  86. /* 模式配置:PWM模式1 */
  87. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  88. /* 输出状态设置:使能输出 */
  89. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  90. /* 设置跳变值,当计数器计数到这个值时,电平发生跳变 */
  91. TIM_OCInitStructure.TIM_Pulse = 0;
  92. /* 当定时器计数值小于CCR3_Val时为低电平 */
  93. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  94. /* 初始化定时器通道1输出PWM */
  95. TIM_OC3Init(TIM3, &TIM_OCInitStructure);
  96. /* 定时器比较输出通道1预装载配置:使能预装载 以更改比较*/
  97. TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
  98. /* 使能定时器重载寄存器ARR 立即还是缓冲*/
  99. TIM_ARRPreloadConfig(TIM3, ENABLE);
  100. /* 使能定时器 */
  101. TIM_Cmd(TIM3, ENABLE);
  102. /* 配置NVIC */
  103. NVIC_Config_PWM();
  104. /* 定时器更新中断 */
  105. TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
  106. }
  107. void GENERAL_TIMx_PWM_Init(void)
  108. {
  109. GENERAL_TIMx_GPIO_Config();
  110. GENERAL_TIMx_Configuration();
  111. }
  112. //中断 IT
  113. void TIM3_IRQHandler(void)
  114. {
  115. static uint8_t pwm_index = 0; /* 用于PWM查表 */
  116. static uint8_t period_cnt = 0; /* 用于计算周期数 */
  117. /* 定时器更新中断 */
  118. if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
  119. {
  120. period_cnt++;
  121. /* 若输出的周期数大于20,输出下一种脉冲宽的PWM波 */
  122. if(period_cnt >= 20)
  123. {
  124. /* 根据PWM表修改定时器的比较寄存器值 */
  125. TIM3->CCR3 = indexWave[pwm_index];
  126. /* 标志PWM表的下一个元素 */
  127. pwm_index++;
  128. /* 若PWM脉冲表已经输出完成一遍,重置PWM查表标志 */
  129. if( pwm_index >= 40)
  130. {
  131. //测试GPIOG->ODR ^= GPIO_Pin_6;
  132. pwm_index=0;
  133. }
  134. /* 重置周期计数标志 */
  135. period_cnt=0;
  136. }
  137. /* 必须要清除中断标志位 */
  138. TIM_ClearITPendingBit (TIM3, TIM_IT_Update);
  139. }
  140. }