0%

DS3231外设驱动

软件:IAR
硬件:MSP4305419A,DS3231

学习外围时钟模块DS3231并编写驱动.

简介

  • DS3231,高精度I2C实时时钟器件;
  • 具有电池输入,并在设备的主电源中断时保持准确的计时;
  • RTC保留秒,分钟,小时,日期,日期,月份和年份的信息;
  • 对于少于31天的月份,将自动调整月末的日期,包括闰年的更正;
  • 带有AM / PM指示器的时钟以24小时或12小时格式运行;
  • 提供了两个可编程的日时钟警报和一个可编程的方波输出;
  • 地址和数据通过I2C双向总线串行传输。
  • 自带存储芯片:AT24C32 EEPROM芯片(存储容量32K)

引脚

image-20201231141212427

引脚说明:

  • Vcc—引脚电源;
  • INT/SQW—低电平有效中断或方波输出,低电平复位引脚;
  • 32kV—32kHz输出。此漏极开路输出引脚要求外接上拉电阻。使能状态下,输出可工作在任意电源拿下;如不使用,保持开路。
  • N.C.—表示无连接;外部必须接地
  • GND—地;
  • VBAT—备用电源输入;
  • SDA—串行数据输入输出;
  • SCL—串行时钟输入。

DS3231内部框图:

image-20201231141728544

寄存器

image-20201231154044290

寄存器还是很好懂的,主意好各个Bit的控制的时分秒。

程序

设计原理图:

image-20201231165705458

image-20201231165345940image-20201231165515422

image-20201231165515422

宏定义说明:

1
2
3
4
5
6
#define DS3231_SCL_H 	P9OUT |= BIT2											  //P9.2输出SCL高电平
#define DS3231_SCL_L P9OUT &= ~BIT2 //P9.2输出SCL低电平
#define DS3231_SDA_IN do { P9DIR &= ~BIT1; delay_us(2);} while(0) //P9.1设置为SDA输入
#define DS3231_SDA_OUT do { P9DIR |= BIT1; delay_us(2);} while(0) //P9.1设置为SDA输出
#define DS3231_SDA_H do { DS3231_SDA_OUT; P9OUT |= BIT1; delay_us(2);} while(0)//P9.1输出SDA高电平
#define DS3231_SDA_L do { DS3231_SDA_OUT; P9OUT &= ~BIT1; delay_us(2);} while(0)//P9.1输出SDA低电平

函数声明:

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); //选择P9.1和P9.2为SDA , SCK

DS3231_SCL_H;
DS3231_SDA_H;

P2DIR |= BIT5; //VTEM
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
/*   SDA低-SCL高-SDA高   */
void DS3231_I2cError(void)
{
DS3231_SDA_L;
delay_us(10);
DS3231_SCL_H;
delay_us(10);
DS3231_SDA_H;
delay_us(10);
}

开始和结束信号

img从时序图可知,开始信号为: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
 /*******************************************
函数名称:DS3231_Start
功 能:I2C起始数据
参 数:无
返 回 值:无
*******************************************/
void DS3231_Start(void)
{
DS3231_SDA_H; //SCL和SDA先拉高
delay_us(10); //Delay的最小时间要求
DS3231_SCL_H;
delay_us(10);
DS3231_SDA_L; //当SCL为高时,SDA由高变低表示开始
delay_us(10);
DS3231_SCL_L; //SCL拉低表示数据传输准备就绪
delay_us(10);
}

/*******************************************
函数名称:DS3231_Stop
功 能:I2C终止数据
参 数:无
返 回 值:无
********************************************/
void DS3231_Stop(void)
{
DS3231_SCL_L;
delay_us(10);
DS3231_SDA_L; //SCL和SDA拉低为结束信号做准备
delay_us(10);
DS3231_SCL_H; //SCL拉高,对照时序图前半部分[STOP]
delay_us(10);
DS3231_SDA_H; //当SCL为高时,SDA由低到高为结束信号
delay_us(10);
}

发送&读写函数:

image-20210104112626698

​ 由图可知,开始信号之后,将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
/*******************************************
函数名称:DS3231_WriteByte
功 能:I2C发送/写入数据
参 数:ucData
返 回 值:ucData
********************************************/
void DS3231_WriteByte(unsigned char ucData)
{
unsigned char i;
DS3231_SDA_OUT;
for(i=0;i<8;i++) //循环8次,一个字节
{
DS3231_SCL_L;
delay_us(10);
if(ucData & 0x80)
{
DS3231_SDA_H; //高位先发
}
else
{
DS3231_SDA_L;
}
delay_us(10);
DS3231_SCL_H; //SCL拉高,发送数据
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;} //循环20次,若I2C接收到应答信号,F_DS3231_Ack标志置1
}
//-------------------------------------
delay_us(10);
DS3231_SCL_L;
DS3231_SDA_OUT;
DS3231_SDA_H;
}

