SPI总线的概念
SPI接口全称“serial peripheral Interface”,意为串行外围接口。 SPI接口是在CPU外围低速之间进行数据传输,在主器件的移位脉冲下,数据按位传输,高位在前,低位在后,为全双工通信。 SPI以主从方式工作,这种模式通常有一个主器件和一个或多个器件,一般包含4根线。
MOSI——主器件数据输出,从器件数据输入 MISO——主器件数据输入,从器件数据输出 SCLK——时钟信号,由主器件产生 CS/SS——从器件使能信号,由主器件控制
在SPI应用中也有三线制的SPI,实现半双工通信
SPI总线接口连接
什么是实时时钟
RTC:Real Time Clock
我们知道单片机内部有定时器,但定时器只能表示一个时间段,这不是唯一的时间。比如我们应用单片机写一个交通灯;当我们应用单片机来表示一个时间点,这是唯一的,表示单片机需要做一些必须到某一个时间才能做的事情,所以需要用到RTC,如应用单片机写一个定时炸弹,这时我们就需要实时的时钟计时。使用实时的实时计时还有一个好处就是能够减少时间的误差。 关于RTC,在钟表、电子日历、手表、智能手环等都有实际应用。 在STM32F103系列的MCU中,已集成有RTC外设,是一个掉电后还能继续运行的定时器,从定时器角度来说,相当于定时器外设。它十分简单,只有很纯粹的中断和触发功能。
DS1302介绍
用一句话总结DS1302:内部存着一个时间点信息(年月日时分星期几)可以读写; DS1302内部还有31*8RAM用于临时存储数据; 工作电压范围在2.0V-5.5V之间。当工作在低功耗模式下(2.0V)时,消耗不少于300nA。; DS1302传输时钟和内存数据有单字节或多字节(突发模式); 有8-pin DIP或8-pin SOIC封装可选;3线SPI通信接口,兼容TTL(5V)电平,兼容DS1202。
管脚说明: Vcc2:双电源配置中的主电源 X1、X2:与标准的32.768KHZ晶振相连 GND:电源地 CE:使能,CE信号在读写必须保持高电平 I/O:数据 SCLK:时钟 Vcc1:低功率工作单电源和低功率备用电源
DS1302和单片机的IO连接只需3根线:CE数据传输使能、SCLK串行时钟输入端、I/O串行数据端,同时3个引脚分别接4.7K上拉电阻。
关于钟表里的晶振为什么是32.768KHZ的晶振? 钟表运行需要需要一个精准的节拍器,机械钟表依靠的是机械摆,电阻钟表依靠的就是晶振了。晶振能够提供精准稳定的节拍,常用的晶振是32.768KHZ晶振,也就是每秒跳动32768次,对于二进制运行的电子表、计算机来说,32768是一个整数。 对于二进制运行的电子钟表里,有一个16位的计数器,十进制的32768刚好是16位二进制的“1000 0000 0000 0000”。钟表里的晶振每秒跳动32768次就是每秒加32768个1,这样在16位加1速度就被减慢到秒1次。 用32768晶振还有一个好处,晶振的误差体现到秒是上面只有1/32768。 那么为什么不选择更加高频或者低频的晶振呢? 综合成本考虑,32.768KHZ晶振已经是属于低频产品,再低体积会增大,和它搭配的负载电容也会增大,不适合小型化的钟表;如果选择更高频率的晶振对于数字电路来说,工作频率越高,就意味着更高的电力消耗,32768这个频率已经能满足精度和成本要求,也能满足低功耗要求,所有没必要使用到更高的频率。 在数字电路中,晶振决定电路的生与死、好与坏,也会影响到系统 稳定性。
DS1302寄存器
秒寄存器(81h、80h):bit7定义为时钟暂停标志(CH),当该位置为1时,时钟震荡停止,DS1302处于低功耗状态;当置0时,时钟开始运行。 小时寄存器(85h、84h):bit7定义是运行于12小时模式/24小时模式,当为1时选择12小时模式,此时bit5为AM/PM位,在24小时模式时为小时数据位。 控制寄存器(8Fh、8Eh):bit7是写保护位(WP)其他7位均为0.在任何对时钟或者RAM读写操作之前,WP必须为0.当WP为1时,不能对任何时钟日历寄存器或RAM进行读写操作。 突发模式:突发模式是指一次传输多个字节的时钟信号或RAM数据。
Ds1302附加31字节静态RAM
时序图和控制指令
命令字是启动数据传输的控制指令 bit7必须使逻辑1,如果是0则禁止对DS1302写入 bit6在逻辑0时规定为时钟/日历日历数据,为1时规定为RAM数据 bit1-bit5表示输入/输出指定的寄存器 bit0在逻辑0时为写操作,逻辑1时为读操作
参考例程
#include <STC12C5A60S2.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define MAIN_Fosc 11059200UL
#define LCD1602_DB P0
#define DS1302_W_ADDR 0x80
#define DS1302_R_ADDR 0x81
uchar TimeData[7]={00, 59, 23, 15, 9, 4, 22};
sbit LCD1602_RS = P3^5;
sbit LCD1602_RW = P3^6;
sbit LCD1602_EN = P3^4;
sbit TSCLK = P1^0;
sbit TIO = P1^1;
sbit TRST = P1^2;
void Delay_Ms(uint ms)
{
uint i;
do{
i = MAIN_Fosc / 96000;
while(--i);
}while(--ms);
}
void DS1302_W_Byte(uchar dat)
{
uchar i;
for(i = 0; i < 8; i++)
{
TSCLK = 0;
TIO = dat & 0x01;
TSCLK = 1;
dat >>= 1;
}
}
uchar DS1302_R_Byte()
{
uchar i, dat;
for(i = 0; i < 8; i++)
{
TSCLK = 0;
dat >>= 1;
if(TIO) dat |= 0x80;
TSCLK = 1;
}
return dat;
}
void DS1302_W_DAT(uchar cmd, uchar dat)
{
TRST = 0;
TSCLK = 0;
TRST = 1;
DS1302_W_Byte(cmd);
DS1302_W_Byte(dat);
}
uchar DS1302_R_DAT(uchar cmd)
{
uchar dat;
TRST = 0;
TSCLK = 0;
TRST = 1;
DS1302_W_Byte(cmd);
dat = DS1302_R_Byte();
return dat;
}
void DS1302_Clear_WP()
{
DS1302_W_DAT(0x8e,0x00);
}
void DS1302_Set_WP()
{
DS1302_W_DAT(0x8e,0x80);
TRST = 0;
TSCLK = 0;
}
void Set_DS1302_Time(uchar addr)
{
uchar i, j;
DS1302_Clear_WP();
for(i = 0; i < 7; i++)
{
j = TimeData[i]/10;
TimeData[i] %= 10;
TimeData[i] += j*16;
DS1302_W_DAT(addr, TimeData[i]);
addr += 2;
}
DS1302_Set_WP();
}
void Read_DS1302_Time(uchar addr)
{
uchar i;
DS1302_Clear_WP();
for(i = 0; i < 7; i++)
{
TimeData[i] = DS1302_R_DAT(addr);
addr += 2;
}
DS1302_Set_WP();
}
void Read_Busy()
{
uchar busy;
LCD1602_DB = 0xff;
LCD1602_RS = 0;
LCD1602_RW = 1;
do
{
LCD1602_EN = 1;
busy = LCD1602_DB;
LCD1602_EN = 0;
}while(busy & 0x80);
}
void LCD1602_Write_Cmd(uchar cmd)
{
Read_Busy();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
void LCD1602_Write_Dat(uchar dat)
{
Read_Busy();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
void LCD1602_Dis_OneChar(uchar x, uchar y,uchar dat)
{
if(y) x |= 0x40;
x |= 0x80;
LCD1602_Write_Cmd(x);
LCD1602_Write_Dat(dat);
}
void DisplayOneStr(uchar X,uchar Y,uchar DData)
{
LCD1602_Dis_OneChar (X++,Y,DData/16 + '0');
LCD1602_Dis_OneChar (X,Y,DData%16 + '0');
}
void LCD1602_Dis_Str(uchar x, uchar y, uchar *str)
{
if(y) x |= 0x40;
x |= 0x80;
LCD1602_Write_Cmd(x);
while(*str != '\0')
{
LCD1602_Write_Dat(*str++);
}
}
void Init_LCD1602()
{
LCD1602_Write_Cmd(0x38);
LCD1602_Write_Cmd(0x0c);
LCD1602_Write_Cmd(0x06);
LCD1602_Write_Cmd(0x01);
}
void main()
{
uchar TestStr[] = {"Date:"};
uchar str[] = {"Time:"};
Init_LCD1602();
Set_DS1302_Time(DS1302_W_ADDR);
LCD1602_Dis_Str(0, 0, &TestStr[0]);
LCD1602_Dis_Str(0, 1, &str[0]);
while(1)
{
Read_DS1302_Time(DS1302_R_ADDR);
DisplayOneStr (5,0,TimeData[6]);
LCD1602_Dis_OneChar (7,0,'-');
DisplayOneStr (8,0,TimeData[4]);
LCD1602_Dis_OneChar (10,0,'-');
DisplayOneStr (11,0,TimeData[3]);
LCD1602_Dis_OneChar (13,0,'-');
DisplayOneStr (14,0,TimeData[5]);
DisplayOneStr (6,1,TimeData[2]);
LCD1602_Dis_OneChar (8,1,':');
DisplayOneStr (9,1,TimeData[1]);
LCD1602_Dis_OneChar (11,1,':');
DisplayOneStr (12,1,TimeData[0]);
Delay_Ms(1000);
}
}
|