【STM32笔记】STM32 延时函数的实现

在MCU编程中,微秒延时和毫秒延时使用最为频繁,在RTOS中,毫秒延时可以由系统提供,可是微秒延时却需要开发人员编写。本文基于STM32F407ZG芯片实现几种微秒延时操作。

1、定时器延时

STM32F407里提供的定时器有:

  • 高级定时器:TIM1和TIM8,16位定时器
  • 通用定时器:TIM2到TIM5,TIM9到TIM14,16位定时器
  • 基本定时器:TIM6和TIM7,16位定时器

时钟总线:在这里插入图片描述

image-20210123135456834

本文采用基本定时器TIM6来作为微秒延时的定时器。

定时器6初始化:

/**
 * @brief  TIM6初始化 
 * @note   84MHz时钟频率,时钟分频84,每个CNT计数1us,16位定时器,最大可计数65535us
 */
void tim6Init(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);  /* 初始化时钟 */

	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStructure);
	
	TIM_TimeBaseInitStructure.TIM_Period        = 65535;                  /* 最大计数 */ 	
	TIM_TimeBaseInitStructure.TIM_Prescaler     = (84-1);                 /* 定时器分频,分频后频率为1MHz,最低1us定时 */ 
	TIM_TimeBaseInitStructure.TIM_CounterMode   = TIM_CounterMode_Down;   /* 向下计数 */
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;           /* 时钟分频 */

	TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStructure);                    /* 初始化 */
	TIM_Cmd(TIM6,ENABLE);                                                 /* 使能定时器 */
}

关闭定时器:

/**
 * @brief  关闭定时器 
 * @note   延时结束后关闭定时器
 */
void tim6Stop(void)
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,DISABLE);
  TIM_Cmd(TIM6,DISABLE); 
}

微秒延时:

/**
 * @brief  微秒延时 
 * @param  uint32_t nUs  延时数
 */
void delayUs(uint32_t nUs)
{
  volatile uint32_t startCnt = 0;

  (nUs >= 65535) ? (nUs = 65536) : (nUs = nUs);     /* 16位定时器,最大计数延时65535us */

  tim6Init();   /* 初始化定时器 */

  startCnt = TIM6->CNT;

  while((TIM6->CNT-startCnt) < nUs);

  tim6Stop();
}

以上代码在单线程裸机系统中,在准确度上可以满足MCU的微秒延时使用;但是在多线程的RTOS中,会因为在多线程中同时调用微秒延时,导致线程A在while里等待延时时,定时器被线程B关闭,CPU无法释放。

微秒延时(代码段保护):

/**
 * @brief  微秒延时 
 * @param  uint32_t nUs  延时数
 */
void delayUs(uint32_t nUs)
{
  volatile uint32_t startCnt = 0;

  (nUs >= 65535) ? (nUs = 65535) : (nUs = nUs);     /* 16位定时器,最大计数延时65535us */

  rt_enter_critical();            /* 进入保护段,禁止系统调度 */

  tim6Init();   /* 初始化定时器 */

  startCnt = TIM6->CNT;

  while((TIM6->CNT-startCnt) < nUs);

  tim6Stop();

  rt_exit_critical();             /* 离开保护段 */
}

2、嘀嗒定时器(中断方式)

STM32的cotex内核上提供了一个嘀嗒定时器(SysTick),嘀嗒定时器是一个24位的计时器,向下计数的方式,当计数倒数到0时,将从RELOAD寄存器里自动重装载值。只要CTRL寄存器的ENABLE位不被清除,则嘀嗒定时器用不停止。

可以用嘀嗒定时器为系统提供微秒延时。

嘀嗒定时器初始化:

/**
 * @brief  嘀嗒定时器初始化 
 * @param  {type}
 * @retval none
 */
void sysTickInit(void)
{
  RCC_GetClocksFreq(&RCC_Clocks);     /* 获取系统时钟 */
  SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000);   /* 配置嘀嗒为1ms */
  SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;         /* 关闭嘀嗒 */
}

延时实现:

volatile uint32_t delayTimer;

/**
 * @brief  嘀嗒中断服务函数 
 * @param  {type}
 * @retval none
 */
void SysTick_Handler(void)
{
  if(delayTimer)
  {
    delayTimer --;
  }
}

/**
 * @brief  延时函数 
 * @param  {type}
 * @retval none
 */
void delay(uint32_t nTimer)
{
  delayTimer = nTimer;

  SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;           /* 开启嘀嗒 */

  while(delayTimer);

  SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;         /* 关闭嘀嗒 */
}

3、嘀嗒定时器(查询)

中断方式会频繁打断系统,可以使用查询嘀嗒计数器的方式完成延时。
image-20210123152205616
image-20210123152355015
image-20210123152625792

在此贴上一段RT-TRHEAD的嘀嗒初始化函数:

/**
 * @brief  嘀嗒定时器初始化 
 * @note   使用RT-THREAD的嘀嗒初始化,嘀嗒超时事件1ms,嘀嗒时钟8分频
 */
void SysTick_Configuration(void)
{
  RCC_ClocksTypeDef  rcc_clocks;
  rt_uint32_t         cnts;

  RCC_GetClocksFreq(&rcc_clocks);

  cnts = (rt_uint32_t)rcc_clocks.HCLK_Frequency / 1000;
  cnts = cnts / 8;

  SysTick_Config(cnts);
  SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
}

/**
 * This is the timer interrupt service routine.
 *
 */
void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();			/* 系统时间片累加 */
		
    /* leave interrupt */
    rt_interrupt_leave();	
}

时钟8分频,每1ms完成一次中断,每1ms完成一次系统时间片的累加计数。

初始化后的嘀嗒重装载值(SysTick->RELOAD)为:
168000000 / 1000 / 8 = 21000 168000000 / 1000 / 8 = 21000 168000000/1000/8=21000
嘀嗒计数满21000个值,时间刚好是1ms,故每一次计数的时间为:
( 0.001 s / 21000 ) s (0.001 s / 21000) s (0.001s/21000)s
嘀嗒定时器延时实现:

/**
 * @brief  微秒延时 
 * @param  {type}
 * @retval none
 */
void delayUs(uint32_t nUs)
{
  uint32_t ticks;
	uint32_t told,tnow,tcnt=0;
	uint32_t reload=SysTick->LOAD;	

	rt_enter_critical();
	
	ticks = nUs*(168 >> 3); 		//168 MHZ		
	told  = SysTick->VAL;       
   			
	while(1)
	{
		tnow = SysTick->VAL;	

		if(tnow != told)
		{	    
			if(tnow < told)
			{
				tcnt += told - tnow;
			}
			else 
			{
				tcnt += reload - tnow + told;	
			}		

			told = tnow;

			if(tcnt >= ticks)
			{
				break;
			}				
		}  
	}

	rt_exit_critical();
}

最低的延时时间为1us。

  1. 完成1us需要嘀嗒定时器VAL的计数值:

    前面计算的每一次计数时间为:(0.001 s / 21000) s,

    完成1us需要的计数为:
    t i c k s = 1 u s / ( 0.001 s / 21000 ) = 21 ticks = 1us / (0.001s / 21000) = 21 ticks=1us/(0.001s/21000)=21

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在使用STM32CubeMX函数是HAL_Delay()函数。 HAL_Delay()函数是基于操作系统钟的函数,以毫秒为单位进行。然而,如果需要微秒级别的,可以使用通用定器(General-Purpose Timer)来实现。 使用通用定器,可以通过配置计器的预分频器和计数器来实现微秒级的功能。具体的实现方法可以参考《嵌入式-STM32开发指南》和《STM32CubeMX实战教程》中关于定器的章节。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STM32CubeMX学习笔记(4)——系统使用](https://blog.csdn.net/qq_36347513/article/details/112553860)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [CUBEMX生成STM32F429的CAN1和CAN2程序,亲测收发可用](https://download.csdn.net/download/niushijia007/12844401)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [用 STM32 通用定器做微秒函数STM32CubeMX版本)](https://blog.csdn.net/qq_33974167/article/details/110413882)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值