带你模仿正点原子到寄存器编写–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,即
f9=1/1000000s = 1us,f是真实的时钟频率,而9是我们让程序在这个时钟频率下计算9次得到软件分频后的1us
那么重载值就是9nus就是需要延时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; //清空计数器
}