wey05 发表于 2011-4-30 23:01:56

几个重要的东西

这里只是一个关于51C驳岸城的简要介绍,旨在以实例帮助初学者理解C程序,不可能面面俱到,更不可能博大精深,好接着说吧
电脑程序之所以能够千变万化,满足各种应用,很重要的就是在程序的运行中可以根据需要而转向。如果只能一条路走到底,那就麻烦了。C里面下面这些用的多,有很重要,初学者一定要掌握。
1,条件语句:例如
sbit   K1=P1^2   //位变量K1定义,意思是位变量:仅仅一位,对应于P1口的第2引脚
unsigned char a=0;//定义无符号变量a,其值为0
if(K2==0)          //如果K1为0,也就是P1.2引脚被按键按下接地
a=0X01;         //满足以上条件:K1按下,a为0x01,不按下K1则不执行这一句
a=a+0X02;       //这一句完后a是多少?如果K1按下,就是0X01+0X02,否则是0+0X02
如果条件满足后需要执行好几条语句,那么就把这些语句用大括号括起来例如:
if (K1==0)
{   a=0x01;
      P2=0;   }
.......
就是K1按下时要进行a=0x01和P2=0两项操作,否则不进行
再一个条件语句形式是
unsigned char a;
unsigned char b=5;
if (a==1)
   b=b+1;
else
   b=b-1;
那这个语句意思啊哈i如果a等于1,则b为5+1,a不等于1则b为5-1
再就是多条件分支,形式是:
unsigned chara;
unsigned char b=5;
if (a==0)
   b=b+1;
else if (a==1)
b=b+2;
else if(a==2)
b=b+3;
else
b=b+4;
说的是如果a等于0则b为5+1,a等于1则b为5+2 ,a等于2则b为5+3,a除此以外一切情况b为6+4
2.条件循环语句:
形式如下(还是用上面定义过的变量)
while( a)
    {if(K1==0)
       P2=0;
       else
      P2=0X01;}
意思是,当a不为0,则执行下面大括号里的4个语句:如果K1按下P2为0,不按P2为1。执行完以后如果a还是不为0,则又从头到尾来一遍,,,就这样只要a不为0就不停地循环下去,直到a为0,循环停止。
特别是在51C程序中少不了的
   while(1)
   {   .......}
这个就是单片机启动后执行的无限循环程序,因为1总是不为0,那么大括号里的语句就无休止地循环下去。除非断电才能停止,也就是死循环。
在一个很常用的是条件有限循环语句,例如:
unsigned char i;
for( i=0;i<8;i++)
{ .......}
意思是,i从0开始,如果小于8,执行一遍大括号中的语句,然后i递增到1,还是小于8,又执行,i再递增到2,,,那么在i递增到7时已经执行了7遍,再执行一遍,i递增到8,已经不能满足i<8的条件,不再继续,所以就只能循环8次。利用单片机执行一个语句,即使是没有操作仅仅条件判断也要耗费时间,累积起来就可以达到可观的延时
通常如:
unsigned i;
unsigned j;
for(i=0;i<128;i++)
{ for(j=0;j<20;j++)
                ;            }
其作用是内外两层循环:先当i为0,满足小于128的条件,执行20遍内循环条件判断,i加1,,,总共要进行128*20也就是2560系判断循环,一次仅仅几个微妙,加起来也就不少了。
下次讲讲函数和数组,这些都明白了,时钟程序就好说了

wey05 发表于 2011-4-30 23:07:24

原帖由 xiaomu 于 2011-4-30 22:07 发表 http://www.crystalradio.cn/bbs/images/common/back.gif
在89s52 ISR里不清TF2,我try过了,在 ISR 里 把一个I/O口管脚拉高拉低,来观察输出方波的频率,
如果不清 也有方波输出,但频率比 设定的频率高2倍多一些,比如你设定频率是2KHz,输出频率大约为4.3KHz
如果在ISR ...
是的,即使不清除TF2,他也会不停地振荡但这个对要求准确的定时就不行了。

millwood 发表于 2011-5-1 04:52:44

如果不清 也有方波输出,但频率比 设定的频率高2倍多一些

if you don't clear the flag, the mcu will go back to the isr once it exits the isr. how fast the pin is being toggled depends on the length of the isr (and the isr's latency).

millwood 发表于 2011-5-1 04:53:28

for(i=0;i<128;i++)
{ for(j=0;j<20;j++)
                ;            }

that type of code will likely get optimized away.

millwood 发表于 2011-5-1 05:11:02

here is an example showing what clearing TF2 in the isr does:

#include <regx52.h>                                        //at89c52
#include "gpio.h"

//hardware configuration
#define OUT_PORT                        P2
#define OUT_DDR                                P2
#define OUT_TMR2                        (1<<0)        //tmr2 indicator
#define OUT_TMR1                        (1<<1)        //tmr1 indictor
#define OUT_LOOP                        (1<<2)        //loop indicator
#define OUT_TMR0                        (1<<3)        //tmr0 indictor
#define PERIOD_TMR2                        (F_CPU / 1000 * 1)        //tmr2 period = 1ms
#define PERIOD_TMR1                        (F_CPU / 1000 * 2)        //tmr1 period = 10ms
#define PERIOD_TMR0                        (F_CPU / 1000 * 4)        //tmr1 period = 10ms
//end hardware configuration

#define MSB(val)                        ((val) >> 8)        //most significant byte
#define LSB(val)                        ((val) & 0x00ff)        //least significant byte

//tmr2 isr
void tmr2_isr(void) interrupt TF2_VECTOR {
        TF2=0;                                                        //clear tmr interrupt flag
        TH2+=MSB(-PERIOD_TMR2);                        //load up the tmr offset
        TL2+=LSB(-PERIOD_TMR2);
        IO_FLP(OUT_PORT, OUT_TMR2);                        //flip the output pin
}

//initialize tmr2
void tmr2_init(void) {
        TR2=0;                                                        //turn off the timer
        C_T2=0;                                                        //tmr2 as a 16bit tmr
        RCAP2H=0;                                                //do NOT use the auto reload function
        RCAP2L=0;
        TH2=MSB(-PERIOD_TMR2);                        //load up the tmr offset
        TL2=LSB(-PERIOD_TMR2);
        ET2=1;                                                        //turn on tmr2 interrupt
        TR2=1;                                                        //turn on tmr2
}

//tmr1 isr
void tmr1_isr(void) interrupt TF1_VECTOR {
        TF1=0;                                                        //clear tmr interrupt flag
        TH1+=MSB(-PERIOD_TMR1);                        //load up the tmr offset
        TL1+=LSB(-PERIOD_TMR1);
        IO_FLP(OUT_PORT, OUT_TMR1);                        //flip the output pin
}

//initialize tmr1
void tmr1_init(void) {
        TR1=0;                                                        //turn off tmr1
        TMOD = (TMOD & 0x0f) | 0x10;        //tmr1 running in mode 1: 16-bit timer, not gated
        TH1=MSB(-PERIOD_TMR1);                        //load the offset
        TL1=LSB(-PERIOD_TMR1);
        ET1=1;                                                        //turn on tmr1 interrupt
        TR1=1;                                                        //turn on tmr1
}

//tmr1 isr
void tmr0_isr(void) interrupt TF0_VECTOR {
        TF0=0;                                                        //clear tmr interrupt flag
        TH0+=MSB(-PERIOD_TMR0);                        //load up the tmr offset
        TL0+=LSB(-PERIOD_TMR0);
        IO_FLP(OUT_PORT, OUT_TMR0);                        //flip the output pin
}

//initialize tmr1
void tmr0_init(void) {
        TR0=0;                                                        //turn off tmr1
        TMOD = (TMOD & 0xf0) | 0x01;        //tmr1 running in mode 1: 16-bit timer, not gated
        TH0=MSB(-PERIOD_TMR0);                        //load the offset
        TL0=LSB(-PERIOD_TMR0);
        ET0=1;                                                        //turn on tmr1 interrupt
        TR0=1;                                                        //turn on tmr1
}


