51单片机学习笔记-2
模块化编程
- 模块化编程:
- 把各个模块的代码放到不同的
.c文件中,任何自定义的变量,函数在调用前必须有定义或者声明。 - 在
.h文件中提供外部可调用的函数声明,使用方法为引用#include "xxx.h",所有的.h文件最好和main.c文件一个目录下。

- 把各个模块的代码放到不同的
如果没有定义__DELAY_H__,则定义__DELAY_H__,并且声明void Delay(unsigned int xms);__DELAY_H__只有在第一次编译的时候会被定义一次,之后就不满足#ifndef条件了。
- C预编译(在代码真正编译之前的预先处理内容):
#include <REGX52.H>:相当于把REGX52.H文件的所有内容引用到此处。#define PI 3.14: 定义PI,把下文遇到的所有PI都用3.14替代。#define ABC: 定义ABC。#ifndef __XX_H__..#endif: 如果没有定义__XX_H__,那么执行从#ifndef __XX_H__到#endif内的所有内容。- 还有
#ifdef,#if,#else,#elif,#undef等。
#define AAA //定义AAA
#ifdef AAA //判断是否定义AAA
sddsdsds //如果存在AAA,此处在编译时就会出现missing';'的编译错误。否则根本不编译执行体中的内容。
#endif
- 模块化编程demo:

步骤1,写Delay.c

步骤2,写Delay.h

步骤3,主函数中调用。
LCD1602实验
-
原理图


-
lcd1602.c
#include <REGX52.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()
{
unsigned char i, j;
i = 2;
j = 239;
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__ //ifndef:为了避免重复定义,当工程大的时候,就会有重复定义的风险
#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
-
keil编译器的一个注意事项:
- 当出现诸如
:error C141: syntax error near 'char'这一类编译错误时,原因通常是:keil编译器不允许声明或者定义在代码中进行,把声明放在主函数的第一行即可。如下:

- 当出现诸如
-
C语言的没有直接的字符串类型,字符串是以字符数组的方式存储的,以
'\0'结尾,使用案例如下:
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]);
}
}
矩阵键盘
- 原理图

4x4的方式,可以减少IO口的占用。
采用逐行或者逐列的扫描,实现检测所有按键的效果。
其实几乎所有的显示器都是通过扫描方式实现的(靠显卡扫描像素点和RBG),
- 矩阵键盘
MatrixKey.c实现:
#include <REGX52.H>
#include "Delay.h"
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;} //s1
if(P1_6==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=5;} //s5
if(P1_5==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=9;} //s9
if(P1_4==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=13;} //s13
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_7==0);Delay(20);KeyNumber=6;}
if(P1_5==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=10;}
if(P1_4==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=14;}
P1=0xFF;
P1_3=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
if(P1_6==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=7;}
if(P1_5==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=11;}
if(P1_4==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=15;}
P1=0xFF;
P1_0=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}
if(P1_6==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=8;}
if(P1_5==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=12;}
if(P1_4==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=16;}
return KeyNumber;
}
- 矩阵键盘密码锁案例:
#include <REGX52.h>
#include "LCD1602.h"
#include "MatrixKey.h"
unsigned char KeyNum;
unsigned int Password,Count; // 定义一个4位密码,int可以保证装得下
void main()
{
LCD_Init();
LCD_ShowString(1,1,"Password:");
while(1)
{
KeyNum=MatrixKey();
if(KeyNum)
{
if(KeyNum<=10) //数字输入,输入的密码范围S1~S10
{
if(Count<4)
{
//Password=KeyNum%10; //取余,1..9取余就是1..9,10取余是0
Password*=10; //密码左移一位
Password+=KeyNum%10; //获取当前密码(设置的当前密码是个4位密码,int类型可以装的下。)
Count++;
}
LCD_ShowNum(2,1,Password,4); //更新显示
}
if(KeyNum==11) //确认键,S11当作密码确认使用
{
if(Password==2345)
{
LCD_ShowString(1,14,"OK");
Password=0;
Count=0;
LCD_ShowNum(2,1,Password,4);
}
else
{
LCD_ShowString(1,14,"ERR");
Password=0;
Count=0;
LCD_ShowNum(2,1,Password,4);
}
}
if(KeyNum==12) //取消键,S12
{
Password=0;
Count=0;
LCD_ShowNum(2,1,Password,4);
}
}
}
}
蜂鸣器
- 蜂鸣器原理图:


(有源)蜂鸣器本身的控制很简单,给BEEP一个电压即可(给的频率不同,发声就不一样,以此实现不同音阶);但是单片机IO口一般不直接驱动蜂鸣器,本开发板共用的
ULN2003D的第五个口来驱动BEEP;但是大部分时候自己设计可以用三极管(PNP或者NPN都行)驱动蜂鸣器。
- 蜂鸣器的驱动方式,三极管驱动或者其他:

NPN蜂鸣器需要自己供电,PNP则由开关电路供电。
- ULN2003达林顿晶体管阵列,可以
提供高驱动能力:

ULN2003输入端给1,经过取反电压为0,外部电压可以经过蜂鸣器(NPN型)回到COM,实现输出。
-
音符到周期转换表

-
鸣响一个蜂鸣器:
#include <REGX52.H>
#include "Delay.h"
unsigned int i;
sbit Buzzer=P2^5; //如原理图,P2^5控制BEEP
void main()
{
while(1)
{
if(P3_1==0) //按键K1按下
{
for(i=0;i<1000;i++) //循环1000次,1次1毫秒,周期2ms,相当于蜂鸣器以0.5KHZ的频率动作。
{
Buzzer=!Buzzer;
Delay(1);
}
}
}
}
LED点阵屏使用
- 74HC595(移位寄存器)模块原理图:
- RCLK:使能输出,待数据移位结束后给脉冲即可使能输出。
- SRCLK:移位脉冲,一次高电平数据移位一次。
- SER:数据位,先进去的位是高位,最后进去的位是低位。


注意开发板上J24的短接帽要取下来。
- 对74HC595编程:
SFR:特殊功能寄存器声明,比如sfr P0=0x80;,既声明P0口寄存器,物理地址为0x80。sbit:特殊位声明,比如sbit P0_1=0x81;或者sbit P0_1 = P0^1;,用来声明P0寄存器的第1位。可寻址位/不可寻址位:在单片机系统中,操作任意寄存器或者某一位数据时,必须给出物理地址。又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍。单片机无法对所有位进行编码,所以每8个寄存器中,只有一个寄存器可以被位寻址,对于不可以被位寻址的寄存器,若想操作其中一位而不影响其他位时,可用&=,|=,^=的方法进行位操作。
#include <REGX52.H>
sbit RCK=P3^5; //RCLK和系统寄存器重名了,所以用RCK
sbit SCK=P3^6; //即SCLK
sbit SER=P3^4;
//写法1:
void _74HC595_WriteByte(unsigned char Byte)
{
//按位与运算
SER=Byte&0x80; //0x80=2#10000000;Byte的最高位是1,SER就是1,最高位是0,SER就是0.
SCK=1; //移位脉冲执行一次,数据移位一次
SCK=0;
SER=Byte&0x40;
SCK=1;
SCK=0;
SER=Byte&0x20;
SCK=1;
SCK=0;
SER=Byte&0x10;
SCK=1;
SCK=0;
//一共执行8次
...
RCK=1; //输出脉冲
RCK=0;
}
//写法2,用for循环代替重复代码:
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i); //右移指令 >> ,替代循环
SCK=1;
SCK=0;
}
RCK=1; //输出脉冲
RCK=0;
}
void main()
{
//初始化
SCK=0;
RCK=0;
while(1)
{
}
}
-
点阵屏原理图:

-
驱动点阵屏
#include <REGX52.H>
#include "Delay.h"
sbit RCK=P3^5;
sbit SCK=P3^6;
sbit SER=P3^4;
//
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i); //右移指令 >> ,替代循环
SCK=1;
SCK=0;
}
RCK=1; //输出脉冲
RCK=0;
}
//驱动点阵
//以列为单位扫描(类似于LED数码管)
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
_74HC595_WriteByte(Data);
P0=~(0x80>>Column); //利用移位指令实现对应列扫描
Delay(1); //LED动态扫描扫描,始终免不了消影
P0=0xFF;
}
void main()
{
//初始化
SCK=0;
RCK=0;
while(1)
{
//调用,实现点阵显示
MatrixLED_ShowColumn(0,0x3c);
MatrixLED_ShowColumn(1,0x42);
MatrixLED_ShowColumn(2,0xa9);
MatrixLED_ShowColumn(3,0x85);
MatrixLED_ShowColumn(4,0x85);
MatrixLED_ShowColumn(5,0xa9);
MatrixLED_ShowColumn(6,0x42);
MatrixLED_ShowColumn(7,0x3c);
}
}
- 可以用excel做出阵列扫描图:

这是一个笑脸的阵列扫描
- 做流水动画:
- 原理:把数据做成一个数组,在for循环中做偏移和延迟,实现流水动画。
- 有
文字取模软件可以帮助自动生成数据内容。

#include <REGX52.H>
#include "Delay.h"
sbit RCK=P3^5;
sbit SCK=P3^6;
sbit SER=P3^4;
void _74HC595_WriteByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SER=Byte&(0x80>>i);
SCK=1;
SCK=0;
}
RCK=1;
RCK=0;
}
void MatrixLED_ShowColumn(unsigned char Column,Data)
{
_74HC595_WriteByte(Data);
P0=~(0x80>>Column);
Delay(1);
P0=0xFF;
}
//数组用来存数据,长度32+8=40
unsigned char Animation[]={
0xFF,0x10,0x10,0x10,0xFF,0x00,0xFF,0x91, //0..31,生成的数据
0x91,0x91,0x91,0x91,0xDE,0x00,0xFF,0x00,
0xFF,0x00,0xFF,0x81,0x81,0x81,0xFF,0x00,
0x00,0x00,0x38,0xCF,0x8E,0xC4,0x3E,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //清屏
};
void main()
{
//offset:偏移量;count:用来做延迟
unsigned char i,offset=0,count=0;
SCK=0;
RCK=0;
while(1)
{
for(i=0;i<8;i++) //显示一块8*8的阵列图形
{
MatrixLED_ShowColumn(i,Animation[i+offset]);
}
count++;
if(count>10) //用count计数来变相做延迟
{
count=0;
offset++; //屏幕显示左移一列
if(offset>(40-8)) //数组边界不能超过Animation[i+offset],避免数组长度溢出
{
offset=0;
}
}
}
}
DS1302实时时钟
- 作为一种实时时钟芯片,内部也是一个集成芯片。

VCC1,备用电源,此处没用
VCC2,主电源
CE,芯片使能
IO数据输入输出
SCLK,串行时钟
X1,X2:32.768khz晶振,这个晶振的精度很高。
- 使用芯片,最重要的是学会看芯片手册,比如
寄存器地址,比如时序定义等等..

RTC就是实时时钟的意思,RTC这个图详细解析了Figure3中command的内容。
从地址和命令字得到相关寄存器和控制字操作。
控制字第7位常1;第6位,给1操作RAM,给0操作CK;第0位给1读,给0写;中间的字的含义详见图RTC。
写秒80H,读秒81H;写分82H,读分83H...
时间数据存储是以BCD码方式存储的,所以显示时或者显示16进制数,或者BTI;
BCD转十进制:DEC=BCD/16 * 10+BCD%16;(高4位取模,低4位取余);
十进制转BCD:BCD=DEC/10 * 16+DEC%10;(高4位取模,低4位取余);

时序的定义:不管读还是写,CE高电平应该覆盖整个过程;
时钟上升沿写入数据;时钟的下降沿读出数据。
数据先发最低位(最左边的位);命令字写入/读出结束后,紧跟的第二个字节是数据。
注意读的时候,上升沿有16个,下降沿只有15个;从命令字到数据之间的那个下降沿是无用的。
- 手动读写时钟代码测试:
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
void DS1302_Init()
{
DS1302_CE=0;
DS1302_SCLK=0;
}
void DS1302_WriteByte(unsigned char Command,Data)
{
unsigned char i;
DS1302_CE=1;
for(i=0;i<8;i++) //command
{
DS1302_IO=Command&£(0x01<<i); //左移取出对应位
DS1302_SCLK=1; //做脉冲,手册对脉冲持续时间有要求,但其实因为51反应慢,不加延时也行。
Delay(1);
DS1302_SCLK=0;
}
for(i=0;i<8;i++) //data
{
DS1302_IO=Data&£(0x01<<i);
DS1302_SCLK=1;
Delay(1);
DS1302_SCLK=0;
}
DS1302_CE=0; //结束清零
}
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i,Data=0x00;
DS1302_CE=1;
//Command|=0x01; //将指令转换为读指令
for(i=0;i<8;i++)
{
DS1302_IO=Command&£(0x01<<i);
DS1302_SCLK=0; //注意看时序图,这里以及下面都在按照时序图的顺序在做沿
DS1302_SCLK=1;
}
for(i=0;i<8;i++)
{
DS1302_SCLK=1; //注意看时序图,这里按照时序图的顺序在做沿
DS1302_SCLK=0;
if(DS1302_IO){Data=Data|(0x01<<i);} //把读出来的数据给Data
}
DS1302_CE=0;
DS1302_IO=0; //读出完成后记得把IO设置为0,否则读出数据会出错。
return Data;
}
unsigned char Second,Minute;
void main()
{
LCD_Init();
LCD_ShowString(1,1,"RTC");
DS1302_WriteByte(0x80,0x55); //写秒
while(1)
{
Second=DS1302_ReadByte(0x81); //读秒
Minute=DS1302_ReadByte(0x83); //读分
LCD_ShowNum(2,1,Second/16*10+Second%16,2); //BCD转十进制显示
LCD_ShowNum(2,3,Minute/16*10+Minute%16,2);
}
}
- 结构化DS1302工程代码(用数组来做DS1302时间,把函数写在子程序中):
#include <REGX52.H>
//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
//寄存器写入地址/指令定义
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_WP 0x8E
//时间数组,索引0~6分别为年、月、日、时、分、秒、星期
unsigned char DS1302_Time[]={25,03,27,12,59,55,6};
/**
* @brief DS1302初始化
* @param 无
* @retval 无
*/
void DS1302_Init(void)
{
DS1302_CE=0;
DS1302_SCLK=0;
}
/**
* @brief DS1302写一个字节
* @param Command 命令字/地址
* @param Data 要写入的数据
* @retval 无
*/
void DS1302_WriteByte(unsigned char Command,Data)
{
unsigned char i;
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
for(i=0;i<8;i++)
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;
}
/**
* @brief DS1302读一个字节
* @param Command 命令字/地址
* @retval 读出的数据
*/
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i,Data=0x00;
Command|=0x01; //将指令转换为读指令
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1;
}
for(i=0;i<8;i++)
{
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0; //读取后将IO设置为0,否则读出的数据会出错
return Data;
}
/**
* @brief DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
* @param 无
* @retval 无
*/
void DS1302_SetTime(void)
{
DS1302_WriteByte(DS1302_WP,0x00);
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
DS1302_WriteByte(DS1302_WP,0x80);
}
/**
* @brief DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
* @param 无
* @retval 无
*/
void DS1302_ReadTime(void)
{
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
Temp=DS1302_ReadByte(DS1302_MONTH);
DS1302_Time[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DATE);
DS1302_Time[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_HOUR);
DS1302_Time[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MINUTE);
DS1302_Time[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_SECOND);
DS1302_Time[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DAY);
DS1302_Time[6]=Temp/16*10+Temp%16;
}
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
void main()
{
LCD_Init();
DS1302_Init();
LCD_ShowString(1,1," - - ");//静态字符初始化显示
LCD_ShowString(2,1," : : ");
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);//显示秒
}
}

浙公网安备 33010602011771号