STM32-HAL-usDelay

news2024/10/7 16:15:56

一、STM32单片机的延时

STM32单片机的延时,是指在程序中暂停一段时间,等待一定的时间后再继续执行下一条指令。常见的延时方式有循环延时定时器延时

毫秒延时的使用场景:

  • 等待外设完成某项操作:在使用外设时,有时需要等待外设完成某项操作才能进行下一步操作。例如,在使用SPI通信OneWire通信(DS18B20 DHT11等)时,需要等待数据传输完成才能读取接收到的数据。
  • 控制任务执行时间:在多任务系统中,任务的执行时间需要控制在一定范围内,以避免出现任务响应时间不稳定、任务饥饿等问题。
  • 实现延时操作:有时需要在任务中实现一些延时操作,例如等待一定时间后再执行某些操作。
  • 实现周期性任务:在一些周期性任务中,需要在每个周期内执行一定的操作。此时,可以使用毫秒延时来实现周期性的触发。

需要注意的是,使用毫秒延时时应注意精度和稳定性。
在需要更高精度的应用中,可以考虑使用定时器来实现延时。

毫秒延时的实现方法:

  • 使用SysTick定时器

  • 使用TIM定时器

  • 使用for循环进行延时

  • 使用DWT寄存器

二、测试准备

  • 基于STM32L431RCT6的小熊派开发板在这里插入图片描述
  • 安装windows系统并安装CubemxKeil MDK的电脑

三、初始化片上外设

3.1 设置板载的LED-PC13和PA11为推挽输出模式
在这里插入图片描述
同样设置PA11引脚为推挽输出模式,为的是在后面方便测试。

在这里插入图片描述
3.2 使用定时器进行毫秒延时

使用定时器进行精确延时的原理:
定时器在设置好初始值的时候,便会自增,在自增的过程中便会产生一个时间等待,使用定时器的精确计数便可以精确设置要等待的时间。

  • 打开定时器TIM2

在这里插入图片描述
设置定时器2的Clock Source Internal Clock

  • 时钟源设置为外部高速时钟(使用内部产生的时钟源也可)

在这里插入图片描述
【重要】查看开发板的板载晶振的频率(根据自己的开发板的晶振频率设置),因此设置输入的时钟的频率为8Hz,经过分频后最后设置频率为最大80MHz,查看经过设置后定时器所在的外设桥时钟频率亦为80MHz

在这里插入图片描述

  • 针对TIM2的一些参数进行配置

在这里插入图片描述

设置分频系数为84 - 1 ,则定时器的计数时钟频率为1MHz,即为计数一次需要消耗1s/1M = 1us的时间。

设置的计数周期是65535,本次实验不涉及中断,因此不需要开启中断。

  • 设置生成Keil-MDK代码文件

3.3 使用SysTick定时器进行毫秒延时

STM32的滴答定时器(SysTick)是一种基于硬件的定时器,可以提供系统级别的延时和定时功能。它使用一个24位计数器和一些相关寄存器来控制其行为。

滴答定时器的相关寄存器介绍:

[来自core_cm3.h]

/** @addtogroup CMSIS_CM3_SysTick CMSIS CM3 SysTick
  memory mapped structure for SysTick
  @{
 */
typedef struct
{
  __IO uint32_t CTRL;             /*!< Offset: 0x00  SysTick Control and Status Register */
  __IO uint32_t LOAD;             /*!< Offset: 0x04  SysTick Reload Value Register       */
  __IO uint32_t VAL;              /*!< Offset: 0x08  SysTick Current Value Register      */
  __I  uint32_t CALIB;            /*!< Offset: 0x0C  SysTick Calibration Register        */
} SysTick_Type;
CTRLSysTick控制和状态寄存器,用于控制SysTick计数器的启动、中断、时钟源以及清零等操作
LOADSysTick重装载值寄存器,用于设置SysTick计数器的重装载值
VALSysTick当前值寄存器,用于读取SysTick计数器当前的计数值
CALIBSysTick校准寄存器,用于获取SysTick计数器的时钟周期数和是否支持64位读取操作的信息

四、测试

4.1 编写定时器的延时代码

[在tim.c的代码添加处添加]
/* USER CODE BEGIN 1 */

/*设置的milliseconds需要小于65535阈值*/

void us_Delay(uint32_t milliseconds)                     // 延时函数,参数为需要延时的毫秒数
{
	HAL_TIM_Base_Start(&htim2);                          // 启动定时器 2

	__HAL_TIM_SET_COUNTER(&htim2, 0);                    // 将定时器 2 的计数器清零

	while(__HAL_TIM_GET_COUNTER(&htim2) < milliseconds); // 等待定时器 2 的计数器达到指定的毫秒数
	
	HAL_TIM_Base_Stop(&htim2);                           // 停止定时器 2
}
/* USER CODE END 1 */

/*
设置延时时间为1ms 但是由于就是代码在执行的过程中是也有执行时间的,因此就是需要手动调节延时参数。
*/
void 1ms_Delay(void)
{
    us_Delay(951);
}
/*
设置延时时间为1s
*/
void one_s_Delay(void)
{
	for(uint16_t i = 0;i<1000;i++)
	{
		one_ms_Delay();
	}	
}
[在主函数while循环中添加]
 /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_GPIO_WritePin(USER_GPIO_Port,USER_Pin,GPIO_PIN_SET);  //设置PA11为高电平
     	//HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);  //设置PC13为高电平
		one_ms_Delay();                                           //延时1ms
        //one_s_Delay();                                          //延时1s
		HAL_GPIO_WritePin(USER_GPIO_Port,USER_Pin,GPIO_PIN_RESET);//设置PA11为低电平
     	//HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);  //设置PC13为高电平
		one_ms_Delay();                                           //延时1ms
        //one_s_Delay();                                          //延时1s
  }

使用逻辑分析仪器进行电平的分析

设置延时时间为1ms

在这里插入图片描述
设置延时时间为1s 但是不是严丝合缝的1s

在这里插入图片描述
4.2 编写SysTick定时器的延时代码

使用滴答定时器配置相关寄存器,实现us、ms 和s级的延时

下面是适用于固件库中的一个us级延时代码

[用于固件库中使用,节选自一个项目中]
/**
*@brief		初始化延迟函数
*@param		SYSCLK:系统时钟
*@return	无
*/
void systick_init (u8 sysclk)
{
	SysTick->CTRL&=0xfffffffb;							/*bit2清空,选择外部时钟  HCLK/8*/
	fac_us=sysclk/8;		    
	fac_ms=(u16)fac_us*1000;
}	

/**
*@brief	    微秒延时函数
*@param		time_ms:要延时微秒时间数
*@return	无
*/
void delay_us(uint32 time_us)
{		
	u32 temp;	    	 
	SysTick->LOAD=time_us*fac_us; 		/* 将时间加载进SysTick的重载值寄存器 */ 		 
	SysTick->VAL=0x00;                  /*清空计数器*/
	SysTick->CTRL=0x01 ;      	        /* 开始倒数,使用内部时钟,开启SysTick计时器 */	
	do
	{
		temp=SysTick->CTRL;             /* 获取SysTick的CTRL寄存器值 */
	}
	while(temp&0x01&&!(temp&(1<<16)));	/*等待时间到达*/
	SysTick->CTRL=0x00;       			/*关闭SysTick计数器*/
	SysTick->VAL =0X00;       		    /*清空计数器*/
}


/**
*@brief	    毫秒延时函数
*@param		time_ms:要延时毫秒时间数
*@return	无
*/
void delay_ms(uint32 time_ms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)time_ms*fac_ms;		/*时间加载(SysTick->LOAD为24bit)*/
	SysTick->VAL =0x00;           			/*清空计数器*/
	SysTick->CTRL=0x01 ;         			/*开始倒数*/ 
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));		/*等待时间到达*/
	SysTick->CTRL=0x00;       				/*关闭计数器*/
	SysTick->VAL =0X00;      			    /*清空计数器*/	  	    
} 

