2015-08-03 22:59:28 Backspace110 阅读数 3386

 

#include <reg51.H>

#define STC_Y1  97560U  // 89C/LEXX、90C/LEXX
#define STC_Y3  14050U  // 10F/Lxx、11F/Lxx 、12C/LExx、15F104E/L104E(A版)、15F204E/L204EA(A版)
#define STC_Y5  13043U  // 15F/L/Wxx(Y3内核个别型号除外)

#define Fosc 12000000UL
#define Core STC_Y5

/**
 *  功能:延时t毫秒
 *  入参:1~65535
 */
void delay_ms(unsigned int t)
{
    unsigned int i;
    do{
        i = Fosc / Core;
        while(--i);
    }while(--t);
}

void main()
{
    delay_ms(1);
}

取个最大值看看延时误差就行了,t = 65000 ,误差才几十us,较一般的延时准确度已经很高了。

至于最开始的宏定义,看似magic number,其实就是debug得到汇编代码,得到调用函数到返回函数执行的所有指令,然后根据各个内核的指令速度稍作修改就可以了。这几个数可以在一定范围内修改,因为

Fosc/Core是个浮点数,最后做了一下取整,好在这一套操作是在编译时期就解决了,对性能没有影响。

发文的时候STC已经出了Y6内核(STC8)。不过手上没有STC的新片,之后的坑就留给大家自己填了。手头这些STC15系列的还没怎么玩。况且还有一堆mega8/16 另外有几片msp430,stm8/32,下一步打算玩玩这些。

 

 

 

 

2013-11-01 23:27:12 tzshlyt 阅读数 2135
晶振频率:12M 
(一)原程序:
delay: mov  r7,#200     
   d1: mov  r6,#125      
   d2: djnz  r6,d2          
       djnz  r7,d1          
       ret                      
分析过程:
delay: mov   r7,#200        r7=200   执行需1us
   d1: mov   r6,#125        r6=125      1us
   d2: djnz  r6,d2          r6=124   直到r6=0,共执行125次125*2us
       djnz  r7,d1          r7=199       2us
   d1: mov   r6,#125        r6=125
   d2: djnz  r6,d2          r6=124
       djnz  r7,d1          r7=198
         *
         *   共执行200次
         *
   d1: mov   r6,#125        r6=125
   d2: djnz  r6,d2          r6=124
       djnz  r7,d1          r7=1
   d1: mov   r6,#125        r6=124
   d2: djnz  r6,d2          r6=123
       djnz  r7,d1          r7=0      执行下一条程序
       ret                  2us
延时时间为:1+(1+125*2+2)*200+2=50603us

公式:精确延时时间=3+3*r7+2*r7*r6


(二)原程序:

delay: mov   r7,#10
   d1: mov   r6,#200
   d2: mov   r5,#248
   d3: djnz  r5,d3
       djnz  r6,d2
       djnz  r7,d1
       ret
延时时间为:1+[1+(1+248*2+2)*200+2]*10+2=998033us
公式:精确延时时间=1+[1+(1+r5*2+2)*r6+2]*r7+2=3+3*r7+3*r7*r6+2*r7*r6*r5

加上lcall delay的时间,需加 2us,则最终时间就为两条程序执行间隔时间。
   
2017-07-30 11:16:51 ma57457 阅读数 10266

按键扫描,我想应该是比较简单的单片机应用了,但是有时候看起来简单的东西反而不好写。

本文拿大部分人觉得简单的按键扫描聊聊我工作至今对于软件结构的理解。嗯,对的,是结构,不是架构,暂时不敢提架构这个词。

按键扫描,我当时入门的时候是看的郭天祥的51单片机入门的,视频里面讲的是循环扫描io引脚,一旦有电平变化就利用软件延时消抖,模拟延时就是让单片机空转,什么也不做,等待个几十毫秒之后再检测一次如果电平没有变化就认为按键按下。这种方法也能实现按键检测,好处是简单,缺点是占用太多的软件资源,CPU空转这一点我觉得挺不好的。

下面说说我个人对于一个按键检测的代码理解。

按键检测需要做什么事情呢?一个是按键按下的这个物理事件的检测,一个是按下时候的消抖。能想到这两个已经可以写一段代码来实现功能了。

 

