当前位置: 首页 > news >正文

(51单片机)LCD显示日期时间时钟(DS1302时钟模块教学)(LCD1602教程)

目录

 源代码

 main.c

LCD1602.c

LCD1602.h

DS1302.c

DS1302.h

 代码解析与教程:

 LCD1602模块

 DS1302模块

效果视频: 

 源代码

如上图将5个文放在Keli5 中即可,然后烧录在单片机中就行了

烧录软件用的是STC-ISP,不知道怎么安装的可以去看江科大的视频:

【51单片机入门教程-2020版 程序全程纯手打 从零开始入门】https://www.bilibili.com/video/BV1Mb411e7re?p=2&vd_source=ada7b122ae16cc583b4add52ad89fd5e

源代码:

头文件要记得宏定义和重定义,避免重复调用:

#ifndef _Timer0_h_//名字根据文件名定义即可
#define _Timer0_h_//声明函数……#endif

 main.c

#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "DS1302.h"//主程序
void main(){LCD_Init();//LCD1602初始化DS1302_Init();//DS1302初始化LCD_ShowString(1,1,"  -  -  ");//LCD显示年月日间隔符号LCD_ShowString(2,1,"  :  :  ");//LCD显示时分秒间隔符号DS1302_SetTime();//初始化设定日期时间while(1){DS1302_ReadTime();//改变定义并读取日期时间LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒}
}

LCD1602.c

