2018-11-01 11:13:14 nanfeibuyi 阅读数 4786
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4003 人正在学习 去看看 朱有鹏

单片机 软件延时时间控制

一、简述

   记--通过代码方式实现软件延时(不精确延时)。

二、指令周期

         单片机需要一个时钟信号送给内部各个电路,才能使它们有节拍地协同工作。时钟信号的频率是由外部震荡电路的晶振频率决定的。

       外接晶振的频率 = 时钟信号的频率 = 工作频率。(如24MHz,12MHz,11.0592MHz)

       震荡周期:为单片机提供时钟脉冲的振荡源的周期。

                          震荡周期 = 1/晶振频率          (如晶振频率是12MHz时,振荡周期 = 1/12MHz = (1/12)us)

       机器周期:51系列单片机的一个机器周期由12个震荡周期组成。

                         机器周期 = 12 * 振荡周期      

                 (如晶振频率是24MHz时,振荡周期 = 1/24MHz = (1/24)us,机器周期 = 12*(1/24)us = 0.5us)

                 (如晶振频率是12MHz时,振荡周期 = 1/12MHz = (1/12)us,机器周期 = 12*(1/12)us = 1us)

                 (如晶振频率是11.0592MHz时,振荡周期 = 1/12MHz = (1/11.0592)us,机器周期 = 12*(1/11.0592)us = 1.085us)

       指令周期:单片机执行一条指令所用的时间。

                        一般来说,单片机执行1个简单的指令需要一个机器周期,执行复杂的指令需要两个机器周期。

三、软件延时

        通过一个循环来实现延时

       测试代码:

#include <reg51.h>

void delay(void)//延时函数
{
	unsigned char i;
	for(i=0; i<100; i++)
		;
}

void main(void)
{
	while(1)
	{
		//dosomething()
		delay();
	}
}

在Keil C51 环境下编译后,点击"调试"--"开始";在执行"视图"--"反汇编",可以看到延时函数对应的汇编代码。

其中:

             CLR指令消耗1个机器周期

             MOV指令消耗1个机器周期

             INC指令消耗1个机器周期                                      

             CJNE指令消耗2个机器周期

根据循环条件,INC指令和CJNE指令总共要执行100次(0x64=100),共消耗机器周期(1+2)*100=300,

加上CLR指令和MOV指令,循环程序总共消耗机器周期:1+1+300=302

如果单片机的晶振频率为11.0592MHz,则机器周期 = 12*(1/11.0592)us = 1.085us。

那么for循环程序耗时为302*1.085us=327.67us。

deley()函数用时:0.06607530s-0.000422209s=0.065653091s=65.653091ms=65653.091us   

(指令的执行时间不代表函数的执行时间。函数跳转需要时间,开辟函数栈、临时变量、回收栈资源等需要时间,因此c语言有内联(inline)函数,宏函数(带参宏)用来提高执行效率)。

两层循环

#include <reg51.h>

void delay(void)//延时函数
{
	unsigned char i, j;
	for(i=0; i<100; i++)
		for(j=0; j<200; j++)
			;
}

void main(void)
{
	while(1)
	{
		//dosomething()
		delay();
	}
}

注:对于C语言编译器,某些编译器会进行代码优化,比如循环里面什么都没做,编译器有可能将循环去掉。

       软件延时不精准:因为相当于是通过控制CPU做某一些预定消耗一定时间的动作,根据CPU执行完成判断已经过去多长时间。但是CPU有可能在中途被其他进程强行占用(优先级高的进程),CPU被迫中断当前执行,优先执行优先级较高的任务,当执行完优先级较高的任务,才回来继续执行之前中断的任务,那么延时就会加长。比如想要延时1分钟,我们预定执行A动作之后,刚好耗时1分钟,那么让CPU执行这个动作就达到延时1分钟。如果当CPU执行到一半时,突然有一个紧急任务S(优先级高)发生,要求CPU立即处理,那么CPU就会中断当前任务A,优先处理紧急任务S,等待执行完毕,才回来继续执行之前被中断的任务A。那么当A任务执行完毕,我们预期是从执行开始到结束消耗时间1分钟,但是实际上已经过去不止1分钟,因为中途被中断了。所以软件延时时间越长,容易被中断,而且CPU不能长时间被一个进程独占,延时越不精准。

       要实现精确的延时可使用硬件延时:使用定时器/计数器。定时器/计数器是根据时钟信号脉冲计数的,是直接跟硬件晶振相关的。