#define DEBOUNCE 10//延时消抖时间
uint8 key_scan( uint8 keycur )
{
	static uint8 KeyLast = KEY_NULL; 	
	static uint8 KeyCountdowm = 0; 	
	uint8 keyret = KEY_NULL;   	    

	if(  keycur != KEY_NULL )	//如果检测到按键按下,开始进行延时消抖			
	{
		if( KeyLast == keycur  || KEY_NULL == KeyLast)				
		{
			KeyCountdowm++;				
		}
	}
	else
	{
		if( KeyCountdowm >= DEBOUNCE)//按键抬起
		{
			keyret = KeyLast;
		}
		else
		{
		        keyret = KEY_NULL;
		}

		KeyCountdowm = 0;
	}
	
	KeyLast = keycur;//按键备份值更新

	return keyret;
}

上面是一个简单的按键扫描函数,函数需要放置在一个10ms的定时函数里面,注意是定时函数不是定时中断函数,需要传递按键信息,这个信息可以是从通信函数获取的,也可以是直接读取IO端口获得,返回一个消抖过后的键值,键值不做逻辑判断。

 

然后我们来聊聊这个消抖函数,这个消抖函数只实现了一个功能,按下消抖,那如果使用环境或者硬件设计缺陷,导致抬起的时候也有抖动呢?所以需要添加抬起消抖。

下面对函数进行优化一下

 

#define DEBOUNCE 10//延时消抖时间
uint8 key_scan( uint8 keycur )
{
	static uint8 KeyLast = 0; 	
	static uint8 KeyCountdowm = 0; 	
	static uint8 KeyCountup = 0; 	
	unsigned uint8 keyret = 0;   	  

	keyret = KEY_NULL;
	if(  keycur != KEY_NULL )				
	{
		if( KeyLast == keycur || KEY_NULL == KeyLast)			
		{
			KeyCountdowm++;			
			KeyCountup = 0;
		}
	}
	else
	{
		KeyCountup++;	

		if( KeyCountup > 1 ) 
		{
			KeyCountup = 0;	

			if( KeyCountdowm >= DEBOUNCE )
			{
				keyret = KeyLast;
				KeyCountdowm = 0;
			}
		}
	}
	
	KeyLast = keycur;	
	return keyret;
}

添加了20ms的抬起消抖,这段代码添加了两个消抖检测,一个是在抬起的时候有20ms的消抖,一个是在按键按下过程的一个过程消抖。

上面的代码实现的是抬起有效,那么如果需要做到按下有效呢?

#define DEBOUNCE 10//延时消抖时间
uint8 key_scan( uint8 keycur )
{
	static uint8 KeyLast = 0; 	
	static uint8 KeyCountdowm = 0; 	
	static uint8 KeyCountup = 0; 	
	unsigned uint8 keyret = 0;   	  

	keyret = KEY_NULL;
	if(  keycur != KEY_NULL )				
	{
		if( KeyLast == keycur || KEY_NULL == KeyLast)			
		{
			KeyCountdowm++;			
			KeyCountup = 0;
			
			if( KeyCountdowm >= DEBOUNCE )
			{
				keyret = KeyLast;
			}
		}
	}
	else
	{
		KeyCountup++;	

		if( KeyCountup > 1 ) 
		{
			KeyCountup = 0;	
			KeyCountdowm = 0;
		}
	}
	
	KeyLast = keycur;	
	return keyret;
}

挪动按键的延时检测判断语句,在按下超过延时消抖时间的时候,返回按键按下值。

 

然后一个简单按键检测函数就实现了,下面再给这个函数添加长按键判断和连续按键。

#define DEBOUNCE 10//延时消抖时间
#define LONGPRESS 100//长按键判断函数
uint16 key_scan( uint8 keycur )
{
	static uint8 KeyLast = 0; 	
	static uint8 KeyCountdowm = 0; 	
	static uint8 KeyCountup = 0; 	
	unsigned uint16 keyret = 0;   	  

	keyret = KEY_NULL;
	if(  keycur != KEY_NULL )				
	{
		if( KeyLast == keycur || KEY_NULL == KeyLast)			
		{
			KeyCountdowm++;			
			KeyCountup = 0;
			
			if( KeyCountdowm >= DEBOUNCE && KeyCountdowm < LONGPRESS)//短按键判断
			{
				keyret = KeyLast;
			}
			else if( KeyCountdowm == LONGPRESS )//长按键判断				
			{
				keyret = KeyLast;	
				keyret |= 0x0100;		
			}
			else if(KeyCountdowm > LONGPRESS+DEBOUNCE)//连续按键判断
			{
				KeyCountdowm -= DEBOUNCE;
				keyret = KeyLast;	
				keyret |= 0x0200;	
			}
		}
	}
	else
	{
		KeyCountup++;	

		if( KeyCountup > 1 ) 
		{
			KeyCountup = 0;	
			KeyCountdowm = 0;
		}
	}
	
	KeyLast = keycur;	
	return keyret;
}

 