/**
*@brief	    秒延时函数
*@param		time_s:要延时秒时间数
*@return	无
*/
void delay_s(uint32 time_s)
{
  for(;time_s>0;time_s--)
    delay_ms(1000);
}
[在主函数中添加]
systick_init(72);				      	/*初始化Systick工作时钟*/
while(1)
{
    delay_us(1000);
    printf("一毫秒延时打印测试");
}

下面是适用于HAL库中的一个us级延时代码

[用于HAL库中,代码节选自正点原子]
//此段代码需要屏蔽,因为和Cubemx生成的代码冲突
//static uint32_t g_fac_us = 0;       /* us延时倍乘数 */
///**
// * @brief     初始化延迟函数
// * @param     sysclk: 系统时钟频率, 即CPU频率(rcc_c_ck), 168MHz
// * @retval    
// */
//void delay_init(uint16_t sysclk)
//{
//    g_fac_us = sysclk;    
//}


/**
 * @brief       延时nus
 * @param       nus: 要延时的us数.
 * @note        nus取值范围 : 0~190887435(最大值即 2^32 / fac_us @fac_us = 21)
 * @retval      无
 */
void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD;        /* LOAD的值 */
    ticks = nus * g_fac_us;                 /* 需要的节拍数 */
    told = SysTick->VAL;                    /* 刚进入时的计数器值 */
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow;        /* 这里注意一下SYSTICK是一个递减的计数器就可以了 */
            }
            else 
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;                      /* 时间超过/等于要延迟的时间,则退出 */
            }
        }
    }
}
/**
 * @brief       延时nms
 * @param       nms: 要延时的ms数 (0< nms <= 65535)
 * @retval      无
 */
void delay_ms(uint16_t nms)
{
    uint32_t repeat = nms / 540;    /*  这里用540,是考虑到可能有超频应用, 比如248M的时候,delay_us最大只能延时541ms左右了 */
    uint32_t remain = nms % 540;

    while (repeat)
    {
        delay_us(540 * 1000);        /* 利用delay_us 实现 540ms 延时 */
        repeat--;
    }

    if (remain)
    {
        delay_us(remain * 1000);    /* 利用delay_us, 把尾数延时(remain ms)给做了 */
    }
}

/**
 * @brief       HAL库内部函数用到的延时
 * @note        HAL库的延时默认用Systick,如果我们没有开Systick的中断会导致调用这个延时后无法退出
 * @param       Delay : 要延时的毫秒数
 * @retval      None
 */
void HAL_Delay(uint32_t Delay)
{
     delay_ms(Delay);
}


[在主函数while中添加]

/* USER CODE BEGIN 2 */
#define g_fac_us 84   //需要添加此宏定义,然后就可以用正点原子的代码托管后续的延时函数
/* USER CODE END 2 */

/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_GPIO_WritePin(USER_GPIO_Port,USER_Pin,GPIO_PIN_SET);  //设置PA11为高电平
    delay_us(952);                                           //延时1ms  
    HAL_GPIO_WritePin(USER_GPIO_Port,USER_Pin,GPIO_PIN_RESET);//设置PA11为低电平   
    delay_us(952);                                            //延时1ms    
  }

/* USER CODE END 3 */

在我的代码中设置为延时952us可以呈现1ms的电平变化效果,因此在使用的时候需要根据自己的代码自行调试。

后面的逻辑分析仪分析不再展示

4.3 编写for循环实现的延时代码

/* USER CODE BEGIN 4 */
void delay_us(uint32_t us)
{
    for(uint32_t i = 0; i < us * 16; i++)
    {
        __NOP();
    }
}
/* USER CODE END 4 */

[while循环中添加]

/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_GPIO_WritePin(USER_GPIO_Port,USER_Pin,GPIO_PIN_SET);  //设置PA11为高电平
    delay_us(1000);                                           //延时1ms  
    HAL_GPIO_WritePin(USER_GPIO_Port,USER_Pin,GPIO_PIN_RESET);//设置PA11为低电平   
    delay_us(1000);                                            //延时1ms    
  }

