数码之家

 找回密码
 立即注册
搜索
查看: 3047|回复: 21

[C51] 小白自学C51单片机编程,LCD1602数字时钟

[复制链接]
发表于 2024-1-4 09:24:23 | 显示全部楼层 |阅读模式
在昨天的基础上改成了LCD显示时间,
我调了一下keil字体,现在不显示中文了,注释我都是复制下来在另一台电脑上写的,




代码如下:
//利用定时器中断产生1秒定时,通过LCD1602显示出来
#include<reg52.h>    //头文件
typedef unsigned char u8;    //类型定义
typedef unsigned int u16;    //类型定义
sbit lcd_rs=P0^4;    //位定义数字或命令选择
sbit lcd_rw=P0^3;    //位定义,读写选择
sbit lcd_en=P0^2;    //位定义使能
sbit key3=P3^6;    //位定义一个按键加小时
sbit key4=P3^7;    //位定义一个按键加分钟
u8 shi=8;    //定义时,赋初值
u8 fen=45;    //定义分,赋初值
u8 miao=0;    //定义秒,赋初值
u16 dingshi=0;    //定义定时器中断次数,20次为一秒
u8 code table1[]={"DIGIT CLOCK "};    //要显示的字符
u8 code table[]={"0123456789"};    //要显示的数字

void delayms(u16 x);    //声明子函数延时
void lcd1602_write_cmd(u8 cmd);    //声明子函数写命令
void lcd1602_write_dat(u8 dat);    //声明子函数写数据
void lcd1602_init();    //声明子函数LCD1602初始化
void key_scan();    //声明子函数按键
void time0init();    //声明子函数定时器初始化
void jishi();    //声明子函数计时

void main()
{
    u8 i;    //声明一个变量,用于显示第一行字
     time0init();    //调用子函数定时器初始化
    lcd1602_init();    //调用子函数LCD1602初始化

    lcd1602_write_cmd(0x80);    //调用子函数写命令,第一行显示
    for(i=0;i<11;i++)    //要显示前11个字
    lcd1602_write_dat(table1);    //调用写数据函数,把这11个字写入
    delayms(5);    //
   
    lcd1602_write_cmd(0xc0+0x02);    //调用子函数写命令,第二行加2位
    lcd1602_write_dat(‘:’);    //调用写数据函数,写2个点
    delayms(5);   

    lcd1602_write_cmd(0xc0+0x05);    //调用子函数写命令,第二行加5位
    lcd1602_write_dat(‘:’);    //调用写数据函数,写2个点
    delayms(5);

    while(1)
    {
    lcd1602_write_cmd(0xc0);    //写命令,第二行开始
    lcd1602_write_dat(table[shi/10%10]);    //写数据,时十位
    delayms(5);    //

    lcd1602_write_cmd(0xc0+0x01);    //写命令,第二行加1位开始
    lcd1602_write_dat(table[shi%10]);    //写数据,时个位
    delayms(5);    //

    lcd1602_write_cmd(0xc0+0x03);    //写命令,第二行加3位开始
    lcd1602_write_dat(table[fen/10%10]);    //写数据,分十位
    delayms(5);    //

    lcd1602_write_cmd(0xc0+0x04);    //写命令,第二行加4位开始
    lcd1602_write_dat(table[fen%10]);    //写数据,分个位
    delayms(5);    //

    lcd1602_write_cmd(0xc0+0x06);    //写命令,第二行加6位开始
    lcd1602_write_dat(table[miao/10%10]);    //写数据,秒十位
    delayms(5);    //

    lcd1602_write_cmd(0xc0+0x07);    //写命令,第二行加7位开始
    lcd1602_write_dat(table[miao%10]);    //写数据,秒个位
    delayms(5);    //

    key_scan();    //调用键盘函数
    }
}

void delayms(u16 x)    //延时函数
{
     u8 i;    //
    while(x--)    //
    {for(i=0;i<113;i++);}
}

void lcd1602_write_cmd(u8 cmd)    //写命令
{
     lcd_rw=0;    //0写1读
    lcd_rs=0;    //0写命令1写数据
    P2=cmd;    //把命令送到P2口
    delayms(5);    //
    lcd_en=1;    //使能
    delayms(5);    //
    lcd_en=0;    //关使能
}

void lcd1602_write_dat(u8 dat)    //写数据
{
     lcd_rw=0;    //0写1读
    lcd_rs=1;    //1写数据0写命令
    P2=dat;    //把数据送到P2口
    delayms(5);    //
    lcd_en=1;    //使能
    delayms(5);    //
    lcd_en=0;    //关使能
}

