📄 i2c.c
字号:
#include "config.h"
#include "I2c.h"
volatile Uchar8 I2c_Mode; //I2C工作模式0x11表示写,0x22表示读
volatile Uchar8 I2c_sla; //I2C器件从地址
volatile Uint32 I2c_suba; //I2C器件内部子地址
volatile Uchar8 I2c_suba_num; //I2C子地址字节数
volatile Uchar8 *I2c_buf; //I2C数据缓存区指针
volatile Uchar8 I2c_num; //I2C要读取/写入的数据个数
volatile Uchar8 I2c_end; //I2C总线结束标志:结束总线是置1
volatile Uchar8 I2c_suba_en; //I2C子地址控制0:为子地址已经处理或者不震要子地址;1:为读取操作;2:为写操作
/*********************************************************************************************************
** 函数名称: Uchar8 I2cSendByte(Uchar8 sla, Uchar8 dat, Uint32 suba)
** 输 入: sla 器件地址;
dat 要发送的数据
suba 器件子地址
** 输 出: 返回0表示出错;返回1表示正确
** 全局变量: 无
** 调用模块: 无
** 作 者: 邱志城
** 日 期: 2008_08_21
** 功能描述:向无子地址器件发送1字节数据
********************************************************************************************************/
Uchar8 I2cSendByte(Uchar8 sla, Uchar8 dat, Uint32 suba)
{
I2c_sla = sla; //器件的从机地址, R = 1
I2c_suba = suba; //器件子地址
I2c_suba_num = 2; //器件子地址为2个字节
I2c_buf = &dat;
I2c_num = 1;
I2c_suba_en = 2;
I2c_end = 0;
I2C0CONCLR = 0x2c;
I2C0CONSET = 0x40; // 使能I2C
I2C0CONSET = 0x64; // 设置为主机,并启动总线
while(I2c_end == 0);
if(I2c_end == 1) return(1);
else return(0);
}
/*********************************************************************************************************
** 函数名称: Uchar8 I2cRcvByte(Uchar8 sla, Uchar8 *dat, Uint32 suba)
** 输 入: sla 器件地址;
dat 要发送的数据
suba 器件子地址
** 输 出: 返回0表示出错;返回1表示正确
** 全局变量: 无
** 调用模块: 无
** 作 者: 邱志城
** 日 期: 2008_08_21
** 功能描述:从无子地址器件读取1字节数据
********************************************************************************************************/
Uchar8 I2cRcvByte(Uchar8 sla, Uchar8 *dat, Uint32 suba)
{
I2c_sla = sla + 1; //器件的从机地址, R = 1
I2c_suba = suba; //器件子地址
I2c_suba_num = 2; //器件子地址为2个字节
I2c_buf = dat; //数据接收缓冲区指针
I2c_num = 1; //要读取的个数
I2c_suba_en = 1; //读使能
I2c_end = 0;
I2C0CONCLR = 0x2c;
I2C0CONSET = 0x40; // 使能I2C
I2C0CONSET = 0x64; // 设置为主机,并启动总线
while(I2c_end == 0);
if(I2c_end == 1) return(1);
else return(0);
}
/*********************************************************************************************************
** 函数名称: Uchar8 I2c_ReadNByte(Uchar8 sla, Uchar8 suba_type, Uint32 suba, Uchar8 *s, Uint32 num)
** 输 入: sla 器件从地址;
suba_type 子地址结构,1为单字节地址,2为双字节地址, 3为8+X结构
suba 器件子地址
*s 数据接收缓冲区指针
num 读取个数
** 输 出: 返回0表示出错;返回1表示正确
** 全局变量: 无
** 调用模块: 无
** 作 者: 邱志城
** 日 期: 2008_08_21
** 功能描述:从子地址器件任意地址开始读取N字节数据
********************************************************************************************************/
Uchar8 I2c_ReadNByte(Uchar8 sla, Uchar8 suba_type, Uint32 suba, Uchar8 *s, Uint32 num)
{
if(num > 0)
{
switch(suba_type)
{
case 1: //子地址为单个字节
I2c_sla = sla + 1; //器件的从机地址, R = 1
I2c_suba = suba; //器件子地址
I2c_suba_num = 1; //器件子地址为1个字节
break;
case 2: //子地址为两个字节
I2c_sla = sla + 1; //器件的从机地址, R = 1
I2c_suba = suba; //器件子地址
I2c_suba_num = 2; //器件子地址为2个字节
break;
case 3:
I2c_sla = sla + ((suba >> 7) & 0x0e) + 1; //器件的从机地址, R = 1
I2c_suba = suba & 0xff; //器件子地址
I2c_suba_num = 1; //器件子地址为8 + x个字节
break;
}
I2c_buf = s; //数据接收缓冲区指针
I2c_num = num; //要读取的个数
I2c_suba_en = 1; //有子地址
I2c_end = 0;
I2C0CONCLR = (1 << 2) | (1 << 3) | (1 << 5); //置位:AA, SI, STA,清除AA,SI, STA的标地位
I2C0CONSET = (1 << 5) | (1 << 6); //置位STA, I2CEN。启动I2C总线
while(I2c_end == 0);
if(I2c_end == 1) return(1);
else return(0);
}
return(0);
}
/*********************************************************************************************************
** 函数名称: void I2c_ReadData(Uint32 suba, Uchar8 *s, Uint32 num)
** 输 入:
suba 器件子地址
*s 数据接收缓冲区指针
num 读取个数
** 输 出: 返回0表示出错;返回1表示正确
** 全局变量: 无
** 调用模块: 无
** 作 者: 邱志城
** 日 期: 2008_08_21
** 功能描述:从子地址器件任意地址开始读取N字节数据
********************************************************************************************************/
void I2c_ReadData(Uint32 suba, Uchar8 *s, Uint32 num)
{
if(I2c_Mode == I2c_ModeRead)
{
I2c_ReadNByte(AT24C256, 2, suba, s, num);
}
}
/*********************************************************************************************************
** 函数名称: Uchar8 I2c_WriteNByte(Uchar8 sla, Uchar8 suba_type, Uint32 suba, Uchar8 *s, Uint32 num)
** 输 入: sla 器件从地址;
suba_type 子地址结构,1为单字节地址,2为双字节地址, 3为8+X结构
suba 器件子地址
*s 将要写入的数据的指针
num 将要写入的数据的个数
** 输 出: 返回0表示出错;返回1表示正确
** 全局变量: 无
** 调用模块: 无
** 作 者: 邱志城
** 日 期: 2008_08_21
** 功能描述:从子地址器件任意地址开始读取N字节数据
********************************************************************************************************/
Uchar8 I2c_WriteNByte(Uchar8 sla, Uchar8 suba_type, Uint32 suba, Uchar8 *s, Uint32 num)
{
if(num > 0)
{
switch(suba_type)
{
case 1: //子地址为单个字节
I2c_sla = sla; //器件的从机地址, R = 1
I2c_suba = suba; //器件子地址
I2c_suba_num = 1; //器件子地址为1个字节
break;
case 2: //子地址为两个字节
I2c_sla = sla; //器件的从机地址, R = 1
I2c_suba = suba; //器件子地址
I2c_suba_num = 2; //器件子地址为2个字节
break;
case 3:
I2c_sla = sla + ((suba >> 7) & 0x0e); //器件的从机地址, R = 1
I2c_suba = suba & 0xff; //器件子地址
I2c_suba_num = 1; //器件子地址为8 + x个字节
break;
}
I2c_buf = s; //数据接收缓冲区指针
I2c_num = num; //要读取的个数
I2c_suba_en = 2; //有子地址
I2c_end = 0;
I2C0CONCLR = (1 << 2) | (1 << 3) | (1 << 5); //置位:AA, SI, STA,清除AA,SI, STA的标地位
I2C0CONSET = (1 << 5) | (1 << 6); //置位STA, I2CEN。启动I2C总线
while(I2c_end == 0);
if(I2c_end == 1) return(1);
else return(0);
}
return(0);
}
/*********************************************************************************************************
** 函数名称: void I2c_WriteData(Uint32 suba, Uchar8 *s, Uint32 num)
** 输 入: sla 器件从地址;
suba_type 子地址结构,1为单字节地址,2为双字节地址, 3为8+X结构
suba 器件子地址
*s 将要写入的数据的指针
num 将要写入的数据的个数
** 输 出: 返回0表示出错;返回1表示正确
** 全局变量: 无
** 调用模块: 无
** 作 者: 邱志城
** 日 期: 2008_08_21
** 功能描述:从子地址器件任意地址开始读取N字节数据
********************************************************************************************************/
void I2c_WriteData(Uint32 suba, Uchar8 *s, Uint32 num)
{
if(I2c_Mode == I2c_ModeWr)
{
IO1CLR |= I2c_wp; //输出低地平,去掉写保护
I2c_WriteNByte(AT24C256, 2, suba, s, num);
IO1SET |= I2c_wp; //输出低地平,去掉写保护
}
}
/*********************************************************************************************************
** 函数名称: void I2c_IRQ(void) __irq
** 输 入: 无
** 输 出:
** 全局变量: 无
** 调用模块: 无
** 作 者: 邱志城
** 日 期: 2008_08_21
** 功能描述:硬件I2C中断服务程序,注意处理为2字节的情况
********************************************************************************************************/
void __irq I2c_IRQ(void)
{
Uchar8 sta;
sta = I2C0STAT; // 读出I2C状态字
switch(sta)
{
case 0x08: // 己发送起始条件
if(1==I2c_suba_en) I2C0DAT = I2c_sla&0xFE; // 指定子地址读时,先写入地址
else I2C0DAT = I2c_sla; // 否则直接发送从机地址
I2C0CONCLR = 0x28; // SI=0
break;
case 0x10:
I2C0DAT = I2c_sla; // 重启动总线后,发送从地址
I2C0CONCLR = 0x28; // SI=0
break;
case 0x18: // 已发送SLA+W,并已接收应答
switch(I2c_suba_en)
{
case 0: // 无子地址,则直接发送数据
if(I2c_num>0)
{
I2C0DAT = *I2c_buf++;
I2C0CONCLR = 0x28;
I2c_num--;
}
else
{
I2C0CONSET = 0x10; // 无数据发送,结束总线
I2C0CONCLR = 0x28;
I2c_end = 1; // 设置总线操作结束标志
}
break;
case 1:
switch(I2c_suba_num) //地址字节数
{
case 0:
I2C0DAT = (I2c_suba & 0xff);
I2C0CONCLR = 0x28;
I2c_suba_en = 0; // 子地址己处理
break;
case 1:
I2C0DAT = (I2c_suba & 0xff); //发送子地址低字节或单字节地址
I2C0CONCLR = 0x28; //清除SI和STA的标志位
I2c_suba_num--;
break;
case 2: //如果是双字节子地址
I2C0DAT = ((I2c_suba >> 8) & 0xff); //先发送地址字节
I2C0CONCLR = 0x28; //清除SI和STA的标志位
I2c_suba_num--; //字节数据减1
break;
}
break;
case 2:
switch(I2c_suba_num) //地址字节数
{
case 0:
I2C0DAT = (I2c_suba & 0xff);
I2C0CONCLR = 0x28;
I2c_suba_en = 0; // 子地址己处理
break;
case 1:
I2C0DAT = (I2c_suba & 0xff); //发送子地址低字节或单字节地址
I2C0CONCLR = 0x28; //清除SI和STA的标志位
I2c_suba_num--;
break;
case 2: //如果是双字节子地址
I2C0DAT = ((I2c_suba >> 8) & 0xff); //先发送地址字节
I2C0CONCLR = 0x28; //清除SI和STA的标志位
I2c_suba_num--; //字节数据减1
break;
}
break;
}
break;
case 0x28: // 已发送I2C数据,并接收到应答
switch(I2c_suba_en)
{
case 0: // 无子地址,则直接发送数据
if(I2c_num>0)
{
I2C0DAT = *I2c_buf++;
I2C0CONCLR = 0x28;
I2c_num--;
}
else
{
I2C0CONSET = 0x10; // 无数据发送,结束总线
I2C0CONCLR = 0x28;
I2c_end = 1;
}
break;
case 1: // 若是指定地址读,则重新启动总线
switch(I2c_suba_num)
{
case 0:
I2C0CONSET = 0x20;
I2C0CONCLR = 0x18;
I2c_suba_en = 0; // 子地址己处理
break;
case 1:
I2C0DAT = (I2c_suba & 0xff); //发送子地址低字节或单字节地址
I2C0CONCLR = 0x28; //清除SI和STA的标志位
I2c_suba_num--;
break;
case 2: //如果是双字节子地址
I2C0DAT = ((I2c_suba >> 8) & 0xff); //先发送地址字节
I2C0CONCLR = 0x28; //清除SI和STA的标志位
I2c_suba_num--; //字节数据减1
break;
}
break;
case 2: // 双字节地址
switch(I2c_suba_num)
{
case 1:
I2C0DAT = (I2c_suba & 0xff); //发送子地址低字节或单字节地址
I2C0CONCLR = 0x28; //清除SI和STA的标志位
I2c_suba_num--;
I2c_suba_en = 0;
break;
case 2: //如果是双字节子地址
I2C0DAT = ((I2c_suba >> 8) & 0xff); //先发送地址字节
I2C0CONCLR = 0x28; //清除SI和STA的标志位
I2c_suba_num--; //字节数据减1
break;
}
break;
}
break;
case 0x20:
case 0x30:
case 0x38:
I2C0CONCLR = 0x28; // 总线进入不可寻址从模式
I2c_end = 0xFF; // 总线出错,设置标志
break;
case 0x40: // 己发送SLA+R,并已接收到应答
if(1==I2c_num) // 最后一字节,接收数据后发送非应答信号
{
I2C0CONCLR = 0x2C; // AA=0,接收到数据后产生非应答
}
else // 接收数据并发送应答信号
{
I2C0CONSET = 0x04; // AA=1,接收到数据后产生应答
I2C0CONCLR = 0x28;
}
break;
case 0x50:
*I2c_buf++ = I2C0DAT; // 读取数据
I2c_num--;
if(1==I2c_num)
{
I2C0CONCLR = 0x2C; // AA=0,接收到数据后产生非应答
}
else
{
I2C0CONSET = 0x04; // AA=1,接收到数据后产生应答
I2C0CONCLR = 0x28;
}
break;
case 0x58:
*I2c_buf++ = I2C0DAT; // 读取最后一字节数据
I2C0CONSET = 0x10; // 结束总线
I2C0CONCLR = 0x28;
I2c_end = 1;
break;
case 0x48:
I2C0CONCLR = 0x28; // 总线进入不可寻址从模式
I2c_end = 0xFF;
break;
}
VICVectAddr = 0x00; // 中断处理结束
}
/*********************************************************************************************************
** 函数名称: void I2C0_Init(void)
** 输 入: 无
** 输 出:
** 全局变量: 无
** 调用模块: 无
** 作 者: 邱志城
** 日 期: 2008_08_21
** 功能描述:I2C初始化, FI2c是I2C的工作频率
********************************************************************************************************/
void I2C0_Init(void)
{
IO1DIR |= I2c_wp; //设置引脚为输出
PINSEL0 = (PINSEL0 & 0xffffff0f) | 0x50; // P0.3 = SDA, P0.2 = SCL
IO1DIR |= I2c_wp; //设置引脚为输出
IO1CLR |= I2c_wp; //输出低地平,去掉写保护
I2C0SCLH = (Fpclk / FI2c + 1) / 2; //设置I2C时钟
I2C0SCLL = (Fpclk / FI2c) / 2;
I2C0CONCLR = 0x2c;
I2C0CONSET = 0x40; //使能为主I2C
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -