02- 模块化编程-002 DS1302数码显示时间与日期
1、数码显示时间日期的电路
2、电路原理简介
电路组件与功能
-
单片机( PIC16F887):
- 作为系统的主控芯片,处理所有输入输出,进行时间控制和显示信息更新。
-
DS1302(实时时钟芯片):
- 用于记录当前的日期和时间。
- 通过 SPI 接口与单片机连接,传输时间数据。
- 需要接入一个电池(如 CR2032)以备份时间,即使在断电情况下也能保持计时。
-
MAX7219(LED驱动芯片):
- 驱动数码管显示器,以显示时间和日期信息。
- 支持多个段显示,简化了与单片机的连接。
-
数码管:
- 用于显示时钟信息,例如小时、分钟、秒和日期。
- 通过 MAX7219 进行控制和数据显示。
-
晶体振荡器(X2):
- 连接在 DS1302 上,提供稳定的时钟信号(通常为 32.768 kHz)以确保时间准确计时。
连接说明
-
DS1302 连接:
- VCC 和 GND:连接到电源和接地。
- SCLK:连接到单片机的 SPI 时钟引脚,控制时序。
- IO:双向数据线,单片机与 DS1302 之间传输时间数据。
- CE:使能信号,激活 DS1302 进行数据传输。
-
MAX7219 连接:
- DIN:连接单片机数据输出,用于发送显示数据。
- CLK:连接到单片机时钟信号。
- LOAD:控制数据加载信号,也连接到单片机。
-
数码管连接:
- 通过 MAX7219 驱动各个段,便于显示时间和日期。
- 具体连接方式根据显示要求进行布局。
工作原理
-
初始化:
- 系统开机后,单片机初始化 DS1302,从 DS1302 获取当前时间并更新到显示器上。
-
时间管理:
- DS1302 定期通过其内置时钟更新时间,单片机会定期读取这些数据并处理。
-
数据显示:
- MAX7219 根据单片机发送的信息实时更新数码管上的显示,展示当前时间(小时、分钟、秒)及日期(如“日期”)。
-
电池备份:
- 即使在断电情况下,加入的电池会保持 DS1302 的计时功能,使得时间不会丢失。
3、软件代码
3.1 DS1302.c
//***************************************************************************
//* 文件名: DS1302.c
//* 文件描述: DS1302 实时时钟模块
//* 编译环境: Windows XP + MPLAB IDE 8.83 + PICC 9.60
//* 编程者: bianjingyuan
//* 编程日期: 2020年10月08日
//* 版本号: 1.0
//*
//* 硬件资源分配:
//* ds1302_clk RE0 // 实时时钟时钟线引脚
//* ds1302_io RE1 // 实时时钟数据线引脚
//* ds1302_rst RE2 // 实时时钟复位线引脚
//***************************************************************************#include "DS1302.h"// 设置时间初始值数组 (秒、分、小时、星期、日、月、年)
const uchar time_set[7] = {0x00, 0x38, 0x12, 0x29, 0x09, 0x05, 0x06}; //********************************************************************
//* 函数名: ds1302_Open
//* 功能描述: 打开 DS1302
//* 函数说明: 将 DS1302 复位线和时钟线置为正确状态
//* 调用函数: 无
//* 全局变量: ds1302_rst、ds1302_io、ds1302_clk
//* 输 入: 无
//* 返回: 无
//********************************************************************
void ds1302_Open(void)
{ds1302_clk = 0; // 时钟线拉低ds1302_rst = 0; // 禁止 DS1302ds1302_io = 1; // 释放数据总线ds1302_rst = 1; // 使能 DS1302
}//********************************************************************
//* 函数名: ds1302_close
//* 功能描述: 关闭 DS1302
//* 函数说明: 将 DS1302 复位线和时钟线置为正确状态
//* 调用函数: 无
//* 全局变量: ds1302_rst、ds1302_io、ds1302_clk
//* 输 入: 无
//* 返回: 无
//********************************************************************
void ds1302_close(void)
{ds1302_rst = 0; // 禁止 DS1302ds1302_io = 1; // 释放数据总线ds1302_clk = 1; // 时钟线拉高
}//********************************************************************
//* 函数名: ds1302_write_byte
//* 功能描述: 向 DS1302 写入一个字节数据
//* 函数说明: 通过 DS1302 的时钟线和数据线发送一个字节数据
//* 调用函数: asm("NOP")
//* 全局变量: ds1302_clk、ds1302_io、CARRY
//* 输 入: unsigned char address
//* 返回: 无
//********************************************************************
void ds1302_write_byte(unsigned char address)
{unsigned char k, data;data = address;for (k = 8; k > 0; k--) // 发送 8 位数据{ds1302_clk = 0; // 时钟线拉低asm("NOP"); // 空操作data = data >> 1; // 数据右移一位ds1302_io = CARRY; // 将进位标志写入数据线ds1302_clk = 1; // 时钟线拉高asm("NOP"); // 空操作}
}//********************************************************************
//* 函数名: ds1302_read_byte
//* 功能描述: 从 DS1302 读取一个字节数据
//* 函数说明: 通过 DS1302 的时钟线和数据线读取一个字节数据
//* 调用函数: asm("NOP")
//* 全局变量: ds1302_io、TRISE1、dat
//* 输 入: 无
//* 返回: unsigned char 读取的数据
//********************************************************************
unsigned char ds1302_read_byte(void)
{unsigned char i, data;data = 0;dat = 0;ds1302_io = 1; // 释放数据总线ds1302_clk = 0; // 时钟线拉低TRISE1 = 1; // 设置 RE1 为输入for (i = 8; i > 0; i--) // 读取 8 位数据{data = ds1302_io; // 读取数据线状态data = data << (8 - i); // 左移相应位数dat = dat + data; // 组合成一个字节ds1302_clk = 1; // 时钟线拉高ds1302_clk = 0; // 时钟线拉低}TRISE1 = 0; // 设置 RE1 为输出return (dat); // 返回读取的数据
}//********************************************************************
//* 函数名: ds1302_write
//* 功能描述: 向 DS1302 写入数据
//* 函数说明: 先写入地址,再写入数据
//* 调用函数: ds1302_write_byte
//* 全局变量: ds1302_rst、ds1302_clk
//* 输 入: unsigned char add, unsigned char byte
//* 返回: 无
//********************************************************************
void ds1302_write(unsigned char add, unsigned char byte)
{ds1302_rst = 0; // 禁止 DS1302ds1302_clk = 0; // 时钟线拉低ds1302_rst = 1; // 使能 DS1302ds1302_write_byte(add); // 写入地址ds1302_write_byte(byte); // 写入数据ds1302_rst = 0; // 禁止 DS1302
}//********************************************************************
//* 函数名: ds1302_read
//* 功能描述: 从 DS1302 读取数据
//* 函数说明: 先写入地址,再读取数据
//* 调用函数: ds1302_write_byte、ds1302_read_byte
//* 全局变量: ds1302_rst、ds1302_clk、ds1302_io、dat
//* 输 入: unsigned char add
//* 返回: unsigned char 读取的数据
//********************************************************************
unsigned char ds1302_read(unsigned char add)
{ds1302_rst = 0; // 禁止 DS1302ds1302_clk = 0; // 时钟线拉低ds1302_rst = 1; // 使能 DS1302ds1302_write_byte(add); // 写入地址ds1302_io = 1; // 释放数据总线ds1302_read_byte(); // 读取数据ds1302_rst = 0; // 禁止 DS1302return (dat); // 返回读取的数据
}//********************************************************************
//* 函数名: timeset
//* 功能描述: 设置 DS1302 的时间
//* 函数说明: 先允许写入时间,然后循环写入时间数据
//* 调用函数: ds1302_write
//* 全局变量: time_set
//* 输 入: 无
//* 返回: 无
//********************************************************************
void timeset(void)
{unsigned char i;ds1302_write(0x8e, 0x00); // 允许写入时间for (i = 0; i < 7; i++)ds1302_write(0x80 + i + i, time_set[i]); // 设置时间
}//********************************************************************
//* 函数名: ds1302_read_time
//* 功能描述: 读取 DS1302 的当前时间
//* 函数说明: 循环读取 DS1302 的时间数据(BCD 码)
//* 调用函数: ds1302_read
//* 全局变量: timerbcd
//* 输 入: 无
//* 返回: 无
//********************************************************************
void ds1302_read_time(void)
{unsigned char i;for (i = 0; i < 7; i++)timerbcd[i] = ds1302_read(0x81 + i + i); // 读取时间数据(BCD 码)
}
3.2 main.c
//********************************************************************
//* 文件名: main.c
//* 文件描述: PIC16F887A,用于子程序实现实时时钟DS1302的驱动及显示
//* 编译环境:Windows XP + MPLAB IDE 8.83 + Picc 9.60
//* 编程者:bianjingyuan
//* 编程日期:2024年10月30日
//* 版本号:1.0
//*******************************************************************/// 引入头文件
#include "pic.h"
#include "DS1302.h"
#include "max7219.h"
#include "PIC16F887_Delay.h"// 定义数据类型
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long// 定义单片机指令
#define nop asm("NOP") // 执行空操作指令
#define sleep asm("sleep") // 进入睡眠模式// 定义DS1302引脚连接PIC16F887A
#define ds1302_clk RE0 // 实时时钟时钟线引脚
#define ds1302_io RE1 // 实时时钟数据线引脚
#define ds1302_rst RE2 // 实时时钟复位线引脚 // 定义数码管引脚连接 max7219
#define din RD5 // 数据输入引脚
#define clk_1 RD7=1 // 时钟高电平
#define clk_0 RD7=0 // 时钟低电平
#define load_1 RD6=1 // 锁存高电平
#define load_0 RD6=0 // 锁存低电平// 定义芯片工作时的配置位
__CONFIG (0x3F3A); // 设置配置字// 定义变量
uchar m, k, i, l, add, dat, c0; // 临时变量
uchar add8, add7, add6, add5, add4, add3, add2, add1; // 显示位数据
uchar timerbcd[7]; // 存放从DS1302读回的时间数据(BCD码)
uchar timerint[7]; // 存放将BCD码转换成十进制后的时间数据// 系统初始化
void initial(void)
{// 配置OPTION寄存器OPTION = 0b11000010; // 设置看门狗定时器、定时器0预分频器等// 配置I/O口TRISA = 0b101111; // 配置RA4为输出,其他为输入TRISB = 0b000000; // 配置RB0和RB1为输入TRISC = 0b000000; // 配置RC3和RC5为输入TRISD = 0b000000; // 配置RD0和RD1为输入TRISE = 0b00001000; // 配置RE0、RE1和RE2为输出// 关闭所有中断INTCON = 0b00000000; // 关闭所有中断// 初始化端口PORTA = 0b00000000; // PORTA初始化为0PORTB = 0b00000001; // PORTB初始化为1PORTC = 0b00000000; // PORTC初始化为0PORTD = 0b00000000; // PORTD初始化为0PORTE = 0b00000000; // PORTE初始化为0// 配置ADC相关寄存器ADCON0 = 0b10000000; // 设置A/D转换通道为AN0,并使能A/D转换ADCON1 = 0b00001001; // 设置A/D转换参考电压为VDD和VSSANSEL = 0b00001111; // 设置RA0为模拟输入,RA2和RA3为模拟模块的VREF-和VREF+ANSELH = 0b00000000; // 设置所有PORTB和PORTC引脚为数字I/OADRESH = 0b00000000; // 清空A/D转换结果高8位ADRESL = 0b00000000; // 清空A/D转换结果低8位// 初始化变量add1 = add2 = add3 = 0b00000000; // 初始化变量add1、add2和add3为0// 配置中断GIE = 0b00000000; // 关闭总中断PEIE = 0b00000000; // 关闭外围设备中断T0IE = 0b00000001; // 开启定时器0中断T0IF = 0b00000000; // 清除定时器0中断标志INTF = 0b00000000; // 清除外部中断标志// 初始化定时器BCD值timerbcd[0] = 0b00000000; // 初始化定时器BCD数组的第一个元素为0
}/********************************************************************
//* 文件名: main.c
//* 文件描述: DS1302时钟模块读取和显示程序
//* 编译环境: Windows XP + MPLAB IDE 8.83 + PICC 9.60
//* 编程者: bianjingyuan
//* 编程日期: 2023-05-01
//* 版本号: 1.0
//********************************************************************/// 主函数
void main()
{initial(); // 系统初始化// 初始化DS1302引脚ds1302_clk = 0; // 设置时钟线低电平ds1302_io = 0; // 设置数据线低电平ds1302_rst = 0; // 设置复位线低电平max7219_int(); // 初始化数码管芯片ds1302_Open(); // 打开DS1302timeset(); // 设置时间(如需设置时间,取消注释)redo: nop; // 空操作ds1302_read_time(); // 读取DS1302时间ds1302_write(0x8e, 0x80); // 禁止写入DS1302// 将BCD码转换为十进制for (i = 7; i > 0; i--){timerint[7 - i] = timerbcd[7 - i] & 0x0f; // 取个位数据k = timerbcd[7 - i] >> 4; // 取十位数据timerint[7 - i] = (10 * (k & 7)) + (timerint[7 - i]); // 转换为十进制}// 将时间数据分别存入显示数据数组c0 = timerint[0]; // 秒add7 = c0 / 10; // 秒的十位add8 = c0 % 10; // 秒的个位c0 = timerint[1]; // 分add5 = c0 / 10; // 分的十位add6 = c0 % 10; // 分的个位c0 = timerint[2]; // 时add3 = c0 / 10; // 时的十位add4 = c0 % 10; // 时的个位c0 = timerint[3]; // 日add1 = c0 / 10; // 日的十位add2 = c0 % 10; // 日的个位// 发送显示数据到数码管芯片send(0x01, add1); delayms(5);//显示日期send(0x02, add2); delayms(5);send(0x03, add3); delayms(5);//显示小时send(0x04, add4); delayms(5);send(0x05, add5); delayms(5);//显示分钟send(0x06, add6); delayms(5);send(0x07, add7); delayms(5);//显示秒send(0x08, add8); delayms(5);goto redo; // 循环显示
}
4、代码解析
4.1 ds1302.c
此代码实现了与实时时钟模块DS1302的通信和控制,可以设置和读取DS1302的时间数据。主要功能包括:
1. 初始化DS1302的时钟线和复位线,使能或禁止DS1302。
2. 向DS1302写入数据,包括地址和时间值,以设置时间。
3. 从DS1302读取数据,获取当前时间。时间数据以BCD码格式存储。
4. 提供时间设置函数`timeset`和时间读取函数`ds1302_read_time`,方便外部调用实现时间管理功能。
通过该程序,用户可以方便地控制DS1302,设置或获取所需的时间信息。特别是在需要实时时钟功能的嵌入式应用中,该程序可以起到关键作用。
4.2 main.c
该程序基于PIC16F887A微控制器实现对DS1302实时时钟芯片的驱动。通过读取DS1302的时间数据,将其转换为十进制后,利用MAX7219驱动数码管显示时间。主要功能包括:
1. 初始化PIC16F887A的相关I/O端口及ADC模块。
2. 初始化并打开DS1302实时时钟模块。
3. 定义并使用定时器中断实现对时间的读取与显示。
4. 将从DS1302读取的BCD码格式时间数据转换为十进制。
5. 利用MAX7219芯片实现时间数据的数码管显示。
5、硬件资源分配表
序号 | 单片机引脚 | 功能定义 | 字符串替代定义 | 功能内容 | 备注 |
---|---|---|---|---|---|
1 | RE0 | ds1302_clk | - | DS1302时钟线 | 连接DS1302 |
2 | RE1 | ds1302_io | - | DS1302数据线 | 连接DS1302 |
3 | RE2 | ds1302_rst | - | DS1302复位线 | 连接DS1302 |
4 | RD5 | din | - | 数据输入引脚 | 连接MAX7219 |
5 | RD7 | clk_1/clk_0 | - | 时钟高低电平 | 连接MAX7219 |
6 | RD6 | load_1/load_0 | - | 锁存高低电平 | 连接MAX7219 |
通过控制DS1302的时钟线、数据线和复位线,实现对实时时钟芯片的初始化、读写操作。通过控制MAX7219的数码管引脚,实现时间的数码管显示。
6、软件流程图
7、仿真运行结果
好了,暂时这些,下一次使用液晶来显示时间与日期。20241103