void lcd1602_init()
{
     lcd1602_write_cmd(0x38);    //00111000显示2行5*7点阵8位数据
    lcd1602_write_cmd(0x0c);    //00001100开显示,光标关,无闪烁
    lcd1602_write_cmd(0x06);    //00000110写入一个字符后,光标右移,整屏不移
    lcd1602_write_cmd(0x01);    //00000001清屏
}

void key_scan()    //
{
     if(key3==0)     //
    {
         delayms(5);     //
        if(key3==0)      //
        {
             shi+=1;//
            if(shi>=24)    //
            {shi=shi-24;}    //
            while(key3==0);    //
        }
    }
    if(key4==0)     //
    {
         delayms(5);     //
        if(key4==0)      //
        {
             fen+=1;//
            if(fen>=60)    //
            {fen=fen-60;}    //
            while(key4==0);    //
        }
    }
}

void time0init()
{
     EA=1;    //
    ET0=1;    //
    TR0=1;    //
    TMOD=0x01;    //
    TH0=(65536-46080)/256;    //50000/1.085 11.0592M晶振
    TL0=(65536-46080)%256;    //
}

void time0()interrupt 1
{
    TH0=(65536-46080)/256;    //
    TL0=(65536-46080)%256;    //
    if(dingshi==20)        //中断20次为1秒
    {
         dingshi=0;
        jishi();    //调用计时函数
    }
        dingshi++;   
}

void jishi ()
{
     miao++;    //
    if(miao==60)
    {
         miao=0;
        fen++;    //
        if(fen==60)
        {
             fen=0;    //
            shi++;    //
            if(shi==24)
            {shi=0;}
        }
    }
}





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

打赏

参与人数 1家元 +6 收起 理由
nb267213667 + 6 認真發帖

查看全部打赏

 楼主| 发表于 2024-1-4 09:24:58 | 显示全部楼层
上传一个小视频


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

发表于 2024-1-4 09:38:42 | 显示全部楼层
不错不错,继续努力
回复 支持 反对

使用道具 举报

发表于 2024-1-4 09:57:24 | 显示全部楼层
太复杂,谢谢分享。
回复 支持 反对

使用道具 举报

发表于 2024-1-4 10:03:22 | 显示全部楼层
这个有时间玩玩可以
回复 支持 反对

使用道具 举报

发表于 2024-1-4 10:15:29 | 显示全部楼层
恭喜越来越顺手
回复 支持 反对

使用道具 举报

发表于 2024-1-5 08:51:32 | 显示全部楼层
复制的使用 把C文件用 记事本打开啊 如果复制进去就不乱码了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-5 14:59:00 | 显示全部楼层
太难了,今天看一个例程,看不懂,请高手指点一二,谢谢!
IC : TM52F1376
**函数名称: led_init
** 函数描述: 将所有IO配置成推挽模式   

void led_init()
{
    PORTIDX = PORT1;
    PINMOD32 = (PINMOD32 & ~0xf0) | (PIN_H_MODE_PP);                        //P13
    PORTIDX = PORT1;
    PINMOD54 = (PINMOD54 & ~0xf0) | (PIN_H_MODE_PP);                        //P15
    PORTIDX = PORT1;
    PINMOD76 = (PINMOD76 & ~0x0f) | (PIN_L_MODE_PP);                        //p16
    PORTIDX = PORT3;
    PINMOD32 = (PINMOD32 & ~0x0f) | (PIN_L_MODE_PP);                        //p32
    P1_3 = 1;
    P1_5 = 1;   
    P1_6 = 1;
    P3_2 = 1;      
}

前面2个端口的配置是0xf0 PIN_H 后面2个端口的配置是0x0f PIN_L 为什么不是一样呢?

手册中是这样介绍的
7. I/O 端口
该芯片总共有 30 多功能 I/O 引脚。所有的 I/O 引脚遵循标准 8051“读-修改-写”功能。读取 SFR 的,
而不是引脚状态的指令,会读取一个端口或端口位的值,可能改变它,然后将它改写到 SFR。(例
如: ANL P1,A; INC P2; CPL P3.0)。
在进入中断程序时,除了传统已熟知应执行 PUSH,POP 的 SFR A 或 PSW 外,有一些用来索引
的 SFR 也应该加入 PUSH POP 的行列,如 PORTIDX,以避免中断前后写读这些 SFR 可能造成
不一致的问题。
7.1 端口 0~端口 3
IO 引脚可以按以下不同方式定义。



