C51单片机设计之时钟篇(加温控模块)

目录

需求分析..................................................................................................................... 1

需求分析.......................................................................................................................... 2

1.1前言..................................................................................................................... 2

1.2研究价值及意义................................................................................................... 2

1.3任务目标.............................................................................................................. 2

二、总体设计................................................................................................................... 3

2.1设计思路.............................................................................................................. 3

2.2总体框架.............................................................................................................. 3

2.3主要元件说明....................................................................................................... 1

2.3.1 STC89C52单片机....................................................................................... 1

2.3.2 LCD1602液晶显示器.................................................................................. 1

2.3.3 24C02C存储芯片....................................................................................... 2

2.3.4 DS1302时钟芯片........................................................................................ 3

2.3.5 定时器中断................................................................................................ 4

三、详细设计................................................................................................................... 5

3.1 液晶显示模块设计............................................................................................... 5

3.1.1 硬件设计................................................................................................... 5

3.2 时间产生模块设计............................................................................................... 8

3.2.1 硬件设计................................................................................................... 8

3.2.2 软件设计................................................................................................... 8

3.3 数据存储模块设计............................................................................................. 10

3.3.1 硬件设计................................................................................................. 10

3.3.2 软件设计................................................................................................. 10

3.4 按键处理模块设计............................................................................................. 12

3.4.1 硬件设计................................................................................................. 12

3.4.2 软件设计................................................................................................. 13

3.5 秒表模块设计.................................................................................................... 16

3.5.1 软件设计................................................................................................. 16

四、程序运行结果测试与分析........................................................................................ 17

4.1 LCD1602功能测试............................................................................................. 18

4.2 DS1302功能测试............................................................................................... 19

4.3 24C02C功能测试............................................................................................... 19

4.4 调整时间功能测试............................................................................................. 20

拓展温度模块测试................................................................................................... 21

一、需求分析

1.1前言

单片机(Single-Chip Microcomputer)是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而完善的微型计算机系统,在工业控制领域广泛应用。它不是完成某一个逻辑功能的芯片,而是把一个计算机系统集成到一个芯片上。概括的讲:一块芯片就成了一台计算机。它的体积小、质量轻、价格便宜、为学习、应用和开发提供了便利条件。现如今,人们越来越重视单片机在智能电子技术方面的开发和应用,单片机的发展进入到新的时期,无论是自动测量还是智能仪表的实践,都能看到单片机技术的身影。当前工业发展进程中,电子行业属于新兴产业,工业生产中人们将电子信息技术成功运用,让电子信息技术与单片机技术相融合,有效提高了单片机应用效果。作为计算机技术中的一个分支,单片机技术在电子产品领域的应用,丰富了电子产品的功能,也为智能化电子设备的开发和应用提供了新的出路,实现了智能化电子设备的创新与发展。

1.2研究价值及意义

电子钟亦称数显钟(数字显示钟),是一种用数字电路技术实现时、分、秒计时的装置。与机械时钟相比,直观性为其主要显着特点,因非机械驱动,具有更长的使用寿命,相较石英钟的石英机芯驱动,更具准确性。电子钟已成为人们日常生活中必不可少的必需品,广泛用于个人家庭以及车站、码头、剧院、办公室等公共场所,给人们的生活、学习、工作、娱乐带来极大地方便。相对于其他时钟类型,它的特点可归结为“两强一弱”:比机械钟强在观时方便,比石英钟强在走时准确,但是它的弱点为显时较为单调。

1.3任务目标

(1)可以分别对年、月、日、时、分钟和秒进行设置;

(2)利用DS1302时钟芯片写入和读取时间数据;

(3)在LCD1602显示屏上显示时间;

(4)当按下复位键时,时钟实现复位,秒复位键按下后,秒回到00;

(5)用24C02C芯片存储和读取数据。

 

24c02芯片                    DS1302模块                LCD1602显示屏

二、总体设计

2.1设计思路

(1)定义一个表示时间的数组,声明为外部变量,可以在各个模块中进行访问,以此来建立它们之间的关联。

(2)电子时钟的时间数据电DS1302实时时钟芯片来产生并显示在LCD1602上显示;通过定时器扫描按键的方式得到按键按下的键值,在主程序中根据得到的键值调用不同的按键处理函数(调时、保存数据、读取数据等),当要对时间数据进行操作时,可以先对数组中的数进行操作,操作完成后再把数组中的各个元素写入DS1302芯片。

(3)把各种模块需要实现的功能都封装成函数并在头文件中声明。例如LCD1602除了需要显示时间这个数字类型外,还需要显示字符、字符串等,所以可以定义多个函数来实现显示各种数据类型。