当有多个设备的时候,可以将静态局部变量修改为结构体指针的形式,如下

type struct key
{
	uint8 Last;
	uint8 CountDowm;
	uint8 CountUp;
}KEY_TYPE;

#define DEBOUNCE 10//延时消抖时间
#define LONGPRESS 100//长按键判断函数

uint16 key_scan( KEY_TYPE *Key ,uint8 keycur)
{	
	unsigned uint16 keyret = 0;   	  

	keyret = KEY_NULL;
	if(  keycur != KEY_NULL )				
	{
		if( Key->Last == keycur || KEY_NULL == Key->Last)			
		{
			Key->CountDowm++;			
			Key->CountUp = 0;
			
			if( Key->CountDowm >= DEBOUNCE && Key->CountDowm < LONGPRESS)//短按键判断
			{
				keyret = KeyLast;
			}
			else if( Key->CountDowm == LONGPRESS )//长按键判断				
			{
				keyret = Key->KeyLast;	
				keyret |= 0x0100;		
			}
			else if(KeyCKey->CountDowmountdowm > LONGPRESS+DEBOUNCE)//连续按键判断
			{
				Key->CountDowm -= DEBOUNCE;
				keyret = Key->KeyLast;	
				keyret |= 0x0200;	
			}
		}
	}
	else
	{
		Key->CountUp++;	

		if( Key->CountUp > 1 ) 
		{
			Key->CountUp = 0;	
			Key->CountDowm = 0;
		}
	}
	
	Key->KeyLast = keycur;	
	return keyret;
}

 

最后说说这个功能的实现,按键检测分为三个部分,一个是按键获取函数,一个是消抖,一个是按键筛选函数,先把代码贴上来。

type struct key
{
	uint8 Last;
	uint8 CountDowm;
	uint8 CountUp;
}KEY_TYPE;

#define DEBOUNCE 10//延时消抖时间
#define LONGPRESS 100//长按键判断函数
/*
* description: 按键消抖函数
* intput:按键结构体,键值
* output:键值
* 
*/
uint16 key_scan( KEY_TYPE *Key ,uint8 keycur)
{
	unsigned uint16 keyret = 0;   	  

	keyret = KEY_NULL;
	if(  keycur != KEY_NULL )				
	{
		if( Key->Last == keycur || KEY_NULL == Key->Last)			
		{
			Key->CountDowm++;			
			Key->CountUp = 0;
			
			if( Key->CountDowm >= DEBOUNCE && Key->CountDowm < LONGPRESS)//短按键判断
			{
				keyret = KeyLast;
			}
			else if( Key->CountDowm == LONGPRESS )//长按键判断				
			{
				keyret = Key->KeyLast;	
				keyret |= 0x0100;		
			}
			else if(KeyCKey->CountDowmountdowm > LONGPRESS+DEBOUNCE)//连续按键判断
			{
				Key->CountDowm -= DEBOUNCE;
				keyret = Key->KeyLast;	
				keyret |= 0x0200;	
			}
		}
	}
	else
	{
		Key->CountUp++;	

		if( Key->CountUp > 1 ) 
		{
			Key->CountUp = 0;	
			Key->CountDowm = 0;
		}
	}
	
	Key->KeyLast = keycur;	
	return keyret;
}

/*
* description: 获取按键的函数
* intput:none
* output:键值
* 
*/
uint8 GetKeyValue(void)
{
	uint8 ret=NULL;
	/*
	这个部分的代码需要自己实现,这里可以从通信函数如iic,spi获得键值,也可以从gpio端口获得键值
	*/
	return ret;
}


#define KEY_NULL 0
#define KEY_UP 1
#define KEY_DOWN 2
#define KEY_ON 3
#define KEY_UP_L 4
#define KEY_DOEN_L 5
#define KEY_ON_L 6
/*
* description: 筛选按键的函数,只筛选需要的键值
* intput:键值
* output:键值
* 
*/
uint8 GetKeyValue(uint8 key)
{
	uint8 ret=KEYNULL;
	switch(key)
	{
		0x01:
		{
			ret = KEY_UP;
		}
			break;
		0x0101:
		{
			ret = KEY_UP_L;
		}
			break;
		default:
			break;
	}
	
	return ret;
}

 

函数的三个部分的按键获取函数部分需要自己去编写,不同的键值来源不同,我试过从IIC中读取键值,也试过从GPIO中读取键值。

然后是消抖函数,消抖函数需要如果有多个按键来源的话需要定义多个结构体,如果只有一个键值来源可以替换成静态局部变量版的函数。

