Cortex-M架构SysTick系统定时器阻塞和非阻塞延时
Posted 网易独家音乐人Mike Zhou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cortex-M架构SysTick系统定时器阻塞和非阻塞延时相关的知识,希望对你有一定的参考价值。
Cortex-M架构SysTick系统定时器阻塞和非阻塞延时
首先是最常用的阻塞延时
void delay_ms(unsigned int ms)
SysTick->LOAD = 50000000/1000-1; // Count from 255 to 0 (256 cycles) 载入计数值 定时器从这个值开始计数
SysTick->VAL = 0; // Clear current value as well as count flag 清空计数值到达0后的标记
SysTick->CTRL = 5; // Enable SysTick timer with processor clock 使能26MHz的系统定时器
while(ms--)
while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set 等待
SysTick->CTRL = 0; // Disable SysTick 关闭系统定时器
void delay_us(unsigned int us)
SysTick->LOAD = 50000000/1000/1000-1; // Count from 255 to 0 (256 cycles) 载入计数值 定时器从这个值开始计数
SysTick->VAL = 0; // Clear current value as well as count flag 清空计数值到达0后的标记
SysTick->CTRL = 5; // Enable SysTick timer with processor clock 使能26MHz的系统定时器
while(us--)
while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set 等待
SysTick->CTRL = 0; // Disable SysTick 关闭系统定时器
50000000表示工作频率
分频后即可得到不同的延时时间
以此类推
那么 不用两个嵌套while循环 也可以写成:
void delay_ms(unsigned int ms)
SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles) 载入计数值 定时器从这个值开始计数
SysTick->VAL = 0; // Clear current value as well as count flag 清空计数值到达0后的标记
SysTick->CTRL = 5; // Enable SysTick timer with processor clock 使能26MHz的系统定时器
while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set 等待
SysTick->CTRL = 0; // Disable SysTick 关闭系统定时器
void delay_us(unsigned int us)
SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles) 载入计数值 定时器从这个值开始计数
SysTick->VAL = 0; // Clear current value as well as count flag 清空计数值到达0后的标记
SysTick->CTRL = 5; // Enable SysTick timer with processor clock 使能26MHz的系统定时器
while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set 等待
SysTick->CTRL = 0; // Disable SysTick 关闭系统定时器
但是这种写法有个弊端
那就是输入ms后,最大定时不得超过计数值,也就是不能超过LOAD的最大值,否则溢出以后,则无法正常工作
而LOAD如果最大是32位 也就是4294967295
晶振为50M的话 50M的计数值为1s 4294967295计数值约为85s
固最大定时时间为85s
但用嵌套while的话 最大可以支持定时4294967295*85s
如果采用非阻塞的话 直接改写第二种方法就好了:
void delay_ms(unsigned int ms)
SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles) 载入计数值 定时器从这个值开始计数
SysTick->VAL = 0; // Clear current value as well as count flag 清空计数值到达0后的标记
SysTick->CTRL = 5; // Enable SysTick timer with processor clock 使能26MHz的系统定时器
//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set 等待
//SysTick->CTRL = 0; // Disable SysTick 关闭系统定时器
void delay_us(unsigned int us)
SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles) 载入计数值 定时器从这个值开始计数
SysTick->VAL = 0; // Clear current value as well as count flag 清空计数值到达0后的标记
SysTick->CTRL = 5; // Enable SysTick timer with processor clock 使能26MHz的系统定时器
//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set 等待
//SysTick->CTRL = 0; // Disable SysTick 关闭系统定时器
将等待和关闭定时器语句去掉
在使用时加上判断即可变为阻塞:
delay_ms(500);
while ((SysTick->CTRL & 0x00010000)==0);
SysTick->CTRL = 0;
在非阻塞状态下 可以提交定时器后 去做别的事情 然后再来等待
不过这样又有一个弊端 那就是定时器会自动重载 可能做别的事情以后 定时器跑过了 然后就要等85s才能停下
故可以通过内部定时器来进行非阻塞延时函数的编写
基本上每个mcu的内部定时器都可以配置自动重载等功能 网上资料很多 这里就不再阐述了
STM32 系统定时器(SysTick)
SysTick定义
SysTick : 24位系统定时器,只能递减,存在于内核嵌套在NVIC中。所有的Cortex-M中都有这个系统定时器。
重装载值reload 递减,当递减到0会触发中断并且会有 置位countflag标志,VAL表示当前值。
然后reload继续从预设值开始递减,周而复始。
还有一个校准数值寄存器,不常用,想知道的可以看下其他文章会讲。
SysTick定时时间的计算
- T : 一个计数循环的时间,跟reload和 CLK有关
- CLK : 72M 或者9 M, 由CTRL寄存器配置
- reload : 24位,用户自己配置
T = reload * (1/ CLK)
CLK= 72M, 1us = (72 )* (1/ 72 000 000)
CLK= 72M, 1ms = (72 000 )* (1/ 72 000 000)
时间单位换算: 1s = 1000ms = 1000 000 us = 1000 000 000 ns
SysTick结构体
typedef struct
{
__IO uint32_t CTRL; //控制及状态寄存器
__IO uint32_t LOAD; //重装载寄存器
__IO uint32_t VAL; //重装载寄存器
__I uint32_t CALIB; //校准寄存器
} SysTick_Type;
SysTick固件库函数
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); //判断reload的值是否大于最大值 2^24
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; //配置reload寄存器的初值
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); //配置中断优先级 1<<4 -1 =16-1=15
SysTick->VAL = 0; //配置count的值
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | //配置system时钟为 72M
SysTick_CTRL_TICKINT_Msk | //使能中断
SysTick_CTRL_ENABLE_Msk; //使能systick
return (0);
}
SysTick中断优先级
数字越小中断优先级越高
1.system tick 属于内核的外设,它的中断优先级 与片上外设的中断优先级相比谁高?
依据是什么?
答:systick 中断优先级配置的是scb->shprx寄存器
外设中断优先级配置的是nvic-> iprx寄存器
有优先级分组
3.STM32的外设(内核还是片上)都是使用4个二进制来表示中断优先级
4.中断优先级的分组对内核和外设同样适用,只需要将中断优先级的四个位按外设优先级来分组即可,人为的进行分出抢占优先级和子优先级。例举:
1<<4 -1=16 -1= 15 (1 1 1 1 )
前面两位表示抢占优先级 后面两位表示子优先级
3 3 (抢占优先级3 ,子优先级3)
1<<4 = 1 0000 = 16
编写ms,us延时函数
systick和delay区别
delay一直占用cpu,而systick一般都采用中断方式,(不会有人在程序里一直查询标志位的),不占用cpu。除非任务很简单。否则实际项目不会使用循环的延时。不过因为stm32上面有更强大的外设定时器的。定时的范围也比systick要大。还可以级联,所以一般不用systick,只把systick用于操作系统的时钟节拍。
案例
SysTick.h
#include "stm32f10x.h"
void ms_delay(uint32_t ms);
void us_delay(uint32_t us);
SysTick.c
#include "stm32f10x.h"
#include "SysTick.h"
/*
ϵͳ¶¨Ê±Æ÷Ìص㣺
Ö»Äܵݼõ£¬Èç¹û¶¨ÒåµÄÊÇ72000
ÄǾʹÓ72000¿ªÊ¼µÝ¼õµ½0£¬ÎªÒ»¸öÖÜÆÚ
ËùÒÔÖ»ÐèÒªÅжÏÊÇ·ñ¼õµ½1
ÈçºÎÅжϣ¿
¿´COUNTFLAG¼Ä´æÆ÷£¬Ò²¾ÍÊÇSysTick->CTRL
*/
void ms_delay(uint32_t ms)
{
uint32_t i;
SysTick_Config(72000);//Ñ»·Ò»´Î1ms
for(i=0;i<ms;i++)//Ҫѻ·¶àÉÙms
{
while( !((SysTick->CTRL)&(1<<16)) );//ÅжÏSysTickÊÇ·ñµ½0£¬µ½0¸ÃλΪ1
//!(1&1)Ìø³öÑ»·
//Ϊʲô×óÒÆ16λÄØ£¬ÒòΪCTRLÊÇ16λµÄ?
//¿´¿ØÖƼ°×´Ì¬¼Ä´æÆ÷µÄͼдÁË¡£
}
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
void us_delay(uint32_t us)
{
uint32_t i;
SysTick_Config(72);//Ñ»·Ò»´Î1us
for(i=0;i<us;i++)//Ҫѻ·¶àÉÙus
{
while( !((SysTick->CTRL)&(1<<16)) );
}
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
led.h
#include "stm32f10x.h"
void Led_init(void);
led.c
#include "stm32f10x.h"
#include "led.h"
void Led_init(void)
{
GPIO_InitTypeDef Led_init;
//1.ʹÄÜAPB2µÄʱÖÓGPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
//2.½á¹¹ÌåÅäÖÃ
Led_init.GPIO_Mode = GPIO_Mode_Out_PP;
Led_init.GPIO_Pin = GPIO_Pin_13;
Led_init.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOC, &Led_init);
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "relay.h"
#include "shake.h"
#include "exti.h"
#include "usart.h"
#include "stdio.h"
#include "tim.h"
#include "motor.h"
#include "SysTick.h"
void delay(uint16_t time)
{
uint16_t i =0;
while(time--){
i=12000;
while(i--);
}
}
int main()
{
Led_init();
GPIO_SetBits(GPIOA, GPIO_Pin_3);
//ʹÓÃϵͳ¶¨Ê±Æ÷SysTick´úÌædelay;
while(1)
{
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
ms_delay(1000);
GPIO_SetBits(GPIOC, GPIO_Pin_13);
ms_delay(1000);
}
}
以上是关于Cortex-M架构SysTick系统定时器阻塞和非阻塞延时的主要内容,如果未能解决你的问题,请参考以下文章