2.2总体框架

本项目主要包含这么四个模块,分别是DS1302时钟模块、LCD1602显示模块、按键处理模块以及EEPROM存储模块。总体框架如图一所示,流程图如图二所示。

   

 

图一 总体框架

 

图二 流程图

2.3主要元件说明

2.3.1 STC89C52单片机

  STC89C52是一种典型的8位单片机,由STC公司生产。它是基于MCS-51架构的微控制器,具有高性能、低功耗、多功能、易于编程等特点。STC89C52芯片内置了8KB的闪存、256字节的RAM、32个I/O口、3个定时器/计数器、1个串口、1个SPI接口等丰富的外设功能,可以广泛应用于工业控制、仪器仪表、电子设备、通信设备等领域。

STC89C52的主要特点如下:

1. 采用MCS-51架构,具有高性能和低功耗的特点。

2. 内置8KB的闪存和256字节的RAM,可以存储大量的程序和数据。

3. 32个I/O口可以连接各种外设和传感器,实现复杂的控制和数据采集任务。

4. 3个定时器/计数器可以实现多种定时和计数功能,如PWM波形生成、脉冲计数、定时中断等。

5. 1个串口和1个SPI接口可以实现与其他设备的通信,如PC机、传感器、LCD显示屏等。

6. 支持多种编程方式,如ISP、IAP、UART等,方便用户进行程序的下载和调试。

7. 采用CMOS工艺,工作电压范围广,可靠性高。

总之,STC89C52是一种功能强大、易于使用、性价比高的单片机,适用于各种嵌入式应用场景。

 

 

  2.3.2 LCD1602液晶显示器

  LCD1602液晶显示屏是一种字符型液晶显示模块,可以显示ASCll码的标准字符和其他一些内置的特殊字符,还可以内置8个自定义字符。显示容量:16×2个字符,每个字符为5*7点阵或5*10点阵, LCD1602有8位数据总线DO-D7,和RS、R/W、EN三个控制端口,工作电压为5V并且带有字符对比度调节和背光。引脚定义如图3所示

编号

1

2

3

4

5

6

7

8

符号

VSS

VDD

VL

RS

R/W

E

D0

D1

引脚说明

电源地

电源正极

液晶显示偏压

数据/命令选择

使能信号

数据

数据

数据

编号

9

10

11

12

13

14

15

16

符号

D2

D3

D4

D5

D6

D7

BLA

BLK

引脚说明

数据

数据

数据

数据

数据

数据

背光源正极

背光源负极

                      图3 引脚定义

第1引脚:GND为电源地。

第2引脚:VCC接5V电源正极。

第3引脚:V0为液晶显示器对比度调整端

第4引脚RS为寄存器选择,离电平1时选择数据寄存器低电平0时选择指令寄存器

第5引脚RW为读写信号线,高电平1时进行读操作,低电平(0)时进行写操作。

第6引脚:E(或EN)端为使能(enable)端,高电平1时读取信息,负跳变时执行指令。

第7-14引脚DO-D7为8位双向数据端。

第15-16引脚空脚或接电源。

第15引脚接背光正极。

第16引脚接背光负极。

2.3.3 24C02C存储芯片

(1)引脚功能介绍及相关知识

24C02芯片是一种EEPROM存储芯片,可以存储256个字节的数据,工作电压为1.8V-5.5V。它具有低功耗CMOS技术、自定时擦写周期、1000000次编程/擦除周期、可保存数据100年等特点。该芯片通过I2C总线通讯读写数据,通讯时钟频率可达400KHz。它可以在电脑上或专用设备上擦除已有信息,重新编程,即插即用。24C02芯片还有一个8字节缓冲器和一个写保护功能。因此,24C02芯片主要用于存储数据,如单片机程序、传感器数据等。引脚定义如图4所示

引脚名称

A0-A2

SDA

SCL

WP

VCC

GND

引脚功能

器件地址输入

串行数据输入输出

串行时钟输入

写保护

电源

图4 引脚定义

(2)12C总线协议

只有总线非忙时才被允许进行数据传送,在传送时,当时钟线为高电平,数据线必须为

固定状态,不允许有跳变。时钟线为高电平时数据线的任何电平变化将被当作总线的启动或停止条件。

(3)起始条件

起始调教必须在所有操作命令之前发送。时钟线保持高电平期间,数据线电平从高到低

跳变作为I2C总线的启动信号。SDA和SCL电平信号一直被监视,直到条件满足时才响应。

(4)停止条件

时钟线保持高电平期间.数据线电平从低到高跳变作为I2C总线的停止信号。

