关于systick定时的一点理解

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 位)。
  • 减 1 的原因是,当计数器从重载值递减到 0 时,会产生一个计数溢出事件,因此需要将 ticks 值减 1。
设置 SysTick 中断优先级
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 定时器和中断
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 定时器的定时功能,通过循环等待计数器达到指定的时间来实现延时。以下是详细的步骤和示例代码:

实现步骤

  1. 初始化 SysTick 定时器:调用 SysTick_Config 函数,设置合适的重载值,使 SysTick 定时器按照期望的时间间隔产生中断。
  2. 编写延时函数:在延时函数中,通过循环等待 SysTick 定时器的计数标志位,直到达到指定的延时时间。
  3. 调用延时函数:在需要延时的地方调用编写好的延时函数。

示例代码

#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 位的递减定时器,常用于产生精确的时间延迟或作为系统时钟节拍器。

代码详细解释

1. SysTick 结构体

SysTick 是一个在 ARM Cortex - M 系列微控制器中定义的结构体,它代表了 SysTick 定时器的寄存器映射。在标准库中,SysTick 结构体定义在 core_cmxxx.hxxx 代表具体的 Cortex - M 内核版本,如 core_cm3.hcore_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 定时器的工作模式和获取其状态信息。

2. (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。

3. SysTick->CTRL & (1 << 16)

这是一个按位与操作,将 SysTick 控制和状态寄存器 CTRL 的值与 (1 << 16) 进行按位与运算。如果 COUNTFLAG 标志位为 1,则结果不为 0;如果 COUNTFLAG 标志位为 0,则结果为 0。

4. !(SysTick->CTRL & (1 << 16))

这是一个逻辑非操作,对 SysTick->CTRL & (1 << 16) 的结果取反。如果 COUNTFLAG 标志位为 1,SysTick->CTRL & (1 << 16) 结果不为 0,取反后为 0;如果 COUNTFLAG 标志位为 0,SysTick->CTRL & (1 << 16) 结果为 0,取反后为 1。

5. while (!(SysTick->CTRL & (1 << 16)));

这是一个循环语句,只要 !(SysTick->CTRL & (1 << 16)) 的结果为 1(即 COUNTFLAG 标志位为 0,表示 SysTick 定时器还未计数到 0),就会一直循环。当 COUNTFLAG 标志位变为 1(即 SysTick 定时器计数到 0)时,!(SysTick->CTRL & (1 << 16)) 的结果为 0,循环结束。

代码解释

  1. SysTick_Config 函数:用于配置 SysTick 定时器的重载值、中断优先级,并启用定时器和中断。
  2. delay_ms 函数
    • 首先获取系统时钟频率,计算每个毫秒需要的 SysTick 计数值。
    • 然后通过循环调用 SysTick_Config 函数,每次配置 SysTick 定时器为 1 毫秒的延时。
    • 接着通过检查 SysTick->CTRL 寄存器的第 16 位(COUNTFLAG)来判断 SysTick 定时器是否计数到 0。
    • 最后关闭 SysTick 定时器。
  3. main 函数:调用 delay_ms 函数进行 1 秒的延时,然后进入主循环。

注意事项

  • 系统时钟频率需要根据实际情况进行修改。
  • 在实际应用中,可能需要考虑中断优先级和其他系统资源的冲突问题。

你可能感兴趣的:(c语言,单片机)