软件:IAR
硬件:MSP4305419A,DS3231
学习外围时钟模块DS3231并编写驱动.
简介
- DS3231,高精度I2C实时时钟器件;
- 具有电池输入,并在设备的主电源中断时保持准确的计时;
- RTC保留秒,分钟,小时,日期,日期,月份和年份的信息;
- 对于少于31天的月份,将自动调整月末的日期,包括闰年的更正;
- 带有AM / PM指示器的时钟以24小时或12小时格式运行;
- 提供了两个可编程的日时钟警报和一个可编程的方波输出;
- 地址和数据通过I2C双向总线串行传输。
- 自带存储芯片:AT24C32 EEPROM芯片(存储容量32K)
引脚
引脚说明:
- Vcc—引脚电源;
- INT/SQW—低电平有效中断或方波输出,低电平复位引脚;
- 32kV—32kHz输出。此漏极开路输出引脚要求外接上拉电阻。使能状态下,输出可工作在任意电源拿下;如不使用,保持开路。
- N.C.—表示无连接;外部必须接地
- GND—地;
- VBAT—备用电源输入;
- SDA—串行数据输入输出;
- SCL—串行时钟输入。
DS3231内部框图:
寄存器
寄存器还是很好懂的,主意好各个Bit的控制的时分秒。
程序
设计原理图:
宏定义说明:
1 2 3 4 5 6
| #define DS3231_SCL_H P9OUT |= BIT2 #define DS3231_SCL_L P9OUT &= ~BIT2 #define DS3231_SDA_IN do { P9DIR &= ~BIT1; delay_us(2);} while(0) #define DS3231_SDA_OUT do { P9DIR |= BIT1; delay_us(2);} while(0) #define DS3231_SDA_H do { DS3231_SDA_OUT; P9OUT |= BIT1; delay_us(2);} while(0) #define DS3231_SDA_L do { DS3231_SDA_OUT; P9OUT &= ~BIT1; delay_us(2);} while(0)
|
函数声明:
1 2 3 4 5 6 7 8 9
| void DS3231_Start(void); void DS3231_I2cError(void); void DS3231_Stop(void); void DS3231_ACK(void); void DS3231_NoAck(void); unsigned char DS3231_ReadByte(unsigned char endbyte); void DS3231_WriteByte(unsigned char ucData);
int F_DS3231_Ack;
|
DS3231时钟芯片的管脚初始化:
1 2 3 4 5 6 7 8 9 10
| void DS3231_Init_Pins(void) { P9DIR |= (BIT1+BIT2); DS3231_SCL_H; DS3231_SDA_H; P2DIR |= BIT5; P2OUT &= ~BIT5; }
|
启动/关闭函数:
1 2 3 4 5 6 7 8 9 10 11 12
| void DS3231_PowOn(void) { P2OUT |= BIT5; delay_us(20); }
void DS3231_PowOff(void) { delay_us(20); P2OUT &= ~BIT5; }
|
基本功能函数:
1 2 3 4 5 6 7 8 9 10
| void DS3231_I2cError(void) { DS3231_SDA_L; delay_us(10); DS3231_SCL_H; delay_us(10); DS3231_SDA_H; delay_us(10); }
|
开始和结束信号
从时序图可知,开始信号为:SCL高时,SDA由高变低;结束信号为:SCL为低时,SDA由低变高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
void DS3231_Start(void) { DS3231_SDA_H; delay_us(10); DS3231_SCL_H; delay_us(10); DS3231_SDA_L; delay_us(10); DS3231_SCL_L; delay_us(10); }
void DS3231_Stop(void) { DS3231_SCL_L; delay_us(10); DS3231_SDA_L; delay_us(10); DS3231_SCL_H; delay_us(10); DS3231_SDA_H; delay_us(10); }
|
发送&读写函数:
由图可知,开始信号之后,将SCL拉低,准备数据传输,SDA电平变化后,再拉高SCL,发送数据(最高位先发送),循环8次(一个字节);
读函数同理,过程与发送相反,SDA做输入,主机做接收端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
void DS3231_WriteByte(unsigned char ucData) { unsigned char i; DS3231_SDA_OUT; for(i=0;i<8;i++) { DS3231_SCL_L; delay_us(10); if(ucData & 0x80) { DS3231_SDA_H; } else { DS3231_SDA_L; } delay_us(10); DS3231_SCL_H; delay_us(10); ucData <<= 1; DS3231_SCL_L; delay_us(10); } DS3231_SDA_IN; delay_us(10); F_DS3231_Ack = 0; DS3231_SCL_H; for(i=0;i<20;i++) { if(!DS3231_SDA()) {F_DS3231_Ack = 1; break;} } delay_us(10); DS3231_SCL_L; DS3231_SDA_OUT; DS3231_SDA_H; }
unsigned char DS3231_ReadByte(unsigned char endbyte) { unsigned char ucValue; unsigned char ucIndex;
DS3231_SDA_IN; ucValue=0; for(ucIndex=0;ucIndex<8;ucIndex++) { ucValue <<= 1; DS3231_SCL_L; delay_us(10); DS3231_SCL_H; delay_us(10); if(DS3231_SDA()) { ucValue |= 1; } delay_us(10); DS3231_SCL_L; delay_us(10); } DS3231_SDA_OUT; if(endbyte != 0) DS3231_ACK(); else DS3231_NoAck();
return ucValue; }
|
主机应答信号&非应答信号
主机接收完从机数据后,要发送应答或者非应答信号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
void DS3231_ACK(void) { DS3231_SDA_L; delay_us(10); DS3231_SCL_H; delay_us(10); DS3231_SCL_L; delay_us(10); }
void DS3231_NoAck(void) { DS3231_SDA_H; delay_us(10); DS3231_SCL_H; delay_us(10); DS3231_SCL_L; delay_us(10); }
uint8 DS3231_SDA(void) { DS3231_SDA_IN; if(P9IN & BIT1) return 1; else return 0; }
|
DS3231传输时序图
再发送开始信号之后,DS3231接收的第一个字节的前7位时从机地址(DS3231地址),第八位为读/写操作。
读写地址如下图,可以看出DS3231的主机地址为1101000,所以主机的写操作地址为0xD1(1101 0000),读操作地址为0xD0(1101 0001)
DS3231写入模式工作流程:
程序步骤 |
主机 |
从机 |
DS3231 |
1 |
产生START信号 |
|
接收从机地址字节1101 0000 |
2 |
|
|
确认从机地址+写位之后,在SDA上输出一个应答 |
3 |
将字地址发送到DS3231 |
|
设置寄存器指针,确认传输 |
4 |
发送零个或多个字节的数据 |
|
寄存器指针递增,接收每一个字节后都将发送一个确认位 |
5 |
产生一个STOP信号终止数据写入 |
|
|
DS3231读取模式工作流程:
程序步骤 |
主机 |
从机 |
DS3231 |
1 |
产生START信号 |
|
接收从机地址字节1101 0001 |
2 |
|
|
确认之后,在SDA输出一个应答 |
3 |
|
|
从寄存器指针指向寄存器地址开始发送数据 |
4 |
|
|
收到非确认信息才能结束 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
void DS3231_write_byte(uint8 dev_add,uint8 reg,uint8 data) { DS3231_Start(); DS3231_WriteByte(dev_add); DS3231_WriteByte(reg); DS3231_WriteByte(data); DS3231_Stop();
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
uint8 DS3231_read_byte(uint8 dev_add,uint8 reg) { uint8 ret; DS3231_Start(); DS3231_WriteByte(dev_add); DS3231_WriteByte(reg); DS3231_Start(); DS3231_WriteByte(dev_add+1); ret=DS3231_ReadByte(0); DS3231_Stop(); return ret; }
|
硬件读取的为BCD码格式的时间数据,BCD码增加了硬件算数运算的复杂度且存储效率低,因此在读取之后需要进行转码读取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
uint8 BCD2HEX(uint8 val) { unsigned char i; i= val & 0x0f; val >>= 4; val &= 0x0f; val *= 10; i += val; return i; }
uint8 B_BCD(uint8 val) { uint8 i,j,k; i=val/10; j=val%10; k=j+(i<<4); return k; }
|
读取时间、计算、显示函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
void DS3231_ModifyTime(uint8 yea,uint8 mon,uint8 da,uint8 hou,uint8 min,uint8 sec) { uint8 temp=0; DS3231_PowOn(); temp=B_BCD(yea); DS3231_write_byte(0xD0,0x06,temp); temp=B_BCD(mon); DS3231_write_byte(0xD0,0x05,temp); temp=B_BCD(da); DS3231_write_byte(0xD0,0x04,temp); temp=B_BCD(hou); DS3231_write_byte(0xD0,0x02,temp); temp=B_BCD(min); DS3231_write_byte(0xD0,0x01,temp); temp=B_BCD(sec); DS3231_write_byte(0xD0,0x00,temp); DS3231_PowOff(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
void DS3231_GetTime(uint8 *yea,uint8 *mon,uint8 *da,uint8 *hou,uint8 *min,uint8 *sec) { uint8 temp1;
DS3231_PowOn(); temp1=DS3231_read_byte(0xd0,0x06); *yea=BCD2HEX(temp1);
temp1=DS3231_read_byte(0xd0,0x05); *mon=BCD2HEX(temp1); temp1=DS3231_read_byte(0xd0,0x04); *da=BCD2HEX(temp1); temp1=DS3231_read_byte(0xd0,0x02); temp1&=0x3f; *hou=BCD2HEX(temp1);
temp1=DS3231_read_byte(0xd0,0x01); *min=BCD2HEX(temp1); temp1=DS3231_read_byte(0xd0,0x00); *sec=BCD2HEX(temp1); DS3231_PowOff(); }
|