(5)器件地址约定

主器件在发送启动命令后开始传送数据,主器件发送相应的从器件地址,8位从器件地址的高四位固定为1010,接下来的3位用来定义存储器的地址,对于CAT24C021/022,这三位无意义,对于CAT24C41/042,接下来的2位无意义,第三位是地址高位,CAT24C081/082中第一位无意义,后两位表示地址高位。最后一位为读写控制位,“1”表示对从器件进行读写操作,“0”表示写操作。在主器件发送启动命令和一字节从器件地址后,如果与从器件地址吻合,CAT24C02将发送一个应答信号,然后再根据读/写控制为进行读或写操作。

(6)应答信号

每次数据传送成功后,接收器件将发送一个应答信号。当第九个时钟信号产生时,产生应答型号的器件将SDA下拉为低,通知已经接受到8位数据,接收到起始条件和从器件地址后,CAT24C02发送一个应答信号,如果为写操作,每接收到一个字节数据,CAT24C02发送一个应答信号。如果为读操作,CAT24C02发送一个字节数据后释放总线等待应答信号接收到应答信号,它将继续发送数据,如果接收到主器件发送非应答信号,将结束数据传送等待停止条件。

(7)写操作

在写操作模式下,主器件发送起始命令和从器件地址信息给从器件,在从器件响应应答

信号后,主器件将要写入的数据地址发送到CAT24C02的地址指针,主器件收到从器件的应答信号后再送数据到相应的数据存储。CAT24C02再响应一个应答信号,主器件产生一个停止信号,然后CAT24C02启动内部写周期。在内部写如周期期间,CAT24C02不再响应主器件任何请求。

 

图5写操作时序图

(8)读操作

CAT24C02读操作的初始化方式和写操作一样,仅把R/W位置1读操作有三种方式:立即地址读、选择地址读、连续读。

 

图6读时序图

2.3.4 DS1302时钟芯片

DS1302是一个实时时钟芯片,可以提供秒、分、小时、日期、月、年等信息,并且还有自动调整的能力,可以通过配置AM/PM来决定采用24小时格式还是12小时格式,它的引脚及功能如下表所示。

引脚名称

引脚功能

Vcc2

主电源引脚,当Vcc2比Vcc1高0.2V以上时,DS1302由Vcc2供电,当Vcc2低于Vcc1时,由Vcc1供电。

X1

这两个引脚需要接一个32.768K的晶振,给提供一个基准

X2

晶振的引脚负载电容必须是6pF,而不是要加6pF的电容。如果使用有源晶振的话,接到X1上即可,X2悬空。

GND

接地

CE

DS1302的输入引脚。当读写DS1302的时候,这个引脚必须是高电平,DS1302这个引脚的内部有一个40K下拉电阻。

I/O

这个引脚是一个双向通信引脚,读写数据都是通过这个引脚完成。DS1302这个引脚的内部有一个40K下拉电阻

SCLK

输入引脚。SCLK是用来作为通信的时钟信号。DS1302这个引脚的内部含有一个40K的下拉电阻。

Vcc1

备用电源引脚。

图6引脚及功能表

 

                           图7单字节输入时序图

由图可见,先拉高使能端,进行使能选择,然后在时钟上升沿写入一个字节。DS1302在进行读写操作时最少读写两个字节,第一个是控制字节,就是一个命令,说明是读还是写操作,第二个是需要读写的数据。对于单字节写,只有在SCLK为低电平时才能将 CE 置高电平,所以刚开始将SCLK 置低,CE置高,然后把需要写入的字节送入 IO口,然后跳变SCLK,在SCLK下降沿时,写入数据。

2.3.5 定时器中断

51单片机有五个中断源

  1. INT0——外部中断0
  2. INT1——外部中断1
  3. T0——定时器/计数器0中断
  4. T1——定时器/计数器1中断
  5. TI/RI——串行口中断

1.定时器0选择工作方式1

D7

D6

D5

D4

D3

D2

D1

D0

GATE

C/T

M1

M0

GATE

C/T

M1

M0

这是定时器/计数器工作方式寄存器TMOD,不能位寻址(只能一次操作一个字节,八位,不能单独操控某一位)。

TMOD高四位用于设置定时器/计数器1,低四位用于设置定时器/计数器0;

GATE是门控制位,当GATE=0,定时器/计数器启动与停止仅对TCON寄存器中的TRX(X=0,1)控制;我们对TMOD操作时,直接让GATE这一位为0。

CT位为1时选择计数器模式,为0时选择定时器模式。

M1,M0用于选择工作方式,M0=1,M1=0时,选择的是方式一。

