文章目录

  • 单片机电子时钟程序设计
    • 实现效果
  • 最后

单片机电子时钟程序设计

共用体除非必要,否则学长不推荐使用,枚举的用法比较简单,在本书19章的项目实践中有很好的示例,这节课我们先来练习一下结构体的使用。下边这个程序的功能是一个带日期的电子钟,相当于一个简易万年历了,并且加入了按键调时功能。学有余力的同学看到这里,不妨先不看我们提供的代码,自己写写试试。如果能够独立写一个按键可调的万年历程序,单片机可以说基本入门了。如果自己还不能够独立完成这个程序,那么还是老规矩,先抄并且理解,而后自己独立默写出来,并且要边默写边理解。

本例直接忽略了星期这项内容,通过上、下、左、右、回车、ESC 这6个按键可以调整时间。这也是一个具有综合练习性质的实例,虽然在功能实现上没有多少难度,但要进行的操作却比较多而且烦琐,同学们可以从中体会到把繁杂的功能实现分解为一步步函数操作的必要性以及方便灵活性。简单说一下这个程序的几个要点,方便大家阅读理解程序。

  • 把 DS1302 的底层操作封装为一个 DS1302.c 文件,对上层应用提供基本的实时时间的操作接口,这个文件也是我们的又一个功能模块了,我们的积累也越来越多了。

  • 定义一个结构体类型 sTime 用来封装日期时间的各个元素,又用该结构体定义了一个时间缓冲区变量 bufTime 来暂存从 DS1302 读出的时间和设置时间时的设定值。需要注意的是在其它文件中要使用这个结构体变量时,必须首先再声明一次 sTime 类型;

  • 定义一个变量 setIndex 来控制当前是否处于设置时间的状态,以及设置时间的哪一位,该值为0就表示正常运行,1~12分别代表可以修改日期时间的12个位;

  • 由于这节课的程序功能要进行时间调整,用到了 1602 液晶的光标功能,添加了设置光标的函数,我们要改变哪一位的数字,就在 1602 对应位置上进行光标闪烁,所以 Lcd1602.c 在之前文件的基础上添加了两个控制光标的函数;

  • 时间的显示、增减、设置移位等上层功能函数都放在 main.c 中来实现,当按键需要这些函数时则在按键文件中做外部声明,这样做是为了避免一组功能函数分散在不同的文件内而使程序显得凌乱。

