中断函数中调用延时会影响其他中断?_可注册非阻塞型延时设计

基于SysTick Timer的非阻塞延时设计

  • 什么是SysTick Timer
  • SysTick Timer如何应用
  • 阻塞和非阻塞的定义
  • 阻塞和非阻塞延时的应用场景
  • 可注册非阻塞延时的代码实现
SysTick定时器

SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。以前大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。例如,为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。

Cortex‐M3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带有这个定时器,软件在不同 CM3器件间的移植工作得以化简。该定时器的时钟源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟( CM3处理器上的STCLK信号)。不过,STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同,你需要检视芯片的器件手册来决定选择什么作为时钟源。SysTick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有它的一席之地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多了,因为在所有CM3产品间对其处理都是相同的(摘自Cortex‐M3权威指南中文手册)。

SysTick定时器应用
  • 维持操作系统“心跳”节拍
  • 为裸机提供精准延时

先来了解下SysTick的配置,我们需要从其有关的寄存器来入手:

 表1 控制及状态寄存器

a602ef6249b7d708bdf137cd68108cce.png

 表2 重装载数值寄存器

30d6f06af13c293a13bfd5256af72c79.png

 表3 当前数值寄存器

d5bbdc18e9788853e72216a4e41e2bfd.png

 表4 校准数值寄存器

0ef3b1d3776adc54d212b5ae0d2df005.png

SysTick Timer主要有以上的几个寄存器,其实在常见的应用中我们只需要用到控制及状态寄存器和重装载数值寄存器即可,首先需要选择时钟配置,这是第一步也是最关键的一步,清空计数器里面的值,然后写入重装载值来设置触发中断的时间,最后使能异常中断配置就算完成了,来看看代码实现:

/********************************************************************
Program Function: Systick timer delay init function
Program  Version: 1.0
Create      Date: 2020-10-26
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/

void DelayInit(void){
 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);      //select the external clock
 fac_us = SystemCoreClock / 8000000;                 //system clock config  
 NVIC_SetPriority(SysTick_IRQn, (1<<__nvic_prio_bits>1);   //interrupt priority config
 fac_ms = (u16)fac_us * 1000;                  
 BaseSysTickConfg();                                          //clock base config
 SysTickRegTabInit();                                         //register table init
}/********************************************************************
Program Function: BaseSysTick config function
Program  Version: 1.0
Create      Date: 2020-10-27
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/static void BaseSysTickConfg(void){
 SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;    //enable systick interrupt 
 SysTick->LOAD = (u32)fac_ms * BASE_CLOCK;  //load the time value(SysTick->LOAD is 24bit)
 SysTick->VAL = 0x00;       //clear the count
 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;     //start the count down
}

这里需要注意的是SysTick也是可以配置中断优先级的,因为它和NVIC是绑定的,其中断类型为内核中断,优先级默认为是中断分组中最低的,但是如果有其他中断同样设置为中断分组中最低的一个优先级分组,此时SysTick会打断该中断优先运行,可以通过上面的NVIC_SetPriority()函数来进行设置,下面来看看中断处理函数:

/********************************************************************
Program Function: Set the SysTick flag function
Program  Version: 1.0
Create      Date: 2020-10-26
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/

static void SetSysTickFlg(void){
 u8 i;
 
 for(i = 0; i  {
  Tab.TimeFlag[i] = 0x01;
 }
}

/********************************************************************
Program Function: Systick interrupt handle function
Program  Version: 1.0
Create      Date: 2020-10-27
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/

void SysTick_Handler(void){
 SetSysTickFlg();

 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  //stop the count down
 SysTick->VAL = 0x00;             //clear the count
 SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;  //disable the systick interrupt
}

这里其实是有个坑儿的,不知道有没有踩过坑的小伙伴,中断函数名是固定的,需要和中断向量表中的名字保持一致,否则就无法触发中断,其实也很好理解,函数名对应了一个地址,在中断向量表中给此中断配置了一个特定的地址,如果你编写的中断函数名却是另外的一个函数名,那就无法完成索引了,中断向量表代码如下:

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp
                                                  
;  Heap Configuration
;     Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>; 

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB High Priority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB Low  Priority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line 9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler         ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10
                DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
                DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
                DCD     TIM8_UP_IRQHandler         ; TIM8 Update
                DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
                DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
                DCD     ADC3_IRQHandler            ; ADC3
                DCD     FSMC_IRQHandler            ; FSMC
                DCD     SDIO_IRQHandler            ; SDIO
                DCD     TIM5_IRQHandler            ; TIM5
                DCD     SPI3_IRQHandler            ; SPI3
                DCD     UART4_IRQHandler           ; UART4
                DCD     UART5_IRQHandler           ; UART5
                DCD     TIM6_IRQHandler            ; TIM6
                DCD     TIM7_IRQHandler            ; TIM7
                DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
                DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
                DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
                DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY
阻塞和非阻塞定义

阻塞:当Task1为阻塞调用时,其他的Task都将被挂起,只能等待Task1执行完成后(不管当前Task1运行条件是否满足),其他的满足运行条件Task才可以从挂起态转换成运行态,简而言之,阻塞就是只能等CPU或者MCU把当前的事件处理完,才能释放出资源来处理下一个事件。

非阻塞:当Task1的运行条件还未满足时,CPU或者MCU此时不需要死等,依然可以继续去处理其他的Task。

阻塞和非阻塞延时的应用场景(裸机)

阻塞延时应用场景

①外设芯片时序,比如485芯片CS引脚高低电平设置;

②数据接收,比如Modbus通讯中,需先把数据读取完才可以进行写操作;

③单任务运行,系统实时性不高,比如只有一个LED在跑,阻塞延时对其他的也不会有影响。

非阻塞延时应用场景

①报警装置,例如LED呼吸灯、Beep这些;

②多任务运行,系统实时性较高的场景。

可注册非阻塞延时代码实现

其实代码也比较简单,利用了数据结构来实现了可注册的功能,最大可注册数为64,就裸机而言任务数一般都是比较少的,所以最大可注册数也是可以满足一般的需求的,提供给用户的只有两个接口,一个注册任务的接口和一个实现非阻塞延时功能的接口,其他的都封装在模块内了,一般是不需要更改的,好了来看看源码:

/****************************SysTickTimer head file********************************/