综上,在编程的时这样写(八位数的二进制换成两位数的16进制,0000 0001 ——0x01)

TMOD=0x01;

2. 给定时器0装入初值

上面选择的工作方式1是16位定时器。对于T0来说,TL0寄存器作为低八位,TH0作为高八位,组成16位加减计数器。

当开始计数的时候,低八位计数计满,低八位全部归零,向高八位进一位。当高八位计数计满时,全16位归零,进入中断函数,执行相关操作。

之所以要给定时器装初值,是为了计入整数,方便计算使用。

每50ms进入一次中断函数的初值为:

TH0=(65536-45872)/256; TL0=(65536-45872)%256;

3.打开总中断,开启定时器0中断,启动定时器0

首先涉及到中断允许寄存器IE(可以进行位寻址,对某一位操作),全局中断允许位EA,定时器T0中断允许位ET0便在其中。

打开总中断,即EA=1;

开起定时器0中断,即ET0=1;

启动定时器涉及到定时器/计数器控制寄存器TCON(可进行位寻址),定时器0运行控制位TR0便在其中。

启动定时器0,即TR0=1;(在上述GATE=0的前提下)

三、详细设计

3.1 液晶显示模块设计

在液晶显示模块需要实现的功能有:显示字符串、显示字符、显示十进制数字。字符串用来表示提示信息,字符用来表示“:”、“-”等特殊符号,时间数据用十进制表示。

3.1.1 硬件设计

液晶显示模块的硬件连接图如下图所示,LCD1602的VSS与地相连、VDD与电源相连、RS与单片机的P3^3口相连、RW与P3^4口相连、E与P3^5口相连、D0-D7通过上拉电阻连接到单片机的P0口(VEE用于调节对比度,此处没必要连接)。所以给LCD1602送入数据也就是给单片机P0口送数据,再通过单片机的P3^3(RW)、P3^4(RS)、P3^5(E)控制LCD的时序使其正常工作于各个过程。

考虑到代码的可读性,首先在程序开头对一些需要使用到的位变量和数据端口进行了定义,代码如下。

sbit LCD_RS=P3^3;

sbit LCD_RW=P3^4;

sbit LCD_EN=P3^5;

#define LCD_DataPort P0

在使用LCD1602时,我们通过写入指令来控制待显示数据在液晶显示器上的位置以及设置LCD1602的参数;通过写入数据来表示具体要显示的内容。接下来,依次对LCD1602模块中的各个函数进行介绍。

(1)写指令函数。函数的参数即为待写入的指令。通过改变RS、RW和E这三个引脚的电平变化来对写指令的时序进行模拟,代码如下。

/**

  * @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();

}

(2)写数据函数。函数的参数即为待写入的数据。写入数据和写入指令唯一的区别是RS引脚的电平状态不同,RS为低电平期间写入的是指令,为高电平期间写入的是数据,具体代码如下。

/**

  * @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();

}

(3)初始化函数。LCD1602在使用之前需要对它的工作方式进行配置,具体的代码和注释如下。

/**

  * @brief  LCD1602初始化函数

  * @param  无

  * @retval 无

  */

void LCD_Init()

{

       LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵

       LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关

       LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动

       LCD_WriteCommand(0x01);//光标复位,清屏

}

(4)其他函数。为了方便调用液晶显示器,编写了以下三个函数。

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);

3.2 时间产生模块设计

时间产生模块需要实现的功能有:通过DS1302实时时钟芯片实现时间的写入和读取。

3.2.1 硬件设计

DS1302与单片机的硬件接线如下图所示。在DS1302的X1、X2管脚处外接了一个频率为32.768KHZ的晶振,为时钟运行提供一个稳定的时钟频率。RST、CLK、DAT分别与单片机的P3_1、P3_2、P3_0连接,串行数据通过I/O引脚在单片机和DS1302之间进行传输。

3.2.2 软件设计

在程序的最开始首先对需要用到的位变量进行定义和DS1302中特殊意义的常量进行宏定义,此外还定义了一个储存时间的全局变量,具体代码及注释如下。

#define DS1302_SECOND            0x80

#define DS1302_MINUTE             0x82

#define DS1302_HOUR                 0x84

#define DS1302_WP                  0x8E

sbit DS1302_RST=P3^1;

sbit DS1302_SCLK=P3^2;

sbit DS1302_IO=P3^0;

                                                                                                                  

char DS1302_Time[]={23,59,56}; //保存时间信息 时 分 秒

在此模块中最重要也是最基本的两个函数是写入一个字节的函数(即设置时间)和读取一个字节的函数,下面分别对其进行介绍。