//函数名:delay_1ms(uint x)
//功能:利用定时器0精确定时1ms
//调用函数:
//输入参数:x,1ms计数
//输出参数:
//说明:延时的时间为1ms乘以x

void delay_1ms(uint x)
{
	TMOD=0X01;//开定时器0,工作方式为1
	TR0=1;//启动定时器0;
	while(x--)
	{
		TH0=0Xfc;//定时1ms初值的高8位装入TH0
		TL0=0X18;//定时1ms初值的低8位装入TL0
		while(!TF0);//等待,直到TF0为1
		TF0=0;	   //重置溢出位标志
	}		
	TR0=0;//停止定时器0;
}

延时1s:delay_1ms(1000); //1000ms = 1s

2010-11-15 01:35:00 HNSD983704669 阅读数 1342
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4003 人正在学习 去看看 朱有鹏

前几天上单片机课时,老师给我们讲了 单片机 延时子程序,我也发现延时  程序 在单片机中 是非常 常用的,很重要

做作业 写延时程序的时候,感觉根本就不会写,写出来了也是套出来 的,而且 根本就不精确   于是 我就想  我要想一个通用的算法出来,写一个 51单片机 汇编 延时程序 源码生成器,以后 广大的 同学们 或者 单片机 开发者 没 就不用 去套就可以 得到 精确到 一个机器周期的 延时程序了,这个东西也已经做出来了,好了废话少说,现在 开始 和大家分享 我想出来的 算法

 

看几个例子:

一重循环:

   MOV  R0, #3
   DJNZ  R0,$

  

二重循环:

延时时间:T1=2*R0+1

 DELAY:      MOV    R1,#2
LOOP_2:   MOV    R0,#197
                 DJNZ   R0,$
                 DJNZ   R1,LOOP_2

                

 延时: T2=(2*R0+1+2)*R1+1

 

三重循环:

                 MOV      R2,#8
LOOP_3:   MOV    R1,#244
LOOP_4:   MOV    R0,#254
                 DJNZ   R0,$
                 DJNZ   R1,LOOP_4
                 DJNZ   R2,LOOP_3

延时:T3=(  (  (2*R0+1+2)*R1+1)  )+2)*R2+1

 

四重循环:

                 MOV    R3,#18
LOOP_5:   MOV    R2,#255
LOOP_6:   MOV    R1,#255
LOOP_7:   MOV    R0,#255
                DJNZ   R0,$
                DJNZ   R1,LOOP_7
                DJNZ   R2,LOOP_6
                DJNZ   R3,LOOP_5

可以归纳出:

 T2=(T1+2)*R1+1

 T3=(T2+2)*R2+1

 T4=(T3+2)*R3+1

 

仔细看一个  自己也可以再写一个 相信你一定能和我一样 发现这个规律

 

下变进行数学变换一下

将T1 代入T2 表达式

T2=1+3*R2+2*R0*R1

T3=1+3*(R2*R3+R3)+2*R0*R1*R2

T4=1+3*(R2*R3*R4+R3*R4+R4)+2*R0*R1*R2*R3

 

再进行一次归纳,或者说是抽象成一个通式:

 

这个公式是我总结出来的,接下来对公式进行说明:

 

这个是一个K元K次方程     表达式的单位是 机器周期 的个数

 

变量K:循环的次数 R 代表寄存器,下标代表 哪一个寄存器,如 R1

 

假设k=3   则会有 三个未知数 R1  R2  R3

 

大家看到最后加了一个2,原因是 我考虑到 一个子程序 结束时 会有一个返回指令 RET 占用两个机器周期

 

这个东西有什么用呢,很有用 一个未知数 就代表  一个 寄存器的 值 ,表达示结果 就是  用这几个 寄存器 写来来的 K重循环 所延时的机

 

器 周期数,即如果你要 延时 某个 时长(机器周期的整数倍时)  你只要解这个方程就 OK了, 不幸的是 这一个 方程 K个未知数,从数

 

学角度来说就无法解出来,也可能这个方程 根本 就找不到 K 个0—255的数 满足方程的,怎么办呢,是不是放弃呢,大家可以想一想,我

 

们现在是要做什么:

 

我要的是: 输入 要延时的   时长,和晶振   输出: 延时 汇编 源代码

 

你或许会想,也应该明白,如果这个方程 有解,并且你能解出来 那么这个问题就解 决了,而且 就用一个 汇编 循环(不是指一重循环)

 

