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

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精确延时函数_第1张图片

STM32普通定时器TIM6精确延时函数_第2张图片

 

具体实现代码

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

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

完结。

 

你可能感兴趣的:(STM32)