(1)单字节写函数。函数的第二个参数表示待写入的数据,第一个参数表示操作指令。两个8位数据从高位开始逐位通过I/O管脚写入DS1302。具体代码如下。

/********************************************************

*****************参数:   cmd表示指令,dat表示数据

*****************作用:DS1302单字节写

*****************返回值:

**********************************************************/

void DS1302_WriteByte(unsigned char cmd,dat)

{

       unsigned char i;

       DS1302_RST=1;

       for(i=0;i<8;i++)

       {

             

              DS1302_IO=cmd&(0x01<<i);

              DS1302_SCLK=1;

              DS1302_SCLK=0;

       }

       for(i=0;i<8;i++)

       {

             

              DS1302_IO=dat&(0x01<<i);

              DS1302_SCLK=1;

              DS1302_SCLK=0;

       }

       DS1302_RST=0;

       DS1302_SCLK=0;

}

(2)单字节读函数。函数的参数是操作指令,返回值为读取到的数据。要求指令的最后一位必须为1,而单字节写要求为0。所以在代码中对参数cmd进行了处理,确保末位为1,具体代码如下。

/********************************************************

*****************参数:   指令

*****************作用:   DS1302单字节读

*****************返回值:DS1302读到的数据

**********************************************************/

unsigned char DS1302_ReadByte  (unsigned char cmd)

{

       unsigned char i,DATA=0x00;

       cmd|=0x01;

       DS1302_RST=1;

       for(i=0;i<8;i++)

       {

              DS1302_IO=cmd&(0x01<<i);

              DS1302_SCLK=1;

              DS1302_SCLK=0;

       }

       for(i=0;i<8;i++)

       {

              if(DS1302_IO)DATA=DATA|(0x01<<i);

              DS1302_SCLK=1;

              DS1302_SCLK=0;

       }

       DS1302_RST=0;

       return DATA;

}

(3)设置时间函数。为了在其他模块中更好的使用这个模块,我们,为了避免了过于繁琐的调用单字节写和读函数,另外定义了两个函数分别用于读取时间和设置时间。具体代码如下。

/********************************************************

 *****************参数:

 *****************作用:用BCD码设置时间

 *****************返回值:

 **********************************************************/

void setTime  ()

{

       DS1302_WriteByte(DS1302_WP,0x00);

       DS1302_WriteByte(DS1302_HOUR,DS1302_Time[0]/10*16+

DS1302_Time[0]%10);

       DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[1]/10*16+

DS1302_Time[1]%10);

       DS1302_WriteByte(DS1302_SECOND,DS1302_Time[2]/10*16+

DS1302_Time[2]%10);

       DS1302_WriteByte(DS1302_WP,0x80);

}

说明:由于时间在DS1302芯片中是以BCD码的形式进行存储的,而数组中的元素是十进制,所以我们要把十进制转化成BCD码再写入DS1302;读取时间则反过来把BCD码转化成十进制。 

3.3 数据存储模块设计

本模块使用基于I2C协议的存储芯片24C02C来保存时间数据,需要实现的功能包括写入数据和读取数据。

3.3.1 硬件设计

24C02C与单片机的接线图如下图所示,时钟线SCK由单片机P2_1口控制,数据线SDA由P2_0口控制,写保护管脚WP和表示地址的A0-A2均接地。因此,24C02C的写保护处于失能状态,也就是说24C02C始终可读可写,且地址码为1010000X。下面的I2C Debug模块在仿真中用于验证存储的信息是否准确无误。

3.3.2 软件设计

首先对一些需要使用到的位变量进行定义,具体的代码如下。

sbit I2C_SDA=P2^0;

sbit I2C_SCL=P2^1;

24C02C是一种遵循I2C总线协议的存储芯片,它的数据帧由I2C模块的函数拼凑而成。I2C模块的函数声明如表3.1所示。

函数原型声明

意义

void l2C_Start()

起始信号

void l2C_Stop()

终止信号

l2C_SenByte(unsigned char Byte)

发出一个字节

Unsigned char l2C_Byte()

接收一个字节

void l2C_SendAck(unsigned char AckBit)

发送答应信号

unsigned char l2c_ReceiveAck(void)

接收应答信号

图8  l2c模块函数声明表

接下来正式介绍24C02的发送和接收数据函数。

(1)发送数据函数。函数的第一个参数表示的是要发送的地址,第二个参数为待发送的数据,ADDRESS表示器件的地址码,由硬件接线确定,此处为0xA0。具体的代码如下。

void _24C02_WriteByte(unsigned char WordAddress,Data)

