常用单片机编程思想及例程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
  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 《单片机原理及接口技术(C51编程)第3版》是一本关于单片机原理和接口技术的教材,本书包括了对C51编程的详细介绍。 单片机原理指的是了解和掌握单片机的工作原理和结构。单片机是一种集成电路,包含了处理器、存储器和各种输入输出接口等部件。通过学习单片机原理,可以了解到单片机的内部结构和工作模式,从而为后续的编程和应用提供基础。 接口技术是指单片机与外部设备之间的通信和连接方式。本书对于串口、并口、定时器/计数器、中断、ADC/DAC等接口技术进行了详细介绍。通过学习这些接口技术,读者可以了解各种常用外设的工作原理,并学会如何在单片机中进行配置和控制。 本书第3版对C51编程进行了全面更新和扩充。C51是一种常用单片机编程语言,具有丰富的函数库和强大的功能。通过学习C51编程,读者可以掌握单片机的程序设计和调试技巧,能够实现各种功能和应用。 本书内容系统、详细、实用,适合初学者学习和参考。通过阅读本书,读者可以了解到单片机原理和接口技术的基本知识,掌握C51编程的方法和技巧。同时,本书也提供了大量的例程和实例,方便读者进行实践和应用。 总之,《单片机原理及接口技术(C51编程)第3版》是一本很好的教材,可以帮助读者全面了解单片机的原理和接口技术,掌握C51编程的基本方法和技巧。对于学习和应用单片机的人来说,是一本非常有价值的参考书。 ### 回答2: 《单片机原理及接口技术(C51编程)第3版PDF》是一本关于单片机原理和接口技术的教材,提供了丰富的知识和实践指导。单片机是一种集成电路,具备了计算机的核心功能,包括运算、存储和控制等。它广泛应用于各种电子设备中,如家用电器、工业控制、通信设备等。 这本教材主要介绍了C51单片机的编程原理和接口技术。C51是一种常见的单片机型号,它基于哈弗处理器架构,具有灵活的外设接口和丰富的编程资源。教材中详细介绍了C51的内部结构和工作原理,包括CPU、RAM、ROM、IO口等模块的功能和特点。 对于接口技术部分,教材提供了丰富的例子和实践操作。通过学习这些案例,读者可以了解如何将外部设备与C51单片机进行连接和通信。例如,教材介绍了LED灯、LCD屏幕、按键、数码管等常用外设的接口原理和编程方法。 此外,教材还对常见的应用场景进行了案例分析,如温度控制、电机驱动、无线通信等。通过这些案例,读者可以了解单片机在各种实际应用中的优势和实现方法。 总的来说,这本教材是学习C51单片机原理和接口技术的重要参考资料。它既提供了理论知识的讲解,也提供了实践操作的指导,对于初学者和专业人士都具有很高的实用价值。 ### 回答3: 《单片机原理及接口技术(c51编程)第3版pdf》是一本介绍单片机原理和接口技术的教材,采用 C51 编程语言进行讲解。 单片机原理是指单片机的工作原理和内部结构。单片机是一种集成电路,包含中央处理器、存储器和各种接口电路。它具有控制和执行指令、数据处理和通信等功能,被广泛应用于各种电子设备中。这本教材详细介绍了单片机的硬件结构、指令系统、存储器管理等方面的知识,帮助读者全面理解和掌握单片机原理。 接口技术是指单片机与外部设备之间的连接和通信方式。单片机通常需要与各种传感器、执行器、存储器等外部设备进行数据交换和控制。这本教材介绍了单片机与各类接口电路的连接方法和通信协议,包括串口、并口、SPI、I2C等常见接口。同时,教材还涵盖了使用 C51 编程语言进行接口编程的方法和实例,帮助读者学会如何在单片机上实现各种接口功能。 第三版的教材相较于前两版进行了内容的更新和补充。它对最新的单片机技术进行了介绍,加入了更多实际应用的案例和示例,使读者可以更好地了解和应用单片机原理和接口技术。 总之,《单片机原理及接口技术(c51编程)第3版pdf》是一本全面介绍单片机原理和接口技术的教材,具有较高的实用性和教学价值。读者通过学习本书,能够掌握单片机的基本原理和接口编程技巧,为后续的单片机应用和开发奠定坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈大本事er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值