【STM32F103】SysTick系统定时器延时函数

SysTick

SysTick是Cortex-M3内核中的一个外设,内嵌在NVIC中,叫系统定时器。

当处理器在调试期间被喊停时,SysTick也将暂停运作。

一共有四个寄存器,不过我们通常用前三个,不需要校准。下图出自《STM32F10xxx Cortex-M3编程手册》第237页。

CTRL寄存器

第一个CTRL寄存器可用的一共有四个位。

第0位是SysTick的使能为,为1则使能。

第1位是SysTick的异常请求标志位,为1则会触发异常也就是中断使能,Cortex-M3处理器专门为SysTick开出了一个异常类型,也就是说它也在中断向量表中。由于SysTick属于内核外设,因此并没有抢占优先级和响应优先级的概念,一般我们也不使用SysTick中断,但是非要的话也是可以的,我们需要去操作SHPR3这个寄存器,并且这个寄存器只能通过字节访问。

从上图可知这个寄存器的第24位到第31位是给SysTick使用的,不过在STM32F103中,有效的仅是第28位到第31位这高4位有效,因此拥有16位可编程优先级,数值越小优先级越高。

更具体的SysTick中断还需自行查阅资料。

第2位是时钟源选择位,为0则AHB/8,即系统时钟SYSCLK经过AHB预分频器进行8分频(72MHz/8=8MHz),为1则是AHB进行1分频(72MHz/1=72MHz)。

第16位是计数的标志位,如果上次处理器读取到计数器已经计到了0,那么将此为置为1,我们可以通过读取此位来得知一轮计数是否结束。

LOAD寄存器&VAL寄存器

第二个LOAD寄存器是重装寄存器,也就是说计数器记到0之后,会给计数器重新赋值,赋的值就是从这个寄存器取出的。

第三个VAL寄存器就是计数器了,是向下递减的计数器。时钟源每触发一次则记一次(减一次)数,到0则会发出异常请求(如果有设置的话),并且重装计数值。

我们可以知道,重装寄存器和计数器都是24位的,因此能记的最大次数就是2^24(16777216),最大数值为2^24-1(16777215,0xFFFFFF),要注意不能超出最大数值。

固件库函数实现延时效果

以SysTick开头的函数就两个,不过因为SysTick是内嵌到NVIC的,所以以NVIC_SysTick开头的函数还有两个,不过我们用不着,因此仅介绍两个函数就够实现延时效果了,实际上一个就够了。

SysTick_Config

我们使用这个函数就可以对SysTick初始化并且赋上重装寄存器的值。

我们固件库函数的内容比较简单,我们可以简单分析一下,也有助于加深理解。

这个函数需要一个参数,这个参数就是用来设置重装计数器的值的。

库函数的第一行就是用来检验这个参数是否合法的,因为我们之前分析过了,重装寄存器和计数器都是24位的,因此可以赋的最大数值就是16777215,0xFFFFFF。

可以看到第一行检验参数使用的宏定义也是符合我们分析的,其中宏定义中的宏定义SysTick_LOAD_RELOAD_Pos为0,也就是没有任何效果(我也不知道它存在的意义何在)。

库函数的第二行就是将参数赋给重装寄存器。

第三行设置中断优先级的,我们用不着可以不管它。

第四行是将计数器清零的。

第五行设置CTRL寄存器,我们可以看出库函数默认将CTRL寄存器的三个位置1,除了第16位都置1了,也就是说库函数默认使用AHB1分频(72MHz)作为时钟源,并且默认开启异常请求。

调用这个函数之后,我们就成功的让SysTick系统定时器开始运行了,时钟源每来一个脉冲,计数器就向下递减一个数,减到0之后再通过重装寄存器来给计数器赋值。

如果我们需要1us的延时函数,那么调用下面这段代码即可实现SysTick每计数一轮就是1us。

SysTick_Config(SystemCoreClock/1000000);

其中SystemCoreClock是一个宏定义,也就是AHB的大小。也就是说每秒时钟源都会有那么多次的脉冲,计数器也会记那么多个数,我们将这个数除10^6,得到的数就是每us的脉冲次数,这样就可以让SysTick记1us的时间了。

对于我们STM32F103来说写上72000000也是一样的,不过不便于移植修改,例如如果我们修改了处理器的主频,那么就需要对这个数值进行修改,但如果写的是宏定义的话则不需要修改(虽然一般没事也不会修改主频)。

不过目前为止我们还只是让SysTick启动了起来,我们该如何知道SysTick计数完了一轮呢。

我们可以通过查询CTRL寄存器的第16位来知道是否计数一轮,处理器一旦查询到了SysTick的计数器为0之后,就会将CTRL的第16位置1,因此我们开启SysTick之后,只需要使用whlie循环查询CTRL的第十六位即可。

当我们延时了我们想要的时间之后还需要将SysTick关闭,这时候只需要把CTRL寄存器的第0位使能位置0即可。

至此,我们就可以完成us级的延时函数了,具体可以参考下面的代码,如果要实现ms和s级别的延时函数,可以调用10^3次和10^6次us的延时函数。其中ms延时可以修改传给SysTick_Config的参数,将原本的SystemCoreClock/1000000修改为SystemCoreClock/1000即可,但是s延时就不能用类似的方法了,因为超过了计数器可以记的最大值,但可以通过调用10^3次ms延时来实现。

void delay_us(uint32_t us){//SysTick_Config(72000000/1000000);         //不便移植SysTick_Config(SystemCoreClock/1000000);    //固件库函数初始化+设置重装值while(us--){while(!((SysTick->CTRL)&(SysTick_CTRL_COUNTFLAG))); //不断查询计数标志位}SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;    //关闭SysTick
}

SysTick_CLKSourceConfig