#ifndef __DELAY_H
#define __DELAY_H       
#include "sys.h"  

#define SysTickTskMax   64
#define    BASE_CLOCK   10
#define          NULL   0

typedef struct
{
 u8 TimeCunt[SysTickTskMax];
 u8 TimeFlag[SysTickTskMax];
 u8 TaskFlag[SysTickTskMax];
}DelayRegTable;
  
void DelayInit(void);
u8 SysTickRegTask(void);
void SysTick_Handler(void);
void SysTickHandleFunc(void (*CallBack_Func)(void), u8 TskName, u8 DelayTime);

#endif

/**************************@Copyright 2020 reserved by Huang Cheng**************************/
/******************************SysTickTimer source file*********************************/

#include "delay.h"

/******************************Data definition array***********************************/

static u8  fac_us=0;         
static u16 fac_ms=0;   
static u8 TaskIndex = 0;
DelayRegTable Tab;

/******************************End of Data definition array***************************/


/******************************Function declaration array*****************************/

static void BaseSysTickConfg(void);
static void SysTickRegTabInit(void);
static void SetSysTickFlg(void);
static void DelayHandle(u8 DelayTime, u8 TskName);
static void ClrSysTickTaskFlg(u8 TskType);
static   u8 GetSysTickTaskFlg(u8 TskType);

/******************************End of Function declaration array**********************/
      
