Bootstrap

AutoLeaders控制组——51单片机学习笔记(LED点阵屏、DS1302时钟芯片)

本篇内容是观看B站江科大自化协UP主的教学视频所做的笔记,对其中内容有所引用,并结合自己的单片机板块进行了更改调整。

以下笔记内容以一个视频为一个片段(内容较多,可能不适合速食,望见谅)

一些内容涉及前面的知识点,可能需要提前了解(可以翻看本人之前的文章或者去B站看UP主的视频)

目录

9-1、LED点阵屏

LED点阵屏介绍

LED点阵屏分类

LED点阵屏显示原理

8×8LED连接原理图

74HC595运行原理

原理图介绍

运行机制解释

步骤解释:

补充:

①能否LED点阵屏两端都直接连接I/O口

②为什么当点阵屏多个灯亮时,灯比较昏暗

9-2、LED点阵屏显示图形&动画

代码一

写入代码

Ⅰ、新建工程及main.c文件

补充:C51的sfr、sbit

Ⅱ、编写74HC595运行函数

Ⅲ、将对应寄存器初始化

Ⅳ、添加已有的模块化文件

Ⅴ、写入LED点阵屏显示一列的代码函数

Ⅵ、编写主函数代码

Ⅶ、烧录程序

代码二

写入代码:

Ⅰ、将上面的目录复制一份,重命名后打开。

Ⅱ、模块化LED点阵屏显示的相关函数

补充:获得对应图案的每列十六进制方法

 Ⅲ、编写数组,存储流动字幕的每一列十六进制数

Ⅳ、编写主函数

Ⅴ、烧录程序

代码三

写入代码

Ⅰ、将代码二的文件复制一份,重命名

Ⅱ、更改存储数据的数组

Ⅲ、更改主函数中帧数更改的部分

Ⅳ、烧录程序

补充:code关键字

10-1、DS1302实时时钟

DS1302介绍

简介:

补充:

作用:(与之前定时器制作时钟对比)

应用电路

DS1302芯片内部结构

 时钟运行部分

访问部分

寄存器部分

操作步骤:

一、首先设置模式(这里设置为写入)

二、接着选择寄存器类型,按照地址设置

三、选择操作对象(这里为CK,即clock)

四、写入第7位

五、读取或写入设置的寄存器数据

六、回归初始状态

10-2、DS1302时钟&可调时钟

代码一

Ⅰ、新建工程并创建main.c文件

Ⅱ、添加存在的已模块化的文件

Ⅲ、建立DS1302的模块化代码文件

Ⅳ、测试前面写入的代码是否有效

补充:BCD码

补充:DS1302寄存器一些位的作用

Ⅴ、设置存储初始时间的数组

Ⅵ、设置写入时间的函数

Ⅶ、设置读取时间的函数

Ⅷ、将新加的内容的声明添加进DS1302.h文件中

Ⅸ、在主函数中添加液晶屏的时间显示

Ⅹ、将程序烧录进单片机,即可看到预期效果

代码二

Ⅰ、将上面的代码文件复制一份,并重命名

Ⅱ、将需要的已经模块化的代码添加进工程目录下

Ⅲ、将显示时间的液晶屏函数代码单独移出成为一个函数

Ⅳ、添加时间设置函数

Ⅴ、设置中断函数实现进入设置时,设置数字闪烁效果

Ⅵ、编辑主函数部分

Ⅶ、将程序烧录进单片机

 Ps:

尝试:


9-1、LED点阵屏

LED点阵屏介绍

LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合,如汽车报站器、广告屏以及公告牌等

LED点阵屏分类

按颜色:

单色

 双色

 

全彩

 按像素:8*8、16*16等(大规模的LED点阵通常由很多个小点阵拼接而成)。

—— 一般设置为8的倍数。

LED点阵屏显示原理

解释内容

