STM32_06_Systick定时器
Systick定时器又称滴答定时器。
延时时间
一样的代码在不同的机器它的延时时间也会有轻微的区别,因此我们要通过Systick定时器来获取精确的延时。
Systick定时器
原理就是数数,假设一个周期=10ms,我们设个初始值y,然后从y自减到0,我们计算用了20个时钟周期,那这个时候我们所需的时间是20x10=200ms=0.2s,这样我们就能设置一个确定的时间了,初始值y就是200ms,用这个时间我们就能延时。 进入while()循环等待,y的值减至0,这个循环的时间就是200ms。
- 如上图,如果我们初始值是 72
- 000 时钟周期,那我们的延时时间就是1ms。
- 再举个例子:如果初始值是 72 0000 时钟周期,那它的延时时间是?
------ 10ms- 思路:初始值 -> 时钟周期 -> 时间
时间 -> 时钟周期 -> 配置初始值- 滴答定时器实际上是9MHz
1s = 9 000 000 时钟周期 1ms = 9 000 时钟周期 1us = 9 时钟周期
如果延时时间是30ms,时钟周期是 9000 * 30 = 27 0000,初始值就是270000
思考:
- 怎么去设置初始值?
看第二个寄存器(LOAD),比如你的初始值想设置为 9 0000,那我们就把 9 0000 放到这个LOAD寄存器里就可以了。- 你怎么知道你那个初始值最终是减到0了?
判断这个寄存器的第16位。【16】= 1(减到0了);【16】 = 1(还没减到0)。
[0]: 一旦开启定时器,初始值就开始自减;一旦关闭定时器,初始值即停止自减。
LOAD寄存器:比如你的初始值想设置为 9 0000,那我们就把 9 0000 放到这个LOAD寄存器里就可以了。
VAL寄存器:假设LOAD寄存器当中放的是 9 0000,一旦打开定时器,我们就把这个 9 0000 加载到VAL寄存器当中,然后每个时钟周期-1.
当减到0的时候,CTRL【16】= 1,即等待 / 延时的时间就到期了。
Systick寄存器结构体
思考:为什么上面三个寄存器叫CTRL,LOAD,VAL呢?
初始化计数次数
微秒延时
毫秒延时
实战演练-掌控需求
代码示例:
systick.h:
// systick.h #ifndef __SYSTICK_H__ #define __SYSTICK_H__#include "stm32f10x.h" extern void Systick_Init(void); extern void delay_us(u32 n); extern void delay_ms(u32 n);#endif
systick.c:
// systick.c #include "systick.h"static u32 fac_us = 0; static u32 fac_ms = 0; // // 定时器初始化 // void Systick_Init(void){// 1.配置定时器时钟为9MHz// CTRL【2】 = 0SysTick->CTRL &= ~(1 << 2);// 2.1us计数周期fac_us = 9;// 3.1us计数周期fac_ms = 9000; } // // 延时n微秒 // void delay_us(u32 n){u32 temp;// 1.设置初始值SysTick->LOAD = n * fac_us;// 2.将VAL清空SysTick->VAL = 0;// 3.启动定时器SysTick->CTRL |= (1 << 0);// 启动了定时器,初始值会加载到VAL中,每个时钟周期减1// 直到0// 循环等待,直到初始值自减到0// 判断CTRL【16】// [16] = 1,时间到期,结束循环// xxxxxxxxxxxxxxxxxxxxxxxx CTRL// 100000000000000000000000 & (1 << 16)// x00000000000000000000000do{temp = SysTick->CTRL;}while((temp & 0x01) && (!(temp & (1 << 16))));// 结束循环 - 时间到期// 关闭定时器SysTick->CTRL &= ~(1 << 0);SysTick->VAL = 0; }// // 延时n毫秒 // void delay_ms(u32 n){u32 temp;// 1.设置初始值SysTick->LOAD = n * fac_ms;// 2.将VAL清空SysTick->VAL = 0;// 3.启动定时器SysTick->CTRL |= (1 << 0);// 启动了定时器,初始值会加载到VAL中,每个时钟周期减1// 直到0// 循环等待,直到初始值自减到0// 判断CTRL【16】// [16] = 1,时间到期,结束循环// xxxxxxxxxxxxxxxxxxxxxxxx CTRL// 100000000000000000000000 & (1 << 16)// x00000000000000000000000do{temp = SysTick->CTRL;}while((temp & 0x01) && (!(temp & (1 << 16))));// 结束循环 - 时间到期// 关闭定时器SysTick->CTRL &= ~(1 << 0);SysTick->VAL = 0; } }
main.c:
// main.c #include "stm32f10x.h" #include "led.h" #include "beep.h" #include "system.h" #include "systick.h"// 时钟树配置 // 参数1:div 参数2:pllm void RCC_HSE_Config(u32 div, u32 pllm){// 1.设置为默认值RCC_DeInit();// 2.打开HSE时钟RCC_HSEConfig(RCC_HSE_ON);// 3.等待HSE起振if(RCC_WaitForHSEStartUp() == SUCCESS){// 4.配置AHB 不分频RCC_HCLKConfig(RCC_SYSCLK_Div1);// 5.配置APB1 2分频RCC_PCLK1Config(RCC_HCLK_Div2);// 6.配置APB2 不分频RCC_PCLK2Config(RCC_HCLK_Div1);// 7.配置PLL时钟源:div 倍频系数:pllm//RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);RCC_PLLConfig(div,pllm);// 8.使能PLLRCC_PLLCmd(ENABLE);// 9.循环判断PLL是否生效while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);// 10.配置SYSCLK的时钟源为PLLRCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);} }// 位带只是对输入/输出做优化,和初始化无关 int main(void){// 配置时钟树// pll时钟源:HSE 倍频系数:9 PLLCLK=SYSCLK=72MHzRCC_HSE_Config(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);// pll时钟源:HSE/2 倍频系数:9 PLLCLK=SYSCLK=36MHz//RCC_HSE_Config(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9);// pll时钟源:HSE 倍频系数:9 PLLCLK=SYSCLK=128MHz// 长时间超频是不行的,系统不稳定//RCC_HSE_Config(RCC_PLLSource_HSE_Div1,RCC_PLLMul_16);// LED初始化LED_Init();// BEEP初始化BEEP_Init();// Systick初始化Systick_Init();while(1){// 循环开关灯 + 蜂鸣器LED0 = !LED0;// 1 - 0 - 1LED1 = !LED1;BEEP = !BEEP;// 延时1000msdelay_ms(1000);} }
实验现象见视频。