/********************************************************************
Program Function: Systick timer delay init function
Program  Version: 1.0
Create      Date: 2020-10-26
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/

void DelayInit(void){
 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);      //select the external clock
 fac_us = SystemCoreClock / 8000000;                 //system clock config  
 NVIC_SetPriority(SysTick_IRQn, (1<<__nvic_prio_bits>1);   //interrupt priority config
 fac_ms = (u16)fac_us * 1000;                  
 BaseSysTickConfg();                                          //clock base config
 SysTickRegTabInit();                                         //register table init
}/********************************************************************
Program Function: System tick RegTable data init function
Program  Version: 1.0
Create      Date: 2020-10-26
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/static void SysTickRegTabInit(void){
 u8 i;
 u8 *pData = (u8*)&Tab;
 TaskIndex = 0;for(i = 0; i sizeof(DelayRegTable)/sizeof(u8); i++)
 {
  pData[i] = 0;
 }
}/********************************************************************
Program Function: Task delay register function
Program  Version: 1.0
Create      Date: 2020-10-26
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/u8 SysTickRegTask(void){
 u8 Rtn = 0xFF;
 TaskIndex++;if(TaskIndex > SysTickTskMax)
 {
  TaskIndex = 0;
  Rtn =  0xFF;             //Register failed
 }else
 {
  Rtn = TaskIndex - 1;     //Register success
 }return Rtn;
}/********************************************************************
Program Function: Get the SysTick task flag function
Program  Version: 1.0
Create      Date: 2020-10-26
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/static u8 GetSysTickTaskFlg(u8 TskType){return Tab.TaskFlag[TskType];
}/********************************************************************
Program Function: Clear the SysTick task flag function
Program  Version: 1.0
Create      Date: 2020-10-26
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/static void ClrSysTickTaskFlg(u8 TskType){
 Tab.TaskFlag[TskType] = 0;
}/********************************************************************
Program Function: Set the SysTick flag function
Program  Version: 1.0
Create      Date: 2020-10-26
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/static void SetSysTickFlg(void){
 u8 i;for(i = 0; i  {
  Tab.TimeFlag[i] = 0x01;
 }
}/********************************************************************
Program Function: SysTick handle callback function
Program  Version: 1.0
Create      Date: 2020-10-27
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/void SysTickHandleFunc(void (*CallBack_Func)(void), u8 TskName, u8 DelayTime){
 DelayHandle(DelayTime, TskName);if(NULL != CallBack_Func)
 {if(0x01 == (GetSysTickTaskFlg(TskName)))
  {
   (*CallBack_Func)();
   ClrSysTickTaskFlg(TskName);
  }else
  {//do nothing
  }
 }else
 {return;
 }
} /********************************************************************
Program Function: Delay handle function
Program  Version: 1.0
Create      Date: 2020-10-27
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/static void DelayHandle(u8 DelayTime, u8 TskName){if (0x01 == Tab.TimeFlag[TskName])
 {
  Tab.TimeCunt[TskName]++;if (Tab.TimeCunt[TskName] >= DelayTime)   //time = DelayTime * 10ms; 
  {
   Tab.TaskFlag[TskName] = 0x01;
   Tab.TimeCunt[TskName] = 0x00;
  }else
  {//do nothing;
  }
  BaseSysTickConfg();
  Tab.TimeFlag[TskName] = 0x00;
 }else
 {//do nothing;
 }
}/********************************************************************
Program Function: BaseSysTick config function
Program  Version: 1.0
Create      Date: 2020-10-27
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/static void BaseSysTickConfg(void)
{
 SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;    //enable systick interrupt 
 SysTick->LOAD = (u32)fac_ms * BASE_CLOCK;  //load the time value(SysTick->LOAD is 24bit)
 SysTick->VAL = 0x00;       //clear the count
 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;     //start the count down
}/********************************************************************
Program Function: Systick interrupt handle function
Program  Version: 1.0
Create      Date: 2020-10-27
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/void SysTick_Handler(void){
 SetSysTickFlg();
 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  //stop the count down
 SysTick->VAL = 0x00;             //clear the count
 SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;  //disable the systick interrupt
}/**************************@Copyright 2020 reserved by Huang Cheng**************************/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "stm32f10x.h"

/********************************************************************
Program Function: system init
Program  Version: 1.0
Create      Date: 2020-10-27
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/

void SystmInit(void){
 //edit code;
 TestLedInit();
 delay_init();
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
}

/********************************************************************
Program Function: main function
Program  Version: 1.0
Create      Date: 2020-10-27
Author          : Huang Cheng
Modify Instrodtn: None
********************************************************************/ 

int main(void){
 u8 Task1 = 0;
 u8 Task2 = 0;
    
 SystmInit();
 Task1 = SysTickRegTask();
 Task2 = SysTickRegTask();
 
 while(1)
 {
  //example code;
  SysTickHandleFunc(Led0CallBack, Task1, 10);  //10*10ms
  SysTickHandleFunc(Led1CallBack, Task2, 50);  //50*10ms
 }
 
 //return 0;
}

上面的task注册其实也可以封装成一个函数用于统一注册,源码到时候我会传到码云gitee上去,感兴趣的可以自行下载!欢迎一起讨论和优化!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值