DS3231——IIC的使用及源码实现
一、IIC
IIC数据有效性:只有在SCL线为低时,SDA线才允许改变电平。
1、初始宏定义如下:
#define DS_SCL BIT6 //DS_SCL = P1.6
#define DS_SDA BIT5 //DS_SDA = P1.5
#define DS_SCL_IN P1DIR &= ~DS_SCL
#define DS_SCL_OUT P1DIR |= DS_SCL
#define DS_SCL_L P1OUT &= ~DS_SCL
#define DS_SCL_H P1OUT |= DS_SCL
#define DS_SDA_IN P1DIR &= ~DS_SDA
#define DS_SDA_OUT P1DIR |= DS_SDA
#define DS_SDA_L P1OUT &= ~DS_SDA
#define DS_SDA_H P1OUT |= DS_SDA
#define DS_SDA_BIT P1IN & DS_SDA
#define DS_SDA_IN_H P1REN |= DS_SDA
二、IIC时序
2.1 开始与结束
从时序图可以看出来,开始信号为SCL为高时,SDA由高变低;结束信号为SCL为低时,SDA由低变高。
函数名称:DS_Start
功 能:I2C起始数据
参 数:无
返回值 :无
voidDS_Start(void)
{
DS_SDA_OUT; //设置IO口方向
DS_SCL_OUT;DS_SCL_H; //SCL、SDA先拉高
Delay_us(5); //delay应该有个最小时间要求
DS_SDA_H;
Delay_us(5);
DS_SDA_L; //SDA拉低表明开始
Delay_us(5);
DS_SCL_L; //SCL拉低,数据传输准备就绪
Delay_us(5);
}
函数名称:DS_Stop
功 能:I2C终止数据
参 数:无
返回值 :无
void DS_Stop(void)
{
DS_SDA_OUT; //设置IO口方向
DS_SCL_OUT;
DS_SCL_L; //先把SCL、SDA拉低,给结束信号做准备
Delay_us(5);
DS_SDA_L;
Delay_us(5);
DS_SCL_H; //SCL拉高
Delay_us(5);
DS_SDA_H; //当SCL为高,SDA由低变高为结束信号
Delay_us(1);
}
2.2 发送数据与读数据:
由图可知,开始信号后,应把SCL拉低,准备数据传输,SDA电平变化后,再拉高SCL,发送数据(最高位先发送),循环8次(一个字节)。
读取函数同理,只不过过程是反的,SDA是输入,主机做接收端。
函数名称:DS_Send_Byte
功 能:I2C发送数据
参 数:data
返回值 :data
void DS_Send_Byte(u8 data)
{
DS_SDA_OUT; //设置IO口输出方向
DS_SCL_OUT;
DS_SCL_L; //SCL为低时,允许SDA改变电平
unsigned char temp;
for(temp=0x80;temp!=0;temp>>=1)
{
if(temp&data==0) //高位先发
{ DS_SDA_L; }
else { DS_SDA_H; }
Delay_us(5);
DS_SCL_H; //SCL拉高发送数据
Delay_us(5);
DS_SCL_L;
}
}
函数名称:DS_Read
功 能:I2C接收数据
参 数:data
返回值 :data
unsigned char DS_Read(void)
{
unsigned char temp;
unsigned char data;
DS_SDA_IN; //主机做接收,SDA线为输入
DS_SDA_IN_H ;
for(temp=0x80;temp!=0;temp>>=1)
{
DS_SCL_H; //SCL拉高,保证接收数据时SDA不改变
Delay_us(5);
if(DS_SDA_BIT==1)
{
data|=temp; //高电平保留
}
else
{
data&=~temp;
}
DS_SCL_L; //SCL拉低,数据准备
}
return data;
}
2.3 主机应答信号与非应答信号:
主机接收完从机数据后,要发送应答或者非应答信号。
函数名称:DS_Ack
功 能:I2C发送应答信号
参 数:无
返回值 :无
void DS_Ack(void)
{
DS_SDA_OUT; //主机为接收端 发送应答信号
DS_SCL_L; //SCL拉低,允许SDA改变电平
DS_SDA_L; //SDA低电平为应答
DS_SCL_H;
Delay_us(5);
DS_SCL_L;
}
函数名称:DS_NAck
功 能:I2C发送非应答信号
参 数:无
返回值 :无
void DS_NAck(void)
{
DS_SDA_OUT; //此时,相当于主机在接收数据,是被动方
DS_SCL_L; //SCL拉低,允许SDA改变电平
DS_SDA_H; //SDA高电平为非应答
DS_SCL_H;
Delay_us(5);
DS_SCL_L;
}
2.4 主机接收从机应答信号:
从机在接收主机数据后,从机要发送一个应答信号,主机判断此应答信号为应答信号或者非应答信号,做下一步处理。
函数名称:DS_Get_Ack
功 能:I2C接收应答信号
参 数:无
返回值 :ack
uchar DS_Get_Ack(void)
{
unsigned char ack;
DS_SDA_IN; //释放数据线,准备接收应答
DS_SDA_IN_H ; //SDA线输入上拉
DS_SCL_H; //SCL线拉高
if(DS_SDA_BIT==1)
{
ack=0;//无应答信号
}
else
{
ack=1;//有应答信号
}
DS_SCL_L;
Delay_us(5);
}
二、DS3231传输时序
由图可知,在发送开始信号后,DS3231接收的第一个字节的前7位是从机地址,即DS3231地址,第八位为读/写操作。
从DS3231的数据手册中可以看出来,DS3231的地址为1101000,所以主机操作的写地址为0XD1,读地址为0XD0。(不要写反)读寄存器指针的方式是开始信号-写器件地址-写寄存器地址-开始信号-接收数据-停止。
函数名称:DS3231_WriteByte
功 能:I2C总线给DS3231发送单字节
参 数:WriteAddrDataToWrite
返回值 :无
void DS3231_WriteByte(u8 WriteAddr,u8DataToWrite)
{
DS_Start();
DS_Send_Byte(0XD1); //发送器件地址
DS_Get_Ack();
DS_Send_Byte(WriteAddr); //发送首地址
DS_Get_Ack();
DS_Send_Byte(DataToWrite);//发送数据
DS_Get_Ack();
DS_Stop();
Delay_ms(1);
}
函数名称:DS3231_ReadByte
功 能:I2C总线从DS3231接收单字节
参 数:ReadAddrDataToRead
返回值 :Data
uchar DS3231_ReadByte(u8 ReadAddr)
{
uchar R_Data=0;
DS_Start();
DS_Send_Byte(0XD0);//读地址
DS_Ack();
DS_Send_Byte(ReadAddr);
DS_Ack();
DS_Start();
DS_Send_Byte(0XD1);
R_Data=DS_Read();
Delay_us(5);
DS_NAck();
DS_Stop();
return R_Data;
}
函数名称:Readtime
功 能:读取DS3231时间
参 数:R_tmpdate
返回值 :无
void Read_RTC()
{ unsigned char rtc_address[6]={0x00,0x01,0x02,0x04,0x05,0x06};
unsigned char R_tmpdate[6];
unsigned char i,*p;
p=rtc_address; //地址传递
for(i=0;i<6;i++) //分6次读取秒分时日月年
{
R_tmpdate[i]=DS3231_ReadByte(*p);
p++;
}
}
void ModifyTime(uchar yea,uchar mon,ucharda,uchar hou,uchar min,uchar sec)
{
uchar temp=0;
DS3231_WriteByte(0x06,temp);//修改年
DS3231_WriteByte(0x05,temp);//修改月
DS3231_WriteByte(0x04,temp);//修改日
DS3231_WriteByte(0x02,temp);//修改时
DS3231_WriteByte(0x01,temp);//修改分
DS3231_WriteByte(0x00,temp);//修改秒
}