/*
*Author:kingyal
*Date:2018/8/31
*Description:msp430f2418以IIC方式读取/写入AT24C16数据
P1.2作为SCL时钟线,P1.3作为SDA数据线
*/
#include <msp430x24x.h>
#include "at24c16.h"
#include "common.h"
/*
*Author:kingyal
*Date:2018/9/1
*Function:void At24c16_init()
*Description:初始化SCL和SDA引脚
*/
void At24c16_init()
{
P1DIR |= SCL; //将SCL管脚(P1.2)设置为输出管脚
IIC_scl_low();
IIC_stop();
return;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:int At24c16_writebytes(char *r_buffer, int addr, int len)
*Description:向AT24C16写入数据
IIC写数据流程如下:
启动IIC-->发送写设备地址-->等待响应-->发送字地址(数据地址)-->等待应答-->
向AT24C16写入数据-->等待应答-->继续写入,等待答复......直到写完最后一个数据并收到答复-->
--->停止IIC
*/
int At24c16_writebytes(char *r_buffer, int addr, int len)
{
int i = 0;
unsigned char high_byte = addr>>8; //获取设备地址的一二三位
unsigned char word_byte = addr&0x00FF; //字地址
int device_addr = 0xA0+(high_byte<<1); //设备地址
if(addr+len-1 > 2047)//从该地址开始,存储的字符超出了最大值
{
TraceMsg("write data len overflow", 1);
return 1;
}
//启动IIC
IIC_start();
//发送设备地址
IIC_wriebyte(device_addr);
//等待应答
if(IIC_wait_ack() != 0) //没有应答
{
TraceMsg("IIC_write device write addr is not ack ", 1);
IIC_stop();
return 1;
}
TraceMsg("IIC_write device write addr get ack ", 1);
//发送数据地址
IIC_wriebyte(word_byte);
//等待应答
if(IIC_wait_ack() != 0) //没有应答
{
TraceMsg("IIC_write wrod addr is not ack ", 1);
IIC_stop();
return 1;
}
TraceMsg("IIC_write wrod addr get ack ", 1);
for(i = 0; i < len ; i++)
{
IIC_wriebyte(r_buffer[i]);
//等待应答
if(IIC_wait_ack() != 0) //没有应答
{
TraceMsg("IIC_write data is not ack ", 1);
IIC_stop();
return 1;
}
TraceMsg("IIC_write data get ack ", 1);
}
IIC_stop();
return 0;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:int At24c16_writebytes(char *r_buffer, int addr, int len)
*Description:向AT24C16中读取数据
IIC读取数据流程如下:
启动IIC-->发送写设备地址-->等待响应-->发送字地址(数据地址)-->等待应答-->
重新启动IIC-->发送读设备地址-->等待应答-->接受一个数据,发送一个应答,最后一个数据不需要发送应答
-->停止IIC
*/
int At24c16_readbytes(char *r_buffer, int addr, int len)
{
int i = 0;
unsigned char high_byte = addr>>8; //获取设备地址的一二三位
unsigned char word_byte = addr&0x00FF; //字地址
unsigned char device_addr = 0xA0+(high_byte<<1); //设备写地址
if(addr+len-1 > 2047)//从该地址开始,存储的字符超出了最大值
{
TraceMsg("read data len overflow", 1);
return 1;
}
//启动IIC
IIC_start();
//发送设备读地址
IIC_wriebyte(device_addr);
//等待应答
if(IIC_wait_ack() != 0) //没有应答
{
TraceMsg("IIC_read device read addr is not ack ", 1);
return 1;
}
TraceMsg("IIC_read device read addr get ack ", 1);
//发送数据地址
IIC_wriebyte(word_byte);
//等待应答
if(IIC_wait_ack() != 0) //没有应答
{
TraceMsg("IIC_read wrod addr is not ack ", 1);
return 1;
}
TraceMsg("IIC_read wrod addr get ack ", 1);
//启动IIC
IIC_start();
device_addr = 0xA1+(high_byte<<1); //设备读地址
//发送设备读地址
IIC_wriebyte(device_addr);
//等待应答
if(IIC_wait_ack() != 0) //没有应答
{
TraceMsg("IIC_read device write addr is not ack ", 1);
return 1;
}
TraceMsg("IIC_read device write addr get ack ", 1);
for(i = 0; i < len-1; i++)
{
r_buffer[i] = IIC_readbyte();
IIC_ack();
}
r_buffer[i] = IIC_readbyte();
IIC_noack();
IIC_stop();
return 0;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_delayus(int t_us)
*Description:IIC延时t_us的微秒
*/
void IIC_delayus(int t_us)
{
int i = 0;
for(i = 0; i < t_us; i++)
{
_NOP();
}
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_start()
*Description:IIC开始:
在SCL为高电平的情况下,SDA由高电平变为低电平
*/
void IIC_start()
{
IIC_sda_high();
IIC_delayus(20);
IIC_scl_high();
IIC_delayus(20);
IIC_sda_low();
IIC_delayus(20);
IIC_scl_low();
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_stop()
*Description:IIC结束:
在SCL为高电平的情况下,SDA由低电平变为高电平
*/
void IIC_stop()
{
IIC_sda_low();
IIC_delayus(20);
IIC_scl_high();
IIC_delayus(20);
IIC_sda_high();
IIC_delayus(20);
//IIC_scl_low();
System_Delayms(10); //延迟一点时间
//IIC_delayus(2000);
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:int IIC_readbyte()
*Description:IIC读取一个字节:
SDA设置为输入,SCL为高电平时读取一个位,再拉低等待SDA线上的而数据到来
即SCL在高电平时读取,低电平时等待数据到来
*/
int IIC_readbyte()
{
char i = 0;
char rxd = 0;
IIC_sda_high();
IIC_sda_inputdir();
for(i = 0; i < 8; i++)
{
IIC_scl_high();
rxd<<=1;
if(P1IN&SDA)
{
rxd++;
}
IIC_delayus(30);
IIC_scl_low();
}
return rxd;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:int IIC_readbyte()
*Description:IIC写入一个字节:
在发送一个字节时,先发送高位,再发送低位
在SCL为高电平时,要发送的位为高电平时,将SDA设为输出高电平,
反之设为输出低电平,SDA只有在SCL为低电平时才能改变自己的电平值
*/
void IIC_wriebyte(int txd)
{
char i = 0;
for(i = 0; i < 8; i++)
{
if(txd&0x0080) //先发送高字节
{
IIC_sda_high();
}
else
{
IIC_sda_low();
}
IIC_delayus(30);
IIC_scl_high();
IIC_delayus(50);
txd<<=1;
IIC_scl_low();
IIC_delayus(30);
}
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_ack()
*Description:IIC应答:
在SDA为低电平时,SCL从低电平变为高电平,至少保持4us,再变为低电平
*/
void IIC_ack()
{
IIC_scl_low();
IIC_sda_low();
IIC_delayus(10);
IIC_scl_high();
IIC_delayus(10);
IIC_scl_low();;
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_noack()
*Description:IIC无应答:
在SDA为高电平时,SCL从低电平变为高电平,保持至少4us,再变为低电平
*/
void IIC_noack()
{
IIC_scl_low();
IIC_sda_high();
IIC_delayus(10);
IIC_scl_high();
IIC_delayus(10);
IIC_scl_low();
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:int IIC_wait_ack()
*Description:IIC等待应答:
SDA设为输入,SCL为高电平后开始读取SDA上的电平,读取完后再将SCL拉低
*/
int IIC_wait_ack()
{
int time_count = 0;
IIC_scl_low();
IIC_sda_inputdir();
IIC_delayus(30);
IIC_scl_high();
IIC_delayus(30);
while((P1IN&SDA) != 0) //不是低电平则等待
{
if(time_count > 30)
{
return 1; //没有应答
}
time_count++;
}
IIC_scl_low();
return 0;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_scl_high()
*Description:SCL输出高电平
*/
void IIC_scl_high()
{
P1DIR |= SCL; //设置SCL(p1.2)为高电平输出
P1OUT|= SCL;
IIC_delayus(4); //延时4us
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_scl_low()
*Description:SCL输出低电平
*/
void IIC_scl_low()
{
P1DIR |= SCL; //设置SCL(p1.2)为低电平输出
P1OUT &= ~SCL;
IIC_delayus(4); //延时4us
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_sda_high()
*Description:SDA输出高电平
*/
void IIC_sda_high()
{
P1DIR |= SDA; //设置SDA(p1.3)为高电平输出
P1OUT|= SDA;
IIC_delayus(4); //延时4us
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_sda_low()
*Description:SDA输出低电平
*/
void IIC_sda_low()
{
P1DIR |= SDA; //设置SDA(p1.3)为低电平输出
P1OUT &= ~SDA;
IIC_delayus(4); //延时4us
return ;
}
/*
*Author:kingyal
*Date:2018/9/1
*Function:void IIC_sda_inputdir()
*Description:SDA设置为输入方向
*/
void IIC_sda_inputdir()
{
P1DIR &= ~SDA; //设置SDA(p1.3)为输入
IIC_delayus(4); //延时4us
return ;
}