第二个函数就是这个用来修改时钟源的函数了,一般我们就使用库函数默认的AHB,不过想要修改的话也是可以事后改掉的。

使用AHB1分频(72MHz)为时钟源就传入SysTick_CLKSource_HCLK。

使用AHB8分频(8MHz)为时钟源就传入SysTick_CLKSource_HCLK_Div8。

LED闪烁&延时函数

#include "stm32f10x.h"                  // Device headervoid delay_us(uint32_t us){//SysTick_Config(72000000/1000000);         //不便移植SysTick_Config(SystemCoreClock/1000000);    //固件库函数初始化+设置重装值while(us--){while(!((SysTick->CTRL)&(SysTick_CTRL_COUNTFLAG))); //不断查询计数标志位}SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;    //关闭SysTick
}void delay_ms(uint32_t ms){while(ms--) delay_us(1000);        
}void delay_s(uint32_t s){while(s--) delay_ms(1000);
}int main(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);    //打开GPIO口的外设时钟GPIO_InitTypeDef itd;               itd.GPIO_Mode=GPIO_Mode_Out_PP;                         //设置为推挽输出模式itd.GPIO_Pin=GPIO_Pin_0;                                //指定0号引脚itd.GPIO_Speed=GPIO_Speed_2MHz;                         //输出频率为2MHzGPIO_Init(GPIOA,&itd);                                  //初始化while(1){                                               //LED闪烁GPIO_ResetBits(GPIOA,GPIO_Pin_0);                   //设置低电平delay_s(1);                                         //延时一秒GPIO_SetBits(GPIOA,GPIO_Pin_0);                     //设置高电平delay_s(1);}
}

 接线&效果

参考:

《STM32F103xx固件函数库用户手册》

《STM32F10xxx Cortex-M3编程手册》

《STM32库开发实战指南(基于STM32F103)》

《ARM Cortex-M3嵌入式原理及应用(基于STM32F103微控制器)》

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

35--JDK新特性

1、新语法结构 新的语法结构,为我们勾勒出了 Java 语法进化的一个趋势,将开发者从复杂、繁琐的低层次抽象中逐渐解放出来,以更高层次、更优雅的抽象,既降低代码量,又避免意外编程错误的出现,进而提高代码质…

Linux软件包管理器——yum命令

如何使用yum 一、快速认识yum(简单介绍)二、快速使用yum2.1 rzsz2.2 linux命令行小游戏和彩蛋 三、yum的整个生态问题 一、快速认识yum(简单介绍) Linux中我们也要进行工具/指令/程序,安装,检查卸载等,需要yum的软件 安装软件的方式&#xf…

<Icon-ResizER>support

If you get any questions in using app, email me caohechunhotmail.com.

Unity坦克大战开发全流程——开始场景——排行榜数据逻辑

开始场景——排行榜数据逻辑 排行榜单条数据 排行榜列表 然后在数据管理类中声明一个对应的字段 初始化数据 然后再在上一节课所编写的UpdatePanelInfo函数中处理数据更新的逻辑 时间换算算法 然后再在数据管理类中编写一个在排行榜中添加数据的方法以提供给外部 直到当前RankI…

Python压缩图片大小

今天遇到一个问题,制作的网站因为图片尺寸比较大导致加载很慢,所以想通过压缩图片的方式来加快页面的加载速度(当然也可以选择cdn和oss的方式来加快页面加载速度) 话不多说,Python肯定是首选项嘛,那么PIL&…

深度学习核心技术与实践之计算机视觉篇

非书中全部内容,只是写了些自认为有收获的部分 计算机视觉背景 (1)视觉皮层的神经元是一列一列组织起来的,每一列神经元只喜欢某一种特定的形状或者某些简单的线条组合,而不是鱼、老鼠、鲜花 (2&#xf…

【Linux】Linux 下基本指令 -- 详解

无论是什么命令,用于什么用途,在 Linux 中,命令有其通用的格式: command [-options] [parameter] command:命令本身。-options:[可选,非必填]命令的一些选项,可以通过选项控制命令的…

EBU7140 Security and Authentication(二)非对称加密;授权

B2 非对称加密介绍 前面的传统加密算法都是对称加密。就是加密解密用一个密钥。非对称加密就是用不同的密钥,加密复杂度更高。 Diffie-Hellman 密钥交换法 一种密钥交换方法。 common 是公共基础颜色,secret 是各自私有颜色,公共颜色和自己…

【产品设计】信息建设三驾马车:MES系统拆解

本篇文章,将从三个方面对MES系统进行拆解分析,并分析其特殊功能——文档管理。MES系统能实现多个生产信息的互联互通,提高生产效率。 MES系统主要实现生产业务系统管理。 ERP系统主要实现采购、销售、库存(进销存)、财…

算法基础之滑雪

滑雪 核心思想&#xff1a;记忆化搜索 状态表示&#xff1a; f[i][j] 表示所有从(i,j) 开始滑的路径的最大值 状态计算&#xff1a; 分成四个方向 f[i][j] max(f[i][j] , f[i][j1] 1) 且h[a][b] (下一个点) 必须严格小于 h[i][j] 才能滑过去 #include<iostream>#…

大数据背景下基于联邦学习的小微企业信用风险评估研究

摘要&#xff1a; 小微企业信用风险评估难是制约其融资和发展的一个主要障碍。基于大数据的小微企业信用风险评估依然面临着单机构数据片面、跨机构数据共享难、模型不稳定等诸多挑战。针对相关问题和挑战&#xff0c;本项目拟在多主体所有权数据隐私保护与安全共享的背景下&am…

基于斑点鬣狗算法优化的Elman神经网络数据预测 - 附代码

基于斑点鬣狗算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于斑点鬣狗算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于斑点鬣狗优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…