本文不通过NVIC中断延时,只基于SysTick的寄存器CTRL-控制及状态寄存器、VAL-当前数值寄存器、LOAD-重装载数值寄存器,编写基础的延时代码。
附带SYSCLK系统时钟的简单配置。
一、delay函数
Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计数到0时,从自动重装载寄存器中自动装载定时初值,并开始倒计时,循环往复,只要不关闭其使能位,嘀嗒定时器SysTick就会一直计时。
咱先上寄存器手册:
先创建一个delay.h文件,里面定义函数名,delay初始化、毫秒延时、微秒延时,
注意:本文代码中delay_init函数中的形参SYSCLK是系统时钟,单位是M。
再创建一个delay.c的文件存放功能函数,在写功能函数之前先定义两个静态延时倍乘数fac_us、fac_ms,之所以要定义两个延时倍乘数,是因为不同频率下,systick计数一次所经过的时间不同,为了能使两个延时函数delay_us、delay_ms的延时是我们所设置的形参的值,需要一个延时倍乘数去乘以形参,得到一个重装载值,而这个重装载值一但倒计数完毕,所经过的时间便是我们所设置的形参的值。
1.delay_init:
假如:HCLK为72M,则SysTick就是72/8=9M,在9M的频率下,SysTick每倒计数一次,所经过的时间便是1/9us,那么定时1us所需的计数次数就是9次,要使delay_us(1)成功延时1us,那么delay_us函数中,systick的重装载值要设置为9,即形参1乘以延时倍乘数fac_us得9,fac_us就为9
因此在delay_init函数中我们需设置好延时倍乘数fac_us、fac_ms,代码如下:
delay初始化完成之后,就要编写delay_ms、delay_us函数
2.delay_us:
配置寄存器时,一定要先配置重装载值,再清空计数器,若先清空计数器在设置重装载值,开启定时器后,定时器的第一个计数周期是从0开始计数,第二周期才是从你设置的重装载值开始计数,逻辑十分不严谨,极有可能会出现bug,导致延时无效。
计数完成后,即延时成功后,需先关闭计数器,再清空它,若先清空在关闭,就相当于没清空计数器,对下次使用十分不友好。在编写代码时,除非必要,否则我们要习惯在不使用某个寄存器或功能时,要让它处于复位值,即默认状态。这样才能减少在编写复杂代码时出现未知bug。
3.delay_ms:
代码跟delay_us类似,在此就不在赘述了,直接上代码
完整代码如下:
#include "delay.h"
/*---------------------------------------
此代码不通过NVIC中断延时
基于SysTick的寄存器CTRL-控制及状态寄存器、VAL-当前数值寄存器、LOAD-重装载数值寄存器
编写基础的延时代码
-----------------------------------------*/
static int8_t fac_us = 0; // us延时倍乘数
static int16_t fac_ms = 0; // ms延时倍乘数
// SYSTICK固定为HCLK的1/8
// 系统时钟:SYSCLK,单位:M
void delay_init(u8 SYSCLK) // 延时初始化
{
SysTick->CTRL &= ~(1 << 2); // 选择时钟源,0=AHB/8,1=AHB
fac_us = SYSCLK / 8; // us倍乘数为SYSCLK的1/8
fac_ms = (int16_t)fac_us * 1000; // ms倍乘数为fac_us的1000倍
}
void delay_us(u32 nus)
{
uint32_t temp; // 定义一个32的变量,便于后面的循环判断
/*
设置重装载值,即延时时间;直接整体赋值,无需位操作
LOAD为24位寄存器,最大可设为16 777 216
*/
SysTick->LOAD = nus * fac_us;
SysTick->VAL = 0X00; // 清零,VAL只要写入则清零,同时清除CTRL中的COUNTFLAG标
/*
位段0写1,开启定时器使能,开始倒数
复位值为0,且其余位是默认设置,因此直接赋值0X01不影响其余位
*/
SysTick->CTRL = 0X01;
/*
while循环用于等待时间到达
当SysTick计数到0时,COUNTFLAG置一,即位段16置一,否则位段16置0
(temp&(0X01)判断定时器是否开启,防止定时器意外关闭导致延时错误
*/
do
{
temp = SysTick->CTRL;
} while ((temp & (0X01) && !(temp & (1 << 16))));
SysTick->CTRL = 0X00; // 关闭计数器
SysTick->VAL = 0X00; // 清空计数器
}
/*
LOAD为24位寄存器,所以最大延时为:nms<= 0xffffff x 8 x 1000/SYSCLK
SYSCLK单位为Hz,nms单位为ms,72MHz 条件下,nms<= 1864
*/
void delay_ms(u16 nms)
{
uint32_t temp; // 定义一个32的变量,便于后面的循环判断
SysTick->LOAD = (u32)nms * fac_ms;
SysTick->VAL = 0X00; // 清零,VAL只要写入则清零,同时清除CTRL中的COUNTFLAG标
SysTick->CTRL = 0X01;
do
{
temp = SysTick->CTRL;
} while ((temp & (0X01) && !(temp & (1 << 16))));
SysTick->CTRL = 0X00; // 关闭计数器
SysTick->VAL = 0X00; // 清空计数器
}
二、设置系统时钟
本文只简单介绍系统时钟配置步骤及代码,具体原理请读者自行阅览时钟树及寄存器手册,如下:
寄存器手册请对照RCC_CR、RCC_CFGR,
详情参考:http://t.csdnimg.cn/T3WzI
FLASH_ACR如下:
1.配置步骤:
- 选择输入时钟,一般选HSE(外部高速时钟),并等待输入时钟就绪
- 选择三条总线AHB、APB1、APB2分频系数
- 选择PLL锁相环倍频系数,开启PLL输出,当PLL输出>72M时,flash需等待2个延时周期
- 等待PLL锁定
- 选定PLL输出频率作为系统时钟,等待其设置成功。
代码如下: