📄 i2c.c
字号:
#include "LPC210x.h" /* LPC21xx definitions */
#include "target.h"
#include "type.h"
#include "irq.h"
#include "uart.h"
#include "i2c.h"
#include "rtc.h"
/*定义用于和I2C中断传送信息的全局变量*/
volatile BYTE slarv; //子地址接收标志,为1时表示已接收从机子地址
volatile DWORD adrpoint; //定义从机缓冲区读写操作指针
volatile BYTE I2C_n; //I2C器件顺序号
volatile BYTE I2C_sla; //I2C器件从地址
volatile DWORD I2C_suba; //I2C器件的内部子地址
volatile BYTE I2C_suba_num; //I2C子地址字节数
volatile BYTE *I2C_buf; //I2C数据缓冲区指针,
//如果要将I2C接口设置为从机模式,
//那么,在调用软件包前要设置I2C数据缓冲区指针 I2C_buf
volatile DWORD I2C_num; //要读取/写入的数据个数
volatile BYTE I2C_end; //I2C总线结束标志:结束总线时置1
volatile BYTE I2C_suba_en; /* 子地址控制
0-子地址已经处理或者不需要子地址
1-读取操作
2-写操作
*/
void I2C0MasterHandler (void) __irq
{
BYTE StatValue;
/* this handler deals with master read and master write only */
StatValue = I2STAT;
IENABLE;
switch ( StatValue )
{
/*根据状态码进行相应的处理*/
case 0x08: /*已发送起始条件*/ //主发送和主接收都有
/*装入SLA+W或者SLA+R*/
I2DAT = I2C_sla;
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC);
break;
case 0x10: /*已发送重复起始条件*/ //主发送和主接收都有
/*装入SLA+W或者SLA+R*/
I2DAT = I2C_sla;
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC);
break;
case 0x18:
case 0x28: /*已发送I2DAT中的数据,已接收ACK*/
if(I2C_suba_en == 0) //0-子地址已经处理或者不需要子地址
{
if(I2C_num > 0) //如果还有数据需要读取
{
I2DAT = *I2C_buf++; //读取数据
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC);
I2C_num--; //字节数减1
}
else /*没有数据发送了*/
{
/*停止总线*/
I2CONSET = I2CONSET_STO;
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC); //清零SI,STA
I2C_end = 1; //总线已经停止
}
}
if(I2C_suba_en == 1) /*若是指定地址读,则重新启动总线*/
{
if(I2C_suba_num == 2) //如果是双字节子地址
{
I2DAT = ((I2C_suba>>8) & 0xff); //先发送地址字节
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC); //清零SI,STA
I2C_suba_num--; //子地址字节数减1
break;
}
if(I2C_suba_num == 1) //如果是双字节子地址
{
I2DAT = (I2C_suba & 0xff); //发送子地址低字节或单字节子地址
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC); //清零SI,STA
I2C_suba_num--;
break;
}
if(I2C_suba_num == 0)
{
I2CONCLR = (I2CONCLR_SIC);//清零SI
I2CONSET = I2CONSET_STA; //置位STA
I2C_suba_en = 0; //子地址已经处理
break;
}
}
if(I2C_suba_en == 2) /*指定子地址写,子地址尚未指定,则发送子地址*/
{
if(I2C_suba_num > 0)
{
if(I2C_suba_num == 2) //如果是双字节子地址
{
I2DAT = ((I2C_suba>>8) & 0xff); //先发送子地址高字节
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC); //清零SI,STA
I2C_suba_num--; //子地址字节数减1
break;
}
if(I2C_suba_num == 1) //如果是双字节子地址
{
I2DAT = (I2C_suba & 0xff); //发送子地址低字节或单字节子地址
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC); //清零SI,STA
I2C_suba_num--;
I2C_suba_en = 0;
break;
}
}
}
break;
case 0x40: /*已发送SLA+R,已接收ACK*/
if(I2C_num <= 1) /*如果是最后一个字节*/
{
I2CONCLR = I2CONCLR_AAC; //下次发送非应答信号
}
else
{
I2CONSET = I2CONSET_AA; //下次发送应答信号
}
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC); //清零SI,STA
break;
case 0x20: /*已发送SLA+W,已接收非应答*/
case 0x30: /*已发送I2DAT中的数据,已接收非应答*/
case 0x38: /*在SLA+R/W或数据字节中丢失仲裁*/
case 0x48: /*已发送SLA+R,已接收非应答*/
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC); //清零SI,STA
I2C_end = 0xff; //总线出错
break;
case 0x50: /*已接收数据字节,已返回ACK*/
*I2C_buf++ = I2DAT;
I2C_num--;
if(I2C_num == 1) /*接收最后一个字节*/
{
I2CONCLR = (I2CONCLR_STAC | I2CONCLR_SIC | I2CONCLR_AAC); //STA,SI,AA=0
}
else
{
I2CONSET = I2CONSET_AA;
I2CONCLR = (I2CONCLR_SIC | I2CONCLR_STAC);
}
break;
case 0x58: /*已接收数据字节,已返回非应答*/
*I2C_buf++ = I2DAT ; //读取最后一字节数据
I2CONSET = 0x10; //结束总线
I2CONCLR = 0x28;
I2C_end = 1;
break;
/////////以下为从机模式所对应的状态信息/////////
case 0x60: //接收到自身SLA+W
case 0x68:
slarv = 0;
I2CONSET = 0x04;
I2CONCLR = 0x38; //清除I2C标志位,STA、STO、SI
break;
case 0xa8: //接收到SLA+R,或已经发送数据并接收到ACK位
case 0xb0:
case 0xb8:
I2DAT = *(I2C_buf+adrpoint); //将对应地址处的数据放入I2DAT中
adrpoint++;
I2CONSET = 0x04;
I2CONCLR = 0x38; //清除I2C标志位,STA、STO、SI
break;
case 0x80: //接收到数据
if(slarv==0)
{
adrpoint = I2DAT;
slarv = 1;
}
else
{
*(I2C_buf+adrpoint) = I2DAT;
adrpoint++;
}
I2CONSET = 0x04;
I2CONCLR = 0x38; //清除I2C标志位,STA、STO、SI
break;
case 0xc0: //总线结束,或总线重新启动
default: //其它状态
I2CONSET = 0x04;
I2CONCLR = 0x38; //清除I2C标志位,STA、STO、SI
break;
}
IDISABLE;
VICVectAddr = 0x00; //中断处理结束
}
/*********************************************************************************************************
**函数名称:BYTE I2C_Init(BYTE n,BYTE MODE,DWORD Fi2c,BYTE Adr,BYTE slot)
**函数功能:初始化I2C接口
**入口参数:n :I2C接口号,0--I2C0,1--I2C1
** MODE :工作模式,0--从模式,1--主模式
** Fi2c :I2C通信速率,0~400K,如果超过400K,则会强制设置为400KHz,如果设置为从机,该参数无效
** Adr :当设置为从模式时,Adr表示从地址,在主模式下,该参数是无效的,可以任意设置
** slot :由于I2C采用IRQ中断方式,所以需要指定对应的通道,0~15
**出口参数:1--接口初始化成功,0--接口初始化失败
********************************************************************************************************/
BYTE I2C_Init(BYTE MODE, DWORD Fi2c, BYTE Adr)
{
/*--- Clear flags ---*/
I2CONCLR = I2CONCLR_AAC | I2CONCLR_SIC | I2CONCLR_STAC | I2CONCLR_I2ENC;
if(Fi2c > 400000)
{
Fi2c = 400000; //强制将通信速率限制在0~400KHz的范围内
}
if((MODE!=0)&&(MODE!=1))
{
return(0); //MODE只能为0 1
}
if ( install_irq( I2C0_INT, (void *)I2C0MasterHandler ) == FALSE )
{
return( FALSE );
}
//else //设置I2C1接口的引脚和中断
//{
// PINSEL1 = (PINSEL1 &(~(0x0f<<2)))|(0x01<<2)|(0x01<<4); //设置I2C1管脚有效
// SetISR(19,slot,(DWORD)IRQ_I2C); //设置I2C1中断
//}
if(MODE == 1) //I2C设置为主模式
{
I2SCLL = (Fpclk/Fi2c)/2;
I2SCLH = (Fpclk/Fi2c)/2; //设定I2C时钟
I2CONCLR = 0x2C;
I2CONSET = 0x40; //使能主I2C
}
else //I2C设置为从模式
{
I2ADR = Adr&0xfe; //设置从机地址
I2CONSET = 0x44; //使能从I2C
I2CONCLR = 0x28;
}
return( TRUE );
}
/*********************************************************************************************************
**函数名称:BYTE I2C_SendByte(BYTE n,BYTE sla,BYTE dat)
**函数功能:向无子地址器件发送1字节数据
**入口参数:n 接口号0,1
** sla 器件地址
** dat 要发送的数据
**出口参数:返回值为0时表示出错,为1时表示操作正确
**说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式,程序死等待操作完成
********************************************************************************************************/
BYTE I2C_SendByte(BYTE sla, BYTE dat)
{
/*参数设置*/
I2C_sla = sla; //写操作的器件地址
I2C_buf = &dat; //待发送的数据
I2C_num = 1; //发送1字节数据
I2C_suba_en = 0; //无子地址
I2C_end = 0;
I2CONCLR = 0x2c;
I2CONSET = 0x60; //设置为主机,并启动总线
while(I2C_end == 0) ;
if(I2C_end == 1) return(1);
else return(0);
}
/*********************************************************************************************************
**函数名称:BYTE I2C_RcvByte(BYTE n,BYTE sla,BYTE *dat)
**函数功能:向无子地址器件读取1字节数据
**入口参数:n 接口号0,1
** sla 器件地址
** dat 接受数据的指针变量
**出口参数:返回值为0时表示出错,为1时表示操作正确
**说明:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式,程序死等待操作完成
********************************************************************************************************/
BYTE I2C_RcvByte(BYTE sla,BYTE *dat)
{
/*参数设置*/
I2C_sla = sla+1; //读操作的器件地址
I2C_buf = dat;
I2C_num = 1; //读取字节数为1
I2C_suba_en = 0; //无子地址
I2C_end = 0;
I2CONCLR = 0x2c;
I2CONSET = 0x60; //设置为主机,并启动总线
while(I2C_end == 0);
if(I2C_end == 1) return(1);
else return(0);
}
/*********************************************************************************************************
**函数名称:BYTE I2C_ReadNByte(BYTE n,BYTE sla,DWORD suba_type,DWORD suba,BYTE *s,DWORD num)
**函数功能:从有子地址器件任意地址开始读取N字节数据
**入口参数: n 接口号0,1
** sla 器件从地址
** suba_type 子地址结构 1-单字节地址,2-双字节地址,3-8+X结构
** suba 器件子地址
** *s 数据接收缓冲区指针
** num 读取的个数
**出口参数: 1 操作成功
** 0 操作失败
**说明:程序死等待操作完成
********************************************************************************************************/
BYTE I2C_ReadNByte(BYTE sla, DWORD suba_type, DWORD suba, BYTE *s, DWORD num)
{
if(num > 0) /*判断num个数的格式*/
{
/*参数设置*/
if(suba_type == 1)
{
/*子地址为单字节*/
I2C_sla = sla + 1; //读器件的从地址,R=1
I2C_suba = suba; //器件子地址
I2C_suba_num = 1; //器件子地址为1字节
}
if(suba_type == 2)
{
/*子地址为2字节*/
I2C_sla = sla + 1; //读器件的从地址,R=1
I2C_suba = suba; //器件子地址
I2C_suba_num = 2; //器件子地址为2字节
}
if(suba_type == 3)
{
/*子地址结构为8+X*/ //如24C04/08/16的从地址中含有A8,A9,A10
I2C_sla = sla + ((suba>>7) & 0x0e) + 1; //读器件的从地址,R=1
I2C_suba = suba & 0x0ff; //器件子地址
I2C_suba_num = 1; //器件子地址为8+X
}
I2C_buf = s; //数据接收缓冲区指针
I2C_num = num; //要读取的个数
I2C_suba_en = 1; //有子地址读
I2C_end = 0; //
/*清除STA,SI,AA标志位*/
I2CONCLR = (1<<2)| //AA
(1<<3)| //SI
(1<<5); //STA
/*置位STA,启动I2C总线*/
I2CONSET = (1<<5)| //STA
(1<<6); //I2CEN
/*等待I2C操作完成*/
while(I2C_end == 0);
if(I2C_end == 1) return(1);
else return(0);
}
return(0);
}
/*********************************************************************************************************
**函数名称:BYTE I2C_WriteNByte(BYTE n,BYTE sla,BYTE suba_type,DWORD suba,BYTE *s,DWORD num)
**函数功能:向有子地址器件写入N字节数据
**入口参数: n 接口号0,1
** sla 器件从地址
** suba_type 子地址结构 1-单字节地址,2-双字节地址,3-8+X结构
** suba 器件子地址
** *s 数据发送缓冲区指针
** num 要写入的数据的个数
**出口参数:1 操作成功
** 0 操作失败
**说明:程序死等待操作完成
********************************************************************************************************/
BYTE I2C_WriteNByte(BYTE sla, BYTE suba_type, DWORD suba, BYTE *s, DWORD num)
{
/*参数设置*/
if(num > 0) /*判断num个数的格式*/
{
if(suba_type == 1)
{
/*子地址为单字节*/
I2C_sla = sla; //写器件的从地址
I2C_suba = suba; //器件子地址
I2C_suba_num = 1; //器件子地址为1字节
}
if(suba_type == 2)
{
/*子地址为2字节*/
I2C_sla = sla; //写器件的从地址
I2C_suba = suba; //器件子地址
I2C_suba_num = 2; //器件子地址为2字节
}
if(suba_type == 3)
{
/*子地址结构为8+X*/
I2C_sla = sla + ((suba>>7) & 0x0e) ; //写器件的从地址
I2C_suba = suba & 0x0ff; //器件子地址
I2C_suba_num = 1; //器件子地址为8+X
}
I2C_buf = s; //数据发送缓冲区指针
I2C_num = num; //要发送的数据的个数
I2C_suba_en = 2; //有子地址,写操作
I2C_end = 0; //
/*清除STA,SI,AA标志位*/
I2CONCLR = (1<<2)| //AA
(1<<3)| //SI
(1<<5); //STA
/*置位STA,启动I2C总线*/
I2CONSET = (1<<5)| //STA
(1<<6); //I2CEN
/*等待I2C操作完成*/
while(I2C_end == 0);
if(I2C_end == 1) return(1);
else return(0);
}
return(0);
}
//LM75A
int Read_Temp() // 读取当前温度
{
char MRD[2] = {0,0}; //接收缓冲区
int tmp =0;
I2C_ReadNByte(LM75A_ADDR, 1, 0x00, MRD, 2);
if((MRD[0] & 0x80) == 0x80)
{ // 负温度
tmp = MRD[0];
tmp <<= 3;
tmp += (MRD[1]>>5);
tmp |= 0xf800;
}
else
{ // 正温度
tmp = MRD[0];
tmp <<= 3;
tmp += (MRD[1]>>5);
}
tmp *= 10;
tmp /= 8;
return tmp;
}
BYTE EEPROM_Write(const WORD start_address, BYTE user_data)
{
if ( I2C_WriteNByte(EEPROM_ADDR, 1, start_address, &user_data, 1) )
//if ( I2C_WriteNByte(0, EEPROM_ADDR, 2, start_address, &user_data, 1) ) //24c256
return 1;
else
return 0;
}
BYTE EEPROM_Read(const WORD start_address, BYTE *user_data)
{
if ( I2C_SendByte(EEPROM_ADDR, 0) )
{
if (I2C_RcvByte(EEPROM_ADDR, user_data) )
return 1;
else
return 0;
}
else
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -