延时函数:普通延时,硬件定时器延时,系统定时器延时

news2024/12/24 20:38:40

一、普通延时函数

此种延时是基于让MCU做一些无意义的循环操作来打发时间,优点是简单易懂,缺点是会占用MCU的处理资源且精度较低,主要用于程序简单、无严格时间要求的场景中。

//微秒级的延时
void delay_us(uint32_t delay_us)
{    
  volatile unsigned int num;
  volatile unsigned int t;

  
  for (num = 0; num < delay_us; num++)
  {
    t = 11;
    while (t != 0)
    {
      t--;
    }
  }
}
//毫秒级的延时
void delay_ms(uint16_t delay_ms)
{    
  volatile unsigned int num;
  for (num = 0; num < delay_ms; num++)
  {
    delay_us(1000);
  }
}

二、基于硬件定时器中断的延时函数

硬件定时器中断延时,需注意:

1、此种方式会频繁进出中断进行计数;

2、当需要在其他中断中使用此种延时方式时,需要提高该定时器中断优先级高于所调用中断,否则会造成高优先级中断中调用低优先级中断,程序卡死;

3、有些高精度的应用场景不适合,比如其他外设正在输出,不允许任何中断打断的情况。

uint32_t sysTime = 0;

/*
 *  函数名:void TimerInit(void)
 *  输入参数:
 *  输出参数:无
 *  返回值:无
 *  函数作用:初始化定时器,使其时钟频率为4MHz
*/
void Timer3Init(void)
{
    htim3.Instance               = TIM3;
    htim3.Init.Prescaler         = 72-1;                             
    htim3.Init.Period            = 10-1;                             
    htim3.Init.CounterMode       = TIM_COUNTERMODE_UP;               // 向上计数,按上面两行的配置:
                                                                    // 每1MHz即1us会向上计数1次,当计数4次会引发定时器中断或产生溢出事件
    htim3.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;           // 定时器时钟不从HCLK分频
    htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;   // 不重新装载或与装载
    
    // 初始化上面的参数
    if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
    {
        Error_Handler();
    }
    
    if(HAL_TIM_Base_Start_IT(&htim3) != HAL_OK)
    {
        Error_Handler();
    }
}


void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    
    if(htim->Instance == TIM3)
    {
        __HAL_RCC_TIM3_CLK_ENABLE();
        
        /* 配置定时器中断优先级并使能 */
        HAL_NVIC_SetPriority(TIM3_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(TIM3_IRQn);
    }
}

void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim3);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//HAL_TIM_IRQHandler()调用此函数
{
    if(htim->Instance == TIM3)
    {
        sysTime++;
    }
}



/*
 *  函数名:Delay(uint32_t nTime)
 *  输入参数:延时时间
 *  输出参数:无
 *  返回值:无
 *  函数作用:延时函数
*/
void Delay(uint32_t nTime)
{
  sysTime = nTime;
  while(sysTime != 0);
}




三、基于硬件定时器的查询方式延时(推荐☆☆☆)

/*
 * 定义全局变量
*/
TIM_HandleTypeDef htim2;
uint32_t sysTime = 0;

/*
 *  函数名:void TimerInit(void)
 *  输入参数:
 *  输出参数:无
 *  返回值:无
 *  函数作用:初始化定时器
*/
void TimerInit(void)
{
    TIM_ClockConfigTypeDef sClockSourceConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
  
    htim2.Instance               = TIM2;
    htim2.Init.Prescaler         = 72-1;                             // HCLK = 72MHz, 72MHz/72 = 1MHz
    htim2.Init.Period            = 0;                                // 即每1us会计数满一次
    htim2.Init.CounterMode       = TIM_COUNTERMODE_UP;               // 向上计数,按上面两行的配置:
                                                                 // 每1MHz即1us会向上计数1次,当计数4次会引发定时器中断或产生溢出事件
    htim2.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;           // 定时器时钟不从HCLK分频
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;   // 不重新装载或与装载
    
    // 初始化上面的参数
    if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
    {
        Error_Handler();
    }
    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;    // 选用内部时钟作为定时器时钟源
    if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
    {
        Error_Handler();
    }
    
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;           // 计数器复位不产生更新事件
    sMasterConfig.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;  // 主从模式不使能
    if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
    {
        Error_Handler();
    }
}


