矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
楼主: wey05

帮助新手学习单片机数码管电子钟:原理,c程序详解

  [复制链接]
发表于 2011-6-10 22:24:31 | 显示全部楼层
非常感谢!这两天将这个帖子来回看了很多遍。对照一个简单的程序 反复研究,感觉长进很多,现在就是对定时器那一块有问题。希望楼主继续。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-6-10 22:57:19 | 显示全部楼层
回复 100# 清风车影18
什么问题能说说吗?下次准备讲讲字符液晶1702的使用
回复 支持 反对

使用道具 举报

发表于 2011-6-11 15:18:58 | 显示全部楼层
谢谢老师!正想学C呢
回复 支持 反对

使用道具 举报

发表于 2011-7-9 12:07:46 | 显示全部楼层
本帖最后由 清风车影18 于 2011-7-9 12:13 编辑

shumaguan.rar (14.78 KB, 下载次数: 583) 这个是仿真文件。
你的数码管显示的小标 数组 搞的我云里雾里。
希望可以举例:比如动态显示2位数码管,最好函数名字不要起那么长,简单点好懂。

我的代码见下面。
这几天对中断和定时时间的计算已经掌握。包括定时器初始值的设置。
现在没有搞懂的是数码管显示的问题。感觉很纠缠。没有头绪,希望能指点一下。
以下是我做数码管显示的程序,无意中就修改成电子钟了,但是显示部分是很原始的按照思路做出来的,想要改成你这个显示,琢磨了两天也没有实现。
下面是我的代码:
#include<reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define smg P0
uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//共阴数码管字段
//unsigned char tab[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; //这个是共阳数码管的字段
sbit one=P2^3 ;           //以下分别是数码管的共阴驱动引脚。分别是秒 个位 秒十位;分个位。。。。。。。。。。
sbit two=P2^4 ;
sbit three=P2^6 ;
sbit four=P2^7 ;
sbit five=P2^1;
sbit six=P2^0;
sbit nn=P2^2;                 //两个--
sbit nn1=P2^5;

sbit k1=P1^0;                //按键,还没有做  但是这个简单。
uchar i,gw,sw,bw,qw,sec1,sec,fen,shi;
uint count;
void delay(uchar ms) //延时程序
{uchar i,k;
for(i=ms;i>0;i--)
for(k=250;k>0;k--);
}

/**************数码管显示程序******************/

void sec_7duan(void)
{

  smg=0xff;         //关显示
  smg=tab[fen%100/10];          //  查表显示数字
  one=0;                                 //           打开显示,并且延时。
  delay(10);                                  
   one=1;                                 //          关显示
  


  smg=0xff;
  two=1;
  smg=tab[fen%10];
  two=0;
  delay(10);
   two=1;
  
  smg=0xff;
  three=1;
  smg=tab[sec%100/10];
  three=0;
  delay(10);  
  three=1;
  
  smg=0xff;
  four=1;
  smg=tab[sec%10];
  four=0;
  delay(10);
   four=1;
   
   smg=0xff;
five=1;
  smg=tab[shi%10];
  five=0;
  delay(10);
   five=1;
   
   smg=0xff;
  six=1;
  smg=tab[shi%100/10];
six=0;
  delay(10);
   six=1;   
   
   smg=0xff;
   smg=0x40;
   nn1=0;
   delay(10);
   nn1=1;

   smg=0xff;
   smg=0x40;
   nn=0;
   delay(10);
   nn=1;
     
}
/****************中断设置***************/
void init()
{TMOD=0X01;
TH0=0X3c;
TL0=0Xb0;
ET0=1;
EA=1;
TR0=1;
}
/****************中断入口*************/
void time0(void) interrupt 1
{
TH0=0X3c;
TL0=0Xb0;
sec1++;
if(sec1==20)
{
sec1=0;
sec++;
if(sec==60)
{
   sec=0;
   fen++;
   if(fen==60)
   {
   fen=0;
   shi++;
   if(shi==24)
   {
   shi=0;
   }
   }
}
gw=count%10;
sw=count%100/10;

}
}


void main()        //主程序
{
shi=12;
fen=00;
init();


while(1)
{
  sec_7duan();

}
}

