一:Systick定时器介绍
SysTick 定时器也叫 SysTick 滴答定时器, 它是 Cortex-M3 内核的一个外设,被嵌入在 NVIC 中。 它是一个 24 位向下递减的定时器, 每计数一次所需时间为1/SYSTICK, SYSTICK 是系统定时器时钟, 它可以直接取自系统时钟, 还可以通过系统时钟 8 分频后获取, 我们采用后者, 即每计数一次所需时间为1/(72/8)us, 换句话说在 1us 的时间内会计数 9 次。 当定时器计数到 0 时, 将从LOAD 寄存器中自动重装定时器初值, 重新向下递减计数, 如此循环往复。 如果开启 SysTick 中断的话, 当定时器计数到 0, 将产生一个中断信号。 因此只要知道计数的次数就可以准确得到它的延时时间。
SysTick 定时器通常应用在操作系统中, 为其提供时钟周期。
二:Systick定时器操作
在 STM32F1 库函数中, 并没有提供相应的 SysTick 定时器配置函数, 我们要操作 SysTick 定时器就需要了解它的寄存器功能。 其实 SysTick 定时器寄存器很简单, 只有 4 个, 分别是 CTRL、 LOAD、 VAL、 CALIB。
1. Systick定时器寄存器
(1) CTRL 寄存器
CTRL 是 SysTick 定时器的控制及状态寄存器。 其相应位功能如下:
注: CLKSOUTCE 位是用于选择 SysTick 定时器时钟来源, 如果该位为 1, 表示其时钟是由系统时钟直接提供即 72M。 如果为 0, 表示其时钟是由系统时钟八分频后提供即 72/8=9M。
(2) LOAD 寄存器
LOAD 是 SysTick 定时器的重装载数值寄存器。 其相应位功能如下:
因为 STM32F1 的 SysTick 定时器是一个 24 位递减计数器, 因此重装载寄存器中只使用到了低 24 位, 即 bit0-bit23。 当系统复位时, 其值为 0。
(3) VAL 寄存器
VAL 是 SysTick 定时器的当前数值寄存器。 其相应位功能如下:
同样只有 bit0-bit23 有效, 复位时值为 0。
(4) CALIB 寄存器
CALIB 是 SysTick 定时器的校准数值寄存器。 其相应位功能如下:
2. Systick定时器操作步骤
SysTick 定时器的操作可以分为 4 步:
(1) 设置 SysTick 定时器的时钟源。
(2) 设置 SysTick 定时器的重装初始值(如果要使用中断的话, 就将中断使能打开) 。
(3) 清零 SysTick 定时器当前计数器的值。
(4) 打开 SysTick 定时器。
三:软件设计
软件设计部分我们就不再讲述怎么复制工程、 新建文件以及添加对应的头文件路径, 我们直接打开实验例程内的工程进行讲解。 同样 SysTick定时器延时函数在任何一个 STM32F1 应用程序中都用得上, 因此将其驱动文件放在 Public 文件夹内。
1. Systick_Init()函数
void SysTick_Init(u8 SYSCLK)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us=SYSCLK/8; //SYSCLK的 8分频 保存 1us所需的计数次数
fac_ms=(u16)fac_us*1000; //每个 ms 需要的 systick 时钟数
}
SysTick_Init 函数形参 SYSCLK 表示的系统时钟大小, 默认配置我们使用的系统时钟是 72M, 所以调用这个函数时, 形参值即为 72。 函数内部调用了一个库函数 SysTick_CLKSourceConfig, 此函数用来对 SysTick 定时器时钟的选择, 我们 使 用 的 SysTick 定 时 器 时 钟 是 系 统 时 钟 的 8 分 频 , 所 以 参 数 是SysTick_CLKSource_HCLK_Div8。 如果使用系统时钟作为 SysTick 定时器时钟,那么参数即为 SysTick_CLKSource_HCLK。 这个函数在 misc.c 库文件内, 如何查找我们前面介绍过方法。
下面的两条语句是用来求取 SysTick 定时器在 1us 时间内和 1ms 时间内的计数次数。
2. delay_ms()函数
注 意 :nus 的 值 , 不 要 大 于 798915us( 最 大 值 即2^24/fac_us@fac_us=21)
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~0x01; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
①将需要延时多少 us 的计数值加载到 SysTick 的 LOAD 寄存器中, fac_us值是延时 1us 所需的计数值。
②清空当前计数值寄存器 VAL。
③打开 SysTick 定时器, 定时器开始向下递减计数。
④CTRL 寄存器的第 16 位是 SysTick 递减到 0 的标志位, 如果递减到 0, 此为置 1, 通过读取该位来判断延时是否完成, 从而退出 while 循环。
⑤关闭 SysTick 定时器。
⑥清空当前计数值寄存器 VAL。
3. delay_ms()函数
注意:nms 的值,SysTick->LOAD 为 24 位寄存器,不要大于 0xffffff*8*1000/SYSCLK对 72M 条件下,nms<=1864ms
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; // 时 间 加 载(SysTick->LOAD 为 24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=0x01 ; //开始倒数
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~0x01; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
此函数功能与 delay_us 基本一样, 只不过这里是延时 ms。 要注意的是,SysTick 定 时 器 是 24 位 的 , 其 计 数 最 大 值 为 0xffffff , 时 间 为nms<=0xffffff*8*1000/SYSCLK, SYSCLK 是系统时钟为 72M, 所以最大延时为1864ms。 如果需要延时大于 1.864S, 可以调用多个 delay_ms 函数即可。
4. 主函数
int main()
{
SysTick_Init(72);
LED_Init();
while(1)
{
led1=0;
led2=1;
delay_ms(500); //精确延时 500ms
led1=1;
led2=0;
delay_ms(500); //精确延时 500ms
}
}
主函数实现的功能比较简单, 首先对 SysTick 定时器进行初始化配置, 选择系统时钟 8 分频作为 SysTick 的时钟, 然后初始化 LED, 这个初始化过程前面已经介绍过, 大家也可以进入这个函数内查看。 最后进入 while 循环语句, 对 PC0和 PC1 管脚进行位操作, 里面也调用了 delay_ms 延时函数, 这时候的延时是非常精确的。