通讯主要就是scl,sda线,和电源-地。4线
地址由24c32器件的A0,A1,A2来决定。SCL,SDA需要接上拉。
本次说的都是主机端流程。
1.启动和stop
SCL=1,SDA下降沿表示启动。 SCL=1,SDA上升沿表示stop。
2.写
流程:start---->设备地址,最后一位为0表示写,等待ACK(ACK是传输每个字节都有,谁收数据谁发ACK,除最后一次ACK为1,其他ACK为0)----->2个字节数据地址------->写入数据---------->stop
注意一点:
1. 24c32每页32字节,32字节写缓存。写入的数据先到缓存区,收到stop时,写入储存区。
2. 如果写入数据大于32字节,需要分为n/32次发送,否则写缓存会被覆盖。
3. 如果写如数据在页边界(如:写2个字节,31地址开始写,会导致32地址写不进去,需要先写第一页,在写第二页)
3.读
流程: start-------->设备地址(写,等应答ACK)------->写入2字节读取的地址------->restart--------->设备地址(读,等应答)-------->接收n个数据(每接收一个数据,应答ACK=0,最后一字节应答ACK=1)----->stop.
读的时候并没长度限制。启动操作变长了,先要写入地址,然后重新启动开始读取。可以把读分为写和读2个部分,写就和上面一样了,linux的I2C驱动就是这样写的。
4.仲裁失败,表示多个主机时,被别的主机抢占了总线。
linux系统中应用层使用:ioctl(fd, I2C_RDWR, &idata)来系统函数进行读写即可
参考主I2C程序:
#include "stm32f10x.h"
#define SCL_H GPIOB->BSRR = IIC_SCL
#define SCL_L GPIOB->BRR = IIC_SCL
#define SDA_H GPIOB->BSRR = IIC_SDA
#define SDA_L GPIOB->BRR = IIC_SDA
#define SDA_read GPIOB->IDR & IIC_SDA
void I2C_delay(void)
{
u16 i=50; //10-大概2us,50-大概7us,IIC速率大概80khz
while(i)
{
i--;
}
}
u8 IIc_Start(void)
{
SDA_H;
SCL_H;
I2C_delay();
if(!SDA_read)return FALSE; //SDA线为低电平则总线忙,退出
SDA_L;
I2C_delay();
if(SDA_read) return FALSE; //SDA线为高电平则总线出错,退出
SDA_L;
I2C_delay();
return TRUE;
}
void IIc_Stop(void)
{
SCL_L;
I2C_delay();
SDA_L;
I2C_delay();
SCL_H;
I2C_delay();
SDA_H;
I2C_delay();
}
void Rd_Ack(void)
{
SCL_L;
I2C_delay();
SDA_L;
I2C_delay();
SCL_H;
I2C_delay();
SCL_L;
I2C_delay();
}
void Rd_NoAck(void)
{
SCL_L;
I2C_delay();
SDA_H;
I2C_delay();
SCL_H;
I2C_delay();
SCL_L;
I2C_delay();
}
u8 Wr_Ack(void) //返回为:=1有ACK,=0无ACK
{
SCL_L;
I2C_delay();
SDA_H;
I2C_delay();
SCL_H;
I2C_delay();
if(SDA_read)
{
SCL_L;
return FALSE;
}
SCL_L;
return TRUE;
}
void IIc_Send(u8 SendByte) //数据从高位到低位//
{
u8 i=8;
while(i--)
{
SCL_L;
I2C_delay();
if(SendByte&0x80)
SDA_H;
else
SDA_L;
SendByte<<=1;
I2C_delay();
SCL_H;
I2C_delay();
}
SCL_L;
}
u8 IIc_Receive(void) //数据从高位到低位//
{
u8 i=8;
u8 ReceiveByte=0;
SDA_H;
while(i--)
{
ReceiveByte<<=1;
SCL_L;
I2C_delay();
SCL_H;
I2C_delay();
if(SDA_read)
{
ReceiveByte|=0x01;
}
}
SCL_L;
return ReceiveByte;
}
/*****************向有子地址器件发送多字节数据函数*************/
u8 Fm24cl64_WritenBytes(u8 sla,u16 suba,u8 *s,u16 no)
{
u16 i;
u8 error = TRUE;
if(no==0) return error;
IIc_Start(); /*启动总线*/
IIc_Send(sla); /*发送器件物理地址*/
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit1;
}
i=(u8)(suba>>8);
IIc_Send(i); /*发送器件子地址高字节*/
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit1;
}
i=(u8)suba; /*发送器件子地址低字节*/
IIc_Send(i);
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit1;
}
for(i=0;i<no;i++)
{
IIc_Send(*(s+i)); /*发送数据*/
if(Wr_Ack() == FALSE)
{
error = FALSE;
break;
}
}
exit1:
IIc_Stop(); /*结束总线*/
return error;
}
/***********向有子地址器件读取多字节数据函数*****************/
u8 Fm24cl64_ReadnBytes(u8 sla,u16 suba,u8 *s,u16 no)
{
u16 i;
u8 error = TRUE;
if(no==0) return error;
IIc_Start(); /*启动总线*/
IIc_Send(sla); /*发送器件地址*/
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit;
}
i=(u8)(suba>>8);
IIc_Send(i); /*发送器件子地址高字节*/
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit;
}
i=(u8)suba; /*发送器件子地址低字节*/
IIc_Send(i);
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit;
}
IIc_Start();
IIc_Send(sla|0x01);
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit;
}
for(i=0;i<no-1;i++)
{
*(s+i)=IIc_Receive(); /*发送数据*/
Rd_Ack(); /*发送就答位*/
}
*(s+i)=IIc_Receive();
Rd_NoAck(); /*发送非应位*/
exit:
IIc_Stop(); /*结束总线*/
return error;
}
/*****************清除FM24cl64 从suba开始的地址空间0000-0X1FFF*************/
u8 FM24CL64_Clr(u8 sla,u16 suba)
{
u8 i;
u16 y;
u8 error = TRUE;
IIc_Start(); /*启动总线*/
IIc_Send(sla); /*发送器件物理地址*/
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit1;
}
i=(u8)(suba>>8);
IIc_Send(i); /*发送器件子地址高字节*/
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit1;
}
i=(u8)suba; /*发送器件子地址低字节*/
IIc_Send(i);
if(Wr_Ack() == FALSE)
{
error = FALSE;
goto exit1;
}
for(y=0;y<(0x2000 - 40);y++)
{
IIc_Send(0); /*发送数据*/
if(Wr_Ack() == FALSE)
{
error = FALSE;
break;
}
}
exit1:
IIc_Stop(); /*结束总线*/
return error;
}
linux应用层代码。前提底层已经把i2c指定到了/dev/i2c-x
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <time.h>
#include <sys/timeb.h>
#include <string>
#define DEBUG_LEVEL_DEF 5 // do not use value, this is common print.you can use logdbg in project
// extern int DEBUG_LEVEL_DEF;
#define PRINTF_DEF(lev, fmt, lprt, ...) \
do \
{ \
if (DEBUG_LEVEL_DEF >= lev) \
{ \
struct timeb tnow; \
struct tm *tmnow; \
ftime(&tnow); \
tmnow = localtime(&tnow.time); \
printf("[%d-%02d-%02d %02d:%02d:%02d.%03d][%s][%s,%d]" fmt "\n", tmnow->tm_year + 1900, tmnow->tm_mon + 1, \
tmnow->tm_mday, tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec, tnow.millitm % 1000, \
lprt, __func__, __LINE__, ##__VA_ARGS__); \
} \
} while (0);
#define printfat(fmt, ...) PRINTF_DEF(0, fmt, "fat", ##__VA_ARGS__)
#define printerr(fmt, ...) PRINTF_DEF(1, fmt, "err", ##__VA_ARGS__)
#define printwarn(fmt, ...) PRINTF_DEF(2, fmt, "war", ##__VA_ARGS__)
#define printinfo(fmt, ...) PRINTF_DEF(3, fmt, "inf", ##__VA_ARGS__)
#define printdbg(fmt, ...) PRINTF_DEF(4, fmt, "dbg", ##__VA_ARGS__)
#define printtra(fmt, ...) PRINTF_DEF(5, fmt, "tra", ##__VA_ARGS__)
class i2cOperateClass
{
public:
i2cOperateClass(std::string i2DevFile = "/dev/i2c-0")
{
optFileName = i2DevFile;
optFd = -1;
}
~i2cOperateClass()
{
}
static i2cOperateClass &instance()
{
static i2cOperateClass i2work;
return i2work;
}
void closeOpt()
{
if (optFd <= 0)
{
return;
}
close(optFd);
optFd = -1;
}
int initopt()
{
if (optFileName == "")
{
printerr("");
return -1;
}
optFd = open(optFileName.c_str(), O_RDWR);
if (optFd < 0)
{
printerr("%s:open fail!", optFileName.c_str());
return -1;
}
ioctl(optFd, I2C_RETRIES, 2);
return 0;
}
int readopt(uint8_t slaveDeviceAddress, uint16_t registerAddress, std::string &readstr, uint16_t readsize,
uint16_t registerSize = 1)
{
readstr = "";
if (optFd < 0)
{
printerr("%s:open fail!", optFileName.c_str());
return -1;
}
uint8_t registerBuffer[4];
if (registerSize == 1)
{
registerBuffer[0] = registerAddress;
}
else if (registerSize == 2)
{
registerBuffer[0] = registerAddress >> 8;
registerBuffer[1] = registerAddress;
}
else
{
printerr("%d", registerSize);
return -1;
}
printdbg("dev:0x%x, register:0x%x, readsize:%d", slaveDeviceAddress, registerAddress, readsize);
readstr.resize(readsize);
struct i2c_msg msgs[2] =
{
{slaveDeviceAddress, 0, registerSize, registerBuffer},
{slaveDeviceAddress, I2C_M_RD, readsize, (uint8_t *)readstr.data()},
};
struct i2c_rdwr_ioctl_data idata =
{
msgs,
2
};
if (ioctl(optFd, I2C_RDWR, &idata) < 0)
{
printerr("%s:open fail!", optFileName.c_str());
return -2;
}
return readsize;
}
int writeopt(uint8_t slaveDeviceAddress, uint16_t registerAddress, std::string writestr,
uint16_t registerSize = 1)
{
if (optFd < 0)
{
printerr("%s:open fail!", optFileName.c_str());
return -1;
}
uint8_t registerBuffer[4];
if (registerSize == 1)
{
registerBuffer[0] = registerAddress;
}
else if (registerSize == 2)
{
registerBuffer[0] = registerAddress >> 8;
registerBuffer[1] = registerAddress;
}
else
{
printerr("%d", registerSize);
return -1;
}
std::string tmpWriteStr;
tmpWriteStr.append((const char *)registerBuffer, registerSize);
tmpWriteStr.append(writestr);
printdbg("%x", tmpWriteStr.size());
struct i2c_msg msgs[2] =
{
{slaveDeviceAddress, 0, tmpWriteStr.size(), (uint8_t *)tmpWriteStr.data()},
};
struct i2c_rdwr_ioctl_data idata =
{
msgs,
1
};
if (ioctl(optFd, I2C_RDWR, &idata) < 0)
{
printerr("%s:write fail!", optFileName.c_str());
return -2;
}
return writestr.size();
}
private:
std::string optFileName;
int optFd;
};
int main()
{
std::string tmpstr;
int tmpflag;
const unsigned char channel[] = {0x00, 0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07};
i2cOperateClass::instance().initopt();
for(int i = 0; i < 8; i++)
{
uint16_t tmpregister = 0x84;
tmpregister |= (channel[i] << 4);
i2cOperateClass::instance().readopt(0x48, tmpregister, tmpstr, 1);
if(tmpstr.size() < 1)
{
printerr("");
}
printdbg("%d", tmpstr[0]);
}
for(int i = 0; i < 8; i++)
{
uint16_t tmpregister = 0x84;
tmpregister |= (channel[i] << 4);
i2cOperateClass::instance().readopt(0x49, tmpregister, tmpstr, 1);
if(tmpstr.size() < 1)
{
printerr("");
}
printdbg("%d", tmpstr[0]);
}
for(int i = 0; i < 8; i++)
{
uint16_t tmpregister = 0x84;
tmpregister |= (channel[i] << 4);
i2cOperateClass::instance().readopt(0x4A, tmpregister, tmpstr, 1);
if(tmpstr.size() < 1)
{
printerr("");
}
printdbg("%d", tmpstr[0]);
}
for(int i = 0; i < 8; i++)
{
uint16_t tmpregister = 0x84;
tmpregister |= (channel[i] << 4);
i2cOperateClass::instance().readopt(0x4B, tmpregister, tmpstr, 1);
if(tmpstr.size() < 1)
{
printerr("");
}
printdbg("%d", tmpstr[0]);
}
i2cOperateClass::instance().closeOpt();
return 1;
}