打印
[51单片机]

按照周立功C51的例子,写了LCD1602显示时间的程序,在proteus...

[复制链接]
1857|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
any012|  楼主 | 2015-7-26 12:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
沙发
any012|  楼主 | 2015-7-26 12:51 | 只看该作者
#include <reg51.h>
#include <stdio.h>
#include "MacroAndConst.h"

//¶¨Ò嶨ʱÆ÷0µÄÖØ×°Öµ
#define RELOAD_HIGH 0x3C
#define RELOAD_LOW  0xD2

//¶¨Òå°´¼üµ¯Ìøʱ¼ä
#define DB_VAL        10

//¶¨ÒåÉèÖÃģʽµÄ×î´óʱ¼ä¼ä¸ô
#define TIMEOUT 200

//¶¨Òå¹â±êλÖó£Êý
#define HOME 0
#define HOUR 1
#define MIN  2
#define SEC  3

//¶¨Òå¹â±ê״̬³£Êý
#define OFF 0
#define ON  1

//¶¨ÒåÏÔʾÃüÁî³£Êý
#define DISP_BUSY   0x80
#define DISP_FUNC   0x38
#define DISP_ENTRY  0x06
#define DISP_CNTL   0x080
#define DISP_ON     0x04
#define DISP_CURSOR 0x02
#define DISP_CLEAR  0x01
#define DISP_HOME   0x02
#define DISP_POS    0x80
#define DISP_LINE2  0x40

sbit SET         = P3^4;                //ÉèÖð´¼üÊäÈë
sbit SELECT = P3^5;                //Ñ¡Ôñ°´¼üÊäÈë
sbit ENABLE = P3^1;                //ÏÔʾʹÄÜÊä³ö
sbit REGSEL = P3^7;                //ÏÔʾ¼Ä´æÆ÷Ñ¡ÔñÊä³ö
sbit RDWR   = P3^6;                //ÏÔʾģʽÊä³ö

sfr DISPDATA = 0x90;                //ÏÔʾ8λÊý¾Ý×ÜÏß

typedef struct                        //¶¨Òå´æ´¢Ã¿ÈÕʱ¼äµÄ½á¹¹
{
        uchar hour, min, sec;
}timestruct;

bit set_mode         = 0;                //½øÈëÉèÖÃģʽʱÖÃλ
bit disp_updata = 0;        //ÐèҪˢÐÂÏÔʾʱÖÃλ

uchar set_mode_to           = 0;        //Ϊÿ´Î°´¼ü²Ù×÷µÄʱ¼ä¼ä¸ô¼Æʱ
uchar switch_debounce = 0;        //°´¼üÌø¶¯Éè¼Æ
uchar cur_field           = HOME;        //ÉèÖÃģʽµÄµ±Ç°Î»ÖÃÑ¡Ôñ

timestruct curtime;                //´æ·Åµ±Ç°µÄʱ¼ä
timestruct timeholder;        //´æ·ÅÏÔʾʱ¼ä

uchar code fieldpos[3] = {
                                                        DISP_LINE2 | 0x01,
                                                        DISP_LINE2 | 0x04,
                                                        DISP_LINE2 | 0x07,
};

#define T_HOURT        16
#define T_HOUR  17
#define T_MINT         19
#define T_MIN        20
#define T_SECT  22
#define T_SEC   23

char code bcdmap[60][2] = {
        "00","01","02","03","04","05","06","07","08","09",
        "10","11","12","13","14","15","16","17","18","19",
        "20","21","22","23","24","25","26","27","28","29",
        "30","31","32","33","34","35","36","37","38","39",
        "40","41","42","43","44","45","46","47","48","49",
        "50","51","52","53","54","55","56","57","58","59",
};

//º¯ÊýÉùÃ÷
void disp_cmd(uchar);
void disp_init(void);
uchar disp_read(void);
void disp_time(void);
void disp_write(uchar);
void incr_field(void);
void second_tick(void);
void set_cursor(bit, uchar);

/********************************/
//¹¦ÄÜ£ºÖ÷º¯Êý
//ÃèÊö£º³ÌÐòÈë¿Úº¯Êý£¬³õʼ»¯8051£¬¿ªÖжϣ¬½øÈë¿ÕÏÐģʽ¡£Ã¿´ÎÖжÏÖ®ºó²éѯ±ê־룬ÊÇ·ñË¢ÐÂÏÔʾ
//²ÎÊý£ºÎÞ
//·µ»Ø£ºÎÞ
/********************************/

