欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32普通定时器TIM6精确延时函数

程序员文章站 2022-06-09 11:22:38
...

Cortex-M系列都会带有systick定时器,但是有时候会被RTOS占用或者HAL库占用,这里提供一种普通定时器延时的方法。

将定时器用作延时函数的一般步骤

  1. 使能定时器的时钟
  2. 配置定时器的预分频值得到所需的频率
  3. 设置所需要计数的值,自增的计数器计数到这个值的时候就会产生事件
  4. 使能相应的事件,允许定时器产生相应的事件
  5. 开启定时器开始计时
  6. 等待延时时间到达
  7. 关闭定时器

等待延时时间到达的步骤可以是等待相应的标志位,也可以是不断比较定时器计数值寄存器的值跟预计的值

下面利用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 给出了在预分频比发生实时变化时一些计数器行为的示例。

 

STM32普通定时器TIM6精确延时函数

STM32普通定时器TIM6精确延时函数

 

具体实现代码

//延时函数的初始化,这里主要使能定时器时钟
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;

上面两步一定不要忘记,笔者曾经出错的部分,当只使用一个延时函数的时候没有发现,两个延时函数同时使用时就会有问题

完结。