①LED点阵屏的结构类似于数码管,只不过是数码管把每一列的像素以“8”字型排列而已。

LED点阵屏示意图:

 一位数码管:

 

②LED点阵屏与数码管一样,有共阴和共阳两种接法,不同的接法对应的电路结构不同。

LED点阵屏原理图(共阳接法):

 

双色LED点阵屏原理图(共阳接法):

 当为单色LED点阵屏时,共阴接法与共阳解法区别不大(可以通过扫描转换效果);为多色LED点阵屏时,能明显体会到区别(如上图)。

Ps:

(1)共阴接法只需要将LED倒过来,更改连接的相关线即可。

(2)引脚排序为乱序排序(实物接线就近原则),因此需要对比自己单片机的原理图,避免出错。

③LED点阵屏需要进行逐行或逐列扫描,才能使所有LED同时显示。

Ps:扫描概念类通于矩阵键盘扫描,利用视觉暂留效果。

8×8LED连接原理图

 

 

由上图可知,LED点阵屏一侧由P0_0~P0_7的位寄存器(I/O口)控制,另一侧一侧由DPa~DPh控制,而对应的另一侧连接到74HC595中。

由图可见,74HC595可以通过P3_4~P3_6三根线控制右边八根线,由此能极大减少I/O口占用,节省I/O口。

74HC595运行原理

74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展。

板子上的74HC595

 

原理图介绍

 

右侧QA~QH为八位并行的输出端。

OE(Output Enable)为输出使能,当OE接了低电平,右侧才能输出。

(上面有一横线代表低电平有效,或下降沿有效)

RCLK(register clock)为寄存器时钟。

SRCLR(serial clear)为串行清零端,低电平时会将芯片里面数据进行清空(因此这里接VCC)。

SRCLK(serial clock)为串行时钟。

SER(serial)为串行数据。

QH用于多片级联。

在这里使用P34~P36这三个引脚控制右边八位的输出。

运行机制解释

 

①当串行输出时,数据在时钟的激励下一个一个从SER的线输出;

②当并行输出时,数据直接从最右侧八个引脚同时输出外面。

步骤解释:

①写入第一个数据。

首先给SER写入数值。(假设为1)

接着将SERCLK接入高电平(上升沿),此时1会被读取进移位寄存器最上面的位置。

 

然后将SERCLK清零,回到默认状态(低电平)。

——单片机I/O口默认状态为高电平,因此最开始需要先设置成低电平。

②写入第二个数据。

首先给SER写入设置。(假设为0)

接着将SERCLK接入高电平(上升沿),此时0会被读取进移位寄存器最上面的位置,而最开始的1会向下移动一位。

 

然后将SERCLK清零,回到默认状态(低电平)。

③写入接下来的数据。

重复步骤②的原理,最终写完8个位置。

 

④将左边数据移入右边。

将RCLK接入高电平(一开始的I/O口为高电平,因此最开始要初始化为低电平),然后左边数据就会同一时刻被搬入右边(相当于大门被打开),并同时在右侧的输出口输出数据。

 

补充:多片级联的运行原理

多片级联即在QH’后再相连了与之前一样的一片运行电路。

两片级联:

 

当第一片的移位写满之后,就会移位到QH’内(即下一片的SER),于是再写入的值,最下面的会被推入第二片区域里面,以此类推,最终将第二片填满。(两片的SERCLK是连接在同一个I/O口上的)

当填满后,即可将RCLK接入高电平,将数据一次性输出。(两片的RCLK也是连接在一个I/O口上的)

其他的多片级联

原理跟两片级联相同,只是在QH’后加入其他的74HC595芯片进行级联,数量增多了。

作用:

通过这种方式,能将I/O口扩展(节省I/O口)——3个I/O口控制多位,但是由于是一位位推入,因此运行时间将会较缓慢。

补充:

①能否LED点阵屏两端都直接连接I/O口

 

即使想将连接的部分都替换为I/O口直接控制,也不可行。