void main(void)
{
        disp_init();                        //ÏÔʾ³õʼ»¯
        TMOD = 0x11;                        //ÉèÖö¨Ê±Æ÷ģʽ£¬T0,T1 16λ²»¿ÉÖØװģʽ
        TCON = 0x15;
        IE          = 0x82;                        //ÔÊÐíT0ÖжÏ
        while(1)
        {
                if(disp_updata)
                {
                        disp_time();        //ÏÔʾÐÂʱ¼ä
                }
                PCON = 0x01;
        }
}

/********************************/
//¹¦ÄÜ£ºdisp_cmd
//ÃèÊö£ºÏòlcdÇý¶¯Æ÷дÈëÃüÁ²¢µÈ´ýÃüÁî±»Ö´ÐÐ
//²ÎÊý£ºÃüÁî
//·µ»Ø£ºÎÞ
/********************************/
void disp_cmd(uchar cmd)
{
        DISPDATA = cmd;                        //ËøסÃüÁî
        REGSEL = 0;                                //Ñ¡ÔñÃüÁî¼Ä´æÆ÷
        RDWR = 0;                                //Ñ¡Ôñдģʽ
        ENABLE = 1;
        ENABLE = 0;
        TH1 = 0;                                //¶¨Ê±85ms
        TL1 = 0;
        TF1 = 0;
        TR1 = 1;
        while(!TF1 && disp_read() & DISP_BUSY);        //µÈ´ýÃüÁî±»Ö´ÐÐ
        TR1 = 0;
}

/********************************/
//¹¦ÄÜ£ºdisp_init
//ÃèÊö£º³õʼ»¯ÏÔʾ
//²ÎÊý£ºÎÞ
//·µ»Ø£ºÎÞ
/********************************/
void disp_init(void)
{
        TH1 = 0;
        TL1 = 0;
        TF1 = 0;
        TR1 = 1;
        while(!TF1 && disp_read() & DISP_BUSY);
        TR1 = 0;
        disp_cmd(DISP_FUNC);                        //ÉèÖÃÏÔʾ¸ñʽ
        disp_cmd(DISP_ENTRY);                        //ÿÊäÈëÒ»¸ö×Ö·û£¬ÏÔʾµØÖ·¼Ó1
        disp_cmd(DISP_CNTL | DISP_ON);        //´ò¿ªÏÔʾ£¬¹Ø±Õ¹â±ê
        disp_cmd(DISP_CLEAR);                        //Çå³ýÏÔʾ
}

/********************************/
//¹¦ÄÜ£ºdisp_read
//ÃèÊö£º¶ÁÏÔʾ״̬¼Ä´æÆ÷
//²ÎÊý£ºÎÞ
//·µ»Ø£º´Ó״̬¼Ä´æÆ÷ÖжÁ»ØµÄÊý¾Ý
/********************************/
uchar disp_read(void)
{
        uchar value;
        DISPDATA = 0xFF;
        REGSEL = 0;                                //Ñ¡ÔñÃüÁî¼Ä´æÆ÷
        RDWR = 1;                                //Ñ¡Ôñ¶Áģʽ
        ENABLE = 1;                                //ʹÄÜLCDÊä³ö
        value = DISPDATA;                //¶ÁÊý¾Ý
        ENABLE = 0;
        return (value);
}

/********************************/
//¹¦ÄÜ£ºdisp_time
//ÃèÊö£ºÈ¡ÏÔʾÊý¾Ý½øÐиñʽ»¯
//²ÎÊý£ºÎÞ
//·µ»Ø£ºÎÞ
/********************************/
void disp_time(void)
{
        static char time_str[32] = "TIME OF DAY IS:XX:XX:XX";
        uchar i;
        time_str[T_HOURT] = bcdmap[timeholder.hour][0];
        time_str[T_HOUR]  = bcdmap[timeholder.hour][1];
        time_str[T_MINT]  = bcdmap[timeholder.min][0];
        time_str[T_MIN]   = bcdmap[timeholder.min][1];
        time_str[T_SECT]  = bcdmap[timeholder.sec][0];
        time_str[T_SEC]   = bcdmap[timeholder.sec][1];
        putchar(0xFF);
        for(i = 0; i < 32; i++)
        {
                putchar(time_str[i]);
        }
        disp_updata = 0;
}

/********************************/
//¹¦ÄÜ£ºdisp_write
//ÃèÊö£ºÐ´ÈëÒ»¸ö×Ö½ÚÊý¾Ý
//²ÎÊý£ºÒªÐ´ÈëµÄ×Ö½Ú
//·µ»Ø£ºÎÞ
/********************************/
void disp_write(uchar value)
{
        DISPDATA = value;
        REGSEL = 1;
        RDWR = 0;
        ENABLE = 1;
        ENABLE = 0;
}

