一、DS1337介绍 DS1337串行实时时钟芯片是一种低功耗、全部采用BCD码的时钟日历芯片,它带有两个可编程的定时闹钟和一个可编程的方波输出。其地址和数据可通过I2C总线串行传输,能提供秒、分、时、日、星期、月和年等信息。 1.1 DS1337的引脚说明 DS1337的引脚排列示意图如图1所示。各引脚的功能如下:
图1 DS1337引脚示意图
1.2 时间寄存器 常用的时间寄存器地址为00H~06H。设置和初始化时间和日历可通过写相应的寄存器字段来实现,寄存器的数据格式以BCD码表示。DS1337既可以工作在12小时模式下,也可以工作在24小时模式下。小时寄存器的位6被定义为12小时模式或24小时模式选择位,该位为1时,选择12小时模式。在12小时模式时,位5是AM/PM标志位,该位为1时表示PM。当在24小时模式时,位5是第二个十位(表示20到23小时)。月寄存器的第7位是世纪位,当年寄存器从0到99溢出时,该位发生变化。 1.3 控制寄存器和状态寄存器 DS1337中有一个控制寄存器和一个状态寄存器,可用于控制实时时钟、闹钟和方波的输出。 控制寄存器地址为0EH。其中:
DS1337的状态寄存器地址为0FH, 各位的作用如下:
图2 DS1337寄存器 1.4 对DS1337的读与写 图3 DS1337 写寄存器 从上图中,可以看出,主器件发起开始条件,发送从器件的地址和写命令(0xD0|0x00),等待从器件的应答,然后再发送要写的寄存器地址,等待从器件的应答,然后再发送要写的数据,等待从器件的应答,如果写操作完成,主器件发送结束。完成一次写数据过程。 图4 DS1337 读寄存器 从上图中,可以看出,主器件发起开始条件,发送从器件的地址和写命令(0xD0|0x00),等待从器件的应答,然后再发送要读的寄存器地址,等待从器件的应答,然后主器件再重新发送开始条件,发送从器件的地址和读命令(0xD0|0x01),等待从器件的应答,接收从器件发来的数据,主器件应答。当收到最后一个数据后,主器件发送非应答和结束。完成一次读数据过程。 二、SMBus总线介绍 SMBus串行I/O接口是一个双线的双向串行总线,与I2C串行总线兼容。图 5给出了一个典型的SMBus 配置。SMBus 接口的工作电压可以在3.0V和5.0V 之间,总线上不同器件的工作电压可以不同。SCL(串行时钟)和 SDA(串行数据)线是双向的。 必须通过上拉电阻或类似电路将它们连到电源电压。当总线空闲时,这两条线都被拉到高电平。连接在总线上的每个器件的 SCL 和 SDA 都必须是漏极开路或集电极开路的。 图5 SMBus 配置 2.1握手 SMBus采用多种线路条件作为器件间的握手信号。在一次数据传输中,SDA只能在SCL为低时改变电平。在SCL为高电平时SDA发生改变则是代表如下的开始和停止信号:
图6 SMBus 时序 2.2 传输方式 有两种可能的传输方式:写(从主器件到从器件)和读(从从器件到主器件)。在一次传输中,任何一个器件都可以是四种角色之一。这四种角色将在下面说明。注意,从地址+R/W’是指一个8位传输(7位地址,1位R/W)。 1)主发送器:在该方式下,器件在SDA上发送串行数据,在SCL上输出时钟。器件用一个起始条件启动传输过程,发送从地址+W,然后等待从器件的ACK。收到ACK后,器件发送一个或多个字节数据,每个字节都要由从器件确认。在发送完最后一个字节后,器件发送一个停止条件。 2)主接收器:在该方式下,器件在SDA上接收串行数据,在SCL上输出时钟。器件用一个起始条件启动传输过程,之后发送从地址+R。在收到从器件对地址的ACK后,在SCL上输出时钟并在SDA上接收数据。在接收完最后一个字节后,器件将发送一个NACK和一个停止条件。 3)从发送器:在该方式下,器件在SDA上输出串行数据,在SCL上接受时钟。器件接收一个起始条件和它自己的从地址+R,然后发出ACK并进入从发送方式。器件在SDA上发送数据,在发送完每个字节后都要收到一个ACK。在传输完最后一个字节后,主器件发送一个NACK和一个停止条件。 4)从接收器:在该方式下,器件收到来自主器件的起始条件和和它自己的从地址+W。然后发出ACK并进入从接收方式。现在器件在SDA上接收串行数据,在SCL上接收时钟。在接收完每个字节后都要发送一个ACK,在接收到主器件的停止条件后退出从接收方式。 图7为典型的写操作情况。图8为典型的读操作情况。 图7 写操作 (1)为一个成功的传送过程。在(3)中,主器件在收到一个ACK后重新发出起始条件。这一过程允许主器件在不放弃总线的情况下启动一个新的传输过程(例如,从写操作切换到读操作)。重复起始条件通常在访问EEPROM时使用,因为一个读操作前面必须有一个写存储器地址的操作。 图8 读操作 2.3 SMBus特殊功能寄存器对 SMBus 串行接口的访问和控制是通过 5 个特殊功能寄存器来实现的:控制寄存器 SMB0CN、时钟速率寄存器 SMB0CR、地址寄存器 SMB0ADR、数据寄存器 SMB0DAT 和状态寄存器 SMB0STA。系统器件可以有一个或多个 SMBus 串行接口。 2.3.1 控制寄存器SMB0CN SMBus 控制寄存器 SMB0CN 用于配置和控制 SMBus 接口。该寄存器中的所有位都可以 用软件读写。有两个控制位还受 SMBus 硬件的影响。当发生一个有效的串行中断条件时,串 行中断标志(SI,SMB0CN.3)被硬件设置为逻辑 1,该标志只能用软件清 0。当总线上出现 一个停止条件时,停止标志(STO,SMB0CN.4)被硬件清 0。 2.3.2 时钟速率寄存器SMB0CR 在器件工作于主方式时SMBus时钟寄存器用于控制SCL时钟速率。SMB0CR中的8位数决定了时钟速率,公式如下:
其中,SMB0CR是一个负数的补码。因此,对于100kHz的SCL频率和16MHz的SYSCLK,应向SMB0CL装入-80,即0xB0。 2.3.3 地址寄存器 SMB0ADR SMBus地址寄存器保存器件在从方式时将要应答的从地址。位(7:1)保存从地址;位0是通用呼叫允许。如果位0被置位,器件将应答通用呼叫地址(0x00)。 2.3.4 数据寄存器 SMB0DAT SMBus数据寄存器用于保存将要发送或刚刚接收的数据。只有在SI=1时,从该寄存器读出的数据才是有效的。当SI不为1时,SMBus可能处在向SMB0DAT移入数据或从SMB0DAT移出数据的过程中。注意:在传输过程中,从SMB0DAT移出的最高位又移回到最低位,因此在一次传输完成后SMB0DAT中仍然保存着原始数据。 1.3.5 状态寄存器 SMB0STA 共有28个可能的SMBus状态,每个状态对应一个唯一的状态码。状态码的高5位是可变的,而一个有效状态码的低3位固定为0(当SI=1时)。因此所有有效的状态码都是8的整数倍。 三、在C8051F 上的实现 3.1 硬件 3.2 软件 1 typedef struct 2 { 3 u8 second; // 0 to 59 4 u8 minute; // 0 to 59 5 u8 hour; // 0 to 23 (24-hour time) 6 u8 day; // 0 = Sunday, 1 = Monday, etc. 7 u8 date; // 1 to 31 8 u8 month; // 1 to 12 9 u8 year; // 00 to 99 10 } DS1337_time; 11 12 xdata DS1337_time Time; 13 14 bit SMB_BUSY;15 u8 COMMAND;16 u8 Mode;17 u8 wr_data[2]; 18 u8 wrnumber; 19 u8 get_data;20 21 22 void DS1337_Set_time(DS1337_time dt);23 24 25 char bcd2bin(char bcd_value)26 { 27 char temp; 28 temp = bcd_value; 29 temp >>= 1; 30 temp &= 0x78; 31 return(temp + (temp >> 2) + (bcd_value & 0x0f)); 32 } 33 34 35 unsigned char bin2bcd(unsigned char value) 36 { 37 char retval; 38 retval = 0; 39 40 while(1) 41 { 42 if(value >= 10) 43 { 44 value -= 10; 45 retval += 0x10; 46 } 47 else 48 { 49 retval += value; 50 break; 51 } 52 } 53 return(retval); 第25行的函数是将BCD码转化为二进制数。第35行的函数是将二进制数转化为BCD码。 1 void init_iic(void) 2 { 3 SMB0CN = 0x44; // Enable SMBus with acknowledge low (AA = 1) 4 SMB0CR = 146; // SMBus clock rate = 100 kHz 146 5 EIE1 |= 2; // SMBus interrupt enable 6 SI = 0; 7 SMB_BUSY = 1; 8 } 9 10 11 void Write_DS1337(u8 adrress, u8 data1) 12 { 13 Mode = 1;14 wrnumber = 2 ;15 wr_data[0] = adrress;16 wr_data[1] = data1;17 SMB0CN = 0x44;18 COMMAND = DS1337_ADDR;19 STO = 0;20 STA = 1;21 SMB_BUSY = 1;22 while(SMB_BUSY);23 }24 25 26 int Read_DS1337(u8 adrress) 27 {28 Mode=0; 29 SMB0CN = 0x44;30 COMMAND = (DS1337_ADDR | 0x01);31 wr_data[0] = adrress;32 STO = 0;33 STA = 1;34 SMB_BUSY = 1;35 while(SMB_BUSY);36 return get_data;37 } 第1行的函数初始化SMBus总线。第11行的函数为DS1337写操作。第26行的函数为DS1337读操作。 1 void DS1337_init() 2 { 3 u8 status; 4 DS1337_time DT; 5 6 DT.second = 30; 7 DT.minute = 50; 8 DT.hour = 21; 9 DT.day = 2;10 DT.date = 12;11 DT.month = 8;12 DT.year = 14;13 14 status = Read_DS1337(DS1337_STATUS_REG);15 if((status & 0x80) != 0)16 {17 DS1337_Set_time(DT);18 Write_DS1337(DS1337_CONTROL_REG,DS1337_CTRL_REG_INIT_VAL);19 Write_DS1337(DS1337_STATUS_REG,DS1337_CLEAR_STATUS_VAL);20 }21 }22 23 void DS1337_Set_time(DS1337_time dt)24 {25 u8 bcd_sec,bcd_min,bcd_hrs,bcd_day,bcd_date,bcd_mon,bcd_year; 26 27 bcd_sec = bin2bcd(dt.second); 28 bcd_min = bin2bcd(dt.minute); 29 bcd_hrs = bin2bcd(dt.hour); 30 bcd_day = bin2bcd(dt.day); 31 bcd_date = bin2bcd(dt.date); 32 bcd_mon = bin2bcd(dt.month); 33 bcd_year = bin2bcd(dt.year); 34 35 Write_DS1337(DS1337_SECONDS_REG,bcd_sec);36 Write_DS1337(DS1337_MINUTES_REG,bcd_min);37 Write_DS1337(DS1337_HOURS_REG,bcd_hrs);38 Write_DS1337(DS1337_DAY_OF_WEEK_REG,bcd_day);39 Write_DS1337(DS1337_DATE_REG,bcd_date);40 Write_DS1337(DS1337_MONTH_REG,bcd_mon);41 Write_DS1337(DS1337_YEAR_REG,bcd_year);42 }43 44 void DS1337_Get_time() 45 {46 u8 bcd_sec,bcd_min,bcd_hrs,bcd_day,bcd_date,bcd_mon,bcd_year; 47 48 bcd_sec = Read_DS1337(DS1337_SECONDS_REG);49 bcd_min = Read_DS1337(DS1337_MINUTES_REG);50 bcd_hrs = Read_DS1337(DS1337_HOURS_REG);51 bcd_day = Read_DS1337(DS1337_DAY_OF_WEEK_REG);52 bcd_date = Read_DS1337(DS1337_DATE_REG);53 bcd_mon = Read_DS1337(DS1337_MONTH_REG);54 bcd_year = Read_DS1337(DS1337_YEAR_REG);55 56 Time.second = bcd2bin(bcd_sec);57 Time.minute = bcd2bin(bcd_min);58 Time.hour = bcd2bin(bcd_hrs);59 Time.day = bcd2bin(bcd_day);60 Time.date = bcd2bin(bcd_date);61 Time.month = bcd2bin(bcd_mon);62 Time.year = bcd2bin(bcd_year);63 } 第1行的函数为初始化DS1337,读取状态寄存器,如果最高位不为0,则初始化时间。第23行的函数为设置时间,包括年月日星期时分秒。第44行的函数为读取时间。 1 #ifdef eclipse 2 void SMBUS_ISR(void) 3 #else 4 void SMBUS_ISR (void) interrupt ESMB0_VECTOR 5 #endif 6 { 7 switch (SMB0STA) 8 { 9 case SMB_START: //起始条件已发送10 SMB0DAT = (COMMAND & 0xFE);11 STA = 0;12 break;13 case SMB_RP_START: //重复起始条件已发送14 SMB0DAT = COMMAND;15 STA = 0;16 break;17 case SMB_MTADDACK: //地址 + WRITE已发送,收到ACK18 SMB0DAT = wr_data[0]; 19 break;20 case SMB_MTADDNACK: //地址 + WRITE已发送,收到NACK。21 STO = 1; //发送STOP + START重试22 STA = 1;23 break;24 case SMB_MTDBACK: //数据字节已发送,收到ACK。25 switch(Mode)26 {27 case 1:28 wrnumber--;29 if(wrnumber)30 SMB0DAT = wr_data[1];31 else32 {33 STO = 1;34 SMB_BUSY=0; 35 }36 break;37 case 0:38 STA = 1;39 break;40 default: 41 STO = 1;42 SMB_BUSY = 0; 43 break; 44 }45 break;46 case SMB_MTDBNACK: //数据字节已发送,收到NACK。47 STO = 1; 48 STA = 1;49 break;50 case SMB_MTARBLOST: //竞争失败51 STO = 1; 52 STA = 1;53 break;54 case SMB_MRADDACK: //地址 + READ 已发送。收到ACK55 AA = 0; 56 break;57 case SMB_MRADDNACK: //地址 + READ 已发送。收到NACK58 STO = 0; 59 STA = 1;60 break;61 case SMB_MRDBACK: //收到数据字节。ACK已发送62 get_data = SMB0DAT;63 SMB_BUSY = 0; //完成64 STO = 1; 65 break;66 case SMB_MRDBNACK: //收到数据字节。NACK已发送67 get_data = SMB0DAT;68 SMB_BUSY = 0; 69 STO = 1; 70 break;71 default:72 STO = 1;73 break;74 }75 SI = 0;76 } SMBus中断,需要手动清除中断标志。 3.3 实物 |
|