{

       I2C_Start();

       I2C_SendByte(ADDRESS);//0xA0

       I2C_ReceiveAck();

       I2C_SendByte(WordAddress);

       I2C_ReceiveAck();

       I2C_SendByte(Data);

       I2C_ReceiveAck();

       I2C_Stop();

}

(2)接收数据函数。参数为需要接收数据的地址,返回值为该地址中的数据。在接收数据的过程中要求ADDRESS的末位为1。函数的具体代码如下。

unsigned char _24C02_ReadByte(unsigned char WordAddress)

{

       unsigned char Data;

       I2C_Start();

       I2C_SendByte(ADDRESS);

       I2C_ReceiveAck();

       I2C_SendByte(WordAddress);

       I2C_ReceiveAck();

       I2C_Start();

       I2C_SendByte(ADDRESS|0x01);

       I2C_ReceiveAck();

       Data=I2C_ReceiveByte();

       I2C_SendAck(1);

       I2C_Stop();

       return Data;

}

3.4 按键处理模块设计

本模块需要实现的功能包括按键扫描和按键处理。通过定时器扫描按键的方式对按下的按键进行检测,根据得到的键值调用不同的方法。

3.4.1 硬件设计

按键的硬件连接如下图所示。扫描法 行扫描法又称为逐行(或列)扫描查询法,是一种最常用的按键识别方法,如图1所示键盘,介绍过程如下。

1、判断键盘中有无键按下 将全部行线Y0-Y3置低电平,然后检测列线的状态。只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线与4根行线相交叉的4个按键之中。若所有列线均为高电平,则键盘中无键按下。

2、判断闭合键所在的位置 在确认有键按下后,即可进入确定具体闭合键的过程。其方法是:依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平。在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。

       

 

我们采用薄膜键盘代替矩阵键盘实现其功能。经过查找资料和研究相应原理,端口和引脚,我们成功引入薄膜键盘来进行相应操作。

       

 

3.4.2 软件设计  

由于按键的器件特性,在实际使用过程中无论是在按下按键还是松开按键的瞬间都会存在抖动,因此必须要消抖。按键消抖的方式有两种:硬件消抖和软件消抖,本报告采用的是软件消抖。抖动的时间一般不会超过20ms,所以利用单片机的定时器中断函数进行消抖,每隔20ms对按键进行一次扫描,按键扫描程序流程图如下所示。

                         

 

                          图9 按键扫面流程图

部分代码展示如下:

unsigned char MatrixKey()

{

      unsigned char KeyNumber=0;

      P1=0xFF;

      P1_3=0;

      if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}

      if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}

      if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}

      if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}

P1=0xFF;

      P1_2=0;

      if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}

      if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}

      if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}

      if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}

.

.

.

在通过按键扫描程序得到键值后,由主程序根据不同的键值调用不同的功能模块。按键处理的流程图如下图所示。

                                                                                 

 

图10  按键处理流程图

主函数调用代码如下:

void main()

{

      time();

      LCD_Init();

      setTime();

      show();

      while(keynum==0){

     

      keynum=MatrixKey();

      }

      LCD_Init();

      while(1)

      {

             DS1302_ReadTime();

             key();

             LCD_show();

      }

}

在设置时间功能模块需要对每一次设置的时间进行判断,判断的流程图如下所示。

 

图11  时间设置流程图

3.5 秒表模块设计

3.5.1 软件设计

void 函数名() interrupt 中断号

{

中断服务程序内容

}

主要代码如下:

void t() interrupt 3

{

       TH1=0xFC;

   TL1=0x18;

    n++;

       m=jishu[1]*10+jishu[0];

       f=jishu[3]*10+jishu[2];

    if(n==1000)

    {

        n=0;

        m++;

                   

        if(m==60)

        {

            m=0;

            f++;

            if(f==60)

            {

                f=0;

            }

        }

        jishu[0]=m%10;

        jishu[1]=m/10;

        jishu[2]=f%10;

        jishu[3]=f/10;

             show1();

    }

}

四、程序运行结果测试与分析

系统的总体原理图如下所示,接下来分别对各个模块进行测试。

 

图4-1 总体原理图

4.1 LCD1602功能测试

(1)显示字符串。在第一行第一列开始显示第一名同学的“9213020230”,在第二行第一列开始显示第二名同学的“9213020210”,接着在第一行第一列开始显示第三名同学的“J2022002”。测试结果如下所示。

 

图4-2  LCD1602显示字符串测试

(2)显示时钟时间。按下8键,切换至时钟模式,并在第一行展示时间;按下1进行秒数清零;通过2键选择“时”、“分”、“秒”位,并通过3和4键分别进行加减。这个功能是可以随时调整正确时间,且断电后可以继续正常自走。测试结果如下所示。

                 

                    

 

