IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 常用单片机编程思想及例程3——延时篇 -> 正文阅读

[嵌入式]常用单片机编程思想及例程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

之前在状态机处理消抖那篇文章中写过一种基于系统定时器检测超时的方法,其实那也是延时的另外一种写法,只不过那种写法需要频繁的定义标志位、清理标志位,还需要不停的获取滴答始时钟的时基值,不是很方便。后来,自己又整理了一份封装成函数的延时方法,使用的时候直接调用就行,非常方便。

//非阻塞延时函数 需设置系统定时器1ms gSysTickCount++;
/****************************************************************
*函数名:ResetTimer
*功能:	重置定时寄存器
*输入参数:uint32_t *Param			定时器寄存器
****************************************************************/
extern __inline void ResetTimer(uint32_t *Param)
{
    *Param = gSysTickCount;
}

extern __inline uint32_t isOverTime(uint32_t *Param,uint32_t DelayTime)
{
    return ((gSysTickCount - *Param) >= DelayTime);
}
/****************************************************************
*函数名:isOverTime2
*功能:	定时器周期性循环,当定时器超时时,重新重置定时器
*输入参数:uint32_t *Param			定时器寄存器
		   uint32_t DelayTime		定时时间
*返回 	1:							定时器超时
		0:							定时器非超时;
****************************************************************/
extern __inline uint32_t isOverTime2(uint32_t *Param,uint32_t DelayTime)
{
    if(isOverTime(Param,DelayTime))
    {
        *Param = gSysTickCount;
        return 1;
    }
    else
        return 0;
}
/****************************************************************
*函数名:isOverTime3
*功能:	超时计算,防溢出和防止数据穿越
*输入参数:uint32_t *Param			定时器寄存器
		   uint32_t DelayTime		定时时间
*返回 	1:							定时器超时
		0:							定时器非超时;
****************************************************************/
extern __inline uint32_t isOverTime3(uint32_t *Param,uint32_t DelayTime)
{
    if(isOverTime(Param,DelayTime))
    {
        if(isOverTime(Param,DelayTime+1))
        {
            *Param = gSysTickCount - DelayTime - 1;
        }
        return 1;
    }
    else
        return 0;
}

是不是很方便?好了,今天就到这里吧,欢迎大家评论,发表自己的见解。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:42:39  更:2022-03-08 22:45:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:44:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码