分成三个部分的好处是,函数间各干各事情,互不影响,更容易读,我在接手别人的代码时候,如果涉及到按键部分出现问题的话,一般先问上一个维护的人做没做过程消抖,如果回答说有做,好的,接着问怎么做的,一般把对方的过程消抖搞懂了,基本就懂了按键这部分的代码。如果对方说没有做或者很茫然的看着我,好吧,我会直接把对方的这部分代码删掉,重写。

 

写于2017年7月30日 一个没有空调的夏天

深圳

2012-05-07 22:32:01 bandaoyu 阅读数 124
在 IAR 软件430的编译器里面我们可以利用它内部的延时子程序来实现我们想要的高精度软件延时,方法如下:
 

具体如下【引用别人的,这个延时函数很高】:

注意: __delay_cycles(x),x必须是常量或则是常量表达式,如果是变量则编译报错!

IAR FOR AVR中精确软件延时方法

在用单片机的时候常常会用到延时函数,430也不例外,常见的形式有:
void delay(unsigned int ms)
{
unsigned int i,j;

for( i=0;i<ms;i++)

for(j=0;j<1141;j++); //8MHz晶振时
}

复制代码//以上程序段在要求延时精度不高的场合可以用。

但在 IAR 软件430的编译器里面我们可以利用它内部的延时子程序来实现我们想要的高精度软件延时,方法如下:

(1):将以下这段代码复制到你的.C源文件中。

#define CPU_F ((double)8000000)
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))
#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))

复制代码(2):修改

在 #define CPU_F ((double)8000000) 语句里 8000000 修改成你当前MSP430 CPU的主频频率,即CPU的MCLK。

单位为HZ。本例中的8000000为MCLK=8MHZ 的意思。

(3):使用范例

delay_us(1); //1 微秒的延时

delay_ms(1); //1 毫秒的延时

delay_us(3.5); //延时3.5微秒

delay_ms(3.5); //延时3.5毫秒

delay_ms(1000); //延时1秒




-------------------------------------------------------------------------------------


原因:

__delay_cycles()是编译系统"涵数",IAR编译时会替换成相应的循环代码.

看图片,要求delay 100指令(时钟)周期,编译后刚好100周期.

试了半天还是iar自带的延时准啊!!__delay_cycles【转】

 

使用这种延时需要注意:

http://www.ourdev.cn/thread-756021-1-1.html

 

 


  1. #define _delay_us(A)\
  2.   __delay_cycles( (uint32) ( (double)(F_CPU) *((A)/1000000.0) + 0.5))

  3. #define _delay_ms(A)\
  4.   __delay_cycles( (uint32) ( (double)(F_CPU)*((A)/1000.0) + 0.5))

  5. #define _delay_s(A)\
  6.   __delay_cycles( (uint32) ( (double)(F_CPU)*((A)/1.0) + 0.5))
2016-10-04 15:45:13 qq_27312943 阅读数 780

1 低电平触发可能碰到这种状况,即终端执行后低电平依然存在,这个时候就需要在单片机的中断程序中检测低电平是否撤销,如果没有撤销,则在中断程序中进行等待,一般而言可以将低电平触发改为下降沿触发。


2 千万不要在中断程序里面写延时,这是大忌!!!


3 中断定时的定时时间不要太短,如果太短会造成单片机不断地进中断而无法正常工作,通常定时时间可以选择1-2ms


4 在AD转换中可以用整形数来代替浮点数


5 在AD转换中,读I/O的电平不如捕获的速度快,所以捕获常用在对时间要求比较高的地方


6 中断程序的执行时间一定要小于定时器的定时时间


7 判断temp的最高位是不是1

写法

     

if(temp & 0x80)
    即可

      

if((temp&0x80) == 1)

    是错误的!!!


8 写程序之前不要着急打开电脑就乱写一通,要想好每一个部分是怎么工作的,每一个部分之间又是怎么相互联系的,我的一个老师曾经说过,当接到一个项目的时候,项目的设计时间应该占到整个项目完成时间的1/3


9 写好一部分代码后要记得测试,举个简单的例子,当你写好一个外设之后,不要感觉他现在可以安全工作就万事大吉了,要让程序多跑一会,观察程序是否确实稳定可靠。


10 选择计数型变量时,一定要考虑该变量的范围,比如最简单的,当单片机2ms产生一次中断,要定时2分钟,2000ms/2ms=1000,这个时候就不能使用unsigned char类型,因为会导致unsigned char类型的变量溢出。同时不能忽视,uint8_t类型在stm32中的实现也是unsigned char类型





51单片机延时

阅读数 431

C#中的延时小程序

阅读数 1749

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