现在思路是 做一个F0R循环,每一次分别显示一位,当到达最高位的时候返回。
也可以使用左移,但是具体怎么做没有想好。
这几天对1602显示琢磨透了。可以很方便的显示出来自己需要的。但是数码管感觉还是没有入门。
希望能够指教。
回复 支持 反对

使用道具 举报

发表于 2011-7-9 12:17:59 | 显示全部楼层
希望举例的时候能够简单
uchar ucDispBuff[7]={0,0,10,0,0,0,0}你的这句我一直没有明白。
我的笨想法:当uchar ucDispBuff[0]的时候,uchar ucDispBuff[0]=0
uchar ucDispBuff[1]的时候uchar ucDispBuff[1]=0,不知道这个意思。?
回复 支持 反对

使用道具 举报

发表于 2011-7-9 19:58:42 | 显示全部楼层
终于搞明白了 这个是我改进的程序  仿真通过。

#include<reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
uchar code tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};//共阴数码管字段
//unsigned char tab[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; //这个是共阳数码管的字段
uchar disp_on_off,disp,i,sec1,sec,fen,shi;
//sbit =P2;
uint count;
uchar tab1[8]={0,0,0,0,0,0,0,0};

sbit k1=P1^0;                //按键,还没有做  但是这个简单。
void delay(uchar ms) //延时程序
{uchar i,k;
for(i=ms;i>0;i--)
for(k=250;k>0;k--);
}


/****************中断0设置***************/
void init()
{TMOD=0X11;
TH0=0X3c;
TL0=0Xb0;
ET0=1;
EA=1;
TR0=1;
}

/****************中断1设置***************/
void init1()
{
TH1=0Xf8;  //每5毫秒显示一位
TL1=0X30;
ET1=1;
EA=1;
TR1=1;
}
/****************中断0入口*************/
void time0(void) interrupt 1
{
TH0=0X3c;
TL0=0Xb0;
sec1++;
if(sec1==20)
{
sec1=0;
sec++;
if(sec==60)
{
   sec=0;
   fen++;
   if(fen==60)
   {
   fen=0;
   shi++;
   if(shi==24)
   {
   shi=0;
   }
   }
}
tab1[0]=sec%10;  //先显示秒个位。
tab1[1]=sec/10;
tab1[2]=0x10;     //显示负号
tab1[3]=fen%10;
tab1[4]=fen/10;
tab1[5]=0x10;
tab1[6]=shi%10;
tab1[7]=shi/10;
}
}
/***************中断1用于显示***********/
void time1(void) interrupt 3
{
  TH1=0xf8;
  TL1=0x30;
  P2=disp_on_off;
  P0=tab[tab1[disp]];
  disp_on_off=~disp_on_off;
  disp_on_off<<=1;
    disp_on_off=~disp_on_off;
        if(disp_on_off==0xff)
        { disp_on_off=0xfe;}

        disp++;
    if(disp==8)
        {disp=0;}
}

void main()        //主程序
{
shi=12;
fen=12;
P0=0xff;
disp_on_off=0xfe;        //秒个位显示
disp=0;
init();
init1();


while(1)
{
delay();

}
}
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-7-9 22:50:04 | 显示全部楼层
清风朋友做的不错,如果用52的话就用T/C2可以自动加载,可以把走时误差做得尽量小
回复 支持 反对

使用道具 举报

发表于 2011-7-10 10:11:40 | 显示全部楼层
本帖最后由 清风车影18 于 2011-7-10 11:05 编辑

请教wey05