因为单片机的工作模式为弱上拉模式,输出的高电平信号弱,因此会使得LED亮度低,甚至不工作。

所以需要利用74HC595使得输出高电平能力增强。

或者加入一个三极管开关,使得接入高电平时,开关导通,一头的VCC直接接入LED的正极一侧。

截自视频:

 

导通:

 

如上图所示,当给高电平时,VCC直接导通到LED部分,使得LED能正常运行。

②为什么当点阵屏多个灯亮时,灯比较昏暗

因为74HC595芯片为恒压输出,当连接的灯增多时,会使得电流变小,灯亮度变暗。

9-2、LED点阵屏显示图形&动画

代码一

实现效果:在LED点阵屏上显示一个图案(这里以笑脸为例)。

写入代码

Ⅰ、新建工程及main.c文件

方法如之前一样。

补充:C51的sfr、sbit

sfr(special function register):特殊功能寄存器声明

例:sfr P0 = 0x80;

声明P0口寄存器,物理地址为0x80

sbit(special bit):特殊位声明

例:sbit P0_1 = 0x81;    或    sbit P0_1 = P0^1;

声明P0寄存器的第1位

可位寻址/不可位寻址:

在单片机系统中,操作任意寄存器或者某一位的数据时,必须给出其物理地址,又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍,单片机无法对所有位进行编码,故每8个寄存器中,只有一个是可以位寻址的。

对不可位寻址的寄存器,若要只操作其中一位而不影响其它位时,可用“&=”、“|=”、“^=”的方法进行位操作。

具体可联系定时器的部分。

在REGX52.H文件中,能看到对应的单片机位声明。

寄存器声明:

 

位声明:

 

由上面可知,将对应的地址(后面的十六进制数)赋值给P0_0口,使得P0_0代表单片机相应部分地址。——所以可以更改对应的名字,只要地址相同,作用就相同。

 

(这里是在主函数文件操作的,因此使用的是P3^5之类,其代表的就是对应地址数字)

Ⅱ、编写74HC595运行函数

 

解释SER=Byte&(0x80>>i):

利用&运算符中有0为0,全1为1的的规则,使得直接取出Byte(十六进制数,转化为二进制进行运算)的二进制中一个位数;

然后通过移位符>>,实现取出对应的位数;

最后利用单位数变量非0即1的规则,给SER赋值,实现提取对应二进制位进行赋值。

 

Ⅲ、将对应寄存器初始化

 

Ⅳ、添加已有的模块化文件

将Delay函数的模块化文件添加进工程目录下。

 

Delay.c文件

 

Ⅴ、写入LED点阵屏显示一列的代码函数

①前面加入预编译,将MATRIX_LED_PORT的名字在编译后替换为P0(C语言的预编译知识)。——便于理解P0口对应的作用含义。

 

②写入函数代码。

 

解释MATRIX_LED_PORT=~(0x80>>Column):

(1)利用0x80的移位,实现选择对应的列数读取数据,其他列数无作用的目标效果。

(2)利用取反(~)符号,将0置换为1,配合移位符号,实现目标效果(因为移位时会在末尾补零,因此利用仅有一个1,其他都是0的数字方便移位)。

(3)将移位后的值赋予P0口,使得P0口直接显示对应的列数灯状态。

 

Ⅵ、编写主函数代码

①获取图案对应每列的数据。

来自up主视频截图:

 在Excel中弄出对应的图案,然后按照列(这里采用的是列扫描)进行读取二进制数(从高到低),并将其转换为十六进制。

Ps:后面有工具进行取数,所以这一步的转换数字了解即可。

②将对应列数写入主函数。

 

Ⅶ、烧录程序

Main.c文件代码:

 

 

将代码烧录进单片机,即可在LED点阵屏看到预期效果(笑脸)。

代码二

实现效果:在LED点阵屏中流动地循环显示Hello!内容。