/*******************************************
函数名称:DS3231_ReadByte
功 能:I2C发送/写入数据
参 数:endbyte
返 回 值:ucValue
********************************************/
unsigned char DS3231_ReadByte(unsigned char endbyte)
{
unsigned char ucValue;
unsigned char ucIndex;

DS3231_SDA_IN; //P9.1设置为输入
ucValue=0;
for(ucIndex=0;ucIndex<8;ucIndex++)
{
ucValue <<= 1;
DS3231_SCL_L; //P9.2输出低电平
delay_us(10);
DS3231_SCL_H; //P9.2输出高电平,保证接收数据时SDA不改变
delay_us(10);
if(DS3231_SDA()) //引脚状态检测
{
ucValue |= 1; //高电平保留
}
delay_us(10);
DS3231_SCL_L; //SCL拉低,数据准备
delay_us(10);
}
DS3231_SDA_OUT; //P9.1设置为输入
if(endbyte != 0)
DS3231_ACK();
else
DS3231_NoAck();

return ucValue;
}

主机应答信号&非应答信号

img主机接收完从机数据后,要发送应答或者非应答信号。

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
 /*******************************************
函数名称:DS3231_ACK
功 能:I2C发送应答信号
参 数:无
返回值 :无
********************************************/
void DS3231_ACK(void) //主机位接收端,发送应答信号
{
//DS3231_SCL_L; //SCL拉低,允许SDA改变电平
DS3231_SDA_L; //SCL低电平为应答
delay_us(10);
DS3231_SCL_H;
delay_us(10);
DS3231_SCL_L;
delay_us(10);
}

/*******************************************
函数名称:DS3231_NoAck
功 能:I2C发送非应答信号
参 数:无
返回值 :无
********************************************/
void DS3231_NoAck(void)
{
//DS3231_SCL_L; //SCL拉低,允许SDA改变电平
DS3231_SDA_H;
delay_us(10);
DS3231_SCL_H;
delay_us(10);
DS3231_SCL_L;
delay_us(10);
}

/*******************************************
函数名称:DS3231_SDA
功 能:状态检测,I2C接收应答信号
参 数:无
返 回 值:0/1
*******************************************/
uint8 DS3231_SDA(void)
{
DS3231_SDA_IN;
if(P9IN & BIT1)
return 1; //无应答信号
else
return 0; //有应答信号
}

DS3231传输时序图

​ 再发送开始信号之后,DS3231接收的第一个字节的前7位时从机地址(DS3231地址),第八位为读/写操作。

image-20210105163356262

​ 读写地址如下图,可以看出DS3231的主机地址为1101000,所以主机的写操作地址为0xD1(1101 0000),读操作地址为0xD0(1101 0001)

image-20210105164631330

image-20210105165208899

image-20210105164631330

image-20210105165208899

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
 /*******************************************
函数名称:DS3231_write_byte
功 能:I2C总线给DS3231发送单字节
参 数:无
返回值 :无
********************************************/
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
 /*******************************************
函数名称:DS3231_read_byte
功 能:I2C总线给DS3231接收单字节
参 数:无
返回值 :无
********************************************/
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
 /*******************************************
函数名称:BCD2HEX
功 能:BCD转换为Byte
参 数:uint val
返回值 :char i
********************************************/
uint8 BCD2HEX(uint8 val) //BCD转换为Byte
{
unsigned char i;
i= val & 0x0f;
val >>= 4;
val &= 0x0f;
val *= 10;
i += val;

return i;
}

/*******************************************
函数名称:B_BCD
功 能:B码转换为BCD码
参 数:uint val
返回值 :uint i
********************************************/
uint8 B_BCD(uint8 val) //B码转换为BCD码
{

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
 /*******************************************
函数名称:DS3231_ModifyTime
功 能:修改时间
参 数:BCD码
返回值 :
********************************************/
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
 /*******************************************
函数名称:DS3231_GetTime
功 能:获取、计算、显示时间
参 数:BCD码
返回值 :
********************************************/
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); //时 24小时制
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();

}
两种颜色的功德箱(逃