带你模仿正点原子编程风格到深入学习寄存器并手把手编写STM32F103寄存器程序(SYSTICK滴答时钟之delay延时)

带你模仿正点原子到寄存器编写–SYSTICK(delay函数)

以下是本篇文章正文内容,下面案例可供参考
邓家文-广州华软软件学院

一、 前提了解

系统时钟systick是STM32内部的硬件资源,我们首先要知道,MCU内部资源应该在Cortex-M3权威指南上查询;而STM32中文参考手册主要是对MCU外部外设资源的描述。


二、 手册查询和分析

如2.1图示,在Cortex-M3权威指南目录找到systick。
在这里插入图片描述
内容如下图。
在这里插入图片描述
该定时器有4个寄存器控制SysTick定时器,如下图所示。
在这里插入图片描述
如寄存器对应位的描述,可以知道这个寄存器是配置时钟源的选择和该systick定时器模块的是使能。
在这里插入图片描述
这个是我们配置延时或者定时的值,最大重载值为0xFFFFFF。
在这里插入图片描述
这个一般来说是用来获取期值来判断一个周期的计数是否已满。
在这里插入图片描述
这个校准寄存器一般我们不会使用,除非是要设置一些额外精准的东西,寄存器下图蓝色框内有相关的解释说明。(红色介绍此寄存器使用案例等)
在这里插入图片描述

经过上面的分析我们应该有个大致的配置思维了。
在这里插入图片描述在这里插入图片描述
这里还要注明一点,我们使用到的是外部时钟,所以要看看时钟源的配置或者说是时钟源流入的频率是怎么样的,时钟树如下图(在中文参考手册时钟章节)
在这里插入图片描述
在这里插入图片描述

三、 程序编写

1、.h文件的编写
在这里插入图片描述
这一段程序就没什么好说的,清除的,我发布的DMA实验有详细的说明。
2、.c文件
①用无论是用内部还是外部的时钟,都需要使能使用到的时钟。
然后获取已转换过来的外部时钟频率
在这里插入图片描述
②将获取到的source_clk单位是MHz,将进来的时钟频率f*source_clk=10^-6秒,设置到最基本的每微秒的(source_cl)载值后,在乘我们需要的us数即转化为计数器要计数的载数值,然后清除计数器的值,从(空白页)开始计数,计完数后要关闭计数器并漂白计数器。

在这里插入图片描述
③这个ms级延时函数与us级延时函数是相似的,这里我就不多讲了。
在这里插入图片描述
不理解计数的话,看如下:
假设进来分频后的时钟就为9MHz,那么也就是每秒节拍数是9000000次,f=1/9000000,那我需要一个周期为1us,即
f
9=1/1000000s = 1us,f是真实的时钟频率,而9是我们让程序在这个时钟频率下计算9次得到软件分频后的1us
那么重载值就是9
nus就是需要延时nus微秒,这个重载值不得超过0xFFFFFF值。
**

四、 Keil仿真调试

在这里插入图片描述
在这里插入图片描述
运行delay_ms1(1000);这行程序总共计数72005299-5215=72000084次,(在72MHZ)情况下,所以这可以近似认为1s。至于sec为什么7.2秒,我也不清楚,希望各位大神能告诉我。

五、 程序:

test.c:

#include "stdio.h"
#include "gpio.h"
#include "USART1.h"
#include "delay_ms.h"
#include "clock.h"
#include "USART1.h"
#include "key.h"
#include "DMA.h"



int main(void)
{
	RCC_Config(9);				//72MHz
	delay_init1(72);			//打开延时
	GPIO_Init();				//初始化LED口

  while(1)
  {
    GPIOB->GPIO_ODR^=1<<5;
    delay_ms1(1000);
    GPIOE->GPIO_ODR^=1<<5;
  }

}

delay.h:

#ifndef __DELAY_MS_H
#define __DELAY_MS_H
#include "RCC.h"

//systick寄存器基地址
#define SYSTICK_BASE			(0xE000E010)

//systick寄存器结构体
typedef struct
{
	volatile unsigned int CTRL;		//SysTick控制及状态寄存器
	volatile unsigned int LOAD;		//SysTick重装载数值寄存器
	volatile unsigned int VAL;		//SysTick当前数值寄存器
	volatile unsigned int CALIB;	//SysTick校准数值寄存器
}SYSTICK_Type;	

//systick寄存器指针
#define SYSTICK					 	((SYSTICK_Type *) SYSTICK_BASE)

static u16 source_clk;
static u16 count_us=0;							//us延时最小单位载值->倍乘值
static u16 count_ms=0;							//ms延时最小单位载值->倍乘值

void delay_init1(u8 sysclk);
void delay_ms1(u16 nms);
void delay_us1(u16 nus);

#endif

delay.c

#include "delay_ms.h"

//初始化systick作为delay延时函数
void delay_init1(u8 sysclk)
{
	SYSTICK->CALIB&=~(1<<2);                    //使能外部时钟
  source_clk=sysclk/8;                        //转化为外部时钟源的频率,8分频,历72MHz/8=9MHz(也就是进来一开始的时钟)
}

//延时最大是有个度的
//假设时钟就为9MHz,那么也就是每秒节拍数是9000000次,f=1/9000000
//那我需要一个周期为1us,f*9=1/1000000 s = 1us 
//那么以1us,我们可以设置多少微秒呢?f*load_max=1864135us约为1864ms
//最大重载值

//微秒延时
void delay_us1(u16 nus)
{
	u32 temp;                                   //保存当前计数器值
  count_us=source_clk;                        //单位频率MHz也就是us级别,10^-6秒
	SYSTICK->LOAD=count_us*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_ms1(u16 nms)
{
	
	u32 temp;
	count_ms=source_clk*1000;                   //单位频率KHz也就是ms级别,10^-3秒
  SYSTICK->LOAD=count_ms*nms;                 //将需要的定时转化为时间重载值
	SYSTICK->VAL=0x00;           			          //清空计数器
	SYSTICK->CTRL=0x01 ;          			        //开始倒数  
	do
	{
		temp=SYSTICK->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	      //等待时间到达  
	  SYSTICK->CTRL=0x00;      	 			          //关闭计数器
	  SYSTICK->VAL=0X00;       				          //清空计数器	
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邓家文007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值