/********************************/
//¹¦ÄÜ£ºincr_field
//ÃèÊö£ºÔö¼ÓÊýÖµ
//²ÎÊý£ºÎÞ
//·µ»Ø£ºÎÞ
/********************************/
void incr_field(void)
{
        if(cur_field == SEC)
        {
                curtime.sec++;
                if(curtime.sec > 59)
                        curtime.sec = 0;
        }
        if(cur_field == MIN)
        {
                curtime.min++;
                if(curtime.min > 59)
                        curtime.min = 0;
        }
        if(cur_field == HOUR)
        {
                curtime.hour++;
                if(curtime.hour > 23)
                        curtime.hour = 0;
        }
}

/********************************/
//¹¦ÄÜ£ºputchar
//ÃèÊö£ºÌæ´ú±ê×¼putcharº¯Êý£¬Êä³ö×Ö·û
//²ÎÊý£ºÒªÏÔʾµÄ×Ö·û
//·µ»Ø£º¸Õ¸Õ±»Ð´µÄ×Ö·û
/********************************/
char putchar(char c)
{
        static uchar flag = 0;
        if(!flag || c == 255)
        {
                disp_cmd(DISP_HOME);
                flag = 0;
                if(c == 255)
                        return c;
        }
        if(flag == 16)
                disp_cmd(DISP_POS | DISP_LINE2);
        disp_write(c);
        while(disp_read() & DISP_BUSY);
        flag++;
        if(flag >= 32)
                flag = 0;
        return c;
}

/********************************/
//¹¦ÄÜ£ºsecond_tick
//ÃèÊö£ºÃ¿ÃëÖÓÖ´ÐÐÒ»´Îº¯Êý¹¦ÄÜ£¬Ê±¼ä¸üÐÂ
//²ÎÊý£ºÎÞ
//·µ»Ø£ºÎÞ
/********************************/
void second_tick(void)
{
        curtime.sec++;                                        //ÃëÖÓ¼Ó1
        if(curtime.sec > 59)                        //¼ì²âÊÇ·ñ³¬³ö·¶Î§
        {
                curtime.sec = 0;
                curtime.min++;                                //·ÖÖÓ¼Ó1
                if(curtime.min > 59)                //¼ì²âÊÇ·ñ³¬³ö·¶Î§
                {
                        curtime.min = 0;       
                        curtime.hour++;                        //СʱÊý¼Ó1
                        if(curtime.hour > 23)        //¼ì²âÊÇ·ñ³¬³ö·¶Î§
                                curtime.hour = 0;
                }
        }
        if(!disp_updata)                                //È·ÐÅtimeholderûÓб»ÏÔʾ
        {
                timeholder = curtime;                //×°ÈëÐÂʱ¼ä
                disp_updata = 1;                        //¸üÐÂÏÔʾ
        }
}

/********************************/
//¹¦ÄÜ£ºset_cursor
//ÃèÊö£ºÏÔʾ»ò¹Ø±Õ¹â±ê£¬²¢°Ñ¹â±êÒƵ½Ìض¨µÄλÖÃ
//²ÎÊý£ºnew_mode 룬Òþ²Ø¹â±êʱÖÃλ
//                field          ÏÔʾ¹â±êµÄλÖÃ
//·µ»Ø£ºÎÞ
/********************************/
void set_cursor(bit new_mode, uchar field)
{
        uchar mask;
        mask = DISP_CNTL | DISP_ON;
        if(new_mode)
                mask |= DISP_CURSOR;
        disp_cmd(mask);
        if(field == HOME)
                mask = DISP_HOME;
        else
                mask = DISP_POS | fieldpos[field - 1];
        disp_cmd(mask);
}