我手头没有52芯片,也没有实际做,不过用仿真倒是通过了。
我用液晶和1302自己做了一个电子钟。效果还可以。但是现在已经多做电子钟没有兴趣了,很多时候都是在学习其他的程序的时候突然发现我原来做的一个东西可以用另外一种方法更加简便。
比如,这个数码管显示的问题。可以用数组显示。我想也可以用一个函数
VOID disp(disp)
{SWITCH(DISP)
{    CASE 1: FEN%10;  
               BREAK;  
    CASE 2:    FEN/10;
                      BREAK;
。。。。
。。。。。。
}
}
P0=disp(disp);
希望能够介绍一下液晶芯片1602的读操作。
看资料上1602可以将内部的数据读出,通过IO口送出。
但是我一直没有读取成功。
我的想法是做一个1602提示菜单,当用户操作不当的时候,液晶显示一个界面,然后几秒后,返回原来的显示界面。
想在用户错误操作的时候,将液晶内部数据保存,然后显示错误提示,最后将保存的数据从新写入液晶。
不知道wey05 对此有何想法?
多谢指教!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-7-10 16:29:01 | 显示全部楼层
1602读,一般使用较少,假如P0是数据口,
先指定读写显存的地址,在显示范围内,行为Y:0,1;列为X:0-15
void LCDSetxy(unsigned x,unsigned y)
{
    y=y&0x01;//y只能是0或1
    x=x&0x0f;//x只能取值0到0x0f
    if(y==1)   //如果第2行
    x=x|0x40;//第1行显存地址是0到0x0f,第2行的显存地址是0x40到0x4f
    x=x|0x80;//设置显存地址的最高位为1
    LCDWritcomm(x);//用写命令函数
}
读显存函数应该是:
unsigned char LCD1602Read(void)
{
        uchar tmp;
                E=0;
        RS=1;
        RW=1;
         NOP;
         E=1;
         NOP;
         tmp=P0;
         NOP;
         E=0;
         NOP;
        return tmp;
}
读的是刚才写入的显存地址的数据。
你的意思是防止错误输入,实际上可以在程序中限定输入值的范围,你的方法也是可行的,不过操作起来比较复杂,可以设置一个显示缓存数组1,其各个单元和先前显示内容对应,另设一个错误提示显示数组2,显示错误提示,当输入值错误就显示错误提示数组2,然后回到显示缓存数组1。
要注意的是1602写数据和命令一般都没有问题的,但是,我发现有部分液晶用51单片机有读不出的问题(始终读出0xff)这样的液晶读忙也会失败造成显示不了,只有采取延时就可以了。
回复 支持 反对

使用道具 举报

     
发表于 2011-7-10 20:49:43 | 显示全部楼层
好贴!
我正想从这里入手学单片机!
回复 支持 反对

使用道具 举报

发表于 2011-7-10 21:15:06 | 显示全部楼层
here is a slightly more structured way to approaching your code.

it basically replicated your code, giving it more structure, and adds some features / flexibility:

1) it has a blinking half second indicator;
2) it accomodates either common cathode or common anode displays;
3) it runs on pretty much any frequencies - you just need to redefine F_CPU;
4) most importantly, its timing error is only dependent on the crystal used, and it provides a way for you to correct your timing error.

