static __INLINE uint32_t SysTick_Config(uint32_t ticks) { if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */ SysTick->VAL = 0; /* Load the SysTick Counter Value */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ return (0); /* Function successful */ }
以下是对这段代码的详细分析:
这段代码是一个用 C 语言编写的内联函数 SysTick_Config
,其主要功能是配置 ARM Cortex - M 系列微控制器的 SysTick 定时器。SysTick 定时器是一个 24 位的递减计数器,常用于产生系统滴答时钟,为操作系统提供时间基准,或者用于简单的延时操作。
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
static
:表示该函数是一个静态函数,其作用域仅限于当前文件。__INLINE
:通常是一个宏定义,用于指示编译器将该函数内联展开,以减少函数调用的开销。uint32_t
:函数返回值类型为 32 位无符号整数。ticks
:函数的参数,表示 SysTick 定时器的重载值,即定时器从该值开始递减计数。if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick_LOAD_RELOAD_Msk
:这是一个宏定义,代表 SysTick 定时器重载寄存器的最大值。由于 SysTick 是一个 24 位的计数器,其最大值为 0xFFFFFF
。ticks
值大于 SysTick_LOAD_RELOAD_Msk
,则返回 1
,表示重载值无效。SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
SysTick
:这是一个指向 SysTick 定时器寄存器结构体的指针。SysTick->LOAD
:访问 SysTick 定时器的重载寄存器,该寄存器用于设置定时器的初始值。(ticks & SysTick_LOAD_RELOAD_Msk)
:确保 ticks
值在合法范围内(不超过 24 位)。ticks
值减 1。NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
NVIC_SetPriority
:这是一个用于设置中断优先级的函数。SysTick_IRQn
:表示 SysTick 定时器的中断号。(1<<__NVIC_PRIO_BITS) - 1
:设置 SysTick 中断的优先级为最低。__NVIC_PRIO_BITS
是一个宏定义,表示 NVIC(嵌套向量中断控制器)中用于表示中断优先级的位数。SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->VAL
:访问 SysTick 定时器的当前值寄存器。将其清零,确保定时器从 0 开始计数。SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
SysTick->CTRL
:访问 SysTick 定时器的控制寄存器。SysTick_CTRL_CLKSOURCE_Msk
:选择 SysTick 定时器的时钟源。SysTick_CTRL_TICKINT_Msk
:使能 SysTick 定时器的中断功能。SysTick_CTRL_ENABLE_Msk
:使能 SysTick 定时器。return (0); /* Function successful */
0
。该函数通过一系列操作完成了 SysTick 定时器的配置,包括设置重载值、中断优先级、清零计数器和启用定时器及其中断功能。如果传入的重载值无效,则返回 1
;否则,返回 0
表示配置成功。
如何使用这段代码来实现延时操作?
要使用 SysTick_Config
函数来实现延时操作,基本思路是利用 SysTick 定时器的定时功能,通过循环等待计数器达到指定的时间来实现延时。以下是详细的步骤和示例代码:
SysTick_Config
函数,设置合适的重载值,使 SysTick 定时器按照期望的时间间隔产生中断。#include
// 假设 SysTick 相关的寄存器和宏定义已经包含
// 这里简单模拟一下相关的宏和结构体
#define SysTick_LOAD_RELOAD_Msk 0xFFFFFF
#define SysTick_CTRL_CLKSOURCE_Msk 0x00000004
#define SysTick_CTRL_TICKINT_Msk 0x00000002
#define SysTick_CTRL_ENABLE_Msk 0x00000001
#define SysTick_IRQn -1
#define __NVIC_PRIO_BITS 2
typedef struct {
volatile uint32_t CTRL; // SysTick Control and Status Register
volatile uint32_t LOAD; // SysTick Reload Value Register
volatile uint32_t VAL; // SysTick Current Value Register
volatile uint32_t CALIB; // SysTick Calibration Value Register
} SysTick_Type;
#define SysTick ((SysTick_Type *) 0xE000E010)
// NVIC 设置中断优先级的函数模拟
void NVIC_SetPriority(int32_t IRQn, uint32_t priority) {
// 实际实现需要操作 NVIC 寄存器
}
// SysTick_Config 函数
static __INLINE uint32_t SysTick_Config(uint32_t ticks) {
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
// 延时函数,单位为毫秒
void delay_ms(uint32_t ms) {
// 获取系统时钟频率,假设系统时钟频率为 72MHz
uint32_t system_clock = 72000000;
// 计算每个毫秒需要的 SysTick 计数值
uint32_t ticks_per_ms = system_clock / 1000;
// 循环延时
for (uint32_t i = 0; i < ms; i++) {
// 配置 SysTick 定时器
if (SysTick_Config(ticks_per_ms) != 0) {
// 配置失败,可进行错误处理
while(1);
}
// 等待 SysTick 定时器计数到 0
while (!(SysTick->CTRL & (1 << 16)));
}
// 关闭 SysTick 定时器
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
// 主函数示例
int main(void) {
// 延时 1000 毫秒(即 1 秒)
delay_ms(1000);
while (1) {
// 主循环代码
}
}
以下是对这段代码 while (!(SysTick->CTRL & (1 << 16)));
的详细分析:
这段代码的主要功能是等待 SysTick 定时器计数到 0。SysTick 是一个 24 位的递减定时器,常用于产生精确的时间延迟或作为系统时钟节拍器。
SysTick
结构体SysTick
是一个在 ARM Cortex - M 系列微控制器中定义的结构体,它代表了 SysTick 定时器的寄存器映射。在标准库中,SysTick
结构体定义在 core_cmxxx.h
(xxx
代表具体的 Cortex - M 内核版本,如 core_cm3.h
、core_cm4.h
等)文件中,其定义如下:
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
其中,CTRL
是 SysTick 控制和状态寄存器,用于控制 SysTick 定时器的工作模式和获取其状态信息。
(1 << 16)
这是一个位操作表达式,1 << 16
表示将二进制数 0000 0000 0000 0000 0000 0000 0000 0001
向左移动 16 位,得到 0000 0000 0000 0001 0000 0000 0000 0000
,即十进制的 65536
。在 SysTick 的 CTRL
寄存器中,第 16 位是 COUNTFLAG
标志位,当 SysTick 定时器从 1 计数到 0 时,该标志位会被硬件置 1。
SysTick->CTRL & (1 << 16)
这是一个按位与操作,将 SysTick
控制和状态寄存器 CTRL
的值与 (1 << 16)
进行按位与运算。如果 COUNTFLAG
标志位为 1,则结果不为 0;如果 COUNTFLAG
标志位为 0,则结果为 0。
!(SysTick->CTRL & (1 << 16))
这是一个逻辑非操作,对 SysTick->CTRL & (1 << 16)
的结果取反。如果 COUNTFLAG
标志位为 1,SysTick->CTRL & (1 << 16)
结果不为 0,取反后为 0;如果 COUNTFLAG
标志位为 0,SysTick->CTRL & (1 << 16)
结果为 0,取反后为 1。
while (!(SysTick->CTRL & (1 << 16)));
这是一个循环语句,只要 !(SysTick->CTRL & (1 << 16))
的结果为 1(即 COUNTFLAG
标志位为 0,表示 SysTick 定时器还未计数到 0),就会一直循环。当 COUNTFLAG
标志位变为 1(即 SysTick 定时器计数到 0)时,!(SysTick->CTRL & (1 << 16))
的结果为 0,循环结束。
SysTick_Config
函数:用于配置 SysTick 定时器的重载值、中断优先级,并启用定时器和中断。delay_ms
函数:
SysTick_Config
函数,每次配置 SysTick 定时器为 1 毫秒的延时。SysTick->CTRL
寄存器的第 16 位(COUNTFLAG)来判断 SysTick 定时器是否计数到 0。main
函数:调用 delay_ms
函数进行 1 秒的延时,然后进入主循环。