void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2)
    {
        __HAL_RCC_TIM2_CLK_ENABLE();
    }
}


/*
 *  函数名:void us_timer_delay(uint16_t t)
 *  输入参数:t-延时时间us
 *  输出参数:无
 *  返回值:无
 *  函数作用:定时器实现的延时函数,延时时间为t us,为了缩短时间,函数体使用寄存器操作,用户可对照手册查看每个寄存器每一位的意义
*/
void us_timer_delay(uint16_t t)
{
    uint16_t counter = 0;
	__HAL_TIM_SET_AUTORELOAD(&htim2, t);	    //设定计数值	
	__HAL_TIM_SET_COUNTER(&htim2, counter);     //计数器复位
	HAL_TIM_Base_Start(&htim2);                 //定时器开始计数
	while(counter != t)
	{
		counter = __HAL_TIM_GET_COUNTER(&htim2);
	}
	HAL_TIM_Base_Stop(&htim2);
}

四、基于系统定时器的中断方式延时(推荐☆☆☆)

如需要实现毫秒延时,首先设置系统定时器1毫秒中断一次,在 Delay(__IO uint32_t nTime)中和输入需要延时的ms数即可。

/*
 *  函数名:void SysTickInit(uint16_t t)
 *  输入参数:t-设定时间:1->ms; 2->100us; 3->10us
 *  输出参数:无
 *  返回值:无
 *  函数作用:初始化系统滴答时钟的频率和中断优先级
*/
void SysTickInit(uint16_t t)
{
    uint32_t init_t = 0;
    if(t==1)
    {
        init_t = SystemCoreClock/1000;
    }
    else if(t==2)
    {
        init_t = SystemCoreClock/10000;
    }
    else if(t==3)
    {
        init_t = SystemCoreClock/100000;
    }
    
    /* 配置滴答时钟频率
     * SystemCoreClock/1000:   1ms中断一次
     * SystemCoreClock/10000:  10us中断一次
     * SystemCoreClock/100000: 1us中断一次
     * 中断时间计算如下:SystemCoreClock = 72MHz = 72,000,000
     *                   SystemCoreClock/1000 = 72,000
     * 即系统向下计数72,000次系统时钟频率即:(72,000/72MHz)s = (1/1000)s = 1ms后中断一次
    */
    if(HAL_SYSTICK_Config(init_t) != HAL_OK)
    {
        Error_Handler();
    }
    
    // 设置滴答定时器中断优先级:最高
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
    // 使能滴答定时器中断
    HAL_NVIC_EnableIRQ(SysTick_IRQn);
}

/*
 *  函数名:SysTick_Handler(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 *  函数作用:中断服务函数
*/
void SysTick_Handler(void)
{
    if (TimingDelay != 0x00)
  { 
    TimingDelay--;
  }
}

/*
 *  函数名:Delay(__IO uint32_t nTime)
 *  输入参数:延时时间
 *  输出参数:无
 *  返回值:无
 *  函数作用:延时函数
*/
void Delay(__IO uint32_t nTime)
{
  TimingDelay = nTime;
  while(TimingDelay != 0);
}

五、基于系统定时器的查询方式延时(推荐☆☆☆☆☆)

为了解决定时器频繁中断的问题,我们可以使用定时器,但是不使能中断,使用查询的方式去延时,这样既能解决频繁中断问题,又能保证精度。

STM32任何定时器都可以实现,下面我们以SysTick 定时器为例介绍。

STM32的CM3内核的处理器,内部包含了一个SysTick定时器,SysTick是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。

SYSTICK的时钟固定为HCLK时钟的1/8,在这里我们选用内部时钟源120M,所以SYSTICK的时钟为(120/8)M,即SYSTICK定时器以(120/8)M的频率递减。SysTick 主要包含CTRL、LOAD、VAL、CALIB 等4 个寄存器。

void delay_us(uint32_t nus)
{
  uint32_t temp;
  SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000000/8*nus;
  SysTick->VAL=0X00;//清空计数器
  SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
  do
  {
    temp=SysTick->CTRL;//读取当前倒计数值
  }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
  SysTick->CTRL=0x00; //关闭计数器
  SysTick->VAL =0X00; //清空计数器
}
void delay_ms(uint16_t nms)
{
  uint32_t temp;
  SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000/8*nms;
  SysTick->VAL=0X00;//清空计数器
  SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
  do
  {
    temp=SysTick->CTRL;//读取当前倒计数值
  }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
  SysTick->CTRL=0x00; //关闭计数器
  SysTick->VAL =0X00; //清空计数器
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/644793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言实战之、<<、>>

1、&&#xff08;按位与&#xff09; 按位与运算将两个运算分量的对应位按位遵照以下规则进行计算&#xff1a; 0 & 0 0, 0 & 1 0, 1 & 0 0, 1 & 1 1。 即同为 1 的位&#xff0c;结果为 1&#xff0c;否则结果为 0。 例如&#xff0c;设3的内部表示为…

6--Gradle进阶 - 项目的生命周期

6--Gradle进阶 - 项目的生命周期 项目的生命周期 Gradle 项目的生命周期分为三大阶段: Initialization -> Configuration -> Execution. 每个阶段都有自己的职责,具体如下图所示: Initialization 阶段主要目的是初始化构建, 它又分为两个子过程,一个是执行 Init Script,另…

转载:卷积神经网络结构组成与解释

原文链接&#xff1a;卷积神经网络结构组成与解释 卷积神经网络是以卷积层为主的深度网路结构&#xff0c;网络结构包括有卷积层、激活层、BN层、池化层、FC层、损失层等。卷积操作是对图像和滤波矩阵做内积&#xff08;元素相乘再求和&#xff09;的操作。 1. 卷积层 常见的…

华为OD机试真题 JavaScript 实现【货币单位换算】【2023Q1 100分】

一、题目描述 记账本上记录了若干条多国货币金额&#xff0c;需要转换成人民币分 (fen)&#xff0c;汇总后输出每行记录一条金额&#xff0c;金额带有货币单位&#xff0c;格式为数字单位&#xff0c;可能是单独元&#xff0c;或者单独分&#xff0c;或者元与分的组合要求将这…

Vue的传值

目录 1. 属性传值 1.1 语法 1.2 属性和数据源同名 2. 反向传值 2.1 属性绑定自定义事件 简单案例&#xff1a; 购物车算总价案例&#xff1a; 2.2 v-model 组件的双向数据绑定 3. 透传&#xff08;多层组件传值&#xff09; 3.1 类型透传 3.2 属性穿透 v-bind"…

『2023北京智源大会』6月9日会议内容

『2023北京智源大会』6月9日上午|开幕式及全体大会 文章目录 一. 黄铁军丨智源研究院院长1. 大语言模型2. 大语言模型评测体系FlagEval3. 大语言模型生态(软硬件)4. 三大路线通向 AGI(另外2条路径) 二. Towards Machines that can Learn, Reason, and Plan(杨立昆丨图灵奖得主…

Java的垃圾回收机制详解

目录 1、C语言与Java语言垃圾回收区别 2、System.gc() 3、面试题引入Java垃圾回收 3.1 jvm怎么确定哪些对象应该进行回收 3.1.1 引用计数法 3.1.2 可达性分析算法 3.2 jvm会在什么时候进行垃圾回收的动作 3.2 jvm到底是怎么回收垃圾对象的 4、来回收算法 4.1 标记-清…

java是值传递还是引用传递

文章目录 1.前言2.java是值传递还是引用传递 1.前言 java是值传递&#xff1b;值传递是指在调用方法时将实际参数拷贝一份传递到方法中&#xff0c;这样在方法中如果对参数进行修改&#xff0c;将不会影响到实际参数&#xff1b;当传的是基本类型时&#xff0c;传的是值的拷贝…

探究MES系统:工业生产数字化转型的必需品

随着时代的发展&#xff0c;工业生产数字化转型已经成为不可避免的趋势。而MES系统作为工业生产运营管理领域中的一种重要软件系统&#xff0c;更是在数字化转型过程中扮演着重要的角色。 什么是MES系统 MES系统全称为制造执行系统&#xff08;Manufacturing Execution Syste…

快速傅里叶变换python实现

img { margin: auto; display: block } 一、前言 我想认真写好快速傅里叶变换(Fast Fourier Transform&#xff0c;FFT)&#xff0c;所以这篇文章会由浅到细&#xff0c;由窄到宽的讲解&#xff0c;但是傅里叶变换对于寻常人并不是很容易理解的&#xff0c;所以对于基础不牢的人…

基础知识学习---链表基础

1、本栏用来记录社招找工作过程中的内容&#xff0c;包括基础知识学习以及面试问题的记录等&#xff0c;以便于后续个人回顾学习&#xff1b; 暂时只有2023年3月份&#xff0c;第一次社招找工作的过程&#xff1b; 2、个人经历&#xff1a; 研究生期间课题是SLAM在无人机上的应…

Vue中如何进行音频与视频播放?

Vue中如何进行音频与视频播放&#xff1f; 在Vue中&#xff0c;我们可以使用HTML5的<audio>和<video>标签来实现音频和视频的播放。Vue本身并没有提供专门的音频或视频播放组件&#xff0c;但是可以使用Vue的数据绑定和生命周期钩子来控制音频和视频的播放。 音频…

【gcc, cmake, eigen, opencv,ubuntu】一.gcc介绍

文章目录 gcc介绍1.查看当前gcc 版本2.安装其他版本的gcc3.设置多个版本的优先级4.修改默认的版本5.查看cpu信息 gcc介绍 gcc介绍和makefile介绍 1.查看当前gcc 版本 gcc --version2.安装其他版本的gcc sudo apt install gcc-10 g-10这样我们电脑里包含gcc-9 和 gcc-10两个…

[玩游戏想道理]战略的感受

game&#xff1a;金铲铲之战 战略的定义可以说是非常多了&#xff0c;这里主要是就游戏中的过程谈一些实践感受和理解&#xff1b; 这里也不是谈战略的定义xxxx&#xff0c;这方面的理论包括《孙子兵法》都是强太多了&#xff0c;主要就是在游戏这个“现实模拟器”中&#xff0…

全志V3S嵌入式驱动开发(开机脚本、程序运行)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前为止的内容&#xff0c;大部分都是和驱动相关的。就算有部分上层代码&#xff0c;也只是为了测试驱动是否ok而编写的。事实上&#xff0c;作为…

团队管理之性能实施团队日志13

项目经过了&#xff0c;8 个业务系统和 7 个基础架构系统的测试之后&#xff0c;又完成了全链路的两个域的测试&#xff0c;终于进入了尾声。 过程中发现了 257 个问题&#xff08;只统计了 8 个业务系统&#xff09;&#xff0c;平均每个系统 32.125 个。问题 age 达到 861.77…

「微服务架构模式」编曲与编舞——让系统协同工作的不同模式

介绍 Krzysztof&#xff08;采访者&#xff09;&#xff1a;商业组织是由专家组成的&#xff0c;他们在他们最了解的领域提供产品或服务&#xff0c;以获得共同的商业成果。例如&#xff0c;营销团队努力争取新客户&#xff0c;销售团队向这些客户销售产品&#xff0c;客户关系…

用4种回归方法绘制预测结果图表:向量回归、随机森林回归、线性回归、K-最近邻回归

文章目录 表格部分数据如下运行效果如下代码解析完整代码附件 表格部分数据如下 附件里会给出全部数据链接 运行效果如下 代码解析 import pandas as pd import numpy as np import matplotlib.pyplot as plt from matplotlib.font_manager import FontPropertiesfont FontP…

FPGA实现USB3.0 UVC 相机彩条视频输出 基于FT602驱动 提供工程源码和QT上位机源码

目录 1、前言2、UVC简介3、FT602芯片解读4、我这儿的 FT601 USB3.0通信方案5、详细设计方案基于FT602的UVC模块详解 6、vivado工程详解7、上板调试验证8、福利&#xff1a;工程代码的获取 1、前言 目前USB3.0的实现方案很多&#xff0c;但就简单好用的角度而言&#xff0c;FT6…

用代码玩转迷你图:手把手教你用编程语言打造简洁易读的数据图表!

前言 迷你图&#xff08;Mini Chart&#xff09;最早起源于流程图和组织架构图中的一种简化图形&#xff0c;用于表示一个大型数据集合中的趋势和变化。随着数据可视化技术的发展&#xff0c;迷你图也被广泛应用在各种类型的数据图表中&#xff0c;例如折线图、柱形图、散点图…