就能够 达到延时 指定时间的效果,可是我们解不出来,(就算是用穷举法,由于方程可能无满足条件(0—255)的解,所以穷举不一定

 

行,而且很费时间,我已试过 晶振:12MHz  延时:60s  很久 都 穷举不完,下面就说我的方法

 

整体思想:象叠方块样的,大的  上面 叠 小的, 最上面叠  最小的,  当然 不能叠  太多了 ,要不然  汇编 代码 太多了不好 ,占用存储

 

空间

 

具体方法很简单

1、要据上面推导的公式,确定用机重循环可以搞定  即确定K

 

2、个寄存器全部置为255  从最高R8  开始  用二分法  确定  是一个  当R8=R8+1 时 T(k)>要延时时间  

R8=R8-1     T(k)<要延时的时间  且保证  R8=R8时 T(k)<要延时的时间

 直到  R0确定完毕

 

一次近似完毕

3 、 算去 误差 T

 

重复  上面三步  只到 K=0 即 最后 小于一重循环最小值  的机器周期数  的用 NOP 来延迟(也可能不需要,这个由你输入的 晶振 和

延时决定的)

 

从问题的 抽象 到 确定 算法 整个思 路 讲完了,很好的一个东东,可以精确到  每一个机器都期,如:晶振12MHz  就可以精确到 1us

 

 

 

 

 

2017-02-25 14:35:03 m0_37286282 阅读数 7876
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4003 人正在学习 去看看 朱有鹏

       经常看些东西,有时在书上,有时在网上,还有的是突然醒悟,也该做一些总结,最近想总结一下单片机的定时以及延时问题。

单片机主要是两种延时方式:

1.硬件延时:要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;

2.软件延时:软件延时有时候不能够做到非常精确地延时,主要靠循环体或是一些无意义的指令来完成。

单片机都有一个属于自己的晶振频率:11.0592Mhz(主要是为了设置波特率的方便),12Mhz6Mhz等(后面的例子全都用12M晶振)。对于12Mhz的晶振频率,一个机器周期为1us,对于51单片机的库函数就有nop()这个函数(调用时需要#include<intrins.h>),实现延时一个机器周期。那么就有了简单的软件延时程序。可以有delay5us,delay10us等程序,只需要在程序里用nop()就可以了,但是要注意调用该函数需要有一个调用指令(2us),结束后也有个结束指令(2us),而且在函数里调用该函数,只有最内层的函数有结束指令。

例如:

void Delay5us( ) {

_NOP_( );

}

void Delay10us()

{

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

_NOP_( );

}

分别是两个不同的延时函数。


再就是我们会经常使用的for循环延时程序了,我现在也是在学单片机,在郭天祥老师的程序里经常会有

void delay(unsigned int i)

{

while(i--);

}

在这个程序里,如果没有中断完全可以用仿真模拟的方法并自己调整,直到自己想要的延时时间,因为在后面中断,串口,模拟时序的时候并没有那么精确的延时,都是一个比较大的时间段,但是学了就尽量弄得精确一些。(如果是大神完全可以抠汇编,用示波器,当然我现在都不会。),因为这是c语言编程,不是汇编,汇编的一条指令(也就是机器指令)机器周期是一定的,也就是说可以很精确,但是c不行,需要取决于很多东西(如编译器,cpu等等)。

继续说这个程序,他就是用不断循环的做一些无意义的事,达到延时的目的。因为你不能准确的知道一条c指令确是多少时间(或者说会有误差),在上面的程序里,当i=1时,大约延时10us。下面再给出几个延时函数,仅供参考。

200ms延时子程序

程序:

void delay200ms(void)

{

unsigned char i,j,k;

for(i=5;i>0;i--)

for(j=132;j>0;j--)

for(k=150;k>0;k--);

}

10ms延时子程序

程序:

void delay10ms(void)

{

unsigned char i,j,k;

for(i=5;i>0;i--)

for(j=4;j>0;j--)

for(k=248;k>0;k--);

}

关于函数延时以及一些基本的程序,我也只了解这些,再来说说定时器实现精确延时。

定时器有4种模式,一般较为常用的为方式1,因为一旦溢出就会申请中断,因此一次溢出共需要65536us,约等于63.5ms,若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。

在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。使用定时器/计数器延时从程序的执行效率和稳定性两方面考虑都是最佳的方案。

在这里,用定时器中断服务程序中,需要给定时器重装初值,完成定时器中断服务程序就回到主程序,但是要注意,若是没有关闭中断,在运行中断服务程序(进入中断服务程序需要时间)而且没有到给它重新赋值的语句前,定时器也在计数中,只有在重新赋初值后的瞬间,又开始从新的值处开始计时,因此,这是一个误差,解决误差的办法就是赋值初值的时候加上它当前的值。TH0=TH0+初值,TL0=TL0+初值。

另外,中断服务程序不要过长,或者有一个或多个延时程序(不是说不能,是不建议),否则中断服务程序还没结束就又进入中断,会造成崩溃。

下面给出一个程序,实现数码管每隔1s循环显示0-F,实现准确延时,当然不是绝对的准确。(我就直接把我学习单片机开发板上面的程序拷过来了)。这个程序重新赋值的时候没有向我上面说的那样,中断服务程序也略显赘余,可以把if放在主函数while中。

/**************************************************************************************
*		              定时器1实验												  *
实现现象:下载程序后数码管最后一位间隔一秒循环显示0-F。使用单片机内部定时器可以实现准确延时。																				  
***************************************************************************************/

#include "reg52.h"			 //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
					0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
u8 n=0;
/*******************************************************************************
* 函 数 名         : Timer1Init
* 函数功能		   : 定时器1初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer1Init()
{
	TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。

	TH1=0XFC;	//给定时器赋初值,定时1ms
	TL1=0X18;	
	ET1=1;//打开定时器1中断允许
	EA=1;//打开总中断
	TR1=1;//打开定时器			
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	LSA=0;
	LSB=0;
	LSC=0;
	Timer1Init();  //定时器1初始化
	while(1);		
}

/*******************************************************************************
* 函 数 名         : void Timer1() interrupt 3
* 函数功能		   : 定时器0中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer1() interrupt 3
{
	static u16 i;
	TH1=0XFC;	//给定时器赋初值,定时1ms
	TL1=0X18;
	i++;
	if(i==1000)
	{
		i=0;
		P0=smgduan[n++];
		if(n==16)n=0;	
	}	
}

注:这里数码管用的是138译码器。


这就是我的一些理解,我现在也在学习中,想着花点时间总结会有更好的脉络。之后会再来完善。有错欢迎指出。





2019-01-28 09:34:13 weixin_43858743 阅读数 2063
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4003 人正在学习 去看看 朱有鹏

前言

我使用51,STC这一类的单片机做控制好几年,一直是使用现成的程序,在其上修修改改,以达到需求动作目的即可。从来都是不求甚解。想法既是如此,会用即可,了解那么多做什么。
此次又在做一项目,里面用到I2C通讯。本来是直接复制粘贴了事,却没想对里面的一个小小的延时函数起了兴趣,由于本人是基础功底只有5战斗力的渣渣,写写画画了一天才搞了个大体明白。
以前总是在看其他博主的文章,默默潜水。然而此次,突然就忍不住想写篇文章发表一下费尽心思的微不足道的心得体会。

正文

void Delay10us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 27;
	while (--i);
}

上面这段代码是用STC-ISP软件中的软件延时计算器给出的,选用的是8051指令集STC-Y5,延时10us。
以前都是直接这么拿来用的,今天却突然想搞个明白,为什么代码要这么写。

于是查了各方资料。
从单片机计时的源头找起,它由下面几部分依次组成。

首先是时钟周期的算法:时钟周期(T)=1(秒)/晶振频率。

(比如:上面代码的时钟周期为1/12M(秒))。
这是单片机的基本时间单位。是由晶振震荡出来的,也叫震荡周期。

其次是机器周期:机器周期是由时钟周期组成的,机器周期是单片机完成一个基本操作所需要的时间。

关于机器周期,每种单片机可能都不太一样,我也只用过传统51和STC这两款,就拿此来对比下

1 传统的8051单片机:

它的1个机器周期是由12个时钟周期组成的。
以12M晶振举例,它的一个机器周期就是:12(个时钟周期)*1(秒)/12MHz = 1(us)

2 STC单片机:

拿我常用的STC12C5A60S2这款单片机来讲,它可以有两个模式选择,
一个是1T模式,在这个模式下STC单片机1个时钟周期就是1个机器周期;
另一个是12T模式,这个模式下STC单片就和传统的8051单片机一样,12个时钟周期组成1个机器周期。
由此可见1T模式的速度就是12T模式的12倍。
以12M晶振为例,1T模式下就可以算得机器周期是:
1(个时钟周期)*1(秒)/12Mhz = 1/12(us)

最后是指令周期:这个是单片机执行一条指令所需要的时间,它是由机器周期组成的。

现在可以回到正文开头的代码中了。这个10us的函数是怎么得出来的呢?
这个我之前查过很多资料,比如执行while语句需要多少个机器周期。赋值需要多少个周期。也就是查这个占用了我很大一部分时间。直到最后将上面的延时函数直接调到main函数中debug调试,才明白,问题其实很简单啊。
无论是执行什么语句,最终都会回到汇编上来,debug里单步调试,所有的指令周期就会明明白白了。
我用main函数直接调用延时函数,如下:

void Delay10us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 27;
	while (--i);
}
main
{
    Delay10us();
}

我用的keil软件,将上述build之后,点击debug,开始调试
.在这里插入图片描述
看图片上,开始debug,程序的起始就在C:0x0183 020171 LJMP Delay10us(C:0171),
这里有个长转移指令LJMP,它要转移到C:0171行去执行Delay10us这个函数。
那执行LJMP这个指令需要多长时间呢,查找STC数据手册,在1T模式下,此条指令在单片机上运行需要4个时钟周期。
接下来,按单步调试F11键,如下图:
在这里插入图片描述
程序成功转移到C:0171行,跳转到Delay10us函数中,此行程序执行NOP指令,空操作。查STC数据手册,NOP指令占用1个时钟周期。
接下来C:0172行,依然是NOP指令,1个时钟周期。
接下来C:0173行,此行执行 MOV R7,#0x1B,将立即数送入寄存器。是将27赋值给i。依然查手册,此条指令2个时钟周期。
继续:
在这里插入图片描述
此时执行到while语句了,这里执行的指令时 DJNZ R7,C:0175,寄存器减1非0转移。此条指令执行1次4个时钟周期。
上面已经将寄存器填入27了,因此这条指令将执行27次。
继续:
在这里插入图片描述
循环了27次,终于到0了,程序继续向下执行,此行指令RET,子程序返回。此条指令4个时钟周期。
继续:
在这里插入图片描述
程序又回到了起点。
好了,可以计算一下此次延时的时间了。1个LJMP,4时钟;2个NOP,2时钟;1个MOV,2时钟;27个DJNZ,108时钟;1个RET,4时钟。
4+2+2+108+4=120。
单片机的时钟周期是:1(S)/12MHz = 1/12(us)
此次延时的时间是:120 × 1/12(us)= 10(us)

总结

其实并没有绝对的准确延时,上面只是理想化的状态,单片机的中断或者其他事件都可能影响到延时的。
另外,同样的STC单片机,同样的延时10us,同样的1T,官方给出的STC12系列和STC15系列的延时函数就不一样,STC12系列在延时函数内部要少两个NOP指令。debug对比,也是少量NOP,其他都一样。按照12系列和15系列的手册描述,他们的指令周期是相同的。那么他们的差别在哪?单片机内部结构的问题还是官方错误造成的误差?
各位大神若有知晓的望请告知。不胜感激。

2016-02-06 14:01:08 sinat_33031415 阅读数 640
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    4003 人正在学习 去看看 朱有鹏

51单片机延时计算


时钟周期

若时钟晶振的振荡频率为fosc,则振荡周期 Tosc=(1/fosc)。

如:晶振频率为12MHZ,则振荡周期 Tosc=(1/12us)。

机器周期

对于不同的芯片采用的机器周期各不相同,主要分为:12T、6T、4T、1T等等。

如51系列单片机常见的12T机器周期,每个机器周期占用时间为(12 * 时钟周期)。

依此类推,6T占用的时间为(6 * 时钟周期)。

延时计算

对于语句:
    for(a=n;a>0;a--); 
其延时的大小计算如下:

     delay_time = [(a*2)+3] * 机器周期

但准确的说要加上延时程序调用时间:

    delay_time = ([(a*2)+3] + 5) * 机器周期

例如 :晶振为12MHz、时钟周期为12T的单片机,下面for循环语句:
       for(a=50;a>0;a--)
           for(b=10;b>0;b--);
其延时为:

    delay_time = [(10*2 +3)*50+3]*(1/12)*12 us = 1153us

最后 再加上 5us * 12/12us 程序调用延时的时间

其中 1/12 为晶振决定的机器周期,最后面的 12 是指时钟周期为 12T。
// 部分常用的延时程序
#define u8 unsigned char
/*
    小于4us的延时直接使用_nop_();实现
*/

void delay_1ms()
{
    u8 a,b;
    for(a=142;a>0;a--)
        for(b=2;b>0;b--);
    _nop_();

}

void delay_10us()
{
    u8 a,b;
    for(a=1;a>0;a--)
        for(b=1;b>0;b--);

}


void delay_1s()
{
   u8 a,b,c;
   for(c=46;c>0;c--)
    for(b=152;b>0;b--)
      for(a=70;a>0;a--);
   _nop_(); 

}

没有更多推荐了,返回首页