void mcu_init(void) {
}

int main(void) {
        mcu_init();                                                //reset the mcu
        tmr1_init();                                        //initialize tmr1
        tmr2_init();                                        //initialize tmr2
        ei();                                                        //enable global interrupt
        while (1) {
                IO_FLP(OUT_PORT, OUT_LOOP);        //flip out_loop
        }
}


the code runs on at89c52 and contains the set-up and isr pieces for tmr0, tmr1 and tmr2. in this particular example, I only set up tmr1 and tmr2. and for tmr2, to disabled the auto reloading and opt to use manual reloading.

As a result, tmr1 and tmr2's initialization and isr are almost identical: basically, we clear the flags, reload the counters, and flip their respective output pins and life goes on.

we set so that tmr2 flips its pins every 1ms and tmr1 every 2ms.

and simulation shows that.

millwood 发表于 2011-5-1 05:15:00

notice that in tmr2_isr(), we cleared TF2 once we are in the isr?

now, we are going to comment out that sentence and here is what you get in simulation:

millwood 发表于 2011-5-1 05:17:46

you will see that OUT_TMR2 is flipping at much higher frequency (about 20Khz), vs. 500hz before. Because now, TF2 is not cleared. As a result, the mcu spends the bulk of its time servicing tmr2_isr() - in fact, the mcu serves just one instruction in the main while() loop for each time it takes to service tmr2_isr().

that's why OUT_LOOP is also flipping at much lower frequencies too.

millwood 发表于 2011-5-1 05:19:08

you can do similar things to the TF0 and TF1 flags in the tmr0_isr() and tmr1_isr() routines and you will NOT see the same result, because until TF2, TF1 and TF0 are automatically cleared when their respective isrs are serviced.

wey05 发表于 2011-5-1 16:19:13

继续说道很重要重要的函数概念