写入代码:

Ⅰ、将上面的目录复制一份,重命名后打开。

步骤同之前一样。

Ⅱ、模块化LED点阵屏显示的相关函数

MatrixLED.c文件

 MatrixLED.h文件

 

补充:获得对应图案的每列十六进制方法

①利用up主附带的取模软件,打开(取模软件可以上网搜索,功能可能更多)

 

②点击[新建图像],调整对应的宽度与高度。

 

Ps:

设置高度时为8;

(因为预期的流动字幕是从左到右流动的,上下高度限制,如果流动方向为上下,可以更改,但是得将前面的按列扫描模式函数调整一下)

设置宽度为32。

(这里流动是从左到右,因此宽度可以调整大于点阵屏的列数限制,但是最好取8的倍数,显示时可能更流畅——也可不调整,自己可以试试效果)

③可以点击[模拟动画],在下方点击[放大格点],使格点显示变大。

 

④在[参数设置]中调整自己的设置。(调整完成点击[确定])

 

⑤在生成的空白部分点出自己想要的图案

 

⑥在[取模方式]中点击[C51格式],即可在下方的[点阵生成区]看到对应的十六进制代码。

 

 Ⅲ、编写数组,存储流动字幕的每一列十六进制数

将获得的代码复制到数组里面。

 为了便于读懂代码,将每行调整为8个十六进制数。

 

Ⅳ、编写主函数

 利用偏移量Offset,使得每次在LED点阵屏持续显示的内容不断前进,实现流动效果。

Ⅴ、烧录程序

 

Ps:这里将数组进行了调整,在最前面与最后面分别加入了一行8个0x00的十六进制数,并调整了Offset的if越界从头条件,使得动画显示不生硬。

烧录进单片机后,即可看到流动的Hello!效果。

代码三

实现效果:在LED点阵屏上显示笑脸与哭脸不断转换的表情。

写入代码

Ⅰ、将代码二的文件复制一份,重命名

(也可直接在代码二中改动,看是否要保存代码二)

步骤与之前一样

Ⅱ、更改存储数据的数组

在取模工具中画好笑脸、无表情脸、哭脸的图案,然后将生成代码粘贴到数组内。

 

Ⅲ、更改主函数中帧数更改的部分

 这里一次性将偏移量加8,使得每次显示切换显示时直接切换到下一张画面。

Ⅳ、烧录程序

烧录后即可看到预期效果。

补充:code关键字

 这里加入code之前,将存储内容放入ram(随机存储器)中。

——ram为程序运行时的暂存器(可以回到最开始的单片机介绍)。

加入code后,将存储内容放入flash(闪存存储器)中。

——flash空间较大,可以存储内容更多,但是后面无法对数组内容进行更改(比如说Animation[1]=0xFE之类的事情做不了)

10-1、DS1302实时时钟

DS1302介绍

简介:

DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。

补充:

RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片。

例如DS1302、DS3231(精度很高,内部集成化很高,且比较贵)、DS12C887(里面自带电池)。

(单片机内部没有实时时钟RTC,因此可以外接一个RTC的芯片,如DS1302——这里的单片机指的是最中间的横条部分,具体可以回到最开始的单片机介绍了解)

DS1302:

 网上出售的最小功能模块版:

 时钟:

 

单片机上的DS1302芯片:

 

作用:(与之前定时器制作时钟对比)

①计算时间精度比之前的定时器实现的时钟精度更高。

②无需占用单片机的CPU运行时间。(定时器时钟需要占用CPU给定时器计时)

③掉电可以继续运行。(定时器制作的时钟不能)——自带一个备用点出,当掉电的时候会自动切换到备用电池继续工作。

DS1302引脚定义和应用电路。

两种封装:

 其中DIP为直插封装,引脚是立起来的,可以插在PCB的焊盘上。(可以回到最开始的单片机介绍了解)