图4-3  LCD1602显示时间测试

(3)显示计数。按下5键,切换至计数模式,在第二行显示开始计数;再按下6键,计数停止;按下7键对计数进行清零。测试结果如下所示。

                     

 

图4-4  LCD1602显示计数测试

4.2 DS1302功能测试

(1)设置时间。在程序中给DS1302设置一个的时间“23年05月31日00点00时00分22秒,星期二”,测试结果如下。

                            

 

图4-5  DS1302设置时间测试

  1. 读取时间。把DS1302中的数据读取出来显示在LCD1602上。测试结果如下。

                            

 

图4-6  DS1302读取时间测试

4.3 24C02C功能测试

(1)写入数据。把时钟芯片中目前的时间写入24C02C,暂停仿真后打开I2C Debug模块,测试结果如下。

                     

 

图4-7  24C02C写入数据测试

说明:DS1302的时间是“20年01月01号00是01分16秒,星期3”,所有的信息都储存在24C02C芯片以0号地址开始的7个单元中(以十六进制存储)。

(2)读取数据。把上一步存储的信息读出来显示在LCD1602上,测试结果如下。

                            

 

图4-8  24C02C读取数据测试

4.4 调整时间功能测试

通过按键把时间设置成08:08:08,测试结果如下。

                            

 

图4-9  按键调整时间测试

可在仿真上读取当今的时间,如2023.6.1,结果如下:

                            

                            

 

图4-9  当今时间测试

                             

五、拓展温度模块测试

①首先依次显示的是时间“2023-6-2”,“31组”等。

②接着显示“温度”,“最高温度限制”和“最低温度限制”。

 

注:我们的设计初衷是制作一个定时加热器,将时钟定时计数功能和温控模块功能巧妙结合一起,但由于缺少加热模块,我们便制作了温度显示功能。

