STM32普通定时器TIM6精确延时函数
Cortex-M系列都会带有systick定时器,但是有时候会被RTOS占用或者HAL库占用,这里提供一种普通定时器延时的方法。
将定时器用作延时函数的一般步骤
- 使能定时器的时钟
- 配置定时器的预分频值得到所需的频率
- 设置所需要计数的值,自增的计数器计数到这个值的时候就会产生事件
- 使能相应的事件,允许定时器产生相应的事件
- 开启定时器开始计时
- 等待延时时间到达
- 关闭定时器
等待延时时间到达的步骤可以是等待相应的标志位,也可以是不断比较定时器计数值寄存器的值跟预计的值
下面利用STM32的TIM6设计一个延时函数(TIM6功能简单,此函数也可以移植到其他通用定时器上)
TIM6&TIM7定时器介绍(部分摘自STM32Fxx中文参考手册)
TIM6 和 TIM7 简介
基本定时器 TIM6 和 TIM7 包含一个 16 位自动重载计数器,该计数器由可编程预分频器驱动。
此类定时器不仅可用作通用定时器以生成时基, 还可以专门用于驱动数模转换器 (DAC)。实
际上,此类定时器内部连接到 DAC 并能够通过其触发输出驱动 DAC。
这些定时器彼此完全独立,不共享任何资源。
TIM6 和 TIM7 的主要特性
基本定时器(TIM6 和 TIM7)的特性包括:
● 16 位自动重载递增计数器
● 16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改),分频系数
介于 1 和 65536 之间
● 用于触发 DAC 的同步电路
● 发生如下更新事件时会生成中断/DMA 请求:计数器上溢
时基单元
可编程定时器的主要模块由一个 16 位递增计数器及其相关的自动重载寄存器组成。计数器的
时钟可通过预分频器进行分频。
计数器、自动重载寄存器和预分频器寄存器可通过软件进行读写。即使在计数器运行时也可执
行读写操作。
时基单元包括:
● 计数器寄存器 (TIMx_CNT)
● 预分频器寄存器 (TIMx_PSC)
● 自动重载寄存器 (TIMx_ARR)
自动重载寄存器是预装载的。每次尝试对自动重载寄存器执行读写操作时,都会访问预装载寄
存器。预装载寄存器的内容既可以直接传送到影子寄存器,也可以在每次发生更新事件 UEV 时
传送到影子寄存器,这取决于 TIMx_CR1 寄存器中的自动重载预装载使能位 (ARPE)。当计数
器达到上溢值并且 TIMx_CR1 寄存器中的 UDIS 位为 0 时,将发送更新事件。该更新事件也可
由软件产生。下文将针对各配置的更新事件的产生进行详细介绍。
计数器由预分频器输出 CK_CNT 提供时钟,仅当 TIMx_CR1 寄存器中的计数器启动位 (CEN)
置 1 时,才会启动计数器。
请注意,实际的计数器使能信号 CNT_EN 在 CEN 置 1 的一个时钟周期后被置 1。
预分频器说明
预分频器可对计数器时钟频率进行分频,分频系数介于 1 和 65536 之间。该预分频器基于
TIMx_PSC 寄存器中的 16 位寄存器所控制的 16 位计数器。由于 TIMx_PSC 控制寄存器有
缓冲,因此可对预分频器进行实时更改。而新的预分频比将在下一更新事件发生时被采用。
图 189 和图 190 给出了在预分频比发生实时变化时一些计数器行为的示例。
具体实现代码
//延时函数的初始化,这里主要使能定时器时钟
void Delay_Tim_Init(void)
{
//RCC的APB1ENR寄存器的bit4置一,使能 TIM6 时钟
RCC->APB1ENR |= (1<<4);
}
//us级延时函数
//us需要延时的us数
void delay_us(uint16_t us)
{
//设置定时器预分频系数,TIM6时钟为90MHz,分频后时钟为1MHz即1us
//不同CPU的时钟可能不一样,PSC的值=定时器时钟/1MHz -1
//36M的定时器设置PSC为36-1
TIM6->PSC = (90-1);
//设置自动重装载值,定时器计数器的值自增到ARR时,会产生更新事件,ARR的值就是需要延时的时间
TIM6->ARR = us;
//重新初始化定时器计数器并生成寄存器更新事件,确保预分频值被采用,此时定时器将采用刚刚写入的预分频值,如果此处不更新,那么定时器需要等待下次更新事件的到来才会重新加载预分频值
TIM6->EGR |= (1<<0);
//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
TIM6->SR = 0;
//CR1的bit3(OPM)置一,计数器在发生下一更新事件时停止计数,单脉冲模式
TIM6->CR1 |= (1<<3);
//CR1的bit0(CEN)置一,启动定时器开始计数
TIM6->CR1 |= (1<<0);
//等待更新事件到来,计数器的值自增到自动重装载寄存器的时候,会产生更新事件,此时延时时间已到
while((TIM6->SR & 0x01)==0);
//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
TIM6->SR &= ~(1<<0);
}
//ms级延时函数
//最大延时时间 65535/2 = 32767.5ms
void delay_ms(uint16_t ms)
{
//设置定时器预分频系数,TIM6时钟为90MHz,分频后时钟为2KHz即500us,由于PSC为16位寄存器,所以无法分频至1KHz
//不同CPU的时钟可能不一样
TIM6->PSC = (45000-1);
//设置自动重装载值,定时器计数器的值自增到ARR时,会产生更新事件,ARR的值就是需要延时的时间的一半
TIM6->ARR = (ms*2);
//重新初始化定时器计数器并生成寄存器更新事件,确保预分频值被采用,此时定时器将采用刚刚写入的预分频值,如果此处不更新,那么定时器需要等待下次更新事件的到来才会重新加载预分频值
TIM6->EGR |= (1<<0);
//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
TIM6->SR = 0;
//CR1的bit3(OPM)置一,计数器在发生下一更新事件时停止计数,单脉冲模式
TIM6->CR1 |= (1<<3);
//CR1的bit0(CEN)置一,启动定时器开始计数
TIM6->CR1 |= (1<<0);
//等待更新事件到来,计数器的值自增到自动重装载寄存器的时候,会产生更新事件,此时延时时间已到
while((TIM6->SR & 0x01)==0);
//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
TIM6->SR &= ~(1<<0);
}
//重新初始化定时器计数器并生成寄存器更新事件,确保预分频值被采用,此时定时器将采用刚刚写入的预分频值,如果此处不更新,那么定时器需要等待下次更新事件的到来才会重新加载预分频值
TIM6->EGR |= (1<<0);
//清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零
TIM6->SR = 0;
上面两步一定不要忘记,笔者曾经出错的部分,当只使用一个延时函数的时候没有发现,两个延时函数同时使用时就会有问题
完结。
上一篇: Spring集成Quartz定时器实现定时任务(非注解方式)
下一篇: TIM定时器__功能1,定时