文章目录
在 新建工程模板——库函数版本中,我们用到了一个
SYSTEM
文件夹里面的代码,此文件夹里面的代码由
ALIENTEK
提供,是
STM32F4xx
系列的底层核心驱动函数,可以用在
STM32F4xx
系列的各个型号上面,方便大家快速构建自己的工程。
SYSTEM
文件夹下包含了
delay
、
sys
、
usart
等三个文件夹。分别包含了
delay.c
、
sys.c
、
usart.c
及其头文件。
一、delay 文件夹代码介绍
delay
文件夹内包含了 delay.c
和 delay.h
两个文件,这两个文件用来实现系统的延时功能,其中包含 7 个函数:
void delay_osschedlock(void);
void delay_osschedunlock(void);
void delay_ostimedly(u32 ticks);
void SysTick_Handler(void);
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);
在介绍这些函数之前,我们先了解一下编程思想:CM4
内核的处理和 CM3
一样,内部都包含了一个 SysTick
定时器,SysTick
是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD
寄存器中自动重装载定时初值。只要不把它在 SysTick
控制及状态寄存器中的使能位清除,就永不停息。我们就是利用 STM32
的内部 SysTick
来实现延时的,这样既不占用中断,也不占用系统定时器。
这里,我们以 UCOSII
为例,介绍如何实现操作系统和我们的 delay
函数共用 SysTick
定时器。首先,我们简单介绍下 UCOSII
的时钟:ucos
运行需要一个系统时钟节拍(类似 “心跳”),而这个节拍是固定的(由 OS_TICKS_PER_SEC
宏定义设置),比如要求 5ms
一次(即可设置:OS_TICKS_PER_SEC=200
),在 STM32
上面,一般是由 SysTick
来提供这个节拍,也就是 SysTick
要设置为 5ms
中断一次,为 ucos
提供时钟节拍,而且这个时钟一般是不能被打断的(否则就不准了)。
因为在 ucos
下 systick
不能再被随意更改,如果我们还想利用 systick
来做 delay_us
或者delay_ms
的延时,就必须想点办法了,这里我们利用的是时钟摘取法。以 delay_us
为例,比如:delay_us(50)
,在刚进入 delay_us
的时候先计算好这段延时需要等待的 systick
计数次数,这里为 50*180
(假设系统时钟为 180Mhz
,因为我们设置 systick
的频率为系统时钟频率,那么 systick
每增加 1,就是 1/180us
),然后我们就一直统计 systick
的计数变化,直到这个值变化了 50*180
,一旦检测到变化达到或者超过这个值,就说明延时 50us
时间到了。这样,我们只是抓取 SysTick
计数器的变化,并不需要修改 SysTick
的任何状态,完全不影响 SysTick
作为 UCOS
时钟节拍的功能,这就是实现 delay
和操作系统共用 SysTick
定时器的原理。
1.delay_init 函数
该函数用来初始化 2 个重要参数:fac_us
以及 fac_ms
;同时把 SysTick
的时钟源选择为外部时钟,如果需要支持操作系统(OS
),只需要在 sys.h
里面,设置 SYSTEM_SUPPORT_OS
宏的值为 1 即可,然后,该函数会根据delay_ostickspersec
宏的设置,来配置 SysTick
的中断时间,并开启 SysTick
中断。具体代码如下:
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
reload=SYSCLK; //每秒钟的计数次数 单位为K
reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间
//reload为24位寄存器,最大值:16777216,在180M下,约合0.745s左右
fac_ms=1000/delay_ostickspersec; //代表OS可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else
#endif
}
可以看到,delay_init
函数使用了条件编译,来选择不同的初始化过程,如果不使用 OS
的时候,只是设置一下 SysTick
的时钟源以及确定 fac_us
值。而如果使用 OS
的时候,则会进行一些不同的配置,这里的条件编译是根据SYSTEM_SUPPORT_OS
这个宏来确定的,该宏在sys.h
里面定义。SysTick
是 MDK
定义了的一个结构体,里面包含 CTRL
、LOAD
、VAL
、CALIB
等 4 个寄存器,
/**
\brief Structure type to access the System Timer (SysTick).
*/
typedef struct
{
__IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
__IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
__IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
__IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
CTRL
是SysTick
控制和状态寄存器。SysTick->CTRL
的各位定义如图所示:
LOAD
是SysTick
自动重装载除值寄存器。SysTick-> LOAD
的定义如图所示:
VAL
是SysTick
当前值寄存器。SysTick-> VAL
的定义如图所示:
CALIB
是SysTick
校准值寄存器。SysTick-> CALIB
不常用,在这里我们也用不到,故不介绍了。
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
这句代码把 SysTick
的时钟选择为内核时钟,这里需要注意的是:SysTick
的时钟源自 HCLK
,假设我们外部晶振为 25M
,然后倍频到 180MHZ
,那么 SysTick
的时钟即为 180Mhz
,也就是 SysTick
的计数器 VAL
每减 1,就代表时间过了 1/180us
。所以fac_us=SYSCLK;
这句话就是计算在 SYSCLK
时钟频率下延时 1us
需要多少个 SysTick
时钟周期。
在不使用 OS
的时候:fac_us
为 us
延时的基数,也就是延时 1us
,Systick
定时器需要走过的时钟周期数。 当使用 OS
的时候,fac_us
,还是 us
延时的基数,不过这个值不会被写到SysTick->LOAD
寄存器来实现延时,而是通过时钟摘取的办法实现的。而fac_ms
则代表 ucos
自带的延时函数所能实现的最小延时时间(如 delay_ostickspersec=200
,那么 fac_ms
就是 5ms
)。
2.delay_us 函数
该函数用来延时指定的 us
,其参数 nus
为要延时的微秒数。该函数有使用 OS
和不使用 OS
两个版本,这里我们首先介绍不使用 OS
的时候,实现函数如下:
//延时nus
//nus为要延时的us数.
//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
void delay_us