hope it helps.
  1. //time keeping on 8051
  2. //tmr0 runs as a 16-bit rtc (black roman approach)
  3. //tmr1 controls the 7-segment display

  4. #include<regx51.h>                                                                //we use keil c51
  5. #include "gpio.h"
  6. #include <intrins.h>

  7. //hardware configuration
  8. #define _7SEG_PORT                P2                                                //7seg connection, active low
  9. #define _7SEG_DDR                P2
  10. #define _7SEGs                        0xff
  11. #define _7SEG_OFF(segs)        {_7SEG_PORT= (segs);}        //turn on segments, active low
  12. #define _7SEG_ON(segs)        {_7SEG_PORT=~(segs);}        //turn off segments

  13. #define DIG_PORT                P3                                                //digits connection, active high
  14. #define DIG_DDR                        P3
  15. #define DIGs                        0xff
  16. #define DIG_ON(digs)        {DIG_PORT= (digs);}                //turn on digs, active high
  17. #define DIG_OFF(digs)        {DIG_PORT=~(digs);}                //turn off digs

  18. #define HALF_SEC                (500000ul)                                //half a second, in us
  19. #define FULL_SEC                (2*HALF_SEC)                        //a full second, in us
  20. #define TMR0_PERIOD                HALF_SEC                                //tmr0's period
  21. #define TMR1_PERIOD                1000ul                                        //tmr1's period
  22. #define TMR0_ERROR                0                                                //tmr0 error term
  23. //end hardware configuration

  24. #define F_CPU                        2000000ul                                //cpu frequency, in hz
  25. #define DLY_MS                        100                                                //delay cycles to achieve 1ms at 1MIPS
  26. #define MSB(word_t)                ((word_t) >> 8)                        //word_t's most significant byte
  27. #define LSB(word_t)                ((word_t) & 0x00ff)                //word_t's least significant byte

  28. typedef struct {
  29.         unsigned char half_sec;                                                //half second
  30.         unsigned char sec;                                                        //second
  31.         unsigned char fen;                                                        //minute
  32.         unsigned char shi;                                                        //hour
  33. } _time_type;

  34. //global variables
  35. unsigned long _time_counter=0;                                        //time counter, in timer ticks
  36. unsigned short _tmr1_period;                                        //tmr1 period
  37. _time_type _time={                                                                //time for time keeping
  38.         0, 59, 59, 23};                                                                //23:59:59:0
  39. unsigned char vRAM[8];                                                        //display buffer

  40. unsigned char disp_on_off,disp,i,sec1,sec,fen,shi;

  41. //sbit =P2;
  42. //unsigned int count;

  43. sbit k1=P1^0;                                                        //??,????  ???????

  44. void delay(unsigned char dly) {
  45.         while (dly--) continue;
  46. }

  47. void delay_ms(unsigned char ms) //????
  48. {
  49.         //unsigned char i,k;
  50.         while (ms--)
  51.                 delay(DLY_MS * (F_CPU / 1000000ul));
  52. }

  53. /****************??0??***************/
  54. void tmr0_init(void)
  55. {
  56.         TR0=0;                                                                                //stop tmr0
  57.         TMOD=        (TMOD & 0xf0) |                                                //reset tmod's lower 4 bits
  58.                         0x01;                                                                //tmr0 running in mode 1 (16-bit timer), not gated
  59.         TH0=0;                                                                                //reset tmr0
  60.         TL0=0;
  61.         ET0=1;                                                                                //enable tmr0 interrupt
  62. //        EA=1;
  63.         TR0=1;
  64. }

  65. //update time
  66. void update_time(_time_type * time) {
  67.         _time_counter+=0x10000ul+TMR0_ERROR;                //increment time_counter;
  68.         if (_time_counter >= (TMR0_PERIOD * (F_CPU / 1000000ul))) {                //need to make sure that it doesn't overflow at high f_cpu
  69.                 //IO_FLP(P1, 1<<1);                                                //for testing  only
  70.                 _time_counter -= (TMR0_PERIOD * (F_CPU / 1000000ul));                //reset _time_counter
  71.                 time->half_sec+=1;                                                //increment half second
  72.                 if(time->half_sec==2)                                        //full second reached?
  73.                 {
  74.                         time->half_sec=0;                                        //reset half second indicator
  75.                         time->sec+=1;                                                //increment the second
  76.                         if(time->sec==60)                                        //full 60 seconds passed?
  77.                         {
  78.                            time->sec=0;                                                //reset the second
  79.                            time->fen+=1;                                                //increment the minute
  80.                            if(time->fen==60)                                        //full 60 minutes passed?
  81.                            {
  82.                                    time->fen=0;                                        //reset the minute
  83.                                    time->shi+=1;                                        //increment the hour
  84.                                    if(time->shi==24)                                //full 24 hour passed?
  85.                                    {
  86.                                                    time->shi=0;                                //reset the hour
  87.                                    }
  88.                                         vRAM[5]=0x10;                                //update hour display
  89.                                         vRAM[6]=time->shi%10;
  90.                                         vRAM[7]=time->shi/10;
  91.                                    }
  92.                                 vRAM[3]=time->fen%10;                        //update minute display
  93.                                 vRAM[4]=time->fen/10;
  94.                         }
  95.                         vRAM[0]=time->sec%10;  //???????                //update second display
  96.                         vRAM[1]=time->sec/10;
  97.                 }
  98.                 vRAM[2]=(time->half_sec)?0x10:0x7f;                //???? - blinking
  99.         }
  100. }


  101. /****************??0??*************/
  102. void tmr0_isr(void) interrupt TF0_VECTOR
  103. {
  104.         update_time(&_time);                                                //update time keeping
  105. }

  106. //initialize the display
  107. void _7seg_init(void) {
  108.         _7SEG_OFF(_7SEGs);                                                        //clear leds - turn off all segments
  109.         IO_OUT(_7SEG_DDR, _7SEGs);                                        //leds as output

  110.         DIG_OFF(DIGs);                                                                //all display off
  111.         IO_OUT(DIG_DDR, DIGs);                                                //all digit pins as output

  112.         //initialize vRAM
  113.         vRAM[5]=0x10;                                //update hour display
  114.         vRAM[6]=_time.shi%10;
  115.         vRAM[7]=_time.shi/10;
  116.         vRAM[3]=_time.fen%10;                        //update minute display
  117.         vRAM[4]=_time.fen/10;
  118.         vRAM[0]=_time.sec%10;  //???????                //update second display
  119.         vRAM[1]=_time.sec/10;
  120.         vRAM[2]=(_time.half_sec)?0x10:0x7f;                //???? - blinking the half second indicator
  121. }

  122. //display the content of vRAM[8]
  123. void _7seg_display(void) {
  124.         unsigned char code _7seg_font[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};//???????
  125.         //unsigned char _7seg_font[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; //???????????
  126.         static unsigned char digit=0;                        //digit to be displayed

  127.         DIG_OFF(DIGs);                                                        //turn all digits off       
  128.         _7SEG_ON(_7seg_font[vRAM[digit]]);                //display the digit
  129.         DIG_ON(1<<digit);                                                //turn on the digit
  130.         digit+=1;                                                                 //increment the digit
  131.         if (digit==8) digit=0;                                        //reset the digit
  132. }

  133. /****************??1??***************/
  134. void tmr1_init(unsigned short period)
  135. {
  136.         _tmr1_period=period;                                        //save tmr1_period
  137.         TR1=0;                                                                        //stop tmr1
  138.         TMOD=        (TMOD & 0x0f) |                                        //reset tmod's higher 4 bits
  139.                         0x10;                                                        //tmr1 running in mode 1 (16-bit timer), not gated
  140.         TH1=MSB(-_tmr1_period);                                  //load up the offset
  141.         TL1=LSB(-_tmr1_period);
  142.         ET1=1;
  143.         //EA=1;
  144.         TR1=1;
  145. }

  146. /***************??1????***********/
  147. void tmr1_isr(void) interrupt TF1_VECTOR
  148. {
  149.         //IO_FLP(P1, 1<<2);                                                //for testing only
  150.         TH1+=MSB(-_tmr1_period);                                //load up the offset
  151.         TL1+=LSB(-_tmr1_period);                                //load up the offset
  152.         _7seg_display();                                                //display
  153. }

  154. void mcu_init(void) {
  155. }

  156. void main(void)        //???
  157. {
  158.         //shi=12;
  159.         //fen=12;
  160.         //P0=0xff;
  161.         //disp_on_off=0xfe;        //?????
  162.         //disp=0;
  163.         mcu_init();                                                                //reset the mcu
  164.         _7seg_init();                                                        //reset the 7seg display
  165.         tmr0_init();                                                        //reset tmr0 - period set by TMR0_PERIOD
  166.         tmr1_init(TMR1_PERIOD * (F_CPU / 1000000ul));                        //reset tmr1
  167.         EA=1;                                                                        //enable global interrupt

  168.         while(1)
  169.         {
  170.                 //delay();
  171.        
  172.         }
  173. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2011-7-10 21:16:31 | 显示全部楼层
看资料上1602可以将内部的数据读出,通过IO口送出。


on a hd44780, you can read back the status, not the data.

I usually keep a display buffer on the mcu for that.
回复 支持 反对

使用道具 举报

发表于 2011-7-10 21:27:04 | 显示全部楼层
here is the particular connection for the code I posted earlier.

clock.PNG


the one circled in red is the blinking half-second indicator.

the whole thing took me about 15 minutes to code. most of the time, it involves me taking prior code modules and blending it in to one program.

you should think about the opposite approach: taking this code, break it down to two modules for tmr0 and tmr1, and one module for the 7seg display and your main code would be incredibly simple.
回复 支持 反对

使用道具 举报

发表于 2011-7-11 09:08:28 | 显示全部楼层
回复 108# wey05


    能否大概讲一下 如何在单片机中设置一个显存数组,给个思路?我自己琢磨一下。
回复 支持 反对

使用道具 举报

发表于 2011-7-11 09:13:26 | 显示全部楼层
回复 112# millwood

你在15分钟做成这个程序 我很羡慕 也很佩服。我是初学者。说句实话,有时候键盘都不熟悉。不过我也是将自己做过的东西做成了一个例子。以后使用的时候会先看这个例子。有时候直接将例子改写成.H文件,在以后直接使用。
你说的在MCU上设置一个display,能否具体给一个思路或例子?多谢英语先生!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2025-4-28 04:55

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表