SO为贴片封装,贴在电路板表面上。——单片机上的DS1302芯片封装。

应用电路

 电源部分:

DS1302内部连接图:

 

 由上图可知,VCC1连接部分并没有接备用电池,因此这里无法实现时钟掉电继续运行。

晶振部分:

 一般有关实时时钟的晶振值都为32.768KHZ,因为这个频率方便易用,且精度较高。

该晶振为时钟芯片提供稳定的计数脉冲,经过内部的处理,会输出标准的1HZ频率,且频率的精度高。——晶振产生的时钟频率高,且稳定性高。(可回到单片机介绍那里了解)

通讯部分:

 利用这三个引脚,可以读出/写入数据进芯片内部时钟。

根据这三个引脚的通讯协议,可以对芯片内部的寄存器写入或读出数据。

这三根线的通讯方式与74HC595芯片的通讯方式相类似。

DS1302芯片内部结构

 时钟运行部分

通过外部X1和X2传入的晶振进行内部电路处理,最终以1HZ频率输出。

访问部分

CE线:控制读写的总开关(低电平打开)。——CE未打开时不影响其他两根线的数据处理。

I/O线与SCLK线:作用类似于74HC595的写入过程,不过多了读取功能。

 这里的I/O类似于SER,SCLK类似于SERCLK。

寄存器部分

寄存器中的内容已经定义完成,设置好对应的范围,因此只需要进行时钟读写操作即可(无需范围判断等处理)。

寄存器定义:

时钟有关部分的寄存器:

 ①寄存器每个地址存储数据为一个字节;

