最新日志

发表于:2008-5-16 17:36:55
标签:无标签

0

4*4矩阵键盘模块程序的调用

花了两天时间终于做了个4*4的矩阵键盘模块程序,用在51单片机上,后面要是用到矩阵键盘的时候,直接调用这个程序就行了。现在贴出来,供大家学习,同时,有什么不足之处,也请大家指出。

 

产生.lib文件的方法:选择“工程”——“目标target属性”——“输出”——产生库文件*.lib,修改完后,编译一下程序,无措后,就在工程目录下产生一个.lib文件,到时要用到这个模块程序时,直接把.c和.lib文件拷贝到所在的目录下,再把.c的文件加载到工程文件里就OK了。
/*
***************************************************************************************
摘    要: 4*4小键盘模块程序,供以后其他程序调用.其中P2口高四位接键盘列,P2口低四位接键盘行,
版    本: V1.0
完成日期: 2008.4.28
作    者: ZHOUSFE
***************************************************************************************
修改日期: 2008.5.6
版    本: V1.2
****************************************************************************************
*/

#i nclude <reg51.h>
#define uchar unsigned char
#define keyport  P2
//uchar code seg[16]=

{0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0X88,0X83,0XC6,0XA1,0X86,0X8E}; //0~f,

共阳极
uchar code keycode[16]=

{0xEE,0XDE,0XBE,0X7E,0XED,0XDD,0XBD,0X7D,0XEB,0XDB,0XBB,0X7B,0XE7,0XD7,0XB7,0X77};//待查询的

键码值

/*
*******************************************************************************************
函数名称:delay()
功能描述:延时ms个微妙
入口参数:unsigned int ms
出口参数:无
*******************************************************************************************
*/

void delay(unsigned int ms) 
{
  unsigned char a;
  while(!ms--)
  for(a=0;a<124;a++); 
}

/*
*******************************************************************************************
函数名称:keyscan()
功能描述:键盘扫描程序,利用行扫描方式,得到键值
入口参数:无
出口参数:扫描得到的键值~((~sm)+(~fh))
*******************************************************************************************
*/

uchar keyscan(void)        
{
  uchar sm;                  //存放行扫描代码,该函数的返回值的低四位
  uchar fh;                  //该函数的返回值的高四位
  keyport="0xf0";
  if((keyport&0xf0)!=0xf0)        //判断是否有按键按下  
  {
    delay(2);                //去抖动延时(1~10ms)
    if((keyport&0xf0)!=0xf0) 
    {
      sm="0xfe";                //行扫描初值
      while((sm&0x10)!=0)
    {
        keyport="sm";
        if((keyport&0xf0)!=0xf0)       //若本行有键按下,则执行读取键值信息
        {
          fh=(keyport&0xf0)|0x0f;   
          return(~((~sm)+(~fh))); //返回扫描的键值
        }
         else   sm=(sm<<1)|0x01;   //若本行没有键按下,则扫描下一行
       }
     }
   }
   return 0xff;                   //若没有按键按下,则返回0xff
}

/*
*******************************************************************************************
函数名称:getkey()
功能描述:将扫描得到的键值转化为对应的0~f
入口参数:无
出口参数:显示的数字0~f
*******************************************************************************************
*/

uchar getkey()
{
  uchar key;
  uchar temp;
  uchar j;
  while(1)
  {
    key="keyscan"();       
    delay(2);
    for(j=0;j<16;j++)
    {
      if(key==keycode[j])   //通过查询方式,得到按下的按键值
      {
        temp="j";             //按键值先保存于temp中
        if(key==0xff)       //若没有按下下一个键,则一直显示上一个键值
      {
          return temp;  
      }
      else
      {
         return j;
         temp="j";          //更新temp里的值
      }
       }
     }
   }
}
/*
*******************************************************************************************
测试用函数
*******************************************************************************************
*/
/*void main(void)
{
 uchar ch;
 P3&=0xfe;
 while(1)
 {
  ch="getkey"();
  P0=~seg[ch];  //P0口接数码管
 }
}*/

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(27)
发表于:2008-5-2 18:04:14
标签:无标签

0

volatile的用法

volatile的本意是“易变的”

由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:

static int i="0";

int main(void)
{
...
while (1)
{
if (i) dosomething();
}
}

/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}

程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。

一般说来,volatile用在如下的几个地方:

1、中断服务程序中修改的供其它程序检测的变量需要加volatile;

2、多任务环境下各任务间共享的标志应该加volatile;

3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。

 简单点: 就是该变量会以编译器无法预知的方式发生变化,请编译器不要做优化(所有的编译器的优化均假设编译器知道变量的变化规律)

 

 

============================================

 

C 关键字 volatile

C 关键字 volatile

关键词: 关键字    volatile                                          

volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

使用该关键字的例子如下:

int volatile nVint;

  当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。

例如:

volatile int i="10";
int a = i;
...
//
其他代码,并未明确告诉编译器,对i进行过操作

int b = i;

  volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。

××××××××××××××××××××××××××××××××××××××××××

关键字volatile有什么含意?并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
    1).
并行设备的硬件寄存器(如:状态寄存器)
    2).
一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
    3).
多线程应用中被几个任务共享的变量
   
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。
   
假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
    1).
一个参数既可以是const还可以是volatile吗?解释为什么。
    2).
一个指针可以是volatile 吗?解释为什么。
    3).
下面的函数有什么错误:
         int square(volatile int *ptr)
         {
              return *ptr * *ptr;
         }

    下面是答案:
    1).
是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
    2).
是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
    3).
这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
    int square(volatile int *ptr)
    {
         int a,b;
         a = *ptr;
         b = *ptr;
         return a * b;
     }
   
由于*ptr的值可能被意想不到地该变,因此ab可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
     long square(volatile int *ptr)
     {
            int a;
            a = *ptr;
            return a * a;
     }

 

= = = = = = = = = = = = = = = = 实例讲解= = = = = = = = = = = = = = =

