本节主要说明spi通信,通过51单片机模拟spi和ds1302时钟芯片进行通信,通过向ds1302写入当前时间,然后通过spi读出当前时间并以XX-XX-XX在数码管上显示小时,分钟,秒。
(1)spi通信协议
我们大部分的协议其实都是大致相等的,当然也可能会有一些区别,就以spi来说,spi都可以分为三线制和四线制,他们在通信的时候的相位和空闲时电平等都可能不相同,所以spi的协议好像稍微更加复杂,但是大致的原理相似,只要掌握了spi大致的原理,这个时候其实我们去看芯片手册,对应芯片手册上的时序就可以完成通信和相应的功能了。
这就是我们在ds1302上单字节读和单字节写需要完成的时序了,由图可以看到51单片机是通过三根线和ds1302相连,其中CE即片选线在读或写之前都要有低拉高,而读或写结束之后都需要由高拉低,完成读写,而我们spi的写都是在sclk的上升沿,而spi的读都是在下降沿,整个的时序如上图所示,整体还是比较简单 。
(2)硬件相关
DS1302的大致使用过程就是通过寄存器向ds1302配置寄存器,然后读寄存器即可得到当前时钟。DS1302的控制寄存器值如下所示:
第7位:永远都是1
第6位:1表示内部RAM寻址,0表示内部内部寄存器寻址
第5~1位:稍后讲解
第0位:读写位,1表示读,0表示写
其中第5~1位的含义如下所示:
我们可以通过对应的地址来读取或者写相应的值,注意,这些寄存器的值都只需要写一次之后,我们就可以一直读取,不会丢失。而关于每个寄存器对应的值的含义如下:
关于每个每个寄存器的每一位的含义,这个图看的还是比较清楚,最主要的是说一下几点,第一:秒寄存器的第7位ch,当ch=0的时候,内部时钟运行,ch=1的时候内部时钟不运行,简单来说就是秒数会不会自动增加,并且掉电会不会丢失。而秒钟的十位和个位都是由BCD码组成的,其实这些时间都是由BCD码组成。第二:小时寄存器最高位是12/24小时位,当为1的时候表示12小时,那么第5位高电平表示下午,低电平表示上午,而当表示24小时的时候,第五位就变为了实际的小时的一位。第三:年寄存器取值范围为0到99,其实际年数是2000年到2099年。
(3)软件
我们先放出有关于控制ds1302的相关代码
#include "reg52.h"
#include <intrins.h>
sbit MOSI = P3^4;
sbit SCLK = P3^6;
sbit CE = P3^5;
unsigned char read_addr[7] = {0x81,0x83,0x85,0x87,0x89,0x8b, 0x8d};
unsigned char write_addr[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
unsigned char time[7] = {0x00, 0x00,0x23,0x7,0x5, 0x6,0x16};
unsigned char ds1302_read(unsigned char addr){
unsigned char val = 0;
unsigned char byte = 0;
int i;
CE = 0;
_nop_();
SCLK = 0;
_nop_();
CE = 1;
_nop_();
for(i = 0;i < 8;i++){
MOSI = addr & 0x01;
addr >>= 1;
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
_nop_();
for(i = 0;i < 8;i++){
byte = MOSI;
val >>= 1;
val |= (byte << 7);
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
CE = 0;
_nop_();
SCLK = 1;
_nop_();
MOSI = 0;
_nop_();
MOSI = 1;
_nop_();
return val;
}
void ds1302_write(unsigned char addr, unsigned char dat){
int i;
CE = 0;
_nop_();
SCLK = 0;
_nop_();
CE = 1;
_nop_();
for(i = 0;i < 8;i++){
MOSI = addr & 0x01;
addr >>= 1;
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
for(i = 0;i < 8;i++){
MOSI = dat & 0x01;
dat >>= 1;
_nop_();
SCLK = 1;
_nop_();
SCLK = 0;
_nop_();
}
CE = 0;
_nop_();
}
void ds1302_init(){
int i;
ds1302_write(0x8E, 0x00);
for(i = 0;i < 7; i++){
ds1302_write(write_addr[i], time[i]);
}
ds1302_write(0x8E, 0x80);
}
void ds1302_read_time(){
int i;
for(i = 0;i < 7;i++){
time[i] = ds1302_read(read_addr[i]);
}
}
现在我们放出有关于数码管的代码
#include "reg52.h"
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
extern unsigned char time[7];
unsigned char smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
void delay(int n){
while(n--);
}
void full_arr(unsigned char *arr){
arr[0] = smgduan[time[0] % 16];
arr[1] = smgduan[time[0] / 16];
arr[2] = 0x40;
arr[3] = smgduan[time[1] % 16];
arr[4] = smgduan[time[1] / 16];
arr[5] = 0x40;
arr[6] = smgduan[time[2] % 16];
arr[7] = smgduan[time[2] / 16];
}
void key_scan(){
char arr[8];
int i;
full_arr(arr);
for(i = 0;i < 8;i++){
switch(i){
case 0:
LSA = 0; LSB = 0; LSC = 0;
break;
case 1:
LSA = 1; LSB = 0; LSC = 0;
break;
case 2:
LSA = 0; LSB = 1; LSC = 0;
break;
case 3:
LSA = 1; LSB = 1; LSC = 0;
break;
case 4:
LSA = 0; LSB = 0; LSC = 1;
break;
case 5:
LSA = 1; LSB = 0; LSC = 1;
break;
case 6:
LSA = 0; LSB = 1; LSC = 1;
break;
case 7:
LSA = 1; LSB = 1; LSC = 1;
break;
}
P0 = arr[i];
delay(5);
P0 = 0x00;
}
}
最后写出主函数调用:
#include "reg52.h"
#include "ds1302.h"
#include "key.h"
void main(){
ds1302_init();
while(1){
ds1302_read_time();
key_scan();
}
}