②划线部分以上的寄存器存储内容为时分秒,年日月,星期;(最后的RANGE为其范围

③下面的WP(write protect)为写保护,当置一时,写入的操作无效,但依然可以读出数据。

TCS寄存器用于存储涓流充电。(不对电池进行充电的话,无需配置)

⑤READ对应的十六进制数为其读取时的寄存器地址,WRITE对应的十六进制数为其写入时的寄存器地址。

命令字:

 

手册中的命令字:

 

①该命令字为一个字节,一共八位,

②最高为7,固定值为1;

③如果要操作RAM,第六位给1;如果要操作CK(clock,时钟),第六位给0;

④通过控制第5~1位的数字,设置是哪种状态的寄存器(时分秒、年月日之类);

(根据上面的地址写入)

⑤通过给第0位赋值,控制读取还是写入。其中给1为读取,给0为写入。

时序定义:

 

①开始写入时需要将CE置高电平1,写完后置为0。

②当SCLK为上升沿时,将数据写入;当SCLK为下降沿时,DS1302将会把数据输出——类似于74HC595。

(仅限于READ的读取模式时下降沿才输出存储的数据;当为WRITE时,写完命令字节还会继续写数据)

③除了画圈部分由DS1302控制外,其余部分均由单片机控制。

操作步骤:

一、首先设置模式(这里设置为写入)

①将CE置1,开始向寄存器写入数据;

②给I/O口赋上最低位的命令字(第0位),选择读取还是写入;(这里设置0,为写入)

③给SCLK上升沿,将I/O口数据写入命令字寄存器相应位置;

④再将SCLK归零;

二、接着选择寄存器类型,按照地址设置

①根据寄存器的地址给I/O口赋值次低位的命令字(第1位);

②给SCLK上升沿,将I/O口数据写入命令字寄存器相应位置;

③再给SCLK归零;

④以此类推,完成第2位到第5位的赋值。

三、选择操作对象(这里为CK,即clock)

仿照步骤二的①~③过程即可。(赋值给I/O口时给0)

四、写入第7位

仿照步骤二的①~③过程即可。(赋值给I/O口时给1)

五、读取或写入设置的寄存器数据

①当设置为写入时,写入第7位的命令字后,即可紧跟着后面写入设置的寄存器里面的数据;

②当设置为读取时,写入第7位的命令字后,一旦SCLK给下降沿,那么DS1302就开始将设置的寄存器里面的数据输出给单片机,每次给一次下降沿读取一位数据,一直读完八个数据。

六、回归初始状态

最后操作完成后把SCLK置零,将CE置零,完成操作。

10-2、DS1302时钟&可调时钟

代码一

实现效果:通过利用DS1302芯片在LCD_1602液晶屏上第一行显示年月日,第二行显示时分秒。

Ⅰ、新建工程并创建main.c文件

 

Ⅱ、添加存在的已模块化的文件

将LCD1602文件与Delay文件添加进工程目录下。

 其中LCD1602为液晶屏显示的相关函数文件,Delay为延时函数文件(最终Delay文件可能用不到)。

 

Ⅲ、建立DS1302的模块化代码文件

DS1302.c文件

①将引脚名称重新定义名字。(可以不做)

 

这样能更直观的明白对应I/O口的作用,且利用下划线能明白对应的来源。

②添加DS1302相应I/O口的初始化函数

 

根据上节所说,需要先将CE置0,以便置1时开始做写入/读取操作;

当SCLK为1时,开始读取IO的数据,因此需要先置0。

③添加DS1302的写入函数

 

首先写入模板

 

其中Command为命令字,Data为对设置的寄存器写入的数据。

接着配置命令字的寄存器

根据上一节所说的步骤,得到如下配置命令字的循环。

 

解释DS1302_IO=Command&(0x01<<i):

首先利用左移运算符,使得每次将八个位的二进制数唯一一个1移动位置;

 然后通过&运算符(全1为1,有0置0)来将1所在的数字取出,而其他位因为有0,所以全部变成0,使得影响最终赋值的只有1对应的位置。

最后将得到的值赋予IO口,完成填入工作。(非0即1的原则,即不是0的数,统统变成1填入)

Ps:

最后对SCLK的两条语句操作不能太快,存在间隔时间,否则放入数据的工作可能出错。

可以在手册中查询最小的间隔时间

 可以看到最小为50ns,远小于单片机的运行速度(单片机运行一个周期就1us左右了),因此这里不需要加入延时。(如果单片机运行速度很快,则在两条语句中间加入延时即可)

最后配置对设置的寄存器写入数据的函数

这里将上面设置命令字的循环复制过来,更改一些部分即可。(因为操作步骤基本相同)

 

④添加DS1302的读取函数

 

首先写入模板

 

这里只需要Command(命令字数据)参数即可,因为后面是读取的操作。

接着配置命令字的寄存器

根据上节的步骤,可以得到下面的循环。

 

循环步骤与上面写入的命令字循环类似,但是SCLK是先0再1,原因是READ的SCLK的频率中比WRITE的少一次上升,为15次上升。

为了使得下面读取数据时不至于读取不到第一位数据(因为是下降沿读取,所以最后如果SCLK置0的话,会导致第一位数据已经读取出来,但没有接收而导致遗漏的情况),因此需要先0再1。

最后配置读取设置的寄存器里面数据的循环

解释if(DS1302_IO)  Data|=(0x01<<i)  :

首先利用左移运算符,使得每次将八个位的二进制数唯一一个1移动位置;

然后通过 | 运算符(全0为0,有1置1)来将1所在的位置,填入Data中,而其他位因为Data初始化为0x00的缘故,所以都是全0,从而实现将1所在的位置对应填入Data中。

(所以需要用判断语句,使得为0时的位,不会进入语句中置1)

最后将得到的值赋予Data,完成每次的更新读取数据。

最终效果

 

 

DS1302.h文件

 

Ⅳ、测试前面写入的代码是否有效

 根据上面秒数的寄存器地址,写入对应代码,填写相应地址。

 编译后,可在LCD_1602液晶屏中看到对应显示,说明代码有效。

接着将读取与显示秒数的语句移入循环:

 

可以看到秒数以一定速度加一,但是在到9时会突然变为16,是因为DS1302内部寄存器是以BCD码的方式进行存储的(非二进制的方式进行存储)。

Ps:

当移入循环后,发现数字没有增长跳动的话,记得将写保护模式关闭(即上面的DS1302_WriteByte(0x8E,0x00);

补充:BCD码

 

即BCD码通过4位二进制表示1位十进制数,

且高四位的二进制表示十位,低四位的二进制表示个位。

因此4位二进制中对应十进制的0~9的数字是合法的,而出现大于9的数字表示时,是不合法的(如上面的1010,为十进制的10,因此不合法)

解释9突变为16的原因:

 

因为当数据为0000 1001(即数字09)时,因为个位为9,加一变成10,经过BCD码的转换,便是在高四位的二进制数显示1(即二进制的0001),低四位的二进制数显示0(即二进制的0000)来表示数字正常进位。

但是0001 0000经过十进制的转换,便是16,因此才会出现跳跃进位显示的原因。

当使用进行显示时,可以发现没有跳跃进位的现象,是因为这个函数的显示是以四位二进制数为一位显示的,所以当没有超过10时,也就不会出现A这种字母,自然可以显示成与正常进位一样。

 

BCD码转十进制:DEC=BCD/16*10+BCD%16; (2位BCD)

这里将BCD码除以16是因为如同上面的一样,BCD码到二进制的9加一后进位到下一位,而十六进制则是到F(十进制的15)加一后进位到下一位,只要把十六进制A~F的数砍掉,到9进位,就可以实现如同BCD码一样的进位方式。

因此将BCD/16再*10,就可以将数转化为十进制数。

十进制转BCD码:BCD=DEC/10*16+DEC%10; (2位BCD)

这一个转化与上面同理,只是将顺序倒转即可。

因此将显示函数写为时,即可解决突跃问题。

 

补充:DS1302寄存器一些位的作用

 

①在秒寄存器中,可以见到次前三位的(6、5、4)二进制位负责存储十位的秒数(即BCD码的存储),

而在最高位中为CH,这个位作用是控制时钟暂停。当给1时,时钟暂停;给0时,时钟运行。

②在时寄存器中,最高位为1时,设置为12小时模式;为0时,设置为24小时模式。

当为24小时模式时,第三位(5)就与第四位(4)一同显示小时的十位。

当为12小时模式时,第三位(5)显示AM(0代表AM)与PM(1代表PM),而第四位(4)单独显示小时的十位。

③在其他的寄存器中,则可以很明显的看到寄存器以BCD码的形式显示数据。(前四位为十进制的十位,后四位为十进制的个位)

Ⅴ、设置存储初始时间的数组

在DS1302.c文件写入存储初始时间的数组。

 

通过这种方法,便于直接读取数据,防止不知道对应数字的含义,显得混乱。

Ⅵ、设置写入时间的函数

在DS1302.c文件中编写写入时间的函数,这样就无需将年、月、日、时、分、秒、星期的写入每次都在主函数调用,直接实现一步到位。

①写入模板

 

②重新定义DS1302的寄存器

将DS1302相应的时间寄存器重新定义名字,以便后面不用再去翻看手册查地址,可以直接明白对应含义并操作对应寄存器。

③将写保护模式关闭(避免无法写入的情况)

 

④对之前的读取函数添加一条处理

通过下面的语句,直接将前面定义的地址最低位赋1,将地址变成读取寄存器地址,这样可以不用再重新定义读取的寄存器地址。

 

⑤将相应的寄存器的写入函数进行调用

 

Ⅶ、设置读取时间的函数

①写入模板

 ②将相应的寄存器的读取函数进行调用

 在每次调用后,用变量Temp接收返回的读取值,并转化为十进制后存储进时间数组中,便于主函数调用。

Ⅷ、将新加的内容的声明添加进DS1302.h文件中

 Ps:这里在数组前面加入extern,是因为当声明变量或数组为外部可调用时,需要加入关键字extern。(当然数组其实可以不加,但是加上比较好)

Ⅸ、在主函数中添加液晶屏的时间显示

 

Ⅹ、将程序烧录进单片机,即可看到预期效果

main.c文件

 

这里因为没有调用Delay文件,所以可以选择删除掉。

代码二

实现效果:通过按下独立按键K1,进入设置时间模式(再次点击退出设置);按下独立按键K2,可以在设置模式下,选择设置年、月、日、时、分、秒;按下独立按键K3,对选中的部分加1;按下独立按键K4,对选中的部分减1。

而在设置时间模式时,选中的数字会进行闪动。

Ⅰ、将上面的代码文件复制一份,并重命名

Ⅱ、将需要的已经模块化的代码添加进工程目录下

 

这里的Key文件为独立按键的文件,Timer0为定时器0的文件

Key.c

 

Timer0.c

 

Ⅲ、将显示时间的液晶屏函数代码单独移出成为一个函数

 

这样能使main函数更加简洁

Ⅳ、添加时间设置函数

①写入框架

 

②定义需要用到的变量为全局变量

 

③设置独立按键K2的效果

 

④设置独立按键K3的效果

 这里的时、分、秒只需要利用if语句,当达到上限时归零即可。

而这里的年、月、日加入判断条件比较复杂,目的是为了实现下面的效果:

一、由于存在闰年,单双数月导致的日期上限不同的原因,需要加入比较多的条件语句,以实现当闰年时,二月的最大天数为29,设置年份不是闰年时,二月从原先的29能跳到1。

二、当设置月份时,如果日期的数字为当前设置月份所不存在的天数,将日期部分归1。

三、当设置日期时,能被月份、年份所限制,不同的月份与年份的日期极限不同。

⑤设置独立按键K4的效果

 这里的时、分、秒只需要利用if语句,当达到上限时归零即可。

而这里的年、月、日加入判断条件比较复杂,目的与独立按键K3相同。

Ps:当设置的读取数据返回值为unsigned时,从0再减1会直接变到当前变量类型的最大值,所以得小心这一点判断。

⑥设置更新显示的效果

 

Ⅴ、设置中断函数实现进入设置时,设置数字闪烁效果

 

Ⅵ、编辑主函数部分

添加独立按键K1的效果

 

Ⅶ、将程序烧录进单片机

烧录进单片机,即可看到预期效果。

main.c文件

 

 

 

 

 

 Ps:

该代码仍存在缺陷,比如按住独立按键时,时钟无法进行走动,因此可以继续进行改进。

原因:在key.c文件中,有while循环,使得按住时,CPU直接卡住无法继续下一步工作。

 

可以通过定时器中断来扫描按键,对按键的上升沿或下降沿进行单独捕获,这样既可以做好消抖工作,又不会因为按住按键时导致时钟无法走动。

尝试:

可以利用定时器来延时,并将key文件中的内容进行更改即可。

下面以矩阵键盘的更改代码为例,独立按键类推即可。

MatrixKey.c文件

 

 

 MatrixKey.h文件

 

这里利用again将代码分成两部分,

一部分负责检测是否有按下(按下时电平为0),通过Now变量记录按下的是哪一个按键,并将again赋1,进入第二部分;

另一部分负责检测按键是否有松开(松开时电平恢复为1),通过与Now变量配合,锁定对应的按键,

最终实现按下按键并松开后,返回对应的键码;同时实现了按住按键时不会返回键码,且不会将CPU卡在某一步代码中无法出来。

经过测试,该代码文件也可实现不依靠定时器的情况下,实现消抖。

(利用上升沿与下降沿的原理,可以将again放出来,不放在if语句内;但是如果配合定时器使用的话,放进if语句里面能更好的减少延时时间)

;