volatile的本意是一般有两种说法--1.“暂态的2.“易变的
这两种说法都有可行。但是究竟volatile是什么意思,现举例说明(以Keil-ca51为例
例子来自Keil FQA,看完例子后你应该明白volatile的意思了,如果还不明白,那只好
再看一遍了。


1.

void main (void)
{
volatile int i;
int j;

i = 1;  //1  
不被优化 i="1"
i = 2;  //2  
不被优化 i="1"
i = 3;  //3  
不被优化 i="1"

j = 1;  //4  
被优化
j = 2;  //5  
被优化
j = 3;  //6  j = 3
}
---------------------------------------------------------------------
2.

函数:

void func (void)
{
unsigned char xdata xdata_junk;
unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;

t1 = *p;
t2 = *p;
}

编译的汇编为:

0000 7E00    R     MOV     R6,#HIGH xdata_junk
0002 7F00    R     MOV     R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F82          MOV     DPL,R7
0006 8E83          MOV     DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
注意
0008 E0            MOVX    A,@DPTR
0009 F500    R     MOV     t1,A

000B F500    R     MOV     t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
000D 22            RET     

将函数变为:
void func (void)
{
volatile unsigned char xdata xdata_junk;
volatile unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;

t1 = *p;
t2 = *p;
}

编译的汇编为:
0000 7E00    R     MOV     R6,#HIGH xdata_junk
0002 7F00    R     MOV     R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F82          MOV     DPL,R7
0006 8E83          MOV     DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
0008 E0            MOVX    A,@DPTR
0009 F500    R     MOV     t1,A        a


000B E0            MOVX    A,@DPTR
000C F500    R     MOV     t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

000E 22            RET     


比较结果可以看出来,未用volatile关键字时,只从*p所指的地址读一次
如在a*p的内容有变化,则t2得到的则不是真正*p的内容。

---------------------------------------------------------------------
3


volatile unsigned char bdata var;  // use volatile keyword here
sbit var_0 = var^0;
sbit var_1 = var^1;
unsigned char xdata values[10];

void main (void)  {
  unsigned char i;

  for (i = 0; i < sizeof (values); i++)  {
    var = values[i];
    if (var_0)  {
      var_1 = 1; //a

       
      values[i] = var;  // without the volatile keyword, the compiler
                        // assumes that 'var' is unmodified and does not
                        // reload the variable content.
    }
  }
}


在此例中,如在a处到下一句运行前,var如有变化则不会,如var=0xff; 则在
values[i] = var;
得到的还是values[i] = 1;

---------------------------------------------------------------------
应用举例:

1.
#define DBYTE ((unsigned char volatile data  *) 0)

说明:此处不用volatile关键字,可能得不到真正的内容。
---------------------------------------------------------------------

2.


#define TEST_VOLATILE_C

//***************************************************************
// verwendete Include Dateien
//***************************************************************
#if __C51__ < 600
  #error: !! Keil
版本不正确
#endif

//***************************************************************
//
函数 void v_IntOccured(void)
//***************************************************************
extern void v_IntOccured(void);

//***************************************************************
//
变量定义
//***************************************************************
char xdata cvalue1;          //
全局xdata
char volatile xdata cvalue2; //
全局xdata

//***************************************************************
//
函数: v_ExtInt0()
//
版本:
//
参数:
//
用途:cvalue1++,cvalue2++
//***************************************************************
void v_ExtInt0(void) interrupt 0 {
  cvalue1++;
  cvalue2++;
}

//***************************************************************
//
函数: main()
//
版本:
//
参数:
//
用途:测试volatile
//***************************************************************

void main() {
char cErg;

//1.
使cErg=cvalue1;
cErg = cvalue1;

//2.
在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++
if (cvalue1 != cErg)
  v_IntOccured();

//3.
使cErg=cvalue2;
cErg = cvalue2;

//4.
在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++
if (cvalue2 != cErg)
  v_IntOccured();
  
//5.
完成
  while (1);
}

//***************************************************************
//
函数: v_IntOccured()
//
版本:
//
参数:
//
用途: 死循环
//***************************************************************
void v_IntOccured() {
  while(1);
}


仿真可以看出,在没有用volatile时,即2处,程序不能进入v_IntOccured();
但在4处可以进入v_IntOccured();

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 整理

评论(0) | 阅读(155)
发表于:2008-4-30 17:18:04
标签:无标签

1

一个字节的高低位转换的程序

uchar upset(uchar dat) //用12345678代表dat的各个位
{
  dat=(dat>>4)(dat<<4); //dat变成56781234
  dat=((dat&0xcc)>>2)((dat&0x33)<<2);//dat=78563412
  dat=((dat&0xaa)>>1)((dat&0x55)<<1);//dat=87654321
  return dat;
}

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(85)
发表于:2008-4-30 16:59:56
标签:无标签

0

精确计算C51延时小工具

点击下载

Emu51Form是一个软仿真计时器

具体使用方法为:

1、打开 keil\tools,ini 文件,在它的c51栏中加入 AGSI9=Emu51Form.DLL ("delay simulation")  然后存盘。

2、把 Emu51Form.dll 文件 copy 到 keil\c51\bin 中。

3、新建一个工程,编写一个延时程序编译通过后,(见图1)调时时在peripherals下有Emu51Form选项,(见图2)点击后就可以使用了。

图1
点击看大图

图2

选择 Debug\ Start/Stop Debug Session 后,弹出如图3所示的对话框。
图3

选择图3所示的对话框中的Run,即可得到延时程序的延时值,如图4所示。

********************************************
//delay.c

#i nclude 

void delayms(unsigned char ms) 

{
     unsigned char k ;
     ms=1 ;
     while(ms--)
      {
         for(k = 0 ; k < 120 ; k++) ;
      }
}

********************************************

晶振为11.0592MHz时的延时时间为1065us(1.065ms)

********************************************

 

P.S. 在Keil V3 中的文字显示有点问题,比如输入关键字后,光标会在字母的中间,十分不舒服,只需要在tools,ini中的[UV2]栏中加入 ANSI=1 即可正常显示。

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 整理

评论(0) | 阅读(109)
发表于:2008-4-30 16:54:51
标签:无标签

0

单片机C语言精确延时

51单片机 Keil C 延时程序的简单研究,作者:InfiniteSpace Studio/isjfk

(晶振12MHz,一个机器周期1us.)

    . 500ms延时子程序

程序:

      void delay500ms(void)

         {

         unsigned char i,j,k;

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

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

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

         }

产生的汇编:

      C:0x0800      7F0F       MOV        R7,#0x0F

      C:0x0802      7ECA       MOV        R6,#0xCA

      C:0x0804      7D51       MOV        R5,#0x51

      C:0x0806      DDFE       DJNZ       R5,C:0806

      C:0x0808      DEFA       DJNZ       R6,C:0804

      C:0x080A      DFF6       DJNZ       R7,C:0802

      C:0x080C      22         RET      

计算分析:

    程序共有三层循环

    一层循环n:R5*2 = 81*2 = 162us                    DJNZ    2us

    二层循环m:R6*(n+3) = 202*165 = 33330us            DJNZ    2us + R5赋值 1us = 3us

    三层循环: R7*(m+3) = 15*33333 = 499995us          DJNZ    2us + R6赋值 1us = 3us

    循环外:     5us            子程序调用 2us + 子程序返回 2us + R7赋值 1us    = 5us

    延时总时间 = 三层循环 + 循环外 = 499995+5 = 500000us =500ms

计算公式:延时时间=[(2*R5+3)*R6+3]*R7+5

    . 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--);

}

产生的汇编

C:0x0800      7F05       MOV        R7,#0x05

C:0x0802      7E84       MOV        R6,#0x84

C:0x0804      7D96       MOV        R5,#0x96

C:0x0806      DDFE       DJNZ       R5,C:0806

C:0x0808      DEFA       DJNZ       R6,C:0804

C:0x080A      DFF6       DJNZ       R7,C:0802

C:0x080C      22         RET

    . 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--);

}

产生的汇编

C:0x0800      7F05       MOV        R7,#0x05

C:0x0802      7E04       MOV        R6,#0x04

C:0x0804      7DF8       MOV        R5,#0xF8

C:0x0806      DDFE       DJNZ       R5,C:0806

C:0x0808      DEFA       DJNZ       R6,C:0804

C:0x080A      DFF6       DJNZ       R7,C:0802

C:0x080C      22         RET      

    . 1s延时子程序

程序:

void delay1s(void)

{

         unsigned char h,i,j,k;

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

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

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

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

}

产生的汇编

C:0x0800      7F05       MOV        R7,#0x05

C:0x0802      7E04       MOV        R6,#0x04

C:0x0804      7D74       MOV        R5,#0x74

C:0x0806      7CD6       MOV        R4,#0xD6

C:0x0808      DCFE       DJNZ       R4,C:0808

C:0x080A      DDFA       DJNZ       R5,C:0806

C:0x080C      DEF6       DJNZ       R6,C:0804

C:0x080E      DFF2       DJNZ       R7,C:0802

C:0x0810      22         RET

在精确延时的计算当中,最容易让人忽略的是计算循环外的那部分延时,在对时间要求不高的场合,这部分对程序不会造成影响.

void mDelay(unsigned int Delay) //Delay = 1000 时间为1S
{
unsigned int i;
for(;Delay>0;Delay--)
{
for(i=0;i<124;i )
{;}

}
}

void waitms(int i)
{
char m;

for( ; i ;i--)
{
for(m = 203; m ; m--)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
}
}
延时1ms的函数
时钟频率12MHz

unsigned int sleepTime;
unsinged char inSleep = 0;

void sleepService(void)
{
if (inSleep) sleepTime--;
if (sleepTime == 0) inSleep = 0;
}
void isr_timer(void) //假定定时器中断1ms 中断一次。
{
...
sleepService();
...
}
void sleep(unsigned int ms) //延时子程序
{
sleepTime = ms;
inSleep = 1;
while(inSleep);
}

void main(void)
{
....
sleep(1000); //延时 1秒
...
}
---------------------------------------------------------------

如果要求是秒级的

这么长的延时,单片机中一般采取不占CPU时间的延时,利用定时器来实现延时,
如果非得用循环延时,在C中也通常嵌入汇编实现,这样误差比较小

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 转贴

评论(1) | 阅读(138)
发表于:2008-4-19 23:36:33
标签:无标签

0

转贴 掉电保护

在掉电时怎样保护数据到EEPROM中

我想在掉电时保存数据(3个字节)到EEPROM中,用BOD掉电检测,不知怎样使用。望高手指点: 
1。在BOOT区设置好BODEN,BODLEVEL,后软件还要怎样设置? 
2。掉电中断是否是产生复位?我的写EEPROM程序应该放在什么地方?他和其他复位怎样区别? 
3。设置了BOOT区后,硬件上是否要加电源到一个管脚比较后才产生中断?? 
===================================================================================
掉电检测BOD的误解 
AVR自带的BOD(Brown-out Detection)电路,作用是在电压过低(低于设定值)时产生复位信号,防止CPU意外动作. 
对EEPROM的保护作用是当电压过低时保持RESET信号为低,防止CPU意外动作,错误修改了EEPROM的内容 

而我们所理解的掉电检测功能是指 具有预测功能的可以进行软件处理的功能。 
例如,用户想在电源掉电时把SRAM数据转存到EEPROM,可行的方法是 
外接一个在4.5V翻转的电压比较器(VCC=5.0V,BOD=2.7V),输出接到外部中断引脚(或其他中断) 
一但电压低于4.5V,马上触发中断,在中断服务程序中把数据写到EEPROM中保护起来 
注意: 写一个字节的EEPROM时间长达8mS,所以不能写入太多数据,电源滤波电容也要选大一些 
====================================================================================
将AVR的BOD设为2.7V,从4.5v到2.7这段时间写EEPROM。AVR的供电采用14楼方案,掉电检测使用IMP809。 
软件编写思路请参考我的《M128》书是第5章,或10月出版的书的第7章。参考电路如下: 

点击看大图

 

在图中,外部9V电源通过7805稳压到5V,作为系统电源使用。而AVR的工作电源则是单独提供的,由5v系统电源通过低压差肖特基二极管1N5817后得到。IN5817的正向压降为0.3v,因此,AVR的工作电压为4.7v。电源监控芯片IMP809-L的监控电压为4.63V,当系统电源的电压低于4.63V时,在R脚上产生由高电平到低电平的变化,使AVR进入INT0中断。 
     
    该电路的工作原理为:首先通过配置AVR的熔丝位,设置BOD掉电检测电压门限为2.7V,并允许BOD检测。因此,当AVR的Vcc电压掉到2.7v以下时,AVR就停止工作(掉电检测功能是AVR片内的功能之一,见第二章的2.6.2 AVR的复位源和复位方式)。电源监控芯片IMP809-L检测电压门限为4.63v,用于检测系统电源的电压。当系统电源大于4.63v时,IMP809-L的R端输出高电平,整个系统正常工作。当系统电源的电压跌到4.63v以下时,IMP809-L的R脚输出低电平,作为AVR外部中断INT0的申请。INT0设计为掉电处理中断,其主要任务是备份系统运行的重要数据到EEPROM中。 

    在提供AVR工作的电源系统中,大容量的电解电容C5作为储能电容,一旦系统电源电压下降,二极管1N5817截止,此时AVR可以靠C5提供的电储可以继续工作一段时间。C5容量应足够大,在系统电源掉电过程中,IMP809-L的R端输出低电平(下降到4.63v)时,要能够保证维持AVR的工作电压Vcc从4.7v降到2.7V的时间超过300ms,使AVR有时间做紧急处理和备份数据。AVR写EEPROM大约需要50-100mA的电流,所以电容C5的值应该在1000u~4700u,需要保存的数据越多,C5的容量应该越大。 

    INT0是AVR优先级最高的中断,采用外部电平变化的下降沿触发方式。一旦IMP809-L的R脚电平由正常的高电平变为低电平时,将触发INT0中断,进入INT0掉电中断服务程序。 
在INT0掉电保护中断服务程序中,应按以下的步骤和过程处理: 
A)紧急处理,关闭所有外部器件的工作,或将外部状态设置到安全模式,如关闭马达、开关等,保证系统不出事故。 
B)将AVR所有I/O设置为输入方式,最大程度的减少AVR芯片对电源的消耗。 
C)将重要数据写入到EEPROM中。 
D)循环检测INT0引脚是否恢复高电平。如为高电平则转到下一步E执行;如果INT0电平一直为低,程序将在此循环,直到完全停止运行(因为储能电容C5的电压低于2.7v后,AVR的BOD起作用,产生内部复位,AVR停止运行程序)。 
E)软件延时一段时间。 
F)再次检测INT0引脚电平。为低电平时转回D再次循环检测;为高电平时继续向下执行(这种情况表示系统电源受到干扰或短时掉电,现已经恢复正常)。 
G)恢复外部器件工作(此时尽管进入了掉电保护程序,但AVR在C5的维持下,一直正常工作,所有的数据并没有破坏,可以继续进行工作); 
H)中断返回。 
在实际应用中,系统断电保护的设计是一个比较难的问题,实现的方法和手段也有不同。这个设计主要是作为一个使用外部中断的例子,让读者可以从中体会到如何合理和正确的使用外部中断。 

 

http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=787122&bbs_page_no=1&bbs_id=1003

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(158)
发表于:2008-4-19 21:22:37
标签:无标签

1

转载 学习单片机的八个步骤

转载 学习单片机的八个步骤

学习使用单片机就是理解单片机硬件结构,以及内部资源的应用,在汇编或C语言中学会各种功能的初始化设置,以及实现各种功能的程序编制。


第一步:数字I/O的使用
  使用按钮输入信号,发光二极管显示输出电平,就可以学习引脚的数字I/O功能,在按下某个按钮后,某发光二极管发亮,这就是数字电路中组合逻辑的功能,虽然很简单,但是可以学习一般的单片机编程思想,例如,必须设置很多寄存器对引脚进行初始化处理,才能使引脚具备有数字输入和输出输出功能。每使用单片机的一个功能,就要对控制该功能的寄存器进行设置,这就是单片机编程的特点,千万不要怕麻烦,所有的单片机都是这样。

第二步:定时器的使用   学会定时器的使用,就可以用单片机实现时序电路,时序电路的功能是强大的,在工业、家用电气设备的控制中有很多应用,例如,可以用单片机实现一个具有一个按钮的楼道灯开关,该开关在按钮按下一次后,灯亮3分钟后自动灭,当按钮连续按下两次后,灯常亮不灭,当按钮按下时间超过2s,则灯灭。数字集成电路可以实现时序电路,可编程逻辑器件(PLD)可以实现时序电路,可编程控制器(PLC)也可以实现时序电路,但是只有单片机实现起来最简单,成本最低。
定时器的使用是非常重要的,逻辑加时间控制是单片机使用的基础。

第三步:中断

  单片机的特点是一段程序反复执行,程序中的每个指令的执行都需要一定的执行时间,如果程序没有执行到某指令,则该指令的动作就不会发生,这样就会耽误很多快速发生的事情,例如,按钮按下时的下降沿。要使单片机在程序正常运行过程中,对快速动作做出反应,就必须使用单片机的中断功能,该功能就是在快速动作发生后,单片机中断正常运行的程序,处理快速发生的动作,处理完成后,在返回执行正常的程序。中断功能使用中的困难是需要精确地知道什么时候不允许中断发生(屏蔽中断)、什么时候允许中断发生(开中断),需要设置哪些寄存器才能使某
种中断起作用,中断开始时,程序应该干什么,中断完成后,程序应该干什么等等 。
中断学会后,就可以编制更复杂结构的程序,这样的程序可以干着一件事,监视着一件事,一旦监视的事情发生,就中断正在干的事情,处理监视的事情,当然也可以监视多个事情,形象的比喻,中断功能使单片机具有吃着碗里的,看着锅里的功能。
以上三步学会,就相当于降龙十八掌武功,会了三掌了,可以勉强护身。

第四步:与PC机进行RS232通信

  单片机都有USART接口,特别是MSP430系列中很多型号,都具有两个USART接口。USART接口不能直接与PC机的RS232接口连接,它们之间的逻辑电平不同,需要使用一个MAX3232芯片进行电平转换。

  USART接口的使用是非常重要的,通过该接口,可以使单片机与PC机之间交换信息,虽然RS232通信并不先进,但是对于接口的学习是非常重要的。正确使用USART接口,需要学习通信协议,PC机的RS232接口编程等等知识。试想,单片机实验板上的数据显示在PC机监视器上,而PC机的键盘信号可以在单片机实验板上得到显示,将是多么有意思的事情啊!

第五步:学会A/D转换

  MAP430单片机带有多通道12位A/D转换器,通过这些A/D转换器可以使单片机操作模拟量,显示和检测电压、电流等信号。学习时注意模拟地与数字地、参考电压、采样时间,转换速率,转换误差等概念。
使用A/D转换功能的简单的例子是设计一个电压表。

第六步:学会PCI、I2C接口和液晶显示器接口

  这些接口的使用可以使单片机更容易连接外部设备,在扩展单片机功能方面非常重要。

第七步:学会比较、捕捉、PWM功能

  这些功能可以使单片机能够控制电机,检测转速信号,实现电机调速器等控制起功能。
如果以上七步都学会,就可以设计一般的应用系统,相当于学会十招降龙十八掌,可以出手攻击了。

第八步:学习USB接口、TCP/IP接口、各种工业总线的硬件与软件设计
  学习USB接口、TCP/IP接口、各种工业总线的硬件与软件设计是非常重要的,因为这是当前产品开发的发展方向。

到此为止,相当于学会15招降龙十八掌,但还不到打遍天下无敌手的境界。即使如此,也算是单片机大虾了。

 

理解高手与新手的区别:


同样一个项目,拿到手之后,高手可以在心里很快画出它的模型,因为他接触的东西足够多,新手会在网上去到处找东西,如果不知道到网上去找东西的人,连新手都算不上。

写一个项目程序,高手会从自己的库里面找以往整理好的程序库,新手从第一个程序写起。

高手可以很轻松的排除开发工程中遇到问题和错误,新手有可能因为一个分号或者一个函数名的错误郁闷半天。

高手遇到问题查手册,新手遇到问题,到处跪求。

高手与新手的差别在于基本功是否扎实,而不是,某些人自已为是的,“我看过UCOS2和RTOS的程序代码”。过时的技术在心里,比时髦的技术在嘴边强的多。

http://bbs.avrvi.com/read-htm-tid-6985.html

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(272)
发表于:2008-4-11 22:45:48
标签:无标签

0

[转]Keil C51程序设计中几种精确延时方法

实现延时通常有两种方法:一种是硬件延时,要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;另一种是软件延时,这种方法主要采用循环体进行。

1 使用定时器/计数器实现精确延时

  单片机系统一般常选用11.059 2 MHz12 MHz6 MHz晶振。第一种更容易产生各种标准的波特率,后两种的一个机器周期分别为1 μs2 μs,便于精确延时。本程序中假设使用频率为12 MHz的晶振。最长的延时时间可达216=65 536 μs。若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。

  在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。使用定时器/计数器延时从程序的执行效率和稳定性两方面考虑都是最佳的方案。但应该注意,C51编写的中断服务程序编译后会自动加上PUSH ACCPUSH PSWPOP PSWPOP ACC语句,执行时占用了4个机器周期;如程序中还有计数值加1语句,则又会占用1个机器周期。这些语句所消耗的时间在计算定时初值时要考虑进去,从初值中减去以达到最小误差的目的。

2 软件延时与时间计算

  在很多情况下,定时器/计数器经常被用作其他用途,这时候就只能用软件方法延时。下面介绍几种软件延时的方法。

2.1 短暂延时

  可以在C文件中通过使用带_NOP_( )语句的函数实现,定义一系列不同的延时函数,如Delay10us( )Delay25us( )Delay40us( )等存放在一个自定义的C文件中,需要时在主程序中直接调用。如延时10 μs的延时函数可编写如下:

  void Delay10us( ) {
    
_NOP_( );
    
_NOP_( );
    
_NOP_( );
    
_NOP_( );
    
_NOP_( );
    
_NOP_( );
  }

  Delay10us( )函数中共用了6_NOP_( )语句,每个语句执行时间为1 μs。主函数调用Delay10us( )时,先执行一个LCALL指令(2 μs