目录
一、模块化编程
1.传统方式编程:所有的函数均放在main.c里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路
2.模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等
3.模块化编程的注意事项
(1).c文件:函数、变量的定义
(2).h文件:可被外部调用的函数、变量的声明
(3)任何自定义的变量、函数在调用前必须有定义或声明(同一个.c)
(4)使用到的自定义函数的.c文件必须添加到工程参与编译
(5)使用到的.h文件必须要放在编译器可寻找到的地方(工程文件夹根目录、安装目录、自定义)
4.模块化编程的框图
5.C预编译
C语言的预编译以#开头,作用是在真正的编译开始之前,对代码做一些处理(预编译)
预编译 | 意义 |
#include <REGX52.H> | 把REGX52.H中内容搬到此处 |
#define PI 3.14 | 定义PI,将PI替换为3.14 |
#define ABC | 定义ABC |
#ifndef __XX_H__ | 如果没有定义__XX_H__ |
#endif | 与#ifndef,#if匹配,组成“括号” |
(1)此外还有#ifdef,#if,#else,#elif,#undef等,对程序中的某些部分是否编译进行选择。其实也很好理解,因为ifdef是if+def也就是如果定义什么,才执行#ifdef与ifend之间的代码。
#ifdef AAA
代码段
#endif
(2)一般在模块化的头文件中会在开头加#ifndef,#define在结尾加入#endif防止重复定义
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int xms);
#endif
程序一读到ifdef的语句马上就遇到define,于是在文件中其他部分就不用在检测是否ifdef_xxx_了
具体流程:
1.在source group里添加新的项目并写入函数,类型为.c
2.在source group里添加新的项目将函数的声名写到该文件里,类型为.h。
3.再将现存的文件.h的添加到source group里
二、Lcd602调试工具
1.数码管的缺点是需要不断地扫描不然会闪烁。
2.使用LCD1602液晶屏作为调试窗口,提供类似printf函数的功能,可实时观察单片机内部数据的变换情况,便于调试和演示。
3.这些LCD1602代码属于模块化的代码,使用者只需要知道所提供函数的作用和使用方法就可以很容易的使用LCD1602
函数 | 作用 |
LCD_Init(); | 初始化 |
LCD_ShowChar(1,1,'A'); | 显示一个字符 |
LCD_ShowString(1,3,"Hello"); | 显示字符串 |
LCD_ShowNum(1,9,123,3); | 显示十进制数字 |
LCD_ShowSignedNum(1,13,-66,2); | 显示有符号十进制数字 |
LCD_ShowHexNum(2,1,0xA8,2); | 显示十六进制数字 |
LCD_ShowBinNum(2,4,0xAA,8); | 显示二进制数字 |
忽略警告
三、矩阵键盘
1.在键盘中按键数量较多时,为了减少I/O口的占用,通常将按 键排列成矩阵形式
2.采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态
3. 扫描的概念
以上两种扫描方式的共性:节省I/O口
(1)数码管扫描(输出扫描)
原理:显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果
(2)矩阵键盘扫描(输入扫描)
原理:读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果
按键扫描以行进行扫描,P15会一会高一会低,蜂鸣器会异响,因为P15也有接在步进电机上,进而连接到Bz,进而连接到蜂鸣器上。
简略图
第一项‘project’是工程列表
第二项‘books’是软件说明书
第三项是‘funtion’会列出文件里所有的函数
第四项‘Templates’是模板
双击即可快速加入代码
扫描代码段
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_7==0);Delay(20);KeyNumber=5;}
if(P1_5==0)
{Delay(20);while(P1_7==0);Delay(20);KeyNumber=9;}
if(P1_4==0)
{Delay(20);while(P1_7==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_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_3=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;}
这应该是最简单的矩阵键盘按键扫描的方法了
但是有一个缺点就是在检测松手时整个程序会卡在while里
运用线返法即可解决
#include <reg52.h>
#include "Nixie.h"
#include "delay.h"
//按键按下返回键值(1-16)没有按下返回0
unsigned char Matrixkey(void)
{
unsigned char temp,row,col,i=0;
static unsigned char Num,Num_last;//保证上一次的数据并不会被销毁
P1=0x0f;//行为1列为0 0000 1111 ->0x0f
/*第一行若有按键按下 0000 1110 ->0x0e*/
/*第二行若有按键按下 0000 1101 ->0x0d*/
/*第三行若有按键按下 0000 1011 ->0x0b*/
/*第四行若有按键按下 0000 0111 ->0x07*/
row=0;//给row和col清零,不然会一直弹出按键
col=0;
Delay(1);
temp=P1;
if(temp!=0x0f)//说明有按键按下
{
if(temp==0x0e)row=1;
else if(temp==0x0d)row=2;
else if(temp==0x0b)row=3;
else if(temp==0x07)row=4;
P1=0xf0;//行为0列1 1111 0000->0xf0
temp=P1;
if(temp!=0xf0)//说明有按键按下
{
switch(temp)
{
/*第一列若有按键按下 1110 0000 ->0xe0*/
/*第二列若有按键按下 1101 0000 ->0xd0*/
/*第三列若有按键按下 1011 0000 ->0xb0*/
/*第四列若有按键按下 0111 0000 ->0x70*/
case 0xe0:col=1;break;
case 0xd0:col=2;break;
case 0xb0:col=3;break;
case 0x70:col=4;break;
}
}
}
Num_last=Num;//上次的数据
Num=(col-1)*4+row;//本次的数据
if((row!=0&&col!=0)&&Num_last!=Num) return Num;
else return 0;
}
四、定时器
1.定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成
2.定时器作用:
A.用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作
B.替代长时间的Delay,提高CPU的运行效率和处理速度
Eg:在用Delay写流水灯时,由于处于Delay时的CPU被占用,此时无法做其他事情,所以如果在用Delay写的流水灯中还要按键扫描就会导致按键不灵敏。
3. STC89C52定时器资源
(1)定时器个数:3个(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源
(2)注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的
(3)定时器框图
定时器在单片机内部就像一个小闹钟一样,根据(闹钟里面的小时钟)时钟的输出信号,每隔“一秒”,计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间”时,计数单元就会向中断系统发出中断申请,产生“响铃提醒”,使程序跳转到中断服务函数中执行
(4)定时器工作模式
STC89C52的T0和T1均有四种工作模式:
模式0:13位定时器/计数器
模式1:16位定时器/计数器(常用)
模式2:8位自动重装模式
模式3:两个8位计数器
工作模式1框图:
以上是与配置定时器相关的寄存器
A.定时器时钟
时钟来源有两个:一个是SYSclk(系统时钟),一个是T0Pin(单片机外部接口)。(时钟可由单片机内部提供也可以由外部提供)
a.当时钟由外部引脚提供时,定时器就是一个计数器,因为外部引脚每来一个脉冲计数单元就会加一。
b.SYSclk:系统时钟,即晶振周期,本开发板上的晶振为11.0592MHz.用11.0592MHz的时钟会进行分频,默认由12Tmode下12分频输出(如图所示就是系统时钟由SYSclk出发后走上面的路线)的脉冲就是11.0592M/12Hz。一个周期就是大概是1.08154878微秒,由12Tmode输出后进入选择开关C/T——就是在高电频(1)的话配置C的功能,就是Counter(也就是计数器)对外部脉冲进行计数,在输出低电频(0)的话,就是输出C/T——的功能(定时器),就是Timer对时钟周期进行计数。(使用寄存器进行选择)
B.计数单元
a.TL0和TH0是timerlow和timerhigh是一个十六位的计数器(0是代表定时器0),可 计入两个字节,两个字节最多可存65535(俩个字节共十六位以二进制存储数据)。闹钟每来一个脉冲计数系统就会加一,当加到65535数值溢出,然后置标志位timerflag向中断系统申请中断,在下一个脉冲会回到0。(确实像沙漏,沙子堆满后沙漏就倒过来)
b.控制位
c.Gate是控制器的一个非门,从gate中输入一输出为0(反之亦然)。
d.Gate和INTO接入的门是或门,就是只要有一个是一输出就为一(||)。计数器和定时器可由TR0单独控制也可以.Gate和INTO共同控制。
(两个或门)
4.中断系统
(1)高优先级的中断可以打断低优先级的中断
(2)中端可是CPU具有对外界的紧急处理能力
(3)中断流程
- STC89C52中断资源
A.中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3)
B.中断优先级个数:4个
C.中断号:
D.注意:中断的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的中断资源,例如中断源个数不同、中断优先级个数不同等等
5. 定时器和中断系统
其实问题不大,因为后来版本的51单片机是向下兼容之前的版本的
模式图中有很多的开关,都是通过寄存器来控制的
Stc89C52的图
6. 定时器相关寄存器
(1)寄存器是连接软硬件的媒介(单片机通过配置寄存器来配置内部电路的连接)
(2)在单片机中寄存器就是一段特殊的RAM存储器,一方面,寄存器可以存储和读取数据,另一方面,每一个寄存器背后都连接了一根导线,控制着电路的连接方式
(3)寄存器相当于一个复杂机器的“操作按钮”
IE为使能
TCON是timerconctrol
其实好好读STC89C52的手册挺好的,因为真的很详细
五、按键控制LED流水灯模式&定时器时钟
初始化
void Timer0_Init()//配置寄存器
{
TMOD=0x01;//0000 0001因为TMOD寄存器是不可位寻址的,所以只能整体赋值
TMOD=TMOD&0xF0;//把TMOM的低四位清零,高四位保持不变
TMOD=TMOD|0xF0;//把TMOM的最低位置1,高四位保持不变
TF0=0;//TCOM是可位寻址,所以可以单独赋值也可以整体赋值TF清零防止装配使产生中断
TR0=1;//开启定时器(由TR0单独控制)
//(在TR0、Gate、INTO同时控制时TR0=1打开,Gate=1打开、INTO置于高位)
TH0=64535/256; //给计数单元赋初值,距离溢出剩1000,计数时间1ms
TL0=64535%256;//把64535以二进制存储在两个8位的存储单元(最多存255)中
//(除和求余)(配置数少了一位)
//例如123->123/100=1 123%100=23 以上就是计数器的部分接下来写中断部分
//以下是配置中断系统的代码
ET0=1;//打开通路
EA=1;//打开通路
PT0=0;//将该中断系统置于低优先级
}
void Timer0Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
//(这个就不用自己计算初值)
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count=0;//局部变量每次用过这个数就会被销毁
//加了static后就是静态变量
//在函数结束后依然占据空间不会清零
//保证每次退出函数后不会丢失数据
TH0=64535/256;//每次计数器溢出进入中断都要重置沙漏
TL0=64535%256+1;
T0Count++;
if(T0Count>=500)
{
T0Count=0;
if(LEDMode==0)P2=_crol_(P2,1);
if(LEDMode==1)P2=_cror_(P2,1);
}
}
void main()
{
P2=0xFE;
Timer0Init();
while(1)
{
KeyNum=Key();
if(KeyNum)
{
if(KeyNum==1)
{
LEDMode++;
if(LEDMode>=2)LEDMode=0;
}
}
}
}
定时器时钟
#include <REGX52.H>
#include <LCD1602.h>
void Timer0Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;//一个中断可以不用配置优先级
}
unsigned char Sec,Min,Hour;
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count=0;//局部变量每次用过这个数就会被销毁
//加了static后就是静态变量
//在函数结束后依然占据空间不会清零)
//保证每次退出函数后不会丢失数据
TH0=64535/256;//每次计数器溢出进入中断都要重置沙漏
TL0=64535%256+1;
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
Sec++;
if(Sec>60)
{
Sec=0;
Min++;
if(Min>60)
{
Min=0;
Hour++;
if(Hour>24)
{
Hour++;
}
}
}
}
}
void main()
{
LCD_Init();
Timer0Init();
LCD_ShowString(1,1,"Clock:");
LCD_ShowString(2,1," : :");
while(1)
{
LCD_ShowNum(2,1,Hour,2);
LCD_ShowNum(2,4,Min,2);
LCD_ShowNum(2,7,Sec,2);
}
}
六、串口信号
(一)串口介绍
1.串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
2.单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
3.51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。
(二)硬件电路
- TXD与RXD要交叉连接
- 当只需单向的数据传输时,可以直接一根通信线
- 当电平标准不一致时,需要加电平转换芯片
- 简单双向串口通信有两根通信线(发送端TXD(transmit exchange data)和接收端RXD)
(三)电平标准
1.电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
2.TTL电平:+5V表示1,0V表示0
3.RS232电平:-3~-15V表示1,+3~+15V表示0
4.RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)(传输距离长,前两种传输距离小,承受电压差大)(51单片机不支持流控制)
(四)常见通信接口比较
名称 | 引脚定义 | 通信方式 | 特点 |
UART(没有总线的说法) | TXD、RXD | 全双工、异步 | 点对点通信 |
I²C | SCL(时钟线)、SDA | 半双工、同步 | 可挂载多个设备 |
SPI | SCLK(时钟线);MOSI;MISO;CS | 全双工、同步 | 可挂载多个设备 |
SPI | DQ | 半双工、异步 | 可挂载多个设备 |
此外还有:CAN、USB等
TXD和RXD支持一发多收但不支持多发一收
(五)相关术语
1.全双工:通信双方可以在同一时刻互相传输数据(两个设备之间又两根线)
2.半双工:通信双方可以互相传输数据,但必须分时复用一根数据线(两个设备间只有一根线)
3.单工:通信只能有一方发送到另一方,不能反向传输(例如遥控器跟空调)
4.异步:通信双方各自约定通信速率
5.同步:通信双方靠一根时钟线来约定通信速率
6.总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)
(六)51单片机的UART
STC89C52有1个UART
STC89C52的UART有四种工作模式:
模式0:同步移位寄存器
模式1:8位UART,波特率可变(常用)
模式2:9位UART,波特率固定
模式3:9位UART,波特率可变
P3_0、P3_1和RxD、TxD公用引脚
操作P3_0、P3_1的寄存器用的就是I/O口
操作RxD、TxD的寄存器用的就是串口
输出用RxD、TxD,接到单片机的引脚(而且交叉连接)
(七)串口参数及时序图
1.波特率:串口通信的速率(传输字符)(发送和接收各数据位的间隔时间)(与异步通讯联系起来)
2.比特率:串口通信的速率(传输位)
3.检验位:用于数据验证
奇偶检测位通过在发送数据时奇数/偶数的数量是约定好的(例如奇校验0000 0011 1校验位为1补成奇数,1110 0000 0校验就补0,校验1的数量,但是如果两位同时反转就无法被发现)
4.停止位:用于数据帧间隔
8位数据格式
9位数据格式(八位一个字节的数据再加上一个检验位)
(八)串口模式图
整个电路被封装在单片机里面,通过RXD、TXD输入输出
波特率通过定时器的计数单元的溢出确定速率。
SBUF:串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器。读入和读出的在程序中的表现是赋值赋在左边/右边。
(九)串口和中断系统
(十)串口相关寄存器
(十一)串口向电脑发送数据
打开STC-ISP中的串口助手
七、串口向电脑发送信号&电脑通过串口控制LED
void UART_Init()
{
SCON=0x40;
PCON=0;
TMOD &= 0x0F; //设置定时器0
TMOD |= 0x20; //设置8位自动重装
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
#include <REGX52.H>
#include "Delay.h"
unsigned char Sec;
void UartInit(void) //4800bps@11.0592MHz
{
PCON |= 0x80; //使能波特率倍速位SMOD
SCON = 0x40; //8位数据,可变波特率,串口不接受数据
//以上为串口寄存器配置部分
//以下为定时器部分
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
void UART_SendByte(unsigned char Byte)
{
SBUF=Byte;
while(TI==0);//当TI==0是SendByte就会陷进去直到TI==1时的发送标志位
TI=0;//软件置0
}
void main()
{
UartInit();
while(1)
{
UART_SendByte(Sec); //没有Delay的话晶振会有误差导致出错
Sec++;
Delay(1000);
}
}
八、LED点阵屏
LED点阵屏由若干个独立的LED组成,LED以矩阵的形式排列,以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合,如汽车报站器、广告屏以及公告牌等
LED点阵屏分类
按颜色:单色、双色、全彩
按像素:8*8、16*16等(大规模的LED点阵通常由很多个小点阵拼接而成)
列的引脚直接接在p0口(阴极);行的引脚(阳极)是接到74HC595模块,左边用P34、P35、P36控制
74HC595是串行输入并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于IO口扩展。首先SER给移位寄存器输入,在经过SERCLK每遇到一个上升沿就会移位,每写入一位数据来一个上升沿直到写入八位每次SERCLK都需要置低电平,之后如果RCLK有上升沿的话就将输入的八位数据同时搬运到右边的输出缓存区。如果八位寄存器满了之后再写入数据,溢出的数据就会跑到QH‘处,QH’若再接移位寄存器就可以做到多片级联的效果。
1.QA-QH’是输出端
2.OE加横线(outputenable使能)代表低电平有效或者下降沿有效,此时在J24中需要将OE与GND短接
3.RCLK是寄存器时钟
4.SRCLR是串行清零端,如果接VCC就代表不清零(因为SRCLR也是低电平有效)
5.SRCLK是串行时钟
6.SER是串行数据。串行数据是一个一个从引线出去的,而并行数据是八个数据同时输出的
重要用RCLK、SRCLR、SER控制八个口的输出
如何驱动点阵LED显示:控制行选择和列选择(列直接给P0赋值)(行用74HC595输出)
操作需要驱动芯片,因为单片机的I/O口是弱上拉,输出低电平能接受很大电频而输出高电平则不行。相当于接了一个电阻再接VCC,给一是弱的。如果硬要用I/O口控制LED得用一个三极管。
九、LCD点阵屏显示图案
(一)C51的sfr、sbit
1.sfr(special function register):特殊功能寄存器声明
例:sfr P0 = 0x80;
声明P0口寄存器,物理地址为0x80
2.sbit(special bit):特殊位声明
例:sbit P0_1 = 0x81; 或 sbit P0_1 = P0^1;
声明P0寄存器的第1位
3.可位寻址/不可位寻址:在单片机系统中,操作任意寄存器或者某一位的数据时,必须给出其物理地址,又因为一个寄存器里有8位,所以位的数量是寄存器数量的8倍,单片机无法对所有位进行编码,故每8个寄存器中,只有一个是可以位寻址的。对不可位寻址的寄存器,若要只操作其中一位而不影响其它位时,可用“&=”、“|=”、“^=”的方法进行位操作
十、DS1302实时时钟
1.DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能
2.RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片
(一)DS1302较计时器优点:
(1)精度高
(2)不用占用cpu资源
(3)掉电可以自动切换到备用电池(当然单片机上Vcc没有 接备用电池)
(二)51单片机开发板上的DS1302是SO贴片封装
引脚名 | 作用 | 引脚名 | 作用 |
VCC2 | 主电源 | CE | 芯片使能置1时才有效 |
VCC1 | 备用电池 | IO | 数据输入/输出 |
GND | 电源地 | SCLK | 串行时钟 |
X1、X2 | 32.768KHz晶振 石英晶体振荡器 提供稳定脉冲 |
(三)内部结构框图
(四)RTC相关寄存器与地址命令字节
WP(Writeprotect)置1时只能读出不能输入
秒的一行低四位存的是秒,高三位存的是10秒,最高位CH是时钟暂停的意思,如果给1秒的寄存器就不会运行,给0就行
最后一行是涓流充电相关的寄存器
地址命令字完成“在哪”和“读出/写入”的操作
在xx寄存器(的地址)写入xx数据
在xx寄存器(的地址)读出xx数据(DS1302产生的数据)
第七位固定是1
第六位给1是操纵ram给,0是操纵clock
第五位到第一位是确定地址
第0位给1是read,给0是write
(五)时序定义
在时钟的上升沿I/O口上的电频就会被写入,下降沿DS1302就会把他的数据输出。即是在上升沿写入数据,在下降沿读出数据(看地址命令字最低位是什么)
两个字节分别是“在哪”“写入/读出”和“数据”
DS1302初始化
void DS1302_Init(void)//单片机上电的时候IO口会置1。有下拉电阻?需要吗?
{
DS1302_CE=0; //CE线置0
DS1302_SCLK=0; //时间线置0
}
void DS1302_WriteByte(unsigned char Command,unsigned char Data)//8位Command是命令字
{
unsigned char i;
DS1302_CE=1;//CE线置1
// DS1302_IO=Command&0x01;//八位数字的Command与上0000 0001
// //相当于取出第0位剩余位数清零
// DS1302_SCLK=1;//单片机的操作速率很慢可以不加延时
// DS1302_SCLK=0;
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; //CE线置0
}
DS1302Read_Byte
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i,Data=0x00;
Command|=0x01;//最低位置1,将写的操作命令改成读的命令
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1; //下降沿将地址的内容读进Data
}
for(i=0;i<8;i++)
{
DS1302_SCLK=1;//此时的数据已经给到IO口上了(D0)
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0;
return Data;
}
DS1302_SetTime
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);
}
DS1302_ReadTime
void DS1302_ReadTime(void)
{
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_Time[0]=Temp/16*10+Temp%16;
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;
}