常用单片机编程思想及例程3——延时篇
目录
嵌入式编程中,很多地方都要用到延时程序,常用的单片机延时有很多种,大概分为两种类型:阻塞型延时非阻塞型延时,今天将就以下几种常见的延时函数使用进行说明。
延时应用
阻塞型延时
顾名思义,这种延时是通过程序“死等”来完成延时操作的,一般在时效性要求不高的场合下使用,但不宜延时太长时间,过长的阻塞延时会极大的影响CPU的效率。实现这种延时的方法有很多,如利用空指令、循环空跑等封装出的延时函数,这类延时大都做粗延时(不太精确)使用,如果想要做到精确的延时,还可以配合定时器使用,以下以华大单片机为例说明这些延时的具体实现方法。
///*空指令实现延时(在stm32环境中,一般多少M的主频就放多少个空指令,就能实现1us的延时,但是华大单片机上好像不行,可以做粗延时使用)*///
void delay(void)
{
_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();
_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();_NOP();
}
//循环空跑
void delay_nms(unsigned short time)
{
unsigned short i = 0, j = 0;
for(i = 0;i < time;i++)
{
for(j = 0;j < 925;j++) //mcu环境不同,j的阈值也不同,需要多次尝试
{
}
}
}
//利用定时器实现延时
#include "timer4.h"
static uint16_t timer_timeout = 0;
void Timer4_Init(void)
{
stc_adt_basecnt_cfg_t stcAdtBaseCntCfg;
DDL_ZERO_STRUCT(stcAdtBaseCntCfg);
Sysctrl_SetPeripheralGate(SysctrlPeripheralAdvTim, TRUE); ///< ADT外设时钟使能
stcAdtBaseCntCfg.enCntMode = AdtSawtoothMode; ///< 锯齿波模式
stcAdtBaseCntCfg.enCntDir = AdtCntUp; ///< 递加计数
stcAdtBaseCntCfg.enCntClkDiv = AdtClkPClk0Div16;
Adt_Init(M0P_ADTIM4, &stcAdtBaseCntCfg);
/// 设置定时器周期 500us 主频16M 16分频
Adt_SetPeriod(M0P_ADTIM4, 500);
Adt_ClearAllIrqFlag(M0P_ADTIM4); ///< 清标志位
Adt_CfgIrq(M0P_ADTIM4, AdtOVFIrq, TRUE); ///< 上溢中断配置
EnableNvic(ADTIM4_IRQn, IrqLevel3, TRUE); ///< 使能AdvTimer4 中断
Adt_StartCount(M0P_ADTIM4);
}
void Tim4_IRQHandler(void)
{
if (Adt_GetIrqFlag(M0P_ADTIM4,AdtOVFIrq))
{
Adt_ClearIrqFlag(M0P_ADTIM4,AdtOVFIrq);
/* 500us timer */
if (timer_timeout != 0) {
timer_timeout--;
}
}
}
void delay_500us(uint16_t usec) //时基为500us的精确延时
{
timer_timeout = usec;
while(timer_timeout != 0);
}
以上几种延时方式换到其他MCU环境中大同小异,根据这些思想还可以衍生出很多种其他的延时,有兴趣的可以尝试一下。
非阻塞型延时
相比于阻塞型的延时,非阻塞型延时既可以实现计时功能,又可以最大化的释放MCU的性能,一个成熟的程序员一般是不会允许自己的代码中存在阻塞的。其实这种延时的实现原理也很简单:通过定时器计时,每隔一段时间判断一次(该过程一般在中断中进行),如果延时时间到了就执行用户要执行的语句,延时时间没有到也不影响其他语句的执行。(类似于:你明天早上八点钟要坐飞机出去玩,这个时候你会提前定一个闹钟提醒你明天要准时起床,只要闹钟还没有响,这段等待时间内你就还可以做其他的事情;如果是阻塞型延时的话,你就需要一直坐着,干瞪着眼,等到你起床的时间,中间什么也不能干。)
//配合系统定时器使用
SysTick_Config(SystemCoreClock/1000); //1ms 一次中断
void SysTick_IRQHandler(void)
{
gSysTickCount++;
}
int main(void)
{
while(1)
{
if(gSysTickCount > 1000) //每过1s执行一次
{
//user语句
gSysTickCount = 0;
}
}
}
//利用定时器实现非阻塞延时 timer.c
#include "adt.h"
#include "delay.h"
#include "Timer.h"
#define PERIOD 7500 ///< PWM频率:100Hz = 48M/64/7500
uint8_t timer10msFlag,timer100msFlag;
uint8_t timer10msPlus; ///< 10ms脉冲,可供其他函数计时用
uint8_t timer100msPlus; ///< 100ms脉冲,可供其他函数计时用
/**
* @brief 定时器初始化
*/
void TIMER_Init(void)
{
stc_adt_basecnt_cfg_t stcAdtBaseCntCfg;
DDL_ZERO_STRUCT(stcAdtBaseCntCfg);
Sysctrl_SetPeripheralGate(SysctrlPeripheralAdvTim, TRUE); ///< ADT外设时钟使能
stcAdtBaseCntCfg.enCntMode = AdtSawtoothMode; ///< 锯齿波模式
stcAdtBaseCntCfg.enCntDir = AdtCntUp; ///< 递加计数
stcAdtBaseCntCfg.enCntClkDiv = AdtClkPClk0Div64;
Adt_Init(M0P_ADTIM4, &stcAdtBaseCntCfg);
/// 设置定时器周期 10ms
Adt_SetPeriod(M0P_ADTIM4, PERIOD);
Adt_ClearAllIrqFlag(M0P_ADTIM4); ///< 清标志位
Adt_CfgIrq(M0P_ADTIM4, AdtOVFIrq, TRUE); ///< 上溢中断配置
EnableNvic(ADTIM4_IRQn, IrqLevel3, TRUE); ///< 使能AdvTimer4 中断
Adt_StartCount(M0P_ADTIM4);
}
/**
* @brief 定时器4中断服务程序,中断周期10ms
*/
void Tim4_IRQHandler(void)
{
static uint8_t timer100msCnt = 0;
if(TRUE == Adt_GetIrqFlag(M0P_ADTIM4, AdtOVFIrq))
{
timer10msFlag = 1;
tick_10ms += 1;
if(++timer100msCnt >= 10) //10ms中断一次,进来10次则为100ms
{
timer100msCnt = 0;
timer100msFlag = 1;
tick_100ms += 1;
}
Adt_ClearIrqFlag(M0P_ADTIM4, AdtOVFIrq);
}
}
//delay.c
/* SystemFrequency / 1000 1ms中断一次
* SystemFrequency / 100000 10us中断一次
* SystemFrequency / 1000000 1us中断一次
*/
#include "delay.h"
unsigned int tick_10ms = 0;
unsigned int tick_100ms = 0;
//****************************************************************************
//函数名称:Delay10MS
//函数说明:10ms为单位的非阻塞延时
//入口参数:dly 延时结构体
// start 启动延时标志,上升沿:开始延时 0:关闭延时
// tout 延时时间,单位为10ms
//出口参数:无
//返回值: 0:定时时间未到 1:定时时间到
//****************************************************************************
unsigned char Delay10MS(tDelayType *dly, unsigned char start, unsigned int tout)
{
if((dly->start_old == 0) && (start != 0))
{
dly->tick_old = tick_10ms;
}
if(start != 0)
{
if((tick_10ms - dly->tick_old) >= tout)
{dly->timeout = 0xff;}
else
{dly->timeout = 0;}
}
else
{dly->timeout = 0;}
dly->start_old = start;
return dly->timeout;
}
//****************************************************************************
//函数名称:Delay100MS
//函数说明:100ms为单位的非阻塞延时
//入口参数: dly 延时结构体
// start 启动延时标志,上升沿:开始延时 0:关闭延时
// tout 延时时间,单位为100ms
//出口参数:无
//返回值: 0:定时时间未到 1:定时时间到
//****************************************************************************
unsigned char Delay100MS(tDelayType *dly, unsigned char start, unsigned int tout)
{
if((dly->start_old == 0) && (start != 0))
{
dly->tick_old = tick_100ms;
}
if(start != 0)
{
if((tick_100ms - dly->tick_old) >= tout)
{dly->timeout = 0xff;}
else
{dly->timeout = 0;}
}
else
{dly->timeout = 0;}
dly->start_old = start;
return dly->timeout;
}
//delay.h
#ifndef _DELAY_H_
#define _DELAY_H_
typedef struct _delaytype
{
unsigned char start_old;
unsigned char timeout;
unsigned int tick_old;
}tDelayType;
#define DELAY_DEFAULT {0, 0, 0}
extern unsigned int tick_10ms;
extern unsigned int tick_100ms;
//****************************************************************************
//函数名称:DelayReset
//函数说明:复位延时结构体
//入口参数:dly 延时结构体的指针
//出口参数:无
//返回值: 无
//****************************************************************************
#define DelayReset(dly) Delay10MS((dly), 0, 0)
#endif