C语言中,要实行各种操作,最后的程序不是把所有的语句都罗列出来,而是利用一个个的函数装配成一个完整的程序
函数就是一系列的语句和变量构成的一个具备完整功能的程序,自己按照规定格式编好一个函数后,给它起个名:函数名,在程序需要时就需要时就直接用函数名来调用它,实现所需要的功能而不必再把原来的那些代码照抄一遍。
有的函数,只是具有一定的功能,函数执行完后并不返回数值。
有的函数,在执行完后它可以返回某些数值。
有的函数,直接调用即可,调用时不需要输入某些变量控制它。
有的函数,在调用时除了写出函数名以外,还要加入控制函数结果的变量:参数
完整的C程序中都有一个必不可少的主函数,控制整体程序的运行
对于各种原因引发的中断,还要有中断处理函数处理中断应该处理的任务。
和变量一样,函数也需要先声明后使用。
还是用例子说明具体怎么做吧
1:无返回值函数例如按键处理函数,函数名Key1
unsigned char mi-0;
voidKey1(void);//这样就声明了一个新的函数Key1。第一个void表明它没有返回值括号里面的void表示他不需要参数(也可以不写)那么这个函数能做什么呢?还的对他进一步定义出来:
void Key1(void)
{
   mi++;
    if (mi>59)
   mi=0;
}
它的功能就在大括号里面:先前已经有一个变量mi那么执行一遍Key1,mi便增加一次,如果mi大于59,mi便为0,可见它的用处就是改变mi的值。怎样调用它呢?调用时仅仅用函数名
if(K1==0)
Key1( );    //结合上一节所说就是如果K1按键按下,则调用函数Key1( )使得变量mi增加1,直到60回到0.
2,有返回值的函数例如一个函数可以实现两个变量unsigend chara和unsigend charb 相加 函数返回二者之和
unsigned char a=1,b=2;   //a b 中间用英文逗号隔开,都是同样的数据类型函数定义为
unsigned charPlusab()
{
   unsignedchar c;   //函数内部声明一个变量c
   c=a+b;                        //相加
    returnc;                      //返回结果 3
}
程序中如果仅仅和以上那样使用
Plusab();   
就没有起到返回值的作用,而是应该在声明了变量a、b之外再声明一个变量d:
unsigned chanr d;
d=Plusab( );
这样函数中的两数相加就返回到变量d里面了也就是说d==3。
3,有参数函数;上面的函数只是于两个已经预先定义好的变量相加在调用函数时直接用就行了,我们看以下一个花样注意括号里面不是空的,有内容了:
unsigned   Plus_c(unsjgned char a,unsigned char b)
{
    unsigned char c;
   c=a+b;
   return c;
}
小括号里的unsigned char a 和unsigned b就是函数参数,在定义是只是个形式而没有具体数值,简称为“形参”
调用函数时为了让它计算出具体数值,必须加入实际参数“实参”那么
unsigned char d;
d=Plus_c(9x21,9x 15);
调用前d值不定,调用后d==0x36.。
4,主函数:
单片机的C语言里面必不可少的就是这个。形式为
void   main ( )
{
............
..........
}
单片机通电以后,运行的就是这个主函数,一般来说大括号里面有两部分
一部分是初始化,包括对已经声明的变量赋以初值,对所需的特殊功能寄存器给以设置,以及开机自检等等按照顺序执行。
另一部分是主循环,初始化以后,程序就在主循环中不停地绕圈子。
例如:
事先声明了函数Key1( ); Key2( );Delay(unsigned int n);分别是按键K1检测,按键K2检测和延时函数,其定义先不管
voidKey1( );
voidKey2( );
void Delay(unsigned int n);   //主函数在其它函数声明之后,才可以调用其它函数
void main()
{
P0=0;
P2=0;
T2MOD=0X00;
TH2=(65536-110592/60)/256;//2ms
TL2=(65536-110592/60)%256;//2ms
   RCAP2H=TH2;
   RCAP2L=TL2;       
   EA=1;
   ET2=1;
   TR2=1;
while(1)
   {
              if(!K1)
        Delay(5);
        if(!K1)
        Key1( );       
        if(!K2)
        Delay(5);
        if(!K2)
        Key2( );
   }
主函数中while(1){ }死循环前面是初始化设置P0,P2以及定时器2的预置值,开启定时器和中断,
死循环里面的 if(!K1)   等同于if(K1==0)    就是说检查K1按键有没有按下?如果按下,等一会稳定了再查,还是按下就调用按键处理函数Key1( ),再查K2 ,,,,就这么一直不断反复查。
6,中断函数,光有主函数可以解决比较简单的问题,对于复杂些的问题还要使用到单片机的中断功能。所谓中断就是除了主循环之外还有另外一个监视系统在运行,在一定的条件下它会把主循环按下不表,先执行中断函数,等中断处理完了再继续进行主循环。我们这个时钟用到的是定时器T/C2 的定时中断,就是在设置好预置值,开启中断许可之后启动定时器,定时器自动计时到达2毫秒发生中断。在中断时主要要进行时分秒计算,显示一位数码管,完成计时功能。
51的中断函数有固定格式如:
void time2( ) interrupt 5      //固定格式。定时器2第5号中断 52一共可有8个中断源(实用7个)
{
        TF2=0;             //清除硬件中断标记
        Hms( );            //计时函数
        DispHms( );   //数码管显示1位函数
}
当然所用到的函数必须在中断函数之前声明好,关于函数先了解这么多吧。

[ 本帖最后由 wey05 于 2011-5-1 16:21 编辑 ]

millwood 发表于 2011-5-1 20:42:01

voidKey1( );
voidKey2( );
      if(!K1)
      Key1( );      

a better approach is to allow KEY1() to return a value that tells you what key has been pressed. This way, you minimize the use of a global variable.

void time2( ) interrupt 5      //固定格式。定时器2第5号中断 52一共可有8个中断源(实用7个)

don't use a literal 5 here. instead, use TF2_VECTOR as defined in the header file. This way, if tmr2 interrupt is reallocatedto a different spot on a differernt chip, your code still works.


{
      TF2=0;             //清除硬件中断标记
      Hms( );            //计时函数
      DispHms( );   //数码管显示1位函数
}

at most firms, this type of coding will get your fired on the spot: calling a function from within an isr in an embedded environment is something that needs to be handled extremely carefully.

wey05 发表于 2011-5-2 15:05:26

数组的应用

数组

程序中数组是是非有用的,所谓数组,就是在内存中一块连续的存储单元,数据存在于这些单元中。和变量一样,要使用数组,首先得声明,建立一个数组,例如:
unsigned char Disp[];
就声明了一个数组,它的名称是Disp,它的类型是无符号字符型,大的大小:存储单元的多少没说,但是每个单元只能存进无符号字符数,也就是每个单元只能存放1个字节的数据;如果
unsigned char Dsp1;
则声明了另一个无符号字符型数组,Dsp1,它具有11个存储单元也称为“元素”,这些元素分别编号为
0,1,2,3,4,5,6,7,8,9,10.这些编号称为数组元素的“下标”那么在声明了这个数组以后,就可以给它的各个元素存入数据:例如
Dsp1=0x25;
Dsp1=9;
要注意现在是给他的0号元素和10号元素赋值,它的元素下标最大就是10:从0到10共有11个元素!
还可以在声明数组时就一次性给它的各个元素赋初值,例如:
unsigned char Dsp1={0x01,0x03,0x04,0x02,0x10,0x23,0x30,0x35,0x55,0x66,0xa3};11个元素都写满了数,注意,一旦声明数组时赋以初值后,程序中就再也不能用这种形式一次性给多个元素赋值,只能用Dsp1这样的形式分别给它各个元素赋值。数组每个元素大小都是相同的,当然不能在单字节元素中存放具有两个字节的整型数,整型数得使用整型数组存放例如
unsigned int Zuli;则Zuli=0x1022;Zuli=0x324a;Zuli=0xfafe;都是正确的,再来一个Zuli=0x2133;就错了,因为已经超出了数组声明的下标范围0,1,2。
还要注意一点,数组的元素下标排列从左到右是0,1,2...到最大,和数据以及单片机的P口线排列:从左到右是最高位到最低位排列顺序正好相反。
在数字钟程序里数组的使用:
为了在8个数码管上显示时间数据,最开始已经说过,是把待显示的某位数码管字段码送到P0口,然后打开该
位的控制线,显示这一位。然后P0口加上下一个字段码,打开下一位控制线。而显示的时分秒数据是变化的。
这样我们先建立一个具有8个单元-对应于8个数码管;显示缓存数组:
unsigned char DspBuff;它的各个单元存数是
DspBuff=空白;
DspBuff=时十位;
DspBuff=时个位;
DspBuff=负号;
DspBuff=分十位;
DspBuff=分个位;
DspBuff=秒十位;
DspBuff=秒个位;
现在要显示这些数字、符号,还得把它们转换为对应的字段码;为此我们建立一个显示码数组
unsigned char DspCode={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};从0号到9号元素存放的是0到9的数码管显示字段码,最后一个是负号的字段码,那么要显示就按照数码管位置逐个取出
DspBuff的数据,以这个数据为数组下标去查找DspCodeai在这个下标元素的数据:相应的显示码。如下图所示。


宏定义
C程序前面往往有一行或数行“宏定义”:这只是用一些字符代替另一些字符形式为:
#defineucharunsigned char
#defineuint   unsigned int
就是说以后用uchar 代表 unsignedchar 用uint 代表 unsigned int简化打字。注意和前面的包含#include头文件一样最后都不能有分号。他还可以定义出代替数个语句的宏,起到函数的效用,例如
#defineKEY1DO   { if(!KEY1) Delay(10); if(!KEY1) KEY1();}
就是用KEY1DO代替后面大括号里的几个语句。

51C的程序架构也就是格式一般如下:
1,包含文件和宏定义
#include"REG51"
#defineuchar unsigned char
#defineuintunsigned int

2,全局变量声明或定义:全局变量是在函数以外定义的变量,可供以后多个函数使用,一旦声明为全局变量,函数内部就不能再用同样的变量名重新声明为函数内专用的局部变量

3,函数声明或定义

4,主函数

5,中断函数定义

也可以把所有函数声明放在主函数之前,在主函数后面再一一加以定义。注意在函数定义时的排列顺序:如果某个函数A使用了另一个函数B,则本着先声明后使用的原则,函数B的定义要放在函数A定义前面。

注意:初学者写代码时千万不要加入汉字的逗号分号括号等,一定要全部是英文符号。

wey05 发表于 2011-5-2 17:06:57

C程序代码

实际代码如下,程序不复杂运行效果不错主函数的自检也可以不要

#include "reg52.h"                 //头文件
#define uchar unsigned char        //宏定义
#define uint unsigned int
/*共阳极数码管显示字段码*/
uchar code ucDispCode=
{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};//cathode comm
uchar sec=0,min=0,hour=0;//定义秒分时
uchar ucDisp_led_pos;            //数码管在P2口的显示位置0x01,0x02...0x40,显示7位
uchar ucDisp_buf_pos;      //显示缓冲区数组的下标0...6

uchar ucDispBuff={0,0,10,0,0,0,0};//显示缓冲区数组元素0是时十位,元素6是秒个位

sfr T2MOD=0XC9;            //因reg52.h中未定义T2MOD,在此补充定义特殊寄存器内存地址

bit BlinkDot;                //它用于分个位小数点按秒亮灭1亮0灭
sbit K1=P1^2;            //K1按键定义
sbit K2=P1^4;            //K2按键定义

/*****************
毫秒延时函数
无返回值,参数为整型数
******************/
void Delay(uint ms)
{
   uchar i;
        while(ms--)         //ms每递减一次下面循环125次,直到ms为0
        {
                for( i=0;i<125;i++)
                ;
        }
}
/*****************
K2按键处理:按键释放分后加1,分最大为59
*****************/
void Key2(void)
{

        while(!K2);
        min++;
        if(min>59)
        min=0;
}
/*******************
K1按键处理,按键释放后小时加1,小时最大23
*******************/
void Key1(void)
{       
        while(!K1);
        hour++;
        if(hour>23)
        hour=0;
}
/*******************
时分秒计算函数每2毫秒执行一次
*******************/
void Hms(void)
{
        static uint ui2msCount=0;//静态变量,函数执行一次其值改变后保存到下一次
        ui2msCount++;            //由2ms中断使他2ms递增一次
                if(ui2msCount>499)   //递增到500次为1秒
                {
               ui2msCount=0;      //满1秒回到初值0
                sec++;                     //秒递增
                BlinkDot=~BlinkDot;//分小数点改变状态
                        if(sec>59)   //满60秒秒值回0分值递增
                        {
                        sec=0;
                        min++;
                                if(min>59)//满60分分值回0小时递增
                                {
                                min=0;
                                hour++;
                                        if(hour>23)//24小时,时值回0
                                        hour=0;
                                }
                        }
                }
               ucDispBuff=hour/10;//显示缓冲数组各位更新。小时十位
               ucDispBuff=hour%10;//小时个位
               ucDispBuff=min/10;   //分十位
               ucDispBuff=min%10;   //分个位
               ucDispBuff=sec/10;   //秒十位
               ucDispBuff=sec%10;   //秒个位
}
/***********************
数码管显示函数每2毫秒显示一位
************************/
void DispHms(void)
{
        P0=0;                   //P0为0关显示
        P2=ucDisp_led_pos;      //P2为显示位置最初是1位,0位不显示
        if(ucDisp_led_pos==0x04)//如果显示到5位:分个位
        {
        if(BlinkDot)            //如果点闪为1则
        P0=ucDispCode]|0X80;//显示字段码最高位为1,点亮
        else
        P0=ucDispCode];//否则显示字段码最高位为0,点灭
        }
        else                                    //不是第5位显示
        P0=ucDispCode];//ucDisp_buf_pos:显示缓冲位置
                                                                                        //ucDispBuff该位置的数值
                                                                        //ucDispCode]该数值
                                                                        //的显示代码
        ucDisp_led_pos>>=1;                     //显示位置右移一位
        if(ucDisp_led_pos==0)                     //如移出最后返回到1位
        ucDisp_led_pos=0x40;
      ucDisp_buf_pos++;                        //显示缓冲移到下一位
        if(ucDisp_buf_pos==7)                     //如移出末尾回到0位
        ucDisp_buf_pos=0;
}
/*************************
主函数
**************************/
void main(void)
{
        P0=0XFF;         //自检高4位数码管显示8持续半秒
        P2=0XF0;
        Delay(500);
        P2=0X0F;         //自检低4位数码管显示8持续半秒
        Delay(500);
        ucDisp_led_pos=0x40;//数码管显示初始位置定位 左1位
            ucDisp_buf_pos=0;   //显示缓冲区初始定位:0单元

        BlinkDot=0;         //小数点闪亮初始状态
        P1=0XFF;            //P0P1P2初值
        P2=0;
        P0=0;

        T2MOD=0X00;         //定时器T2设置为16位自动重载
        TH2=(65536-110592/60)/256;//2ms高8位初始值
        TL2=(65536-110592/60)%256;//2ms低8位初始值
        RCAP2H=TH2;            //重载计数器高8位
        RCAP2L=TL2;                 //重载计数器低8位
        EA=1;                  //总中断开启允许中断
        ET2=1;                   //定时器2中断开启允许定时中断
        TR2=1;                   //启动定时器
       
        while(1)               //主循环
        {
                if(!K1)          //如果K1按下
                Delay(5);      //延时5毫秒如果还是按下就去K1处理
                if(!K1)
                Key1();       
                if(!K2)          //如果K2按下
                Delay(5);      //延时5毫秒如果还是按下就去K2处理
                if(!K2)
                Key2();

        }
}
/**************************
定时器T22中断处理函数2ms一次
***************************/
void time2() interrupt 5         
{
        TF2=0;             //清除硬件中断标志
        Hms();             //时分秒计算
        DispHms();         //数码管显示
}