/***************************DS1302.c 文件程序源代码*****************************/
#include <reg52.h>sbit DS1302_CE = P1^7;
sbit DS1302_CK = P3^5;
sbit DS1302_IO = P3^4;struct sTime { //日期时间结构体定义unsigned int year; //年unsigned char mon; //月unsigned char day; //日unsigned char hour; //时unsigned char min; //分unsigned char sec; //秒unsigned char week; //星期
};/* 发送一个字节到 DS1302 通信总线上 */
void DS1302ByteWrite(unsigned char dat){unsigned char mask;for (mask=0x01; mask!=0; mask<<=1){ //低位在前,逐位移出if ((mask&dat) != 0){ //首先输出该位数据DS1302_IO = 1;}else{DS1302_IO = 0;}DS1302_CK = 1; //然后拉高时钟DS1302_CK = 0; //再拉低时钟,完成一个位的操作}DS1302_IO = 1; //最后确保释放 IO 引脚
}
/* 由 DS1302 通信总线上读取一个字节 */
unsigned char DS1302ByteRead(){unsigned char mask;unsigned char dat = 0;for (mask=0x01; mask!=0; mask<<=1){ //低位在前,逐位读取if (DS1302_IO != 0){ //首先读取此时的 IO 引脚,并设置 dat 中的对应位dat |= mask;}DS1302_CK = 1; //然后拉高时钟DS1302_CK = 0; //再拉低时钟,完成一个位的操作}return dat; //最后返回读到的字节数据
}
/* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */
void DS1302SingleWrite(unsigned char reg, unsigned char dat){DS1302_CE = 1; //使能片选信号DS1302ByteWrite((reg<<1)|0x80); //发送写寄存器指令DS1302ByteWrite(dat); //写入字节数据DS1302_CE = 0; //除能片选信号
}
/* 用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节 */
unsigned char DS1302SingleRead(unsigned char reg){unsigned char dat;DS1302_CE = 1; //使能片选信号DS1302ByteWrite((reg<<1)|0x81); //发送读寄存器指令dat = DS1302ByteRead(); //读取字节数据DS1302_CE = 0; //除能片选信号return dat;
}
/* 用突发模式连续写入 8 个寄存器数据,dat-待写入数据指针 */
void DS1302BurstWrite(unsigned char *dat){unsigned char i;DS1302_CE = 1;DS1302ByteWrite(0xBE); //发送突发写寄存器指令for (i=0; i<8; i++){ //连续写入 8 字节数据DS1302ByteWrite(dat[i]);}DS1302_CE = 0;
}
/* 用突发模式连续读取 8 个寄存器的数据,dat-读取数据的接收指针 */
void DS1302BurstRead(unsigned char *dat){unsigned char i;DS1302_CE = 1;DS1302ByteWrite(0xBF); //发送突发读寄存器指令for (i=0; i<8; i++){ //连续读取 8 个字节dat[i] = DS1302ByteRead();}DS1302_CE = 0;
}
/* 获取实时时间,即读取 DS1302 当前时间并转换为时间结构体格式 */
void GetRealTime(struct sTime *time){unsigned char buf[8];DS1302BurstRead(buf);time->year = buf[6] + 0x2000;time->mon = buf[4];time->day = buf[3];time->hour = buf[2];time->min = buf[1];time->sec = buf[0];time->week = buf[5];
}
/* 设定实时时间,时间结构体格式的设定时间转换为数组并写入 DS1302 */
void SetRealTime(struct sTime *time){unsigned char buf[8];buf[7] = 0;buf[6] = time->year;buf[5] = time->week;buf[4] = time->mon;buf[3] = time->day;buf[2] = time->hour;buf[1] = time->min;buf[0] = time->sec;DS1302BurstWrite(buf);
}
/* DS1302 初始化,如发生掉电则重新设置初始时间 */
void InitDS1302(){unsigned char dat;struct sTime code InitTime[] = { //2013 年 10 月 8 日 12:30:00 星期二0x2013,0x10,0x08, 0x12,0x30,0x00, 0x02};DS1302_CE = 0; //初始化 DS1302 通信引脚DS1302_CK = 0;dat = DS1302SingleRead(0); //读取秒寄存器if ((dat & 0x80) != 0){ //由秒寄存器最高位 CH 的值判断 DS1302 是否已停止DS1302SingleWrite(7, 0x00); //撤销写保护以允许写入数据SetRealTime(&InitTime); //设置 DS1302 为默认的初始时间}
}

DS1302.c 最终向外提供出与具体时钟芯片寄存器位置无关的、由时间结构类型 sTime 作为接口的实时时间的读取和设置函数,如此处理体现了我们前面提到过的层次化编程的思想。应用层可以不关心底层实现细节,底层实现的改变也不会对应用层造成影响,比如说日后你可能需要换一款时钟芯片,而它与 DS1302 的操作和时间寄存器顺序是不同的,那么你需要做的也仅是针对这款新的时钟芯片设计出底层操作函数,最终提供出同样的以 sTime 为接口的操作函数即可,应用层无需做任何的改动。

/***************************Lcd1602.c 文件程序源代码*****************************/
#include <reg52.h>
#define LCD1602_DB P0sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;/* 等待液晶准备好 */
void LcdWaitReady(){unsigned char sta;LCD1602_DB = 0xFF;LCD1602_RS = 0;LCD1602_RW = 1;do {LCD1602_E = 1;sta = LCD1602_DB; //读取状态字LCD1602_E = 0;} while (sta & 0x80); //bit7 等于 1 表示液晶正忙,重复检测直到其等于 0 为止
}
/* 向 LCD1602 液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd){LcdWaitReady();LCD1602_RS = 0;LCD1602_RW = 0;LCD1602_DB = cmd;LCD1602_E = 1;LCD1602_E = 0;
}
/* 向 LCD1602 液晶写入一字节数据,dat-待写入数据值 */
void LcdWriteDat(unsigned char dat){LcdWaitReady();LCD1602_RS = 1;LCD1602_RW = 0;LCD1602_DB = dat;LCD1602_E = 1;LCD1602_E = 0;
}
/* 设置显示 RAM 起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x, unsigned char y){unsigned char addr;if (y == 0){ //由输入的屏幕坐标计算显示 RAM 的地址addr = 0x00 + x; //第一行字符地址从 0x00 起始}else{addr = 0x40 + x; //第二行字符地址从 0x40 起始}LcdWriteCmd(addr | 0x80); //设置 RAM 地址
}
/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str){LcdSetCursor(x, y); //设置起始地址while (*str != '\0'){ //连续写入字符串数据,直到检测到结束符LcdWriteDat(*str++);}
}
/* 打开光标的闪烁效果 */
void LcdOpenCursor(){LcdWriteCmd(0x0F);
}
/* 关闭光标显示 */
void LcdCloseCursor(){LcdWriteCmd(0x0C);
}
/* 初始化 1602 液晶 */
void InitLcd1602(){LcdWriteCmd(0x38); //16*2 显示,5*7 点阵,8 位数据接口LcdWriteCmd(0x0C); //显示器开,光标关闭LcdWriteCmd(0x06); //文字不动,地址自动+1LcdWriteCmd(0x01); //清屏
}

实现效果



最后

毕业设计 - 基于51单片机的电子时钟设计与实现相关推荐

  1. 基于51单片机的电子时钟设计

    基于51单片机的电子时钟设计 0 功能介绍 1.从DS1302中读取时间显示 2.一共4个按键,按键1按下进入修改时间模式,再按下切换修改的时间变量,这个时候第2和3个按键就是修改时间变量的按键,修改 ...

  2. c51单片机时钟c语言程序设计,基于51单片机的电子时钟设计..doc

    -- I -- - - I -- 设计题目: 基于 51 单片机的电子时钟设计 摘要 单片机,是集 CPU ,RAM ,ROM ,定时器,计数器和多种接口于一体的微控制器.自 20 世纪 70 年代问 ...

  3. 【036】基于51单片机的电子时钟与秒表Proteus仿真设计

    一.压缩包资料内容 (1).基于51单片机的电子时钟与秒表proteus仿真设计一份: (2).基于51单片机的电子时钟与秒表proteus仿真设计keli源代码一份: (3).基于51单片机的电子时 ...

  4. 基于微型计算机系统的实时时钟设计,基于51单片机的实时时钟设计报告.doc

    基于51单片机的实时时钟设计报告 PAGE PAGE 24 课程设计(论文)任务书 信息工程 学 院 信息工程 专 业 (2) 班 一.课程设计(论文)题目 嵌入式课程设计 二.课程设计(论文)工作自 ...

  5. 基于51单片机LCD1602电子时钟实物制作

    之前写了51单片机的LCD1602的程序,现在画了电路图,焊接了电路板,做出了基于51单片机LCD1602电子时钟的实物. 注意事项: 焊接过程中,注意提前布局电路. 对应好端口(避免接线错误),我由 ...

  6. 基于51单片机的电子万年历设计

    基于51单片机的电子万年历 1)LCD1602显示时间.日期.星期.温度 2)按键可调整时间日期星期 3)整点报时 4)DS1302时钟芯片 5)DS18B20温度传感器 6)闹钟功能 7)判断闰年 ...

  7. 基于AT89C51单片机的电子时钟设计

    点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87695258?spm=1001.20 ...

  8. 基于51单片机的电子闹钟设计

    使用的单片机是  STC89C52 此设计可以    年 月 日   时 分 秒显示和闹钟功能      能通过8个按键自由调整 时 分 秒   闹钟响铃时间 带复位按键,要是模块抽风,摁复位按键即可 ...

  9. 基于51单片机的电子时钟万年历带农历温度proteus仿真

    硬件设计 本设计由数据显示模块.温度采集模块.时间处理模块和调整设置模块四个模块组成.系统以AT89S52单片机为控制器,以串行时钟日历芯片DS1302记录日历和时间,它可以对年.月.日.时.分.秒进 ...

