SysTick->LOAD = (SystemCoreClock/1000/1000) *us- 1UL; //系统硬件时钟装载,168MHZ,-1是因为从零开始计数,UL是usigned long类型 SysTick->VAL = 0UL; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; //时钟
时间: 2025-09-06 09:22:37 AIGC 浏览: 18
<think>
我们正在分析一段用于SysTick定时器初始化的代码,目的是实现微秒级延时。代码片段如下:
```c
SysTick->LOAD = (SystemCoreClock/1000/1000) *us - 1UL;
SysTick->VAL = 0UL;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
```
结合之前讨论的`SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;`,我们将逐步解析这段代码。
### 1. **代码解析**
#### (1) `SysTick->LOAD = (SystemCoreClock/1000/1000) *us - 1UL;`
- **目的**:设置SysTick重装载寄存器(`LOAD`)的值。
- **计算过程**:
- `SystemCoreClock`:系统主时钟频率(单位:Hz)。例如:72MHz系统对应`72,000,000 Hz`。
- `SystemCoreClock/1000/1000`:将主时钟频率转换为**每微秒的时钟周期数**。
$$ \text{每微秒周期数} = \frac{\text{SystemCoreClock}}{1,000,000} $$
例如:72MHz系统计算得72周期/微秒。
- `(SystemCoreClock/1000/1000) *us`:计算`us`微秒对应的总时钟周期数。
$$ \text{总周期数} = \frac{\text{SystemCoreClock}}{1,000,000} \times \text{us} $$
- `减去1UL`:由于SysTick是递减计数器且从`N`递减到0实际需要`N+1`个周期,因此重装载值需设为`N-1`[^2]。公式调整为:
$$ \text{LOAD值} = \left( \frac{\text{SystemCoreClock}}{1,000,000} \times \text{us} \right) - 1 $$
#### (2) `SysTick->VAL = 0UL;`
- **目的**:清空当前计数器值(`VAL`寄存器)。
- 写入任何值都会清零计数器,并重新从`LOAD`寄存器加载值[^3]。
#### (3) `SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;`
- **目的**:配置并启动SysTick定时器。
- `SysTick_CTRL_CLKSOURCE_Msk`:选择时钟源为**内核时钟(`HCLK`)**(即`SystemCoreClock`)。
- `SysTick_CTRL_ENABLE_Msk`:使能定时器开始递减计数。
### 2. **整体功能**
这三行代码共同实现了一个**阻塞式微秒级延时函数**的核心逻辑:
1. 根据系统主频和延时时间`us`计算重装载值。
2. 重置计数器。
3. 启动定时器开始倒计时。
使用时需配合等待计数完成的循环,例如:
```c
void delay_us(uint32_t us) {
// 设置重装载值
SysTick->LOAD = (SystemCoreClock / 1000000) * us - 1;
SysTick->VAL = 0; // 清空计数器
// 启动定时器(无中断)
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
// 等待计数结束标志
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
// 关闭定时器(可选)
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
```
### 3. **关键注意事项**
#### (1) 重装载值范围限制
- SysTick的`LOAD`寄存器是24位(最大值$2^{24}-1 = 16,777,215$)。
- 最大可延时时间计算:
$$ \text{最大延时} = \frac{16,777,216}{\text{每微秒周期数}} \mu s $$
例如:72MHz系统最大延时约233ms($16,777,216 / 72 ≈ 233,016 \mu s$)。
#### (2) 中断配置
- 此代码**未启用中断**(无`SysTick_CTRL_TICKINT_Msk`),依赖轮询`COUNTFLAG`标志位。
- 若用于实时操作系统(RTOS)心跳时钟,需启用中断并设置优先级[^2]。
#### (3) 时钟精度
- 使用`HCLK`(内核时钟)确保最高精度。
- 若系统主频可变(如低功耗模式),需动态调整`LOAD`值。
### 4. **与之前代码的关系**
之前的分析聚焦于控制寄存器的配置:
```c
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
```
本代码扩展了完整延时流程:
- 补充`LOAD`和`VAL`寄存器的初始化。
- 明确重装载值的计算方法。
### 5. **优化建议**
1. **防止数值溢出**:
```c
// 推荐先除后乘避免溢出
uint32_t cycles = (SystemCoreClock / 1000000) * us - 1;
```
2. **关闭定时器**:延时完成后建议禁用定时器以节省功耗。
3. **超范围处理**:增加对`us`最大值的校验。
### 相关问题
1. **如何修改此代码以实现毫秒级延时?**
将计算公式中的除数改为1000:
```c
SysTick->LOAD = (SystemCoreClock / 1000) * ms - 1;
```
2. **SysTick定时器在延时过程中能否被中断?**
可以,但中断处理时间会被计入延时时间。若需精确延时,需关闭全局中断或使用高优先级。
3. **如何为SysTick中断编写处理函数?**
在中断向量表中实现`SysTick_Handler`函数:
```c
void SysTick_Handler(void) {
// 处理中断(例如RTOS任务调度)
}
```
4. **SystemCoreClock变量如何获取?**
通常由芯片厂商提供的库函数初始化(如STM32的`SystemInit()`)。
5. **SysTick定时器能否用于非阻塞延时?**
可以,通过全局变量记录状态:
```c
volatile uint32_t ticks;
void SysTick_Handler(void) { ticks--; }
// 启动延时
ticks = us; SysTick_Config(...);
while (ticks != 0);
```
---
### 代码示例:完整微秒延时函数
```c
void delay_us(uint32_t us) {
// 计算重装载值(防止溢出)
uint32_t cycles = (SystemCoreClock / 1000000) * us;
if (cycles == 0) cycles = 1; // 至少1周期
else if (cycles > SysTick_LOAD_RELOAD_Msk + 1) {
cycles = SysTick_LOAD_RELOAD_Msk + 1; // 限制最大值
}
SysTick->LOAD = cycles - 1;
SysTick->VAL = 0; // 清空计数器
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
// 等待计数结束
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
// 关闭定时器
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
```
此函数实现了高精度微秒级延时,适用于对时间敏感的应用场景[^2][^3]。
</think>
### 代码解析:SysTick 微秒级延时实现
#### 1. **完整代码功能**
这段代码实现了基于 SysTick 定时器的**微秒级精确延时**功能,完整代码如下:
```c
SysTick->LOAD = (SystemCoreClock/1000/1000) * us - 1UL;
SysTick->VAL = 0UL;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
```
#### 2. **逐行解析**
**(1) 设置重装载值:**
`SysTick->LOAD = (SystemCoreClock/1000/1000) * us - 1UL;`
- `SystemCoreClock`:系统主时钟频率(Hz),如 72MHz = 72,000,000Hz
- `SystemCoreClock/1000/1000`:计算每微秒的时钟周期数
$$ \text{每微秒周期数} = \frac{\text{SystemCoreClock}}{1,000,000} $$
(如 72MHz 系统:72 周期/μs)
- `* us`:乘以需要的微秒数
- `-1UL`:SysTick 是递减计数器,从 N 到 0 需要 N+1 个周期[^2]
**(2) 复位当前值:**
`SysTick->VAL = 0UL;`
- 清除当前计数器值,触发从 LOAD 寄存器重载初值
- 计数器将从 LOAD 值开始递减
**(3) 配置控制寄存器:**
`SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;`
- `SysTick_CTRL_CLKSOURCE_Msk`:选择内核时钟(HCLK)作为时钟源
- `SysTick_CTRL_ENABLE_Msk`:启动计数器开始递减
#### 3. **工作原理**
1. LOAD 寄存器根据系统时钟和延时时间计算重装载值:
$$ \text{LOAD} = \left(\frac{\text{SystemCoreClock}}{1,000,000} \times \text{us}\right) - 1 $$
2. VAL 寄存器清零后,计数器从 LOAD 值开始递减
3. 当计数器减到 0 时:
- 自动重载 LOAD 值
- COUNTFLAG 标志位置 1(可用作延时完成标志)
#### 4. **与之前代码的关系**
之前分析的代码片段:
```c
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
```
是当前代码的子集,仅包含定时器配置部分。完整延时实现还需:
- 精确计算 LOAD 值(根据时钟频率和延时时间)
- 复位 VAL 寄存器启动计数
#### 5. **实际使用场景**
典型微秒级延时函数实现:
```c
void delay_us(uint32_t us) {
// 计算重装载值
uint32_t reload = (SystemCoreClock / 1000000) * us - 1;
// 防止超出24位范围(0xFFFFFF)
if(reload > 0xFFFFFF) reload = 0xFFFFFF;
SysTick->LOAD = reload; // 设置重装载值
SysTick->VAL = 0; // 复位计数器
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
// 等待计数完成
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
// 关闭定时器(可选)
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
```
#### 6. **关键特性**
| 特性 | 说明 |
|------|------|
| 精度 | 单周期精度(HCLK时钟) |
| 范围 | 最大延时:$ \frac{16,777,216}{\text{SystemCoreClock}} $ 秒 |
| 中断 | 无中断模式(轮询COUNTFLAG) |
| 功耗 | 休眠模式下仍可工作 |
#### 7. **注意事项**
1. **24位计数器限制**
LOAD 值不能超过 0xFFFFFF(16,777,215)
- 72MHz 系统最大延时:233ms
$$ \frac{16,777,216}{72,000,000} \approx 0.233 \text{秒} $$
2. **时钟源一致性**
必须确保实际时钟源与计算使用的 SystemCoreClock 匹配:
```c
// 验证时钟源配置
if(SysTick->CTRL & SysTick_CTRL_CLKSOURCE_Msk) {
// 使用HCLK
} else {
// 使用HCLK/8,需调整计算
}
```
3. **阻塞式延时**
当前实现为阻塞式延时,期间CPU无法执行其他任务
---
### 相关问题
1. **如何将此代码改为毫秒级延时?**
只需修改 LOAD 值计算:
```c
// 毫秒延时:将1000000改为1000
SysTick->LOAD = (SystemCoreClock/1000) * ms - 1;
```
2. **SysTick 延时最小精度是多少?**
最小延时 = $\frac{1}{\text{SystemCoreClock}}$ 秒
(72MHz 系统约 13.9ns)
3. **如何在延时期间响应中断?**
添加中断使能位并实现中断处理函数:
```c
// 启用中断
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
NVIC_EnableIRQ(SysTick_IRQn);
```
4. **SysTick 在多核系统中如何使用?**
每个 Cortex-M 内核有独立 SysTick,需分别配置:
```c
// 对于CM7双核系统
SysTick->LOAD = ...; // 核1
SysTick_1->LOAD = ...; // 核2
```
5. **如何实现非阻塞式延时?**
使用全局时间戳变量:
```c
volatile uint32_t timeout;
void SysTick_Handler(void) {
if(timeout) timeout--;
}
void delay_nonblock(uint32_t us) {
timeout = us;
while(timeout);
}
```
阅读全文
相关推荐



