//end//

xiaomu 发表于 2011-5-3 23:04:41

回复 78# millwood 的帖子

millwood兄 说看不到sms,:lol 感谢millwood兄、Paktu、wey05兄的系列回帖,学到了很多东西:lol
我已经把 中断标号改成了宏定义的方式,这样代码看起来也顺眼:lol

[ 本帖最后由 xiaomu 于 2011-5-3 23:08 编辑 ]

millwood 发表于 2011-5-3 23:26:52

               ucDispBuff=hour/10;//显示缓冲数组各位更新。小时十位
               ucDispBuff=hour%10;//小时个位

those are fairly long calculations so you shouldn't be putting them in the isr. instead, put them in the main() loop.

      P0=0;                   //P0为0关显示
      P2=ucDisp_led_pos;      //P2为显示位置最初是1位,0位不显示

code like that is very unportable: what if you were to move the led to a different port?

instead, define a macro, like LED_PORT (for the segments) and COM_PORT (for the coms) and then write to them.

that way, if you were to change the port / pin assignments, you can just change the macros and recompile.

those are basic and good programming habits.

millwood 发表于 2011-5-3 23:30:32

这样代码看起来也顺眼

and it also makes the code more robust: if you see

void tmr1_isr(void) interrupt TF0_VECTOR

you know right away that that piece of code is wrong.

in comparison, it is tough to see what's wrong with

void tmr1_isr(void) interrupt 1

the ultimate goal of enhanced portability is a robust code that is reusable and bug-free.
页: 1 2 3 4 [5] 6 7 8 9 10 11 12 13 14
查看完整版本: 帮助新手学习单片机数码管电子钟:原理,c程序详解