#include <STC89C5xRC.H>//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0//函数定义:
/*** @brief  LCD1602延时函数,12MHz调用可延时1ms* @param  无* @retval 无*/
void LCD_Delay()		//@11.0592MHz
{unsigned char i, j;i = 11;j = 190;do{while (--j);} while (--i);
}/*** @brief  LCD1602写命令* @param  Command 要写入的命令* @retval 无*/
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602写数据* @param  Data 要写入的数据* @retval 无*/
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602设置光标位置* @param  Line 行位置,范围:1~2* @param  Column 列位置,范围:1~16* @retval 无*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}
}/*** @brief  LCD1602初始化函数* @param  无* @retval 无*/
void LCD_Init()
{LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动LCD_WriteCommand(0x01);//光标复位,清屏
}/*** @brief  在LCD1602指定位置上显示一个字符* @param  Line 行位置,范围:1~2* @param  Column 列位置,范围:1~16* @param  Char 要显示的字符* @retval 无*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}/*** @brief  在LCD1602指定位置开始显示所给字符串* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  String 要显示的字符串* @retval 无*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}
}/*** @brief  返回值=X的Y次方*/
int LCD_Pow(int X,int Y)
{unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;
}/*** @brief  在LCD1602指定位置开始显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~65535* @param  Length 要显示数字的长度,范围:1~5* @retval 无*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置开始以有符号十进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:-32768~32767* @param  Length 要显示数字的长度,范围:1~5* @retval 无*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置开始以十六进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~0xFFFF* @param  Length 要显示数字的长度,范围:1~4* @retval 无*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}}
}/*** @brief  在LCD1602指定位置开始以二进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~1111 1111 1111 1111* @param  Length 要显示数字的长度,范围:1~16* @retval 无*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__//用户调用函数:
void LCD_Init();//初始化
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);//显示单个字符
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);//显示字符串
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示数字
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);//显示带符号数字
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示十进制数字
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示二进制数字#endif

DS1302.c

#include <STC89C5xRC.H>//初始化定义引脚
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;//宏定义日期,和写入保护地址
#define DS1302_S 0x80//秒
#define DS1302_M 0x82//分
#define DS1302_H 0x84//时
#define DS1302_D 0x86//日
#define DS1302_MON 0x88//月
#define DS1302_DAY 0x8A//星期
#define DS1302_Y 0x8C//年
#define DS1302_WP 0x8E//写入保护//初始化定义日期时间数组
//对应年月 日 时 分 秒 星期
unsigned char DS1302_Time[]={25,4,15,20,59,55,2};//十进制转BCD进制函数
unsigned char HEX_BCD(unsigned char n){return n/10*16+n%10;
}
//BCD进制转十进制函数
unsigned char BCD_HEX(unsigned char n){return n/16*10+n%16;
}//DS1302的初始化函数
void DS1302_Init(){DS1302_CE=0;DS1302_SCLK=0;
}//DS1302的写入函数
//第一个参数Command是命令字,第二个参数Data是数据
void DS1302_WriteByte(unsigned char Command,Data){unsigned char i;//循环,移位计数DS1302_CE=1;//改变为高电平,写入for(i=0;i<8;i++){//循环8次,写入寄存器DS1302_IO=Command&(0x01<<i);//低位在前,获取Command的最低位,左移获得当前位DS1302_SCLK=1;//改变高电位,写入寄存器DS1302_SCLK=0;//改变低电位,准备写入寄存器}for(i=0;i<8;i++){//循环8次,写8个数据DS1302_IO=Data&(0x01<<i);//低位在前,获取Command的最低位,左移获得当前位DS1302_SCLK=1;//改变高电位,写入数据DS1302_SCLK=0;//改变低电位,准备写入数据}DS1302_CE=0;//恢复,初始化
}//DS1302的读入函数
//第一个参数Command是命令字
unsigned char DS1302_ReadByte(unsigned char Command){unsigned char i,Data=0x00;//循环,移位计数;数据Command|=0x01;//清空,使最后一位为1;DS1302_CE=1;//改变为高电平,写入for(i=0;i<8;i++){//循环8次,读寄存器DS1302_IO=Command&(0x01<<i);//低位在前,获取Command的最低位,左移获得当前位DS1302_SCLK=0;//改变低电位,准备读寄存器DS1302_SCLK=1;//改变高电位,读寄存器}for(i=0;i<8;i++){DS1302_SCLK=1;//改变高电位,读数据DS1302_SCLK=0;//改变低电位,准备读数据if(DS1302_IO==1){Data=Data|(0x01<<i);}//低位在前,初始化Data的最低位为1,左移初始化当前位为1}DS1302_CE=0;//恢复,初始化DS1302_IO=0;//恢复,初始化return Data;//返回值为当前数据
}//设定日期时间函数
void DS1302_SetTime(){//数据必须为BCD,因此转换DS1302_WriteByte(DS1302_WP,0x00);//关闭写入保护DS1302_WriteByte(DS1302_Y,HEX_BCD(DS1302_Time[0]));//写入年DS1302_WriteByte(DS1302_MON,HEX_BCD(DS1302_Time[1]));//写入月DS1302_WriteByte(DS1302_D,HEX_BCD(DS1302_Time[2]));//写入日DS1302_WriteByte(DS1302_H,HEX_BCD(DS1302_Time[3]));//写入时DS1302_WriteByte(DS1302_M,HEX_BCD(DS1302_Time[4]));//写入分DS1302_WriteByte(DS1302_S,HEX_BCD(DS1302_Time[5]));//写入秒DS1302_WriteByte(DS1302_DAY,HEX_BCD(DS1302_Time[6]));//写入星期DS1302_WriteByte(DS1302_WP,0x80);//打开写入保护
}//改变定义并读取日期时间函数
void DS1302_ReadTime(){//数据必须为十进制,因此转换unsigned char Temp;//定义临时变量Temp=DS1302_ReadByte(DS1302_Y);DS1302_Time[0]=BCD_HEX(Temp);//读年Temp=DS1302_ReadByte(DS1302_MON);DS1302_Time[1]=BCD_HEX(Temp);//读月Temp=DS1302_ReadByte(DS1302_D);DS1302_Time[2]=BCD_HEX(Temp);//读日Temp=DS1302_ReadByte(DS1302_H);DS1302_Time[3]=BCD_HEX(Temp);//读时Temp=DS1302_ReadByte(DS1302_M);DS1302_Time[4]=BCD_HEX(Temp);//读分Temp=DS1302_ReadByte(DS1302_S);DS1302_Time[5]=BCD_HEX(Temp);//读秒Temp=DS1302_ReadByte(DS1302_DAY);DS1302_Time[6]=BCD_HEX(Temp);//读日期
}

DS1302.h

#ifndef __DS1302_H__
#define __DS1302_H__//用户调用函数:
extern unsigned char DS1302_Time[];//声明数组
unsigned char DS1302_ReadByte(unsigned char Command);//声明读数据函数
void DS1302_WriteByte(unsigned char Command,Data);//声明写数据函数
void DS1302_Init();//声明初始化函数
void DS1302_ReadTime();//声明读日期时间函数
void DS1302_SetTime();//声明设置日期时间函数
unsigned char BCD_HEX(unsigned char n);//声明BCD转十进制
unsigned char HEX_BCD(unsigned char n);//声明十进制转BCD
#endif

 代码解析与教程:

 LCD1602模块
  • 包含源代码与头文件,不需要知道怎么实现的会用即可,后续使用,直接将头文件和源代码拿过来用即可;使用格式:(参考江科大的视频素材)

  • LCD1602相关重要知识:
    • LCD1602有两上下两行显示屏,每行各有16个小显示屏,如上图中的LCD_ShowString(1,3,"Hello"),第一个参数是第一行还是第二行,第2个参数是对应第几行的第几个小显示屏,最后一个是输出的东西,同理,到LCD_ShowNum(1,9,123,3)里,前三个和前面一样,最后一个参数是显示的位数,不够就在前面补0,例如输入1,参数为4,显示就是0001,输入23,参数为3,显示就是023

  • 上图是LCD1602的开发原理模块图,由图可知,P10-P17是控制显示屏的重点;像矩阵那样,S1,由P17和P13控制,同理,其他按键一样,由于开发板限制,我们一般使用列遍历来控制按键,例如:S1,S5,S9,S13他们四个共用P13,所以将其为1列,将P13设定为1,P17设定为0,S1就会亮,其他按键同理。
 DS1302模块
  • 包含源代码与头文件,需要知道怎么实现的,会用,后续使用,直接将头文件和源代码拿过来用即可;

  • 先看原理图:

  • 图中可以知道SCLK,IO,CE的地址,因此,在代码中定义地址:
//初始化定义引脚
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

上面是读,下面是写,定义好地址后;如图,只有在CE=1(高电平)时,操作才能实现;SCLK上升就是从0变1,下降就是1变0;IO前半部分就是寄存器(不需要怎么理解),后半部分是数据。

        先看下面的读操作(相对简单):首先CE=1;SCLK从头上升下降到尾,全程写入寄存器和数据;IO的R/W=0,就是写;最后一个固定为1;
        再看上面的读操作(相对简单):首先CE=1;SCLK从头上升下降到中间(读寄存器),然后下降上升开始转换成读数据;IO的R/W=1,就是读;最后一个固定为1;



RTC就是IO总体部分,序号1是Read读的地址,序号2是Write写的地址,第一列是秒,第二列是分,以此类推看图中英文确定作用,WP是写入保护;Address/Command Byte就是IO元素部分,在读写操作时,低位在前。这部分又叫寄存器地址,或者命令符,来看相关代码:
//DS1302的写入函数
//第一个参数Command是命令字,第二个参数Data是数据
void DS1302_WriteByte(unsigned char Command,Data){unsigned char i;//循环,移位计数DS1302_CE=1;//改变为高电平,写入for(i=0;i<8;i++){//循环8次,写入寄存器DS1302_IO=Command&(0x01<<i);//低位在前,获取Command的最低位,左移获得当前位DS1302_SCLK=1;//改变高电位,写入寄存器DS1302_SCLK=0;//改变低电位,准备写入寄存器}for(i=0;i<8;i++){//循环8次,写8个数据DS1302_IO=Data&(0x01<<i);//低位在前,获取Command的最低位,左移获得当前位DS1302_SCLK=1;//改变高电位,写入数据DS1302_SCLK=0;//改变低电位,准备写入数据}DS1302_CE=0;//恢复,初始化
}//DS1302的读入函数
//第一个参数Command是命令字
unsigned char DS1302_ReadByte(unsigned char Command){unsigned char i,Data=0x00;//循环,移位计数;数据Command|=0x01;//清空,使最后一位为1;DS1302_CE=1;//改变为高电平,写入for(i=0;i<8;i++){//循环8次,读寄存器DS1302_IO=Command&(0x01<<i);//低位在前,获取Command的最低位,左移获得当前位DS1302_SCLK=0;//改变低电位,准备读寄存器DS1302_SCLK=1;//改变高电位,读寄存器}for(i=0;i<8;i++){DS1302_SCLK=1;//改变高电位,读数据DS1302_SCLK=0;//改变低电位,准备读数据if(DS1302_IO==1){Data=Data|(0x01<<i);}//低位在前,初始化Data的最低位为1,左移初始化当前位为1}DS1302_CE=0;//恢复,初始化DS1302_IO=0;//恢复,初始化return Data;//返回值为当前数据
}

由上可知,SCLK每次变化电平,就会进行数据的读写操作,但由于数据是先进先出的,所以低位在前。

  • 宏定义日期时间地址,提高代码可读性,便于修改和维护,代码:
//宏定义日期,和写入保护地址
#define DS1302_S 0x80//秒
#define DS1302_M 0x82//分
#define DS1302_H 0x84//时
#define DS1302_D 0x86//日
#define DS1302_MON 0x88//月
#define DS1302_DAY 0x8A//星期
#define DS1302_Y 0x8C//年
#define DS1302_WP 0x8E//写入保护
  • 其余代码,注释很详细:
//初始化定义日期时间数组
//对应年月 日 时 分 秒 星期
unsigned char DS1302_Time[]={25,4,15,20,59,55,2};//十进制转BCD码函数
unsigned char HEX_BCD(unsigned char n){return n/10*16+n%10;
}
//BCD码转十进制函数
unsigned char BCD_HEX(unsigned char n){return n/16*10+n%16;
}//DS1302的初始化函数
void DS1302_Init(){DS1302_CE=0;DS1302_SCLK=0;
}

数组是初始化日期时间;

转换进制函数,以及BCD码是什么:

  • 配合上述所有知识,数组,读写操作,进制转换,宏定义等知识点;设置日期时间,改变定义读日期时间代码:
//设定日期时间函数
void DS1302_SetTime(){//数据必须为BCD,因此转换DS1302_WriteByte(DS1302_WP,0x00);//关闭写入保护DS1302_WriteByte(DS1302_Y,HEX_BCD(DS1302_Time[0]));//写入年DS1302_WriteByte(DS1302_MON,HEX_BCD(DS1302_Time[1]));//写入月DS1302_WriteByte(DS1302_D,HEX_BCD(DS1302_Time[2]));//写入日DS1302_WriteByte(DS1302_H,HEX_BCD(DS1302_Time[3]));//写入时DS1302_WriteByte(DS1302_M,HEX_BCD(DS1302_Time[4]));//写入分DS1302_WriteByte(DS1302_S,HEX_BCD(DS1302_Time[5]));//写入秒DS1302_WriteByte(DS1302_DAY,HEX_BCD(DS1302_Time[6]));//写入星期DS1302_WriteByte(DS1302_WP,0x80);//打开写入保护
}//改变定义并读取日期时间函数
void DS1302_ReadTime(){//数组内容为十进制,因此转换unsigned char Temp;//定义临时变量Temp=DS1302_ReadByte(DS1302_Y);DS1302_Time[0]=BCD_HEX(Temp);//读年Temp=DS1302_ReadByte(DS1302_MON);DS1302_Time[1]=BCD_HEX(Temp);//读月Temp=DS1302_ReadByte(DS1302_D);DS1302_Time[2]=BCD_HEX(Temp);//读日Temp=DS1302_ReadByte(DS1302_H);DS1302_Time[3]=BCD_HEX(Temp);//读时Temp=DS1302_ReadByte(DS1302_M);DS1302_Time[4]=BCD_HEX(Temp);//读分Temp=DS1302_ReadByte(DS1302_S);DS1302_Time[5]=BCD_HEX(Temp);//读秒Temp=DS1302_ReadByte(DS1302_DAY);DS1302_Time[6]=BCD_HEX(Temp);//读日期
}

效果视频: 

DS1302时钟

 注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!

相关文章:

  • STM32提高篇: 以太网通讯
  • S06-Kep的跨通道传输
  • 二极管详解:特性参数、选型要点与分类
  • 【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——CAN、CAN FD测试 #FDCAN
  • Qt/C++学习系列之QTreeWidget的简单使用记录
  • IPD项目管理的“黄金三角“在2025年是否需要重构?
  • 基于RV1126开发板获取系统温度
  • WPS Office安卓版文档编辑功能与兼容性评测【高效编辑】
  • uniappx项目上架各手机平台
  • Uniapp:消息提示框
  • vue3 vite创建项目 vite配置、pinia配置
  • 经典算法 判断一个图中是否有环
  • 界面控件DevExpress .NET MAUI v24.2亮点:MVVM功能增强
  • Rust学习之实现命令行小工具minigrep(二)
  • qt/C++面试题自用学习(更新中)
  • Gitlab SSH Jenkins Pipeline Supervisor部署
  • 大模型训练显存压缩实战:ZeRO-3 vs 梯度累积 vs 量化混合策略
  • 深度为16,位宽8bit的单端口SRAM——学习记录
  • 全网通emotn ui桌面免费吗?如何开机自启动
  • leetcode:3210. 找出加密后的字符串(python3解法)
  • 做网站要考虑的/世界比分榜
  • 网站 团队/公司品牌营销策划
  • wordpress导航字体/seo外链查询工具
  • 网站建设费用大全/软文营销方法有哪些
  • 宜春做网站的联系电话/短视频seo搜索优化
  • 2021不良正能量免费网站/网络营销的定义是什么