/* USER CODE END 3 */

这个延时数量不需要修改,非常准确的延时

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

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

相关文章

【安全与风险】总结篇

总结篇 期望学习效果学习关键点安全基础一些术语安全策略CIA 密码学概论对称vs非对称对称密码:定义非对称密码学(公钥密码学)密钥生成加密解密技术反向使用:数字签名 基础计算资源安全访问控制列表读、写、执行权限位DoS攻击 恶意软件什么是恶意软件恶意软件的类型基于主机的恶…

SpringBoot中集成任务调度

文章目录 SpringBoot中集成任务调度1. 任务调度基本介绍2. corn表达式介绍2-1 corn的每一个位置功能介绍2-2 占位符说明2-3 常用cron举例 3. SpringBoot项目中&#xff0c;集成任务调度Scheduled3-1 添加SpringBoot启动依赖3-2 具体corn任务调度计划3-3 SpringBoot启动类添加注…

java如何实现深拷贝(IT枫斗者)

java如何实现深拷贝 Java浅拷贝 浅拷贝是按位拷贝对象&#xff0c;它会创建一个新对象&#xff0c;这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型&#xff0c;拷贝的就是基本类型的值&#xff1b;如果属性是内存地址&#xff08;引用类型&#xff09;&#…

2016湖南湘潭邀请赛b题思路

最近训练时写的比赛&#xff0c;当时b题没写&#xff0c;事后补一下&#xff0c;看了下题解&#xff0c;想写下自己的解释 原题解&#xff1a;2016湖南湘潭邀请赛题解&#xff1a;2016年“长城信息”杯中国大学生程序设计比赛中南地区邀请赛&#xff08;迟来的题解&#xff09…

Koordinator 一周年,新版本 v1.2.0 支持节点资源预留,兼容社区重调度策略

作者&#xff1a;佑祎、吕风 背景 Koordinator 是一个开源项目&#xff0c;基于阿里巴巴在容器调度领域多年累积的经验孵化诞生&#xff0c;可以提升容器性能&#xff0c;降低集群资源成本。通过混部、资源画像、调度优化等技术能力&#xff0c;能够提高延迟敏感的工作负载和…

第3章:select

1.最基本的select语句 select … from…select 字段1&#xff0c;字段2&#xff0c;…from 表名* 表中所有字段&#xff08;列&#xff09; 2.列的别名 字段1 as 别名1字段1 别名1as&#xff1a;alias&#xff08;别名&#xff09;可以省略如果别名有空格使用一对””引起来…

应用于音箱领域中的音频功放IC型号推荐

音箱音频功放ic俗称“扩音机”又叫音频功率放大器IC&#xff1b;是各类音响器材中不可缺少的部分&#xff0c;其作用主要是将音源器材输入的较微弱信号进行放大后&#xff0c;产生足够大的电流去推动扬声器进行声音的重放。 现如今&#xff0c;音频功放芯片伴随着人工交互及智…

APS中零件工序间的移动方式解析

在加工装配的成批生产类型企业里&#xff0c;由于零件多种多样&#xff0c;工艺路线、加工方法和技术装备千差万别&#xff0c;因而&#xff0c;产品有多种流转方式。一般来说&#xff0c;零件在各道工序间的移动方式主要有顺序移动、平行移动和平行顺序&#xff08;平顺&#…

网络威胁情报:数据的力量

在一个日益互联和数字化的世界中&#xff0c;网络威胁已成为一项重大挑战&#xff0c;可能危及您组织的声誉、财务稳定性和整体运营效率。 事实上&#xff0c;根据 IBM 2022 年的一份报告&#xff0c;数据泄露的平均成本现在为 435 万美元。 鉴于网络威胁的重要性和影响日益突…

Spring《三》DI 依赖注入

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; 上一篇&#xff1a;Spring《二》bean 的实例化与生命周期 下一篇&#xff1a;敬请期待 目录 一、setter 注入&#x1f349;1.注入引用数据类型2.注入简单数据类型 二、构造器注入&#x1f34a;1.注入引用数据类型2.…