若需要代码,原理图,联系1766872722@qq.com,备注来意。谢谢点赞关注
  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现实时时钟,电源拔插时间不重置(内置电池供电维持时间变化)。功能有查看日期,查看星期,查看闹钟和分别的调整时间,调整日期,调整闹钟,调整星期。且默认情况下不可以调整,当按下可调键之后才可以调整、再次按可调键关闭变为不可调整状态。还有整点报时功能(有个小瑕疵就是闹钟正好是整点的时候和这个整点报时同时间的时候不会报时和闹钟,这个很容易改好,因为下午3点要答辩了,懒得改了机子老是写不进去,学校穷便宜机子没办法)。然后这个闹钟可以设置星期几几点闹也可以设置为普通的每天的这个时间点闹铃,这些都是可以调的。时钟所有的功能都有,只差一个秒表,,这个很简单,,,外部中断来一个或者定时器T1中断来一个都可以,我没弄,因为我这个已经代码很长了,头疼、加中断还得加显示函数和秒表变化函数if分大于60 时++啥的,但因为这个采用的显示是低四位高四位制的,我强行加一个也比较麻烦所有就没加了。欢迎下载干货,难看懂的都有备注,写了断断续续一周+时间左右(恕在下才疏学浅,因为书上上课的时候没学过I2C总线和pcf8563所以写的比较久。)部分代码如下: #define MAIN_Fosc 22118400L //定义主时钟 频率也是计数计时周期一秒的计数值 #include "STC15Fxxxx.H" /***********************************************************/ #define DIS_DOT 0x20 #define DIS_BLACK 0x10 #define DIS_ 0x11 /****************************** 用户定义宏***********************************/ #define Timer0_Reload (65536UL -(MAIN_Fosc / 1000)) //Timer 0 中断频率,1000次/秒 频率倍数计数即周期 周期为1秒 除以一千就是1000次每秒 /*****************************************************************************/ /************* 本地常量声明 **************/ u8 code t_display[]={ //标准字库 // 0 1 2 3 4 5 6 7 8 9 A B C D E F //共阴 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71, //black - H J K L N o P U t G Q r M y 0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e, 0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46}; //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1 u8code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //位码 取反就是书上213面 0从低位到高位 /************* IO口定义 **************/ sbit P_HC595_SER = P4^0; //pin 14 SER datainput sbit P_HC595_RCLK = P5^4; //pin 12 RCLk store(latch) clock sbit P_HC595_SRCLK = P4^3; //pin 11 SRCLK Shift data clock sbit led=P1^7; sbit led1=P4^7; sbit key=P3^2; /************* 本地变量声明 **************/ u8 LED8[8]; //显示缓冲 u8 display_index; //显示位索引 bit B_1ms; //1ms标志 u8IO_KeyState, IO_KeyState1, IO_KeyHoldCnt; //行列键盘变量 u8 KeyHoldCnt; //键按下计时 u8 KeyCode; //给用户使用的键码, 1~16有效 u8 cnt50ms; u8hello; u8nao; u8minute2; u8KeyCode0; u8 hour,hour1,minute,minute1,second,day,week,week1,month,year; //RTC变量 u16 msecond; u16msecond1; u8hello; u8naofu; u8zhuangtai; u8xunhuan; u8tuinao; //闹钟制开启退出 u8xinqinao;//星期·闹钟 /************* 本地函数声明 **************/ void CalculateAdcKey(u16 adc); void IO_KeyScan(void); //50ms call void WriteNbyte(u8 addr, u8 *p, u8 number); void ReadNbyte( u8 addr, u8 *p, u8 number); void DisplayRTC(void); void DisplayRTC1(void); void DisplayRTC2(void); void ReadRTC(void);//读取时钟 void ReadRTC1(void); void ReadRTC2(void); void ReadRTC3(void); void WriteRTC(void); void WriteRTC1(void); void WriteRTC2(void); void WriteRTC3(void); void DisplayRTC3(void); void DisplayRTC4(void); void DisplayRTC5(void); /**************** 外部函数声明和外部变量声明*****************/ /**********************************************/ voidmain(void) { u8 i; P0M1= 0; P0M0 = 0; //设置为准双向口 P1M1= 0; P1M0 = 0; //设置为准双向口 P2M1= 0; P2M0 = 0; //设置为准双向口 P3M1= 0; P3M0 = 0; //设置为准双向口 P4M1= 0; P4M0 = 0; //设置为准双向口 P5M1= 0; P5M0 = 0; //设置为准双向口 P6M1= 0; P6M0 = 0; //设置为准双向口 P7M1= 0; P7M0 = 0; //设置为准双向口 display_index= 0; AUXR= 0x80;//T0时钟无分频 TMOD=0x00; //Timer0 set as 1T, 16 bits timer auto-reload,T0时钟无分频,16位自动重装 TH0= (u8)(Timer0_Reload / 256); //定时器取其高低8位为状态值 TL0= (u8)(Timer0_Reload % 256); ET0= 1; //Timer0 interrupt enable T0中断允许 TR0= 1; //Tiner0 run T0启动 EA= 1; //打开总中断 ,总中断允许 hello=0; if(nao==1){}//断电重置 elseif(nao==2){} else { nao=0; } for(i=0;i= 60) F0 = 1; //错误 if(minute>= 60) F0 = 1; //错误 if(hour >= 24) F0= 1; //错误 if(F0==1) //有错误, 默认12:00:00 { second= 0; minute= 0; hour = 12; WriteRTC(); } if(day>=32) F0=2; if(week>=8) F0=2; if(month>=13)F0=2; if(year>=100)F0=2; if(F0==2) //有错误, 默认12:00:00 { day=6; week=3; month=6; year=18; WriteRTC1(); } if(minute1>= 60) F0 = 3; //错误 if(hour1 >= 24) F0= 3; //错误 if(F0==3) { minute1=30; hour1=7; WriteRTC2(); } if(week1>=8) { WriteRTC3(); } DisplayRTC(); KeyHoldCnt= 0; //键按下计时 KeyCode= 0; //给用户使用的键码,1~16有效 IO_KeyState= 0; IO_KeyState1= 0; IO_KeyHoldCnt= 0; cnt50ms= 0; zhuangtai=0; KeyCode0=0; tuinao=0; minute2=61; naofu=0;//制闹钟加1加5与分钟同步 xunhuan=0;//制循环闹钟 xinqinao=0;//星期闹钟制 if(msecond1==0){} else { msecond1=0; } while(1) { if(B_1ms) //1ms到 { B_1ms= 0; if(++msecond>= 500) //1秒到 刷新时钟 { if(hello==0) { ReadRTC(); DisplayRTC(); if(minute==0) //整点报时 { if(hour==hour1) { if(minute==minute1) {}else{ led1=0; } }else { led1=0; } } else{ led1=1; } if(hour==hour1) //闹钟实现块 { if(second==0)//保证和时钟分钟同步++乘其下一分钟前进行+5 +1操作 { msecond1=0; naofu=1; } else { if(naofu==1) { if(++msecond1>=60) {msecond1=0; naofu=0; } } } if(minute==minute1-1)//取出闹钟分钟值 { ReadRTC2(); minute2=minute1; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值