STM32 定时器介绍,stm32介绍
终极管理员 知识笔记 57阅读
STM32F103系列单片机定时器主要有系统定时器SysTick2个高级定时器TIM1和TIM84个通用定时器TIM2/3/4/52个基本定时器TIM6和TIM7。下面先简单介绍一下
基本定时器基本定时器只能计时不能产生中断。它们适合于需要简单计时的应用程序。

通用定时器通用定时器可以配置为计时器或者PWM发生器并且可以产生中断。它们可以用于控制电机、LED灯等。
高级定时器高级定时器有更多的功能如输入捕获、输出比较和PWM发生器。它们适用于需要高精度计时或PWM控制的应用程序。

看门狗定时器看门狗定时器可以监视系统是否正常工作并在系统停止响应时重置系统。它们可以确保系统的稳定性和可靠性。
SysTick定时器可以作为系统节拍计时器可以用于实现延时函数。
在我们基础的开发中对于SysTick定时器、基本定时器、通用定时器、高级定时器的使用较多因此我们展开详细描述。其中通用定时器的功能在高级定时器中都存在不做赘述。所有案例程序的源码附在文末直接下载就可使用。
目录
SysTick定时器
一、结构图与寄存器
二、SysTick定时时间计算
三、Systick-系统定时器配置程序
如何更改systick中断优先级
四、实验设计
1 通过查询标志位来写延时函数
2通过使能中断来写延时函数
基本定时器
一、功能框图
(1) 时钟源
(2) 控制器
(3) 时基单元
(4) 影子寄存器
二、定时时间的计算
三、时基初始化结构体
四、实验设计
(1) 配置时基初始化结构体
(2) 开启定时器更新中断即定时时间到了
(3) 编写main函数
高级定时器
一、功能框图
(1) 时钟源
(2) 时基单元
(3) 输入捕获
(4) 输出比较
二、输入捕获的应用
三、输出比较的应用
四、初始化结构体
(1) 时基初始化结构体
(2) 输出比较结构体
(3) PWM信号 周期和占空比的计算--以通用定时器为例
(4) 输入捕获结构体
(5) 断路和死区初始化结构体
五、程序设计
(1) 高级定时器定时功能设计
(2) 通用定时器PWM输出功能设计
文章案例程序源码下载
一、SysTick定时器的延时函数毫秒、微秒源码
二、基本定时器LED反转设计源码
三、高级定时器定时功能设计源码
四、通用定时器PWM输出功能设计源码
SysTick定时器
SysTick定时器简介
SysTick定时器即系统定时器24位只能递减存在于内核嵌套在NVIC中。
所有的Cortex-M内核的单片机都具有这个定时器。通过系统定时器我们可以实现精准的软件延时毫秒、微秒级。
一、结构图与寄存器重装载寄存器存放初始值 STK_CLK时钟
counter在时钟的驱动下从reload初值开始往下递减计数到0这样为一个循环产生中断和置位COUNTFLAG标志。然后又从reload值开始重新递减计数如此循环。
其相关寄存器在M3编程手册里面
1.STK_CTRL寄存器控制及状态寄存器第2位AHB指的是高速总线时钟—HCLK为72M这一位置0时钟就是9M这一位置1时钟就是72M。第1位置0不产生中断置1产生中断。
2. STK_LOAD寄存器重装载数值寄存器存放一个24位的初始值。
3. STK_VAL寄存器当前数值寄存器。
二、SysTick定时时间计算首先我们要确定3个变量。
1-t一个计数循环的时间从初始值递减到0所用的时间跟reload存放初始值的寄存器和CLK有关
2-CLK72M或者9M由STK_CTRL寄存器配置
3-RELOAD初始值24位用户自己配置
t reload * ( 1/clk )
Clk 72M时t (72) *(1/ 72 M ) 1US
Clk 72M时t (72000) *(1/ 72 M ) 1MS
三、Systick-系统定时器配置程序SysTick寄存器结构体在固件库文件core_cm3.h中定义
SysTick配置库函数在固件库文件core_cm3.h中定义
SysTick配置中断优先级在固件库文件core_cm3.h中定义
SysTick配置的过程此程序已经写好
步骤如下
1.判断自己规定的初始值是否超过了最大值。
2.初始化reload寄存器的值确定初始值。
3.配置systick的中断优先级。
static __INLINE uint32_t SysTick_Config(uint32_t ticks){ if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ /*ticks自己规定的初始值 SysTick_LOAD_RELOAD_Msk2的24次方 初始值超过了最大值肯定是不行的*//*初始化reload寄存器的值*/ SysTick->LOAD (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ /*配置systick的中断优先级*/NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */ /*读取当前计数的值计数器值清0*/SysTick->VAL 0; /* Load the SysTick Counter Value *//*配置systick的时钟为AHB时钟使能systick的中断使能systick*/ SysTick->CTRL SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ return (0); /* Function successful */}
systick中断优先级配置的是scb->shprx寄存器内核的而外设的中断优先级配置的是nvic->iprx有优先级分组有抢占优先级和子优先级的说法。
如何更改systick中断优先级我们看一下中断优先级的第二个形参;__NVIC_PRIO_BITS官网给的备注是STM32使用4位作为优先级所以这个形参的官网给的值是4。在这个函数中1左移4位就是1616-115。总共有4个位来表示优先级15就是“1111”也就是说systick中断这个优先级永远是最低的。
比较内核和外设的中断优先级自己先确定好外设的NVIC分组systick就套用相同的分组方法。内核外设的中断优先级的四个位按照外设的中断优先级来分组来解析即人为的分出抢占优先级和子优先级。例如前面这个15解析成4个位就为1111。如果NVIC分组选择的是组1即第一个1代表着主优先级后面三个1代表着子优先级。这个时候如果你想提高systick中断的优先级的话就更改主/子优先级的大小比外设的主/子优先级高就行也就是上面4位2进制的数变低。
所以我们只需要更改“__NVIC_PRIO_BITS”这个变量的大小即可。
4.读取当前计数的值计数器值清0VAL寄存器。
5.配置systick的时钟为AHB时钟使能systick的中断使能systick。
四、实验设计1-编写一个微秒延时函数 2-编写一个毫秒延时函数
方法有两种通过查询标志位来写延时函数通过使能中断来写延时函数
1 通过查询标志位来写延时函数先定义一个变量循环一轮1ms/us然后在for循环里面等待到了我们规定的延时的时间CTRL寄存器寄存器第16位为1就完成了延时。
/*通过查询标志位来写延时函数*/void SysTick_Delayms(uint32_t ms)//1毫秒延时函数延时多少毫秒更改形参即可{uint32_t i;SysTick_Config(72000);//循环1轮就是1毫秒for(i0;i<ms;i){while(((SysTick->CTRL) & (1<<16)) ! 1);//判断CTRL这个寄存器的16位不为1就继续在while循环里面等待}SysTick->CTRL & ~SysTick_CTRL_ENABLE_Msk;//把这位清0失能systick}void SysTick_Delayus(uint32_t us)//1微秒延时函数延时多少微秒更改形参即可{uint32_t i;SysTick_Config(72);//循环1轮就是1微秒for(i0;i<us;i){while(((SysTick->CTRL) & (1<<16)) ! 1);//判断CTRL这个寄存器的16位不为1就继续在while循环里面等待}SysTick->CTRL & ~SysTick_CTRL_ENABLE_Msk;//把这位清0失能systick}
2通过使能中断来写延时函数 定义一个全局变量毫秒和微秒要分开用先把我们要延时的这个数值赋给这个全局变量然后走一轮循环在中断服务函数里面找到systick的中断服务函数每进一次中断这个全局变量就减一建立while循环减到了0之后失能systick。
中断函数文件内
extern volatile uint32_t isr_ms;//这个全局变量在bsp_systick.c文件中extern volatile uint32_t isr_us;//这个全局变量在bsp_systick.c文件中void SysTick_Handler(void)//systick 的中断服务函数{isr_ms--;isr_us--;}
延时函数文件内
/*通过使能中断来写延时函数*/volatile uint32_t isr_ms;//定义一个全局变量void SysTick_Delayms_INT(uint32_t ms)//1毫秒延时函数延时多少毫秒更改形参即可{isr_ms ms;SysTick_Config(72000);//循环1轮就是1毫秒while(isr_ms);//把这位清0失能systickSysTick->CTRL & ~SysTick_CTRL_ENABLE_Msk;}volatile uint32_t isr_us;//定义一个全局变量void SysTick_Delayus_INT(uint32_t us)//1毫秒延时函数延时多少毫秒更改形参即可{isr_us us;SysTick_Config(72);//循环1轮就是1毫秒while(isr_us);//把这位清0失能systickSysTick->CTRL & ~SysTick_CTRL_ENABLE_Msk;}
本程序源码见文章末尾SysTick定时器的延时函数毫秒、微秒
基本定时器基本定时器简介
计数器16bit只能从0开始向上计数。没有外部的GPIO是内部资源只能用来定时。时钟来自PCLK1为72M可实现1~65536分频。
一、功能框图 (1) 时钟源时钟源来自RCC的TIMx_CLK属于内部的CK_INT
基本定时器和通用定时器是挂载到APB1总线上由于APB1的预分频系数是2所以频率要×2也就是72M。
(2) 控制器控制器用于控制定时器的复位、使能、计数、触发DAC。涉及到的寄存器为CR1/2、DIERDMA/中断使能寄存器、EGR事件产生寄存器、SR状态寄存器
(3) 时基单元定时器最主要的就是时基部分包括预分频器、计数器、自动重装载寄存器。这些所有定时器都有。
预分频器PSC (寄存器 TIMx_PSC控制)
结构框图中CK_PSC就是从APB1得到的时钟72M预分频控制寄存器的值就是PSC里面的值而CK_CNT就是分频最终得到的值。
16位的预分频器PSC对内部时钟CK_PSC进行分频之后得到计数器时钟CK_CNTCK_PSC预分频器时钟/(PSC预分频因子1)
计数器CNT在计数器时钟的驱动下开始计数计数一次的时间为1/CK_CNT。
计数器、自动重装载寄存器
定时器使能(CEN置1)后计数器 CNT在CK_CNT 驱动下计数当TCNT值计数器的值与ARR 的设定值自动重装载寄存器的值自动重装载寄存器是16位的最大值是65535相等时就自动生成事件如果使能中断就产生中断并 CNT 自动清零然后自动重新开始计数如此重复以上过程。
(4) 影子寄存器功能框图上有个影子PSC和ARR都有影子寄存器。
什么是影子寄存器
这表示在物理上这个寄存器对应2个寄存器一个是我们可以可以写入或读出的寄存器称为预装载寄存器另一个是我们看不见的、无法真正对其读写操作的但在使用中真正起作用的寄存器称为影子寄存器。
每次发生更新事件(UEV)时所有真正起作用的影子寄存器(shadowregister)可以同时被更新为预装载寄存器 (preloadregister)里面的值这样可以保证多个通道的操作能够准确同步如果没有影子寄存器(shadowregister)或者预装载寄存器 (preloadregister) 和影子寄存器 (shadowregister)是直通的当软件更新了预装载寄存器 (preloadregister)也就立即更新了影子寄存器 (shadowregister)由于软件不能同时更新多个影子寄存器这就会导致多个通道的时序不能同步。
原文链接
影子寄存器的存在起到一个缓冲的作用用户值->寄存器->影子寄存器->起作用。例如自动重装载的影子寄存器ARR寄存器自动重载寄存器是预装载的。对自动重载寄存器执行写入或读取操作时会访问预装载寄存器。预装载寄存器的内容既可以直接传送到影子寄存器也可以在每次发生更新事件 (UEV) 时传送到影子寄存器。也就是说在CNT计数器计数的过程中自动重装载寄存器的值可以随时更改更改的数值可以立刻起作用也可以在本轮计数完成之后再起作用。是否立刻起作用可以通过软件来进行控制。由控制寄存器 1(TIMx_CR1)的ARPE位来控制。0TIMx_ARR寄存器没有缓冲 1TIMx_ARR寄存器具有缓冲。
而PSC的影子寄存器只能在这一轮计数完成之后的下一轮才开始起作用。这个是不能通过软件来控制的
如果不使用影子寄存器则用户值在写到寄存器之后则里面起作用。
二、定时时间的计算PSC 72-1定时器频率72M/(PSC1)1MHZ近似时间为1微秒。
若我们设定ARR自动重装载寄存器 1000-1从0计数到999则计了1000次。
中断周期 T 1000 *1/1000000 1ms
三、时基初始化结构体时基初始化结构体 TIM_TimeBaseInitTypeDef 里面有5个成员TIM6和TIM7的寄存器里面只有TIM_Prescaler和TIM_Period所以使用TIM6和TIM7的时候只需初始化这两个成员即可另外三个成员是通用定时器和高级定时器才有。TIM_Prescaler对应预分频器(TIMx_PSC)TIM_Period对应自动重装载寄存器(TIMx_ARR)。
typedef struct{ TIM_Prescaler 都有 TIM_CounterMode TIMx,x[6,7]没有其他都有 TIM_Period 都有 TIM_ClockDivision TIMx,x[6,7]没有其他都有 TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有}TIM_TimeBaseInitTypeDef;
四、实验设计 基本定时器产生500MS定时在主函数里面让LED反转
设计思路让基本定时器产生1ms的中断到了1ms之后进入中断服务函数中断服务函数里的变量自加加到500之后代表到了500ms。我们可以在主函数内部判断变量值是否等于500到了500让LED翻转一次。
这个就与51单片机或者蓝桥杯单片机的定时器0、1、2的定时功能差不多我们通过定时的功能完成一个固定周期的重复事件。
(1) 配置时基初始化结构体首先宏定义硬件基本定时器的时钟自动重装载的值时钟分频因子定时器的中断通道在使用高级定时器的时候只更改这些即可。
/********************基本定时器TIM参数定义只限TIM6、7************/#define BASIC_TIM6 // 如果使用TIM7注释掉这个宏即可#ifdef BASIC_TIM6 // 使用基本定时器TIM6#define BASIC_TIM TIM6#define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd#define BASIC_TIM_CLK RCC_APB1Periph_TIM6#define BASIC_TIM_Period 1000-1 //自动重装载的值 1000次计数进一次中断也就是1毫秒进一次中断#define BASIC_TIM_Prescaler 71 //时钟分频因子 控制计数一次的时间 72/(711) 1M 也就是1微秒#define BASIC_TIM_IRQ TIM6_IRQn //中断源 所有的中断源在 stm32f10x.h文件中#define BASIC_TIM_IRQHandler TIM6_IRQHandler#else // 使用基本定时器TIM7#define BASIC_TIM TIM7#define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd#define BASIC_TIM_CLK RCC_APB1Periph_TIM7#define BASIC_TIM_Period 1000-1#define BASIC_TIM_Prescaler 71#define BASIC_TIM_IRQ TIM7_IRQn#define BASIC_TIM_IRQHandler TIM7_IRQHandler#endif
配置、初始化时基结构体配置、初始化NVIC结构体。
// 中断优先级配置static void BASIC_TIM_NVIC_Config(void)//涉及到中断就要配置、初始化 NVIC{ NVIC_InitTypeDef NVIC_InitStructure; // 设置中断组为0 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);// 设置中断来源 NVIC_InitStructure.NVIC_IRQChannel BASIC_TIM_IRQ ;// 设置主优先级为 0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; // 设置抢占优先级为3 NVIC_InitStructure.NVIC_IRQChannelSubPriority 3; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(&NVIC_InitStructure);}static void BASIC_TIM_Mode_Config(void)//初始化时基结构体{ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 开启定时器时钟,即内部时钟CK_INT72M BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);// 自动重装载寄存器的值累计TIM_Period1个频率后产生一个更新或者中断 TIM_TimeBaseStructure.TIM_Period BASIC_TIM_Period; // 时钟预分频数为 TIM_TimeBaseStructure.TIM_Prescaler BASIC_TIM_Prescaler;// 时钟分频因子 基本定时器没有不用管 TIM_TimeBaseStructure.TIM_ClockDivisionTIM_CKD_DIV1;// 计数器计数模式基本定时器只能向上计数没有计数模式的设置 TIM_TimeBaseStructure.TIM_CounterModeTIM_CounterMode_Up; // 重复计数器的值基本定时器没有不用管TIM_TimeBaseStructure.TIM_RepetitionCounter0; // 初始化定时器 TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);// 清除计数器中断标志位 TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update); // 开启计数器中断 TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);// 使能计数器 TIM_Cmd(BASIC_TIM, ENABLE);}void BASIC_TIM_Init(void){BASIC_TIM_NVIC_Config();BASIC_TIM_Mode_Config();}
(2) 开启定时器更新中断即定时时间到了 具体哪个定时器具体哪种中断。这里用到的是更新中断计数器从n计数到m为一轮产生的中断就叫做更新中断。
extern volatile uint32_t time;void BASIC_TIM_IRQHandler (void){if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) ! RESET )//判断是否产生中断 {time;TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update); } }
(3) 编写main函数 int main(void){/*在程序来到main函数这里的时候系统时钟已经配置成72M*/LED_GPIO_Config();//初始化函数BASIC_TIM_Init();while(1){if(time 500){time 0; LED1_TOGGLE;/* LED1 取反 实现LED1的亮灭反转*/ }}}
本程序源码下载链接见文章末尾基本定时器LED反转设计源码。
高级定时器高级定时器简介
计数器16bit可从上/下/两边计数上从0计数到用户配置的值下从用户配置的值倒数到0两边从0计数到用户配置的值再从用户配置的值倒数到0。TIM1和TIM8时基单元里还有一个重复计数器RCR独有。
相比基本定时器通用定时器和高级定时器有4个GPIO其中通道1~3还有互补输出GPIO加起来共有7个。
时钟来自PCLK2为72M所有的定时器都是72M可实现1~65536分频。
一、功能框图1-时钟源 2-控制器 3-时基 4-输入捕获 5-输出比较 6-断路功能
(1) 时钟源内部时钟源
时钟源有四种模式我们最常用的就是CK_INT来自RCC的TIMx_CLK
高级定时器挂载到APB2总线上APB2的预分频系数是1所以不变还是72M。
外部时钟模式1—外部的GPIO Tixx1 2 3 4和外部时钟模式2—外部的GPIO ETR不常用。外部时钟模式1可以从外部的引脚功能框图左侧的TIMx_CH1、2、3、4引脚给计数器提供时钟外部时钟模式2可以从外部的特殊引脚功能框图左侧的TIMx_ETR引脚给计数器提供时钟。
这些引脚对应哪些具体引脚参考下图
外部时钟1
虽然有4个引脚但不能同时使用两个只能使用1个。
①时钟信号输入引脚 外部的GPIO Tix对应TIMx_CH1/2/3/4TIM_CCMRx寄存器的位CCxS[1:0]配置其中CCMR1控制TI1/2CCMR2控制TI3/4。
②滤波器 如果来自外部的时钟信号的频率过高或者混杂有高频干扰信号的话我们就需要使用滤波器对ETRP信号重新采样来达到降频或者去除高频干扰的目的由TIMx_CCMRx寄存器的位ICxF[3:0]配置。
③边沿检测 边沿检测的信号来自于滤波器的输出在成为触发信号之前需要进行边沿检测决定是上升沿有效还是下降沿有效。由TIMx_CCER寄存器的位CCxP和CCxNP 配置。
④触发选择 当使用外部时钟模式1时触发源有两个一个是滤波后的定时器输入1TI1FP1和滤波后的定时器输入2TI2FP2。由TIMx_SMCR寄存器的位TS[2:0]配置。
⑤从模式选择 选定了触发源信号后最后我们需把信号连接到TRGI引脚让触发信号成为外部时钟模式1的输入最终等于 CK_PSC然后驱动计数器CNT计数。具体的配置TIMx_SMCR寄存器的位SMS[2:0]为 000 即可选择外部时钟模式1。
经过上面的5个步骤之后 最后我们只需使能计数器开始计数外部时钟模式1的配置就算完成。使能计数器由TIMx_CR1寄存器的位CEN 配置。
外部时钟2
①时钟信号输入引脚 当使用外部时钟模式2的时候时钟信号来自于定时器的特定输入通道TIMx_ETR只有1个。
②外部触发极性 来自ETR引脚输入的信号可以选择为上升沿或者下降沿有效。具体的由TIMx_SMCR寄存器的位ETP 配置。
③外部触发预分频器 由于ETRP的信号的频率不能超过TIMx_CLK180M的 1/4当触发信号的频率很高的情况下就必须使用分频器来降频。具体的由TIMx_SMCR寄存器的位ETPS[1:0]配置。
④滤波器 如果ETRP的信号的频率过高或者混杂有高频干扰信号的需要使用滤波器对ETRP信号重新采样来达到降频或者去除高频干扰的目的。具体的由TIMx_SMCR寄存器的位ETF[3:0]配置其中的fDTS是由内部时钟CK_INT分频得到具体的由TIMx_CR1寄存器的位CKD[1:0]配置。
⑤从模式选择 经过滤波器滤波的信号连接到ETRF引脚后触发信号成为外部时钟模式2的输入最终等于CK_PSC然后驱动计数器 CNT 计数。具体的配置TIMx_SMCR寄存器的位ECE为 1即可选择外部时钟模式2。
经过上面的5个步骤之后最后我们只需使能计数器开始计数外部时钟模式2的配置就算完成。使能计数器由TIMx_CR1寄存器的位CEN配置。
(2) 时基单元预分频器 PSC
16位由预分频器(TIMx_PSC)控制。有一个输入时钟CK_PSC和一个输出时钟CK_CNT。输入时钟CK_PSC就是上面时钟源的输出输出CK_CNT则用来驱动计数器CNT计数。通过设置预分频器PSC的值可以得到不同的CK_CNT。计数器的时钟频率(CK_CNT)fck_psc/( PSC[15:0]1)
计数器CNT
16位由计数器(TIMx_CNT)控制。
递增计数模式下计数器从0开始计数每来一个CK CNT脉冲计数器就增加1直到计数器的值与自动重载寄存器ARR值相等然后计数器又从0开始计数并生成计数器上溢事件计数器总是如此循环计数。如果禁用重复计数器在计数器生成上溢事件就马上生成更新事件(UEV)如果使能重复计数器每生成一次上溢事件重复计数器内容就减1直到重复计数器内容为0时才会生成更新事件。
递减计数模式下计数器从自动重载寄存器ARR值开始计数每来一个CK CNT脉冲计数器就减1直到计数器值为0然后计数器又从自动重载寄存器ARR值开始递减计数并生成计数器下溢事件计数器总是如此循环计数。如果禁用重复计数器在计数器生成下溢事件就马上生成更新事件如果使能重复计数器每生成一次下溢事件重复计数器内容就减1直到重复计数器内容为0时才会生成更新事件。
中心对齐模式下计数器从0开始递增计数直到计数值等于(ARR-1)值生成计数器上溢事件然后从ARR值开始递减计数直到1生成计数器下溢事件。然后又从0开始计数如此循环。每次发生计数器上溢和下溢事件都会生成更新事件。
自动重装载寄存器ARR
16位由自动重装载寄存器(TIMx_ARR)控制与前面基本定时器的ARR基本相同。
重复计数器RCR
8位。由重复计数寄存器(TIMx_RCR)控制。基本/通用定时器发生上/下溢事件时直接生成更新事件。高级控制定时器多出了重复计数器。定时器发生上溢、下溢事件时递减重复计数器的值。当重复计数器为0时才会生成更新事件。发生N1个上溢、下溢事件(N为RCR的值)时产生更新事件。
(3) 输入捕获输入捕获可以对输入的信号的上升沿下降沿或者双边沿进行捕获常用的有测量输入信号的脉宽和测量PWM输入信号的频率和占空比这两种。
输入捕获的大概的原理就是当捕获到信号的跳变沿上升或者下降沿的时候把计数器CNT的值锁存到捕获寄存器CCR中把前后两次捕获到的CCR寄存器中的值相减就可以算出脉宽或者频率。如果捕获的脉宽的时间长度超过你的捕获定时器的周期,就会发生溢出这个我们需要做额外的处理。
①输入通道 当使用需要被测量的信号从定时器的外部引脚 TIMx_CH1/2/3/4进入通常叫TI1/2/3/4我们称被测量的信号为Tix。
②输入滤波和边沿检测 当输入的信号存在高频干扰的时候我们需要对输入信号进行滤波即进行重新采样根据采样定律采样的频率必须大于等于两倍的输入信号。比如输入的信号为1M,又存在高频的信号干扰那么此时就很有必要进行滤波我们可以设置采样频率为2M这样可以在保证采样到有效信号的基础上把高于2M的高频干扰信号过滤掉。
滤波器的配置由CR1寄存器的位CKD[1:O]和CCMR1/2的位ICxF[3:0]控制。从ICxF位的描述可知采样频率fSAMPLE可以由fCK INT和fDTs分频后的时钟提供其中是fCK_INT内部时钟Ds是fCK INT经过分频后得到的频率分频因子由CKD[1:O]决定可以是不分频2分频或者是4分频。
边沿检测器用来设置信号在捕获的时候是什么边沿有效可以是上升沿下降沿或者是双边沿具体的由CCER寄存器的位CCxP和CCxNP决定。
③捕获通道 捕获通道就是图中的IC1/2/3/4每个捕获通道都有相对应的捕获寄存器CCR1/2/3/4当发生捕获的时候计数器CNT的值就会被锁存到捕获寄存器中。
这里我们要搞清楚输入通道和捕获通道的区别输入通道是用来输入信号的捕获通道是用来捕获输入信号的通道一个输入通道的信号可以同时输入给两个捕获通道。比如输入通道TI1的信号经过滤波边沿检测器之后的TIIFP1和TIIFP2可以进入到捕获通道IC1和IC2其实这就是我们后面要讲的PWM输入捕获只有一路输入信号(TI1)却占用了两个捕获通道(IC1和IC2)一路测量周期一路测量占空比。PWM信号输入只能通道1、2有这种功能。PWM信号可以分为TIxFP1和TIxFP2分别和两个捕获寄存器联系起来要选择一路作为触发从模式哪一路触发从模式哪一路就是测量周期另外一路测量占空比。TIxFP1触发从模式测量周期称为直连TIxFP2触发从模式测量周期称为非直连。
当只需要测量输入信号的脉宽时候用一个捕获通道即可。输入通道和捕获通道的映射关系具体由寄存器CCMRx的位CCxS[1:0]配置。
④预分频器 一般不分频。
⑤捕获寄存器 经过预分频器的信号ICxPS是最终被捕获的信号当发生捕获时第一次计数器CNT的值会被锁存到捕获寄存器CCR中还会产生CCxI中断相应的中断位CCxIF(在SR寄存器中)会被置位通过软件或者读取CCR中的值可以将CCxF清0。如果发生第二次捕获即重复捕获CCR寄存器中已捕获到计数器值且CCxF标志已置1则捕获溢出标志位CCxOF(在SR寄存器中)会被置位CCxOF只能通过软件清零。
(4) 输出比较例如输出一个PWM波信号。
CCR寄存器用于输出信号叫做比较寄存器。计数器从0开始计数计数到CCR的时候输出高电平或者低电平从CCR计数到ARR输出低电平或者高电平。然后重新开始计数一轮。占空比就与比较寄存器CCR相关。信号在输出通道输出之前还要经过一个寄存器的控制刹车和死区寄存器(TIMx_BDTR)。
什么是死区时间
MOTORA是用来驱动电机的由上下两个MOS管来控制
一路一个MOS管高电平转换成低电平之后延迟一段时间之后另一路另一个MOS管才由低电平转换成高电平反之亦然。这样就不会同时导通从而避免功率元件烧毁延迟的那段时间称为死区时间死区时间控制在通常的单片机所配备的PWM中都有这样的功能。
死区时间由刹车和死区寄存器(TIMx_BDTR)的UTG[7:0]位控制。
其中的Tdts跟内部时钟相关由TIM1和TIM8控制寄存器 1(TIMx_CR1)的CKD[1:0]位控制。
在刹车和死区寄存器(TIMx_BDTR)中
二、输入捕获的应用一是测量脉宽和频率
上升沿捕获到一次之后进入中断在中断中改变捕获的边沿改成下降沿捕获捕获到下降沿之后进入中断再次改变为上升沿捕获……
二是PWM输入模式
只能通过通道12进行捕获3、4通道不能捕获。
三、输出比较的应用1-输出比较模式总共有 8 种常用的是PWM模式。
2-由寄存器CCMRx的位OCxM[2:0]配置。
PWM 输出就是对外输出脉宽即占空比可调的方波信号信号频率由自动重装寄存器 ARR 的值决定占空比由比较寄存器 CCR 的值决定。
PWM的输出模式
有效高电平无效低电平
边沿对齐与中心对齐的不同
最常用的是边沿对齐。
1-根据CNT的计数方向PWM波形分成边沿对齐和中心对齐两种。边沿对齐主要用于直流电机中心对齐主要用于交流电机。
2-边沿对齐时CNT只工作在递增或者递减。
3-中心对齐时CNT工作在递增和递减。
四、初始化结构体 (1) 时基初始化结构体TIM_TimeBaseInitTypeDef也就是前面功能框图里面的时基单元。
typedef struct { uint16_t TIM_Prescaler; // 预分频器 uint16_t TIM_CounterMode; // 计数模式 uint32_t TIM_Period; // 定时器周期 uint16_t TIM_ClockDivision; // 时钟分频 uint8_t TIM_RepetitionCounter; // 重复计算器} TIM_TimeBaseInitTypeDef;
TIM_Prescaler定时器预分频器设置时钟源经该预分频器才是定时器计数时钟CK_CNT它设定 PSC 寄存器的值。计算公式为 计数器时钟频率 (fCK_CNT) 等于fCK_PSC / (PSC[15:0] 1)【对应TIMx_PSC寄存器】可实现 1 至 65536 分频。
TIM_CounterMode定时器计数方式可设置为向上计数、向下计数以及中心对齐。高级控制定时器允许选择任意一种。基本定时器只能选择向上计数配置基本定时器时不用配置此成员。对应TIM_CR1寄存器的DIR位。
TIM_Period定时器周期实际就是设定自动重载寄存器ARR的值ARR为要装载到实际自动重载寄存器即影子寄存器 的值 可设置范围为0至65535。具体介绍看基本定时器
TIM_ClockDivision时钟分频设置定时器时钟 CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择1、2、4 分频。
TIM_RepetitionCounter重复计数器只有 8 位只存在于高级定时器。
(2) 输出比较结构体TIM_OCInitTypeDef
typedef struct { uint16_t TIM_OCMode; // 比较输出模式 uint16_t TIM_OutputState; // 比较输出使能 uint16_t TIM_OutputNState; // 比较互补输出使能 uint32_t TIM_Pulse; // 脉冲宽度 uint16_t TIM_OCPolarity; // 输出极性 uint16_t TIM_OCNPolarity; // 互补输出极性 uint16_t TIM_OCIdleState; // 空闲状态下比较输出状态 uint16_t TIM_OCNIdleState; // 空闲状态下比较互补输出状态} TIM_OCInitTypeDef;
TIM_OCMode比较输出模式选择总共有八种常用的为 PWM1/PWM2。它设定CCMRxx控制1或2通道寄存器OCxM[2:0]位的值。
TIM_OutputState比较输出使能决定最终的输出比较信号OCx 是否通过外部引脚输出。它设定TIMx_CCER寄存器CCxE位的值。也就是说启用之后可以产生PWM波信号。
TIM_OutputNState比较互补输出使能决定 OCx 的互补信号 OCxN 是否通过外部引脚输出。它设定 CCER 寄存器 CCxNE 位的值。
TIM_Pulse比较输出脉冲宽度实际设定比较寄存器 CCR 的值决定脉冲宽度。可设置范围为 0 至 65535。
TIM_OCPolarity比较输出极性可选 OCx 为高电平有效或低电平有效。它决定着定时器通道有效电平。它设定 CCER 寄存器的 CCxP 位的值。也就是正周期的电平是高/低。
TIM_OCNPolarity比较互补输出极性可选OCxN 为高电平有效或低电平有效。它设定 TIMx_CCER 寄存器的 CCxNP 位的值。
当我们需要输出PWM波时小车电机驱动就需要配置时基结构体和输出比较结构体即可而输出比较结构体只需要配置三个加粗的成员即可。
(3) PWM信号 周期和占空比的计算--以通用定时器为例这一部分在时基结构体初始化中配置。
ARR 自动重装载寄存器的值
CLK_cnt计数器的时钟(驱动CNT计数器的时钟周期)等于 Fck_int / (psc1) 72M/(psc1) psc GENERAL_TIM_Prescaler 宏定义的值为 71。CLK_cnt 1 MHZ
PWM 信号的周期 T ARR * (1/CLK_cnt) ARR*(PSC1) / 72M。这里的ARR代表的是自动重装载寄存器的值累计TIM_Period1TIM_Period在宏定义中为9也就是9110。周期 10 * (1 / 1 MHZ) 10 us
占空比 P CCR/ARR CCR/(TIM_Period1) TIM_Period就是宏定义中 GENERAL_TIM_Period 值为9也就是说占空比为 CCR / 10
详细例程如下这是一个定时器3四路PWM通道不同输出值的配置程序主要是看一下PWM周期和占空比是如何配置的
.c文件内容定时器3配置函数
static void GENERAL_TIM3_Mode_Config(void){ // 开启定时器时钟,即内部时钟CK_INT72MGENERAL_TIM3_APBxClock_FUN(GENERAL_TIM3_CLK,ENABLE);/*--------------------时基结构体初始化-------------------------*/// 配置周期这里配置为100K TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 自动重装载寄存器的值ARR累计TIM_Period1个频率后产生一个更新或者中断 TIM_Period1 9110TIM_TimeBaseStructure.TIM_PeriodGENERAL_TIM3_Period;// 驱动CNT计数器的时钟 Fck_int/(psc1)TIM_TimeBaseStructure.TIM_Prescaler GENERAL_TIM3_Prescaler;// 时钟分频因子 配置死区时间时需要用到TIM_TimeBaseStructure.TIM_ClockDivisionTIM_CKD_DIV1;// 计数器计数模式设置为向上计数TIM_TimeBaseStructure.TIM_CounterModeTIM_CounterMode_Up;// 重复计数器的值没用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter0;// 初始化定时器TIM_TimeBaseInit(GENERAL_TIM3, &TIM_TimeBaseStructure);/*--------------------输出比较结构体初始化-------------------*/ // 需要 PWM波 输出的就需要配置此结构体// 占空比配置uint16_t CCR1_Val 9; // 占空比 CCR1_Val / (GENERAL_TIM_Period 1)uint16_t CCR2_Val 6;uint16_t CCR3_Val 3;uint16_t CCR4_Val 1;TIM_OCInitTypeDef TIM_OCInitStructure;// 配置为PWM模式1TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; //比较输出模式选择.常用的为 PWM1/PWM2// 输出使能TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; //比较输出使能决定最终的输出比较信号OCx是否通过外部引脚输出也就是说启用之后可以产生PWM波信号// 输出通道电平极性配置TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; //比较输出极性可选 OCx 为高电平有效或低电平有效。它决定着定时器通道有效电平。也就是正周期的电平是高/低// 输出比较通道 1TIM_OCInitStructure.TIM_Pulse CCR1_Val;TIM_OC1Init(GENERAL_TIM3, &TIM_OCInitStructure);TIM_OC1PreloadConfig(GENERAL_TIM3, TIM_OCPreload_Enable);// 输出比较通道 2TIM_OCInitStructure.TIM_Pulse CCR2_Val;TIM_OC2Init(GENERAL_TIM3, &TIM_OCInitStructure);TIM_OC2PreloadConfig(GENERAL_TIM3, TIM_OCPreload_Enable);// 输出比较通道 3TIM_OCInitStructure.TIM_Pulse CCR3_Val;TIM_OC3Init(GENERAL_TIM3, &TIM_OCInitStructure);TIM_OC3PreloadConfig(GENERAL_TIM3, TIM_OCPreload_Enable);// 输出比较通道 4TIM_OCInitStructure.TIM_Pulse CCR4_Val;TIM_OC4Init(GENERAL_TIM3, &TIM_OCInitStructure);TIM_OC4PreloadConfig(GENERAL_TIM3, TIM_OCPreload_Enable);// 使能计数器TIM_Cmd(GENERAL_TIM3, ENABLE);}
.h文件内容一些宏定义
/************************************通用定时器TIM参数定义只限TIM2、3、4、5********************************/// 当使用不同的定时器的时候对应的GPIO是不一样的这点要注意/******************************* TIM3 ***************************************/#define GENERAL_TIM3 TIM3#define GENERAL_TIM3_APBxClock_FUN RCC_APB1PeriphClockCmd#define GENERAL_TIM3_CLK RCC_APB1Periph_TIM3#define GENERAL_TIM3_Period 9#define GENERAL_TIM3_Prescaler 71// TIM3 输出比较通道1#define GENERAL_TIM3_CH1_GPIO_CLK RCC_APB2Periph_GPIOA#define GENERAL_TIM3_CH1_PORT GPIOA#define GENERAL_TIM3_CH1_PIN GPIO_Pin_6// TIM3 输出比较通道2#define GENERAL_TIM3_CH2_GPIO_CLK RCC_APB2Periph_GPIOA#define GENERAL_TIM3_CH2_PORT GPIOA#define GENERAL_TIM3_CH2_PIN GPIO_Pin_7// TIM3 输出比较通道3#define GENERAL_TIM3_CH3_GPIO_CLK RCC_APB2Periph_GPIOB#define GENERAL_TIM3_CH3_PORT GPIOB#define GENERAL_TIM3_CH3_PIN GPIO_Pin_0// TIM3 输出比较通道4#define GENERAL_TIM3_CH4_GPIO_CLK RCC_APB2Periph_GPIOB#define GENERAL_TIM3_CH4_PORT GPIOB#define GENERAL_TIM3_CH4_PIN GPIO_Pin_1
整个工程的详细代码在文末有下载链接。参照 5.2-通用定时器PWM输出功能设计
(4) 输入捕获结构体typedef struct { uint16_t TIM_Channel; // 输入通道选择 uint16_t TIM_ICPolarity; // 输入捕获触发选择 uint16_t TIM_ICSelection; // 输入捕获选择 uint16_t TIM_ICPrescaler; // 输入捕获预分频器 uint16_t TIM_ICFilter; // 输入捕获滤波器} TIM_ICInitTypeDef;
TIM_Channel捕获通道ICx选择可选 TIM_Channel_1、 TIM_Channel_2、TIM_Channel_3 或 TIM_Channel_4 四个通道。它设定 CCMRx 寄存器 CCxS 位 的值。
TIM_ICPolarity输入捕获边沿触发选择可选上升沿触发、下降沿触发或边沿跳变触发。它设定 CCER 寄存器 CCxP 位和 CCxNP 位的值。
TIM_ICSelection输入通道选择捕获通道 ICx 的信号可来自三个输入通道分别为TIM_ICSelection_DirectTI、TIM_ICSelection_IndirectTI 或 TIM_ICSelection_TRC。它设定 CCRMx 寄存器的 CCxS[1:0]位的值。
TIM_ICPrescaler输入捕获通道预分频器可设置 1、 2、 4、 8 分频它设定 CCMRx寄存器的 ICxPSC[1:0]位的值。如果需要捕获输入信号的每个有效边沿则设置 1 分频即可。
TIM_ICFilter输入捕获滤波器设置可选设置 0x0 至 0x0F。它设定 CCMRx 寄存器ICxF[3:0]位的值。一般我们不使用滤波器即设置为 0。
(5) 断路和死区初始化结构体TIM_BDTRInitTypeDef
断路和死区结构体 TIM_BDTRInitTypeDef 用于断路和死区参数的设置属于高级定时器专用用 于配置断路时通道输出状态以及死区时间。它与 TIM_BDTRConfig 函数配置使用完成参数配置。 这个结构体的成员只对应寄存器断路和死区寄存器TIMx_BDTR。
typedef struct { uint16_t TIM_OSSRState; // 运行模式下的关闭状态选择 uint16_t TIM_OSSIState; // 空闲模式下的关闭状态选择 uint16_t TIM_LOCKLevel; // 锁定配置 uint16_t TIM_DeadTime; // 死区时间 uint16_t TIM_Break; // 断路输入使能控制 uint16_t TIM_BreakPolarity; // 断路输入极性 uint16_t TIM_AutomaticOutput; // 自动输出使能} TIM_BDTRInitTypeDef;
(1) TIM_OSSRState运行模式下的关闭状态选择它设定 BDTR 寄存器 OSSR 位的值。
(2) TIM_OSSIState空闲模式下的关闭状态选择它设定 BDTR 寄存器 OSSI 位的值。
(3) TIM_LOCKLevel锁定级别配置BDTR 寄存器 LOCK[1:0] 位的值。
(4) TIM_DeadTime配置死区发生器定义死区持续时间可选设置范围为 0x0 至 0xFF。它设定 BDTR 寄存器 DTG[7:0] 位的值。
(5) TIM_Break断路输入功能选择可选使能或禁止。它设定 BDTR 寄存器 BKE 位的值。
(6) TIM_BreakPolarity断路输入通道 BRK 极性选择可选高电平有效或低电平有效。它设定 BDTR 寄存器 BKP 位的值。
(7) TIM_AutomaticOutput自动输出使能可选使能或禁止它设定 BDTR 寄存器 AOE 位的值。
这个在我平常做的时候用不到这个功能就不做相应配置了。
五、程序设计 (1) 高级定时器定时功能设计功能设计使用高级定时器实现双色LED灯间隔1秒完成红绿颜色切换高级定时器的定时功能
配置过程
首先进行定时器和中断优先级的配置
#include bsp_AdvanceTim.h // 中断优先级配置static void ADVANCE_TIM_NVIC_Config(void){ NVIC_InitTypeDef NVIC_InitStructure; // 设置中断组为0 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);// 设置中断来源 NVIC_InitStructure.NVIC_IRQChannel ADVANCE_TIM_IRQ ;// 设置主优先级为 0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; // 设置抢占优先级为3 NVIC_InitStructure.NVIC_IRQChannelSubPriority 3; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(&NVIC_InitStructure);}///*// * 注意TIM_TimeBaseInitTypeDef结构体里面有5个成员TIM6和TIM7的寄存器里面只有// * TIM_Prescaler和TIM_Period所以使用TIM6和TIM7的时候只需初始化这两个成员即可// * 另外三个成员是通用定时器和高级定时器才有.// *-----------------------------------------------------------------------------// *typedef struct// *{ TIM_Prescaler 都有// *TIM_CounterMode TIMx,x[6,7]没有其他都有// * TIM_Period 都有// * TIM_ClockDivision TIMx,x[6,7]没有其他都有// * TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有// *}TIM_TimeBaseInitTypeDef; // *-----------------------------------------------------------------------------// */static void ADVANCE_TIM_Mode_Config(void){TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 开启定时器时钟,即内部时钟CK_INT72M ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK, ENABLE);// 自动重装载寄存器的值累计TIM_Period1个频率后产生一个更新或者中断 TIM_TimeBaseStructure.TIM_PeriodADVANCE_TIM_Period; // 时钟预分频数 TIM_TimeBaseStructure.TIM_Prescaler ADVANCE_TIM_Prescaler;// 时钟分频因子 没用到不用管 TIM_TimeBaseStructure.TIM_ClockDivisionTIM_CKD_DIV1;// 计数器计数模式设置为向上计数 TIM_TimeBaseStructure.TIM_CounterModeTIM_CounterMode_Up; // 重复计数器的值没用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter0; // 初始化定时器 TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);// 清除计数器中断标志位 TIM_ClearFlag(ADVANCE_TIM, TIM_FLAG_Update); // 开启计数器中断 TIM_ITConfig(ADVANCE_TIM,TIM_IT_Update,ENABLE);// 使能计数器 TIM_Cmd(ADVANCE_TIM, ENABLE);}void ADVANCE_TIM_Init(void){ADVANCE_TIM_NVIC_Config();ADVANCE_TIM_Mode_Config();}
关于高级定时器的一些宏定义
#ifndef __BSP_ADVANCETIME_H#define __BSP_ADVANCETIME_H#include stm32f10x.h/********************高级定时器TIM参数定义只限TIM1、8************/#define ADVANCE_TIM1 // 如果使用TIM8注释掉这个宏即可#ifdef ADVANCE_TIM1 // 使用高级定时器TIM1#define ADVANCE_TIM TIM1#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM1#define ADVANCE_TIM_Period (1000-1)#define ADVANCE_TIM_Prescaler 71#define ADVANCE_TIM_IRQ TIM1_UP_IRQn#define ADVANCE_TIM_IRQHandler TIM1_UP_IRQHandler#else // 使用高级定时器TIM8#define ADVANCE_TIM TIM8#define ADVANCE_TIM_APBxClock_FUN RCC_APB2PeriphClockCmd#define ADVANCE_TIM_CLK RCC_APB2Periph_TIM8#define ADVANCE_TIM_Period (1000-1)#define ADVANCE_TIM_Prescaler 71#define ADVANCE_TIM_IRQ TIM8_UP_IRQn#define ADVANCE_TIM_IRQHandler TIM8_UP_IRQHandler#endif/**************************函数声明********************************/void ADVANCE_TIM_Init(void);#endif/* __BSP_ADVANCETIME_H */
高级定时器的中断函数
void ADVANCE_TIM_IRQHandler (void){if ( TIM_GetITStatus( ADVANCE_TIM, TIM_IT_Update) ! RESET ) {time;TIM_ClearITPendingBit(ADVANCE_TIM , TIM_FLAG_Update); } }
主函数程序
int main(void){LED_GPIO_Config();//初始化函数ADVANCE_TIM_Init(); while(1){if(time 1000){ GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);}else if(time 2000){time0;GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);}}}
想要验证这个程序需要一个双色LED灯当然也可以用别的外设去替代。完整程序链接在文章末尾高级定时器定时功能设计源码。
(2) 通用定时器PWM输出功能设计功能设计通过四个通用定时器2、3、4、5分别实现LED灯四级亮度等级变化4个通道有不同的占空比代表4个不同的亮度等级。这个程序只是展示了通用定时器的 PWM波输出功能在使用此功能时是不需要使用中断的。
配置过程
单以定时器3为例其实定时器3做出来了其他的也就会了只不过改改引脚即可。
首先配置定时器3
#include bsp_GeneralTim.h /******************************** TIM3 PWM输出配置 ******************************/static void GENERAL_TIM3_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // 输出比较通道1 GPIO 初始化RCC_APB2PeriphClockCmd(GENERAL_TIM3_CH1_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin GENERAL_TIM3_CH1_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GENERAL_TIM3_CH1_PORT, &GPIO_InitStructure);// 输出比较通道2 GPIO 初始化RCC_APB2PeriphClockCmd(GENERAL_TIM3_CH2_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin GENERAL_TIM3_CH2_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GENERAL_TIM3_CH2_PORT, &GPIO_InitStructure);// 输出比较通道3 GPIO 初始化RCC_APB2PeriphClockCmd(GENERAL_TIM3_CH3_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin GENERAL_TIM3_CH3_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GENERAL_TIM3_CH3_PORT, &GPIO_InitStructure);// 输出比较通道4 GPIO 初始化RCC_APB2PeriphClockCmd(GENERAL_TIM3_CH4_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin GENERAL_TIM3_CH4_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GENERAL_TIM3_CH3_PORT, &GPIO_InitStructure);}///*// * 注意TIM_TimeBaseInitTypeDef结构体里面有5个成员TIM6和TIM7的寄存器里面只有// * TIM_Prescaler和TIM_Period所以使用TIM6和TIM7的时候只需初始化这两个成员即可// * 另外三个成员是通用定时器和高级定时器才有.// *-----------------------------------------------------------------------------// *typedef struct// *{ TIM_Prescaler 都有 定时器的预分频值。预分频值越大计数频率越低定时器的计数周期就越长。// *TIM_CounterMode TIMx,x[6,7]没有其他都有 定时器的计数模式向上、向下、中心对齐// * TIM_Period 都有 定时器的自动重装载值。当计数器达到自动重装载值时会产生更新事件并重新从0开始计数。// * TIM_ClockDivision TIMx,x[6,7]没有其他都有 定时器的时钟分频系数。用于将计数器时钟进一步分频从而得到一个更低的计数频率。时钟分频系数的取值范围为1到65535。// * TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有 配置定时器在重复计数模式下的重复计数次数。高级定时器特有// *}TIM_TimeBaseInitTypeDef; // *-----------------------------------------------------------------------------// *//* ---------------- PWM信号 周期和占空比的计算--------------- */// ARR 自动重装载寄存器的值// CLK_cnt计数器的时钟(驱动CNT计数器的时钟周期)等于 Fck_int / (psc1) 72M/(psc1) psc GENERAL_TIM_Prescaler 宏定义的值为 71// CLK_cnt 1 MHZ// PWM 信号的周期 T ARR * (1/CLK_cnt) ARR*(PSC1) / 72M // 这里的ARR代表的是自动重装载寄存器的值累计TIM_Period1也就是9110跟下面的ARR不一样下面的ARR没加一// 周期 10 * (1 / 1 MHZ) 10 us// 占空比 P CCR/(GENERAL_TIM_Period1) GENERAL_TIM_Period 宏定义中为9也就是说占空比为 CCR / 10static void GENERAL_TIM3_Mode_Config(void){ // 开启定时器时钟,即内部时钟CK_INT72MGENERAL_TIM3_APBxClock_FUN(GENERAL_TIM3_CLK,ENABLE);/*--------------------时基结构体初始化-------------------------*/// 配置周期这里配置为100K TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;// 自动重装载寄存器的值ARR累计TIM_Period1个频率后产生一个更新或者中断 TIM_Period1 9110TIM_TimeBaseStructure.TIM_PeriodGENERAL_TIM3_Period;// 驱动CNT计数器的时钟 Fck_int/(psc1)TIM_TimeBaseStructure.TIM_Prescaler GENERAL_TIM3_Prescaler;// 时钟分频因子 配置死区时间时需要用到TIM_TimeBaseStructure.TIM_ClockDivisionTIM_CKD_DIV1;// 计数器计数模式设置为向上计数TIM_TimeBaseStructure.TIM_CounterModeTIM_CounterMode_Up;// 重复计数器的值没用到不用管TIM_TimeBaseStructure.TIM_RepetitionCounter0;// 初始化定时器TIM_TimeBaseInit(GENERAL_TIM3, &TIM_TimeBaseStructure);/*--------------------输出比较结构体初始化-------------------*/ // 需要 PWM波 输出的就需要配置此结构体// 占空比配置uint16_t CCR1_Val 9; // 占空比 CCR1_Val / (GENERAL_TIM_Period 1)uint16_t CCR2_Val 6;uint16_t CCR3_Val 3;uint16_t CCR4_Val 1;TIM_OCInitTypeDef TIM_OCInitStructure;// 配置为PWM模式1TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; //比较输出模式选择.常用的为 PWM1/PWM2// 输出使能TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; //比较输出使能决定最终的输出比较信号OCx是否通过外部引脚输出也就是说启用之后可以产生PWM波信号// 输出通道电平极性配置TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; //比较输出极性可选 OCx 为高电平有效或低电平有效。它决定着定时器通道有效电平。也就是正周期的电平是高/低// 输出比较通道 1TIM_OCInitStructure.TIM_Pulse CCR1_Val;TIM_OC1Init(GENERAL_TIM3, &TIM_OCInitStructure);TIM_OC1PreloadConfig(GENERAL_TIM3, TIM_OCPreload_Enable);// 输出比较通道 2TIM_OCInitStructure.TIM_Pulse CCR2_Val;TIM_OC2Init(GENERAL_TIM3, &TIM_OCInitStructure);TIM_OC2PreloadConfig(GENERAL_TIM3, TIM_OCPreload_Enable);// 输出比较通道 3TIM_OCInitStructure.TIM_Pulse CCR3_Val;TIM_OC3Init(GENERAL_TIM3, &TIM_OCInitStructure);TIM_OC3PreloadConfig(GENERAL_TIM3, TIM_OCPreload_Enable);// 输出比较通道 4TIM_OCInitStructure.TIM_Pulse CCR4_Val;TIM_OC4Init(GENERAL_TIM3, &TIM_OCInitStructure);TIM_OC4PreloadConfig(GENERAL_TIM3, TIM_OCPreload_Enable);// 使能计数器TIM_Cmd(GENERAL_TIM3, ENABLE);}void GENERAL_TIM3_Init(void){GENERAL_TIM3_GPIO_Config(); // 通用定时器GPIO口配置GENERAL_TIM3_Mode_Config();// 通用定时器输出模式等配置}
.h文件进行引脚等宏定义
#ifndef __BSP_GENERALTIME_H#define __BSP_GENERALTIME_H#include stm32f10x.h/*******************通用定时器TIM参数定义只限TIM2、3、4、5******************/// 当使用不同的定时器的时候对应的GPIO是不一样的这点要注意/******************************* TIM3 ***************************************/#define GENERAL_TIM3 TIM3#define GENERAL_TIM3_APBxClock_FUN RCC_APB1PeriphClockCmd#define GENERAL_TIM3_CLK RCC_APB1Periph_TIM3#define GENERAL_TIM3_Period 9#define GENERAL_TIM3_Prescaler 71// TIM3 输出比较通道1#define GENERAL_TIM3_CH1_GPIO_CLK RCC_APB2Periph_GPIOA#define GENERAL_TIM3_CH1_PORT GPIOA#define GENERAL_TIM3_CH1_PIN GPIO_Pin_6// TIM3 输出比较通道2#define GENERAL_TIM3_CH2_GPIO_CLK RCC_APB2Periph_GPIOA#define GENERAL_TIM3_CH2_PORT GPIOA#define GENERAL_TIM3_CH2_PIN GPIO_Pin_7// TIM3 输出比较通道3#define GENERAL_TIM3_CH3_GPIO_CLK RCC_APB2Periph_GPIOB#define GENERAL_TIM3_CH3_PORT GPIOB#define GENERAL_TIM3_CH3_PIN GPIO_Pin_0// TIM3 输出比较通道4#define GENERAL_TIM3_CH4_GPIO_CLK RCC_APB2Periph_GPIOB#define GENERAL_TIM3_CH4_PORT GPIOB#define GENERAL_TIM3_CH4_PIN GPIO_Pin_1/**************************函数声明********************************/void GENERAL_TIM3_Init(void);#endif/* __BSP_GENERALTIME_H */
main函数
int main(void){LED_GPIO_Config();//初始化函数//GENERAL_TIM3_Init();//GENERAL_TIM2_Init();//GENERAL_TIM4_Init();GENERAL_TIM5_Init(); while(1){GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);}}
想要验证这个程序需要一个LED灯当然也可以用别的可现实PWM变化的外设去替代。下载链接见文章末尾通用定时器PWM输出功能设计源码。
文章案例程序源码下载 一、SysTick定时器的延时函数毫秒、微秒源码链接
提取码rf8k
--来自百度网盘超级会员V1的分享
链接
提取码hjaw
链接
提取码ssun
--来自百度网盘超级会员V1的分享
链接
提取码wa4e
--来自百度网盘超级会员V1的分享
参考博客
STM32 定时器介绍--基本定时器-博客
STM32 系统定时器--SysTick_systick中断-博客
STM32 定时器介绍--通用、高级定时器_高级定时器和通用定时器_Haohao fighting的博客-博客