/********************************/
//¹¦ÄÜ£ºsystem_tick
//ÃèÊö£º¶¨Ê±Æ÷0µÄÖжϷþÎñ³ÌÐò£¬ÃÃ50msÖØ×°Ò»´Î¶¨Ê±Æ÷
//²ÎÊý£ºÎÞ
//·µ»Ø£ºÎÞ
/********************************/
void system_tick(void) interrupt 1
{
        static uchar second_cnt = 20;
        TR0 = 0;
        TH0 = RELOAD_HIGH;                        //É趨ÖØ×°Öµ
        TL0 = RELOAD_LOW;
        TR0 = 1;                                        //¿ªÊ¼¶¨Ê±
        if(switch_debounce)                        //°´¼ü¶¶¶¯
                switch_debounce--;
        if(!switch_debounce)
        {                                       
                if(!SET)                                                        //Èç¹ûÉèÖð´¼ü±»°´ÏÂ
                {
                        switch_debounce = DB_VAL;                //ÉèÖÃÏû¶¶Ê±¼ä
                        if(!set_mode && !disp_updata)        //Èç¹û²»ÊÇÉèÖÃģʽ
                        {
                                set_mode = 1;                                //½øÈëÉèÖÃģʽ
                                set_mode_to = TIMEOUT;                //ÉèÖÿÕÏÐʱ¼ä
                                cur_field = HOUR;                        //Ñ¡Ôñ¹â±êÆðʼλÖÃ
                                set_cursor(ON, HOUR);                //ʹÄܹâ±ê
                        }
                        else
                        {
                                cur_field++;                                //¹â±êλÖÃÇ°½ø
                                if(cur_field > SEC)                        //¹â±êÊÇ·ñ³¬³ö·¶Î§
                                {
                                        set_mode = 0;                        //Í˳öÉèÖÃģʽ
                                        set_mode_to = 0;
                                        set_cursor(OFF, HOME);        //½ûÄܹâ±ê
                                }
                                else
                                {
                                        set_cursor(ON, cur_field);        //¹â±êÒƵ½ÏÂÒ»¸öλÖÃ
                                        set_mode_to = TIMEOUT;
                                }
                        }
                }
                if(set_mode && !SELECT)                                //Èç¹û°´ÏÂÑ¡Ôñ°´Å¥
                {
                        set_mode_to = TIMEOUT;
                        incr_field();                                        //ËùÑ¡Ôñ´¦ÊýÖµÔö¼Ó
                        disp_time();                                        //ÏÔʾʱ¼ä
                }
        }
        if(!set_mode)                        //ÉèÖÃģʽֹͣʱÖÓ
        {
                second_cnt--;                //¼ÆÊýÖµ¼õ1
                if(!second_cnt)                //Èç¹û¾­¹ý1Ãë
                {
                        second_cnt = 20;//ÉèÖüÆÊýÖµ
                        second_tick();
                }
        }
}

使用特权

评论回复
板凳
any012|  楼主 | 2015-7-26 13:13 | 只看该作者
试着修改了下,T0中断累计到1s时,使P2输出口自加一,protues仿真正确。
那可能是Lcd驱动程序的问题了。

使用特权

评论回复
地板
any012|  楼主 | 2015-7-26 13:49 | 只看该作者
本帖最后由 any012 于 2015-7-26 13:50 编辑

找到问题了:
#define DISP_CNTL   0x08
写成了#define DISP_CNTL   0x080
导致没有打开显示.
------------------------------------------------
不过还有其他问题,估计也是输入错误引起的。
本来应该是两行显示,现在是滚动显示。

使用特权

评论回复
5
any012|  楼主 | 2015-7-28 19:55 | 只看该作者
现在显示总是向后滚动,修改了下void disp_time(void)函数的条件循环,
for(i = 0; i < 32; i++)
        {
                putchar(time_str[i]);
        }
改成for(i = 0; i<31;i++)
不滚动了,但是首字符是个黑块。

使用特权

评论回复
6
whirt_noob| | 2015-7-29 13:20 | 只看该作者
注释是哪国语言?

使用特权

评论回复
评分
参与人数 1威望 +2 收起 理由
any012 + 2 淡定
7
any012|  楼主 | 2015-7-29 15:48 | 只看该作者
whirt_noob 发表于 2015-7-29 13:20
注释是哪国语言?

编码不对,赋值过来就成这样子了。
注释内容和附件里的PDF注释一致。

使用特权

评论回复
8
any012|  楼主 | 2015-8-14 11:09 | 只看该作者
whirt_noob 发表于 2015-7-29 13:20
注释是哪国语言?

简体中文...
不过我每次复制程序过来,注释都会变成乱码。
不知道别人是怎么处理的。

使用特权

评论回复
9
qq4988| | 2015-8-14 13:16 | 只看该作者
这个例子,网上现成好好多,好多,随便找一个就可显示了

使用特权

评论回复
评分
参与人数 1威望 +2 收起 理由
any012 + 2 淡定
10
any012|  楼主 | 2015-8-14 14:45 | 只看该作者
qq4988 发表于 2015-8-14 13:16
这个例子,网上现成好好多,好多,随便找一个就可显示了