最新文章

  1. 简单介绍Go语言错误处理异常捕获+异常抛出
  2. Spring Boot(Spring的自动整合框架)
  3. python — 列表与元组
  4. 威纶通触摸屏维修_威纶通WEINVIEW触摸屏维修故障检测
  5. 推荐几本对创业者很有用的书籍
  6. TS流头部的调整字段
  7. zynq 和fpga区别_FPGA复位的正确打开方式
  8. [zz]基于libvirt API监测xen初探
  9. luoguP1991无线通讯网
  10. 我的 Visual C++ 6.0学习网站
  11. 关于最优化问题的个人理解以及黑塞矩阵的示例
  12. 信息系统项目管理师 - 必考记忆口诀
  13. 怎么扩展服务器内存?
  14. 人有见识就不轻易发怒;宽恕人的过失便是自己的荣耀。
  15. 爬虫基础之HTTP基本原理
  16. 期货公司最低标准的手续费和保证金
  17. IDEA使用MyBatis【超级详细,绝对能运行】
  18. 重温数据结构:二叉排序树的查找、插入、删除
  19. VMware(1):MacOS虚拟机无法设置自动调整大小(已解决)
  20. 我是如何从0开始,在23天里完成一款Android游戏开发的 – Part4~7

热门文章

  1. 软件工程第二章 软件工程
  2. 线路通寻线器是网络工程技术人员的新选择
  3. 幼儿园学数字计算机,幼儿数学电脑版
  4. 违禁词查询 麦谈帮API数据接口
  5. webstorm破解版下载
  6. 云创大数据与云思科技正式签订战略合作协议
  7. 【技巧】matlab时间序列栅格数据趋势分析
  8. ImageX 语法详解
  9. 学生信息管理系统(练习版)
  10. 帆软多个报表合并成一个excel 下载