如果一个端口 0~端口 3 引脚用于施密特触发输入,S/W 必须设置 I/O 引脚到模式 0,模式 1,模
式 4,模式 5,模式 8,模式 9,模式 12 或模式 13 (开漏,带上拉的开漏或带下拉的开漏),并设
置相应的端口数据 SFR 为 1 来禁止该引脚的输出驱动电路。
除了 I/O 端口功能外,每个端口 1~端口 3 引脚还具有一个或多个替代功能,例如 LED,ADC 和
LCD。通过将单独的引脚模式控制 SFR 设置为模式 3,模式 7,模式 11 或模式 15,可以激活大多
数功能。端口 1 和端口 3 引脚具有标准的 8051 辅助定义,例如 INT0/INT1,T0/T1/T2 或 RXD/TXD。
这些引脚功能需要将引脚模式 SFR 设置为模式 0,模式 1,模式 5,模式 8,模式 12 或模式 13 (开
漏或带上拉的开漏),并将 P1.n/P3.n SFR 保持为 1。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

发表于 2024-1-5 15:40:46 | 显示全部楼层
hellozwt 发表于 2024-1-5 14:59
太难了,今天看一个例程,看不懂,请高手指点一二,谢谢!
IC : TM52F1376
**函数名称: led_init

我之前发过一个STM32的视频教程的帖子,你的问题看完第四节和第五节估计就能明白了,这个课程非常好,即便你学的是51,前面的几节基础课也建议你看一下,对51也是很有帮助的:
https://www.mydigit.cn/thread-429923-1-1.html
回复 支持 反对

使用道具 举报

发表于 2024-1-5 15:58:34 | 显示全部楼层
猪小呆 发表于 2024-1-5 15:40
我之前发过一个STM32的视频教程的帖子,你的问题看完第四节和第五节估计就能明白了,这个课程非常好,即便 ...

第二课位运算配置寄存器的原理最好也先看一下,它可以帮你自己分析出来程序里的0xf0 和0x0f最终影响的是寄存器里的哪些位,再结合规格书看具体起什么作用
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-5 16:09:04 | 显示全部楼层
猪小呆 发表于 2024-1-5 15:58
第二课位运算配置寄存器的原理最好也先看一下,它可以帮你自己分析出来程序里的0xf0 和0x0f最终影响的是 ...

谢谢,有空去看看
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-5 16:14:41 | 显示全部楼层
又修改了一下,增加了星期和天的自动累加,现在还不知道怎样处理大月和小月的天数,可以把年月日星期时间都做出来就好了,就是一个简单的日历时钟了



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-5 16:35:48 | 显示全部楼层
这个逻辑行得通吗?
day++;
if(day==28)
{
  if(月==2)
  day=1;
}

if(day==30)
{
  if(月==4或6或9或11)
  day=1;
}

if(day==31)
{
  day=1;
}
回复 支持 反对

使用道具 举报

发表于 2024-1-5 18:45:50 来自手机浏览器 | 显示全部楼层
改一下编码格式就可以了
回复 支持 反对

使用道具 举报

发表于 2024-1-6 08:01:36 来自手机浏览器 | 显示全部楼层
点亮了不错
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-6 15:06:42 | 显示全部楼层
刚试了一下,这个逻辑是对的,只是天数都要加1,比如2024年2月有29天,程序要写if(day==30)如果不加1的话,显示完28就到2月1了,我调小了定时,实际观察发现的,
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-6 15:07:43 | 显示全部楼层
计时部分的代码如下:


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

发表于 2024-1-6 17:24:47 来自手机浏览器 | 显示全部楼层
hellozwt 发表于 2024-1-6 15:07
计时部分的代码如下:

这个逻辑不是常人使用的逻辑,一般判断大小月,2月还要判断平年闰年,得到当月天数,日计数大于当前月天数,月计数加1,日计数循环到1,月大于12,年加1月循环到1。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-6 22:42:39 | 显示全部楼层
所以这是一个只管一年的电子时钟,如果要管多年的话,也可以把闰年列出来,做一个判断,
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-1-6 22:43:10 | 显示全部楼层
所以这是一个只管一年的电子时钟,如果要管多年的话,也可以把闰年列出来,做一个判断,
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-11 01:43 , Processed in 0.483601 second(s), 12 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

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