不是为了做这个功能,而是想学里面教的编程方法进而试着把例程打了一遍。

使用特权

评论回复
评论
qq4988 2015-8-14 14:49 回复TA
先找一个可以显示的例程,然后再去学习里面的东西 
11
any012|  楼主 | 2015-8-14 14:59 | 只看该作者
any012 发表于 2015-8-14 14:45
不是为了做这个功能,而是想学里面教的编程方法进而试着把例程打了一遍。 ...

已经可以显示了。
之前不能显示是有个命令,是控制显示开关的,应该给LCD0602命令字0x08,结果我输入成了0x080。当然,显示就没有被打开。

现在的问题都是,字符是滚动的...
正在研究为什么会这样。

使用特权

评论回复
12
any012|  楼主 | 2015-8-14 16:17 | 只看该作者
void disp_time(void)
{
        static char time_str[32] = "TIME OF DAY IS: XX:XX:XX        ";
        uchar i;
        time_str[T_HOURT] = bcdmap[timeholder.hour][0];
        time_str[T_HOUR]  = bcdmap[timeholder.hour][1];
        time_str[T_MINT]  = bcdmap[timeholder.min][0];
        time_str[T_MIN]   = bcdmap[timeholder.min][1];
        time_str[T_SECT]  = bcdmap[timeholder.sec][0];
        time_str[T_SEC]   = bcdmap[timeholder.sec][1];
        putchar(0xFF);
        for(i = 0; i < 31; i++)
        {
                putchar(time_str[i]);
        }
        disp_updata = 0;
}
time_str[32]是定义的一个32个字节的数组,对应屏上的32个字符。(最后有几个空字符,不知道为什么合并到一块了)
T_HOURT被宏定义为16,按PDF上打的字符算,其实对应的应该是15,后来我在IS:后多加了个空格。

putchar(0xFF);是将flag标志位清零,并且指向屏的第一个字符。

for(i = 0; i < 31; i++)
    putchar(time_str);

这个循环是输出屏上的32个字符,不知道为什么设成32的话,字就循环向后移位。


感觉前边多了个黑字符...

使用特权

评论回复
13
any012|  楼主 | 2015-8-14 17:01 | 只看该作者
本帖最后由 any012 于 2015-8-14 17:06 编辑

time_str[T_HOURT] = bcdmap[timeholder.hour][0];
#define T_HOURT        16
T_HOURT定义为16,time_str[T_HOURT]应该对应的是第二行第一个字符。结果却在第二行第二个字符显示...


使用特权

评论回复
14
any012|  楼主 | 2015-8-15 09:47 | 只看该作者
本帖最后由 any012 于 2015-8-15 09:48 编辑

找到问题了。
putchar(0xFF);
for(i = 0; i < 32; i++)
{
        putchar(time_str);
}
在发送需要显示的字符之前,调用了这么个函数putchar(0xFF),在这个函数里,判断如果传入的参数是256的话,就光标回原位。
问题出在putchar()函数的参数是声明为char类型的,改成unsigned char就好了。

使用特权

评论回复
15
any012|  楼主 | 2015-8-15 11:09 | 只看该作者
发现按完设置键后,再按选择键,对应的时间应该增加,但没有显示出来,设置完成后才可看到增加了。
所以修改了下:
if(set_mode && !SELECT)                                //Èç¹û°´ÏÂÑ¡Ôñ°´Å¥
                {
                        set_mode_to = TIMEOUT;
                        incr_field();                                       
                        timeholder = curtime;                                //此处是为了显示出来我后加上去的
                        disp_time();                                       
                        set_cursor(ON, cur_field);                   //此处是为了显示光标我后加上去的
                }
并且,原来按了选择按键后,光标马上就关闭了,看上去仿佛是退出了设置模式,其实没有。我在上边函数里最后加上了显示光标。

使用特权

评论回复
16
any012|  楼主 | 2015-8-15 11:16 | 只看该作者
本帖最后由 any012 于 2015-8-18 16:38 编辑

还有些其他问题。
SET按键设置了消抖(其实好象是防止按下不放误判成多次),但SELECT按键没有设置,并且SELECT也不适合设置成像SET那样消抖500ms,那样太久了。如果单独给他设置个消抖变量,感觉程序又不那么简练了。
现在退出设置模式,必须是按三次SET按键,才能推出。程序里其实定义了个变量set_mode_to,好象是用来作为在设置模式下长时间无按键动作时的超时判断用的,不过原例子里没有做处理。
LCD1602.zip (57.94 KB)


proteus.zip (91.91 KB)

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

232

帖子

6

粉丝