吴恩达团队AI诊断心律失常研究:准确率超人类医生

2019年&#xff0c;吴恩达团队在AI医疗领域实现了一项革命性的突破&#xff0c;他们成功地让AI诊断心律失常&#xff0c;其准确率高达83.7%&#xff0c;超过了人类心脏病医生的78.0%。这项研究成果已经发表在了知名期刊Nature Medicine上。 一、如何让AI学会诊断心律失常&…

Linux多媒体子系统02:V4L2核心框架分析

1 V4L2框架结构概述 1.1 imx8视频输入通路硬件结构 软件框架是对硬件结构的映射与描述&#xff0c;所以在说明V4L2框架结构之前先说明一下硬件结构&#xff0c;此处以imx8视频输入通路为例&#xff08;下图中红框部分&#xff09; 1. MIPI-CSI2&#xff08;Camera Serial Int…

测试Ocr工具IronOCR(续:编写图片圈选程序)

上一篇文章学习了IronOCR的基本用法之后&#xff0c;计划做一个加载本地图片后&#xff0c;从图片中圈选某一位置的文字&#xff0c;然后调用IronOCR识别圈选区域文本的程序。本文实现从本地加载图片并完成圈选的功能。   主要的功能包括以下几点&#xff1a;   1&#xff…

提效降本应对无序竞争,采埃孚+东软睿驰的组合样本

降价与降本&#xff0c;就好似车企与供应商之间的“窗户纸”&#xff1b;如果是持续的无序竞争&#xff0c;势必一捅就破。而只有通过产业链的通力协作&#xff0c;才有机会维持一定的平衡。 多元化需求、车企降本、新车开发周期缩短等一系列因素&#xff0c;正在驱动智能化在中…

Spring Security实现JWT token验证

Spring Security实现JWT token验证 Spring Security是Spring提供的一个安全框架&#xff0c;提供认证和授权功能&#xff0c;最主要的是它提供了简单的使用方式&#xff0c;同时又有很高的灵活性&#xff0c;简单、灵活、强大 一般系统里关于角色方面通常有这么几张表&#xf…

【Dubbo核心 详解三】Dubbo服务接口的详解

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Dubbo专栏 文章目录 引言一、简介1. 介绍 Dubbo 服务接口的基本概念和特点1.1 Dubbo 服务接口的基础概念1.2 Dubbo 服务接口的特点2. 介绍 Dubbo 服务接口的…

机器学习——SVM的易错题型

问&#xff1a;支持向量机仅可以用于处理二分类任务 答&#xff1a;错误。支持向量机可以用于处理多分类任务&#xff0c;通过使用一对多或一对一的方法&#xff0c;将多个类别分别与其他类别做二分类。也可以使用多类支持向量机算法&#xff0c;直接将多个类别一起纳入训练和…

路侧激光雷达目标检测系统-篇1

说明&#xff1a;又到了毕业的季节&#xff0c;拿出来我之前做的小雷达识别项目&#xff0c;给学弟学妹们做毕设一点参考。这个主要是根据雷达采集的数据包进行聚类识别&#xff0c;看那些是汽车&#xff0c;更改数据的特征之后可以识别特定目标&#xff0c;比如路上新人等。  …

SpringCloud --- Nacos注册中心

一、认识和安装Nacos Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud中的一个组件。相比Eureka功能更加丰富&#xff0c;在国内受欢迎程度较高。 二、服务注册到nacos Nacos是SpringCloudAlibaba的组件&#xff0c;而SpringCloudAlibaba也遵循SpringCloud中定义的服务注…

Stable Diffusion公司发布首个大语言模型StableLM,已开源公测!

文 | 智商掉了一地 20号凌晨&#xff0c;Stability AI 发布了一个新的开源语言模型—— StableLM&#xff0c;该公司曾开发了 Stable Diffusion 图像生成工具。这则新闻意味着它不再局限于图像与视频生成领域&#xff0c;将正式加入文本生成 AI 赛道。 StableLM 模型可以生成文…