一、简介
1.1 I2C简介
I2C(Inter-Integrated Circuit)是一种串行通信总线,由飞利浦公司在1980年代为了让主机连接其它外设而定制的一种低速通信协议,目前广泛应用于手机、电脑、家电等嵌入式设备。
图1.0 典型的I2C总线(挂载多个从设备)
1.2 电气特性
I2C使用两根数据总线,一根是数据线(SDA),一根是时钟线(SCL),采用双向漏极开路,并使用电阻进行上拉,I2C允许比较大的工作电压范围,但一般采用3.3V或者5V。设备地址使用7位长度和10位长度。I2C 传输速率有不同的模式:
·标准模式:100Kbit/s
·快速模式:400Kbit/s
·高速模式:3.4Mbit/s
图1.1 I2C内部结构图
1.3 I2C 协议层
1.3.1 I2C读写帧格式
图1.2 I2C写一个字节数据
图1.3 I2C读一个字节数据
1.3.2 I2C起始信号
当主机要与从机开始通信时需要发送起始信号,SCL线在高电平时,SDA线由高变为低。一般流程是拉高SCL线与SDA线保持4us,再拉低SDA线保持4us,最后拉低SCL。
图1.4 I2C起始信号
1.3.3 I2C停止信号
当主机要与从机结束通信时需要发送停止信号,SCL线在高电平时,SDA线由低变为高。一般流程是拉低SCL线和SDA线保持4us,再拉高SCL线和SDA线保持4us。
图1.5 I2C停止信号
1.3.4 I2C应答信号
应答信号也相当于一位数据,SCL为高时,数据线拉低,表示为应答。
图1.6 I2C应答信号
1.3.5 I2C非应答信号
非应答信号也相当于一位数据,SCL为高时,数据线拉高,表示为非应答。
图1.7 I2C非应答信号
1.3.6 I2C数据
数据的传输格式,高位先发,SCL为高时数据有效,SCL为低时数据变化。
图1.8 一个字节数据传输示例
二、程序
I2C设备初始化函数
extern CyU3PReturnStatus_t
CyU3PI2cInit (
void);
I2C配置函数
extern CyU3PReturnStatus_t
CyU3PI2cSetConfig (
CyU3PI2cConfig_t *config, /**< I2C configuration settings */
CyU3PI2cIntrCb_t cb /**< Callback for getting the events */
);
I2C读数据函数
extern CyU3PReturnStatus_t
CyU3PI2cReceiveBytes (
CyU3PI2cPreamble_t *preamble, /**< Preamble information to be sent out before the data transfer. */
uint8_t *data, /**< Pointer to buffer where the data is to be placed. */
uint32_t byteCount, /**< Size of the transfer in bytes. */
uint32_t retryCount /**< Number of times to retry request in case of a NAK response or error. */
);
I2C写数据函数
extern CyU3PReturnStatus_t
CyU3PI2cTransmitBytes (
CyU3PI2cPreamble_t *preamble, /**< Preamble information to be sent out before the data transfer. */
uint8_t *data, /**< Pointer to buffer containing data to be written. */
uint32_t byteCount, /**< Size of the transfer in bytes. */
uint32_t retryCount /**< Number of times to retry request in case of a slave NAK response. */
);
I2C源文件
#include "cyfx3_i2c.h"
#include "cyu3error.h"
#include "cyu3gpio.h"
#include "cyu3utils.h"
static CyU3PReturnStatus_t CyFx3GpioConfig(void)
{
CyU3PReturnStatus_t CyFx3Status = CY_U3P_SUCCESS;
//GPIO时钟配置(整个工程中只需要初始化一次)
CyU3PGpioClock_t GpioClkConifg = {
.fastClkDiv = 2,
.slowClkDiv = 0,
.halfDiv = 0,
.simpleDiv = CY_U3P_GPIO_SIMPLE_DIV_BY_2,
.clkSrc = CY_U3P_SYS_CLK,
};
CyFx3Status = CyU3PGpioInit(&GpioClkConifg,NULL);
#if !I2C_HARDWARE
//重定向为普通IO
CyU3PDeviceGpioOverride(I2C_SDA_GPIO_PIN,CyTrue);
CyU3PDeviceGpioOverride(I2C_SCL_GPIO_PIN,CyTrue);
//GPIO配置为输出模式,不使能中断
CyU3PGpioSimpleConfig_t I2cSdaGpioConfig = {
.outValue = CyTrue,
.driveLowEn = CyTrue,
.driveHighEn = CyTrue,
.inputEn = CyFalse,
.intrMode = CY_U3P_GPIO_NO_INTR,
};
CyFx3Status = CyU3PGpioSetSimpleConfig(I2C_SDA_GPIO_PIN,&I2cSdaGpioConfig);
CyU3PGpioSimpleConfig_t I2cSclGpioConfig = {
.outValue = CyTrue,
.driveLowEn = CyTrue,
.driveHighEn = CyTrue,
.inputEn = CyFalse,
.intrMode = CY_U3P_GPIO_NO_INTR,
};
CyFx3Status = CyU3PGpioSetSimpleConfig(I2C_SCL_GPIO_PIN,&I2cSclGpioConfig);
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
#endif
return CyFx3Status;
}
CyU3PReturnStatus_t CyFx3I2cInit(void)
{
CyU3PReturnStatus_t CyFx3Status;
#if I2C_HARDWARE
CyFx3Status = CyU3PI2cInit();
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
CyU3PI2cConfig_t I2cConfig = {
.bitRate = I2C_FREQUENCY,
.isDma = CyFalse,
.busTimeout = I2C_BUS_TIMEOUT_VALUE,
.dmaTimeout = I2C_DMA_TIMEOUT_VALUE,
};
return CyU3PI2cSetConfig(&I2cConfig,NULL);
#else
return CyFx3GpioConfig();
#endif
}
#if I2C_HARDWARE
CyU3PReturnStatus_t CyFx3I2cReadBytes(uint8_t DevAddr,
uint16_t RegAddr,
uint8_t *ReadData,
uint32_t ReadLen,
uint32_t RetryCnt,
uint8_t RegBit)
{
CyU3PI2cPreamble_t Preanble;
if(RegBit == I2C_REG_8BIT_ADDR)
{
Preanble.buffer[0] = (DevAddr << 1) | I2C_CMD_WRITE;
Preanble.buffer[1] = (RegAddr & 0xff);
Preanble.buffer[2] = (DevAddr << 1) | I2C_CMD_READ;
Preanble.length = 3;
Preanble.ctrlMask = 0x0002;
}
else
{
Preanble.buffer[0] = (DevAddr << 1) | I2C_CMD_WRITE;
Preanble.buffer[1] = (RegAddr >> 8);
Preanble.buffer[2] = (RegAddr & 0xff);
Preanble.buffer[3] = (DevAddr << 1) | I2C_CMD_READ;
Preanble.length = 4;
Preanble.ctrlMask = 0x0004;
}
return CyU3PI2cReceiveBytes(&Preanble,ReadData,ReadLen,RetryCnt);
}
CyU3PReturnStatus_t CyFx3WriteBytes(uint8_t DevAddr,
uint16_t RegAddr,
uint8_t *WriteData,
uint32_t WriteLen,
uint32_t RetryCnt,
uint8_t RegBit)
{
CyU3PI2cPreamble_t Preanble;
if(RegBit == I2C_REG_8BIT_ADDR)
{
Preanble.buffer[0] = (DevAddr << 1) | I2C_CMD_WRITE;
Preanble.buffer[1] = (RegAddr & 0xff);
Preanble.length = 2;
Preanble.ctrlMask = 0x0000;
}
else
{
Preanble.buffer[0] = (DevAddr << 1) | I2C_CMD_WRITE;
Preanble.buffer[1] = (RegAddr >> 8);
Preanble.buffer[2] = (RegAddr & 0xff);
Preanble.length = 3;
Preanble.ctrlMask = 0x0000;
}
return CyU3PI2cTransmitBytes(&Preanble,WriteData,WriteLen,RetryCnt);
}
#else
CyU3PReturnStatus_t I2cSdaIn(void)
{
//GPIO配置为输出模式,不使能中断
CyU3PGpioSimpleConfig_t I2cSdaGpioConfig = {
.outValue = CyTrue,
.driveLowEn = CyFalse,
.driveHighEn = CyFalse,
.inputEn = CyTrue,
.intrMode = CY_U3P_GPIO_NO_INTR,
};
return CyU3PGpioSetSimpleConfig(I2C_SDA_GPIO_PIN,&I2cSdaGpioConfig);
}
CyU3PReturnStatus_t I2cSdaOut(void)
{
CyU3PGpioSimpleConfig_t I2cSdaGpioConfig = {
.outValue = CyTrue,
.driveLowEn = CyTrue,
.driveHighEn = CyTrue,
.inputEn = CyFalse,
.intrMode = CY_U3P_GPIO_NO_INTR,
};
return CyU3PGpioSetSimpleConfig(I2C_SDA_GPIO_PIN,&I2cSdaGpioConfig);
}
static CyU3PReturnStatus_t I2cSetSdaValue(CyBool_t isHigh)
{
return CyU3PGpioSetValue(I2C_SDA_GPIO_PIN,isHigh);
}
static CyU3PReturnStatus_t I2cSetSclValue(CyBool_t isHigh)
{
return CyU3PGpioSetValue(I2C_SCL_GPIO_PIN,isHigh);
}
static CyBool_t I2cGetSdaValue(void)
{
return CyFx3GpioReadBit(I2C_SDA_GPIO_PIN);
}
void I2CStart(void)
{
I2cSdaOut();
I2cSetSdaValue(CyTrue);
I2cSetSclValue(CyTrue);
CyU3PBusyWait(4);
I2cSetSdaValue(CyFalse);
CyU3PBusyWait(4);
I2cSetSclValue(CyFalse);
}
void I2cStop(void)
{
I2cSdaOut();
I2cSetSclValue(CyFalse);
I2cSetSdaValue(CyFalse);
CyU3PBusyWait(4);
I2cSetSclValue(CyTrue);
I2cSetSdaValue(CyTrue);
CyU3PBusyWait(4);
}
CyBool_t I2cWaitAck(void)
{
uint8_t ErrTime = 0;
I2cSdaIn();
I2cSetSdaValue(CyTrue);
CyU3PBusyWait(1);
I2cSetSclValue(CyTrue);
CyU3PBusyWait(1);
while(I2cGetSdaValue() == CyTrue)
{
ErrTime++;
if(ErrTime > 250)
{
I2cStop();
return CyTrue;
}
}
I2cSetSclValue(CyFalse);
return CyFalse;
}
void I2cAck(void)
{
I2cSdaOut();
I2cSetSclValue(CyFalse);
I2cSetSdaValue(CyFalse);
CyU3PBusyWait(2);
I2cSetSclValue(CyTrue);
CyU3PBusyWait(2);
I2cSetSclValue(CyFalse);
}
void I2cNAck(void)
{
I2cSdaOut();
I2cSetSclValue(CyFalse);
I2cSetSdaValue(CyTrue);
CyU3PBusyWait(2);
I2cSetSclValue(CyTrue);
CyU3PBusyWait(2);
I2cSetSclValue(CyFalse);
}
void I2cWriteByte(uint8_t Data)
{
uint8_t i;
I2cSdaOut();
I2cSetSclValue(CyFalse);
for(i = 0;i < 8;i++)
{
if((Data & 0x80) >> 7)
{
I2cSetSdaValue(CyTrue);
}
else
{
I2cSetSdaValue(CyFalse);
}
Data <<= 1;
CyU3PBusyWait(2);
I2cSetSclValue(CyTrue);
CyU3PBusyWait(2);
I2cSetSclValue(CyFalse);
CyU3PBusyWait(2);
}
}
uint8_t I2cReadByte(CyBool_t isAck)
{
uint8_t i;
uint8_t Data = 0x0;
I2cSdaIn();
for(i = 0;i < 8;i++ )
{
I2cSetSclValue(CyFalse);
CyU3PBusyWait(2);
I2cSetSclValue(CyTrue);
Data <<= 1;
if(I2cGetSdaValue() == CyTrue)
{
Data++;
}
CyU3PBusyWait(1);
}
if(isAck)
{
I2cAck();
}
else
{
I2cNAck();
}
return Data;
}
CyU3PReturnStatus_t CyFx3I2cReadBytes(uint8_t DevAddr,
uint16_t RegAddr,
uint8_t *ReadData,
uint32_t ReadLen,
uint32_t RetryCnt,
uint8_t RegBit)
{
uint32_t i = 0;
I2CStart();
I2cWriteByte((DevAddr << 1) | I2C_CMD_WRITE);
I2cWaitAck();
if(RegBit == I2C_REG_8BIT_ADDR)
{
I2cWriteByte(RegAddr);
I2cWaitAck();
}
else
{
I2cWriteByte((uint8_t)(RegAddr >> 8));
I2cWaitAck();
I2cWriteByte((uint8_t)(RegAddr & 0xFF));
I2cWaitAck();
}
I2CStart();
I2cWriteByte((DevAddr << 1) | I2C_CMD_READ);
I2cWaitAck();
if(ReadLen > 1)
{
for(i = 0 ;i < ReadLen - 1;i++)
{
ReadData[i] = I2cReadByte(CyTrue);
}
}
ReadData[i] = I2cReadByte(CyFalse);
I2cStop();
return CY_U3P_SUCCESS;
}
CyU3PReturnStatus_t CyFx3WriteBytes(uint8_t DevAddr,
uint16_t RegAddr,
uint8_t *WriteData,
uint32_t WriteLen,
uint32_t RetryCnt,
uint8_t RegBit)
{
uint32_t i;
I2CStart();
I2cWriteByte((DevAddr << 1) | I2C_CMD_WRITE);
I2cWaitAck();
if(RegBit == I2C_REG_8BIT_ADDR)
{
I2cWriteByte(RegAddr);
I2cWaitAck();
}
else
{
I2cWriteByte((uint8_t)(RegAddr >> 8));
I2cWaitAck();
I2cWriteByte((uint8_t)(RegAddr & 0xFF));
I2cWaitAck();
}
for(i = 0 ;i < WriteLen;i++)
{
I2cWriteByte(WriteData[i]);
if(I2cWaitAck())
{
I2cStop();
return CY_U3P_ERROR_TIMEOUT;
}
CyU3PBusyWait(2);
}
I2cStop();
return CY_U3P_SUCCESS;
}
#endif
I2C头文件
#ifndef CYFX3_I2C_H_
#define CYFX3_I2C_H_
#include "cyu3i2c.h"
#include "gpio_regs.h"
#define I2C_BUS_TIMEOUT_VALUE 0xFFFFFFFFU
#define I2C_DMA_TIMEOUT_VALUE 0xFFFFU
#define I2C_FREQUENCY 100000U
#define I2C_REG_8BIT_ADDR 8
#define I2C_REG_16BIT_ADDR 16
#define I2C_CMD_WRITE 0
#define I2C_CMD_READ 1
#define I2C_HARDWARE 1 //0软件模拟I2C 1硬件I2C
#if !I2C_HARDWARE
#define CyFx3GpioReadBit(GpioPin) ((GPIO->lpp_gpio_simple[GpioPin]& CY_U3P_LPP_GPIO_IN_VALUE) >> 1)
#define CyFx3GpioSetBit(GpioPin) ((*(uvint32_t *)&GPIO->lpp_gpio_simple[GpioPin]) |= CY_U3P_LPP_GPIO_OUT_VALUE)
#define CyFx3GpioResetBit(GpioPin) ((*(uvint32_t *)&GPIO->lpp_gpio_simple[GpioPin]) &= ~CY_U3P_LPP_GPIO_OUT_VALUE)
#define I2C_SDA_GPIO_PIN 59
#define I2C_SCL_GPIO_PIN 58
void I2CStart(void);
void I2cStop(void);
CyBool_t I2cWaitAck(void);
void I2cAck(void);
void I2cNAck(void);
void I2cWriteByte(uint8_t Data);
uint8_t I2cReadByte(CyBool_t isAck);
#endif
CyU3PReturnStatus_t CyFx3I2cInit(void);
CyU3PReturnStatus_t CyFx3I2cReadBytes(uint8_t DevAddr,
uint16_t RegAddr,
uint8_t *ReadData,
uint32_t ReadLen,
uint32_t RetryCnt,
uint8_t RegBit);
CyU3PReturnStatus_t CyFx3WriteBytes(uint8_t DevAddr,
uint16_t RegAddr,
uint8_t *WriteData,
uint32_t WriteLen,
uint32_t RetryCnt,
uint8_t RegBit);
#endif /* CYFX3_I2C_H_ */
M24M02源文件
#include "m24m02.h"
#include "cyu3error.h"
CyU3PReturnStatus_t M24M02WriteBytes(uint16_t WriteAddr,uint8_t *WriteData,uint32_t WriteByteNum)
{
return CyFx3WriteBytes(M24M02_ADDRESS,
WriteAddr,
WriteData,
WriteByteNum,
2,
I2C_REG_16BIT_ADDR);
}
CyU3PReturnStatus_t M24M02ReadBytes(uint16_t ReadAddr,uint8_t *ReadData,uint32_t ReadByteNum)
{
return CyFx3I2cReadBytes(M24M02_ADDRESS,
ReadAddr,
ReadData,
ReadByteNum,
2,
I2C_REG_16BIT_ADDR);
}
M24M02头文件
#ifndef M24M02_H_
#define M24M02_H_
#include "cyfx3_i2c.h"
#define M24M02_ADDRESS 0x50
CyU3PReturnStatus_t M24M02WriteBytes(uint16_t WriteAddr,uint8_t *WriteData,uint32_t WriteByteNum);
CyU3PReturnStatus_t M24M02ReadBytes(uint16_t ReadAddr,uint8_t *ReadData,uint32_t ReadByteNum);
#endif /* M24M02_H_ */
主函数
#include "cyu3os.h"
#include "cyu3error.h"
#include "cyu3uart.h"
#include "cyfx3_i2c.h"
#include "m24m02.h"
CyU3PThread I2cThreadHandler;
#define I2C_THREAD_PRIORITY 8
#define I2C_THREAD_STACK_SIZE 512
static CyU3PReturnStatus_t CyFx3LogInit(void)
{
CyU3PReturnStatus_t CyFx3Status;
//初始化串口设备
CyFx3Status = CyU3PUartInit();
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
//配置uart
CyU3PUartConfig_t UartConfig = {
.txEnable = CyTrue,
.rxEnable = CyFalse,
.flowCtrl = CyFalse,
.isDma = CyTrue,
.baudRate = 115200,
.stopBit = CY_U3P_UART_ONE_STOP_BIT,
.parity = CY_U3P_UART_NO_PARITY,
};
CyFx3Status = CyU3PUartSetConfig(&UartConfig,NULL);
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
//设置发送字节大小
CyFx3Status = CyU3PUartTxSetBlockXfer(0xFFFFFFFF);
if (CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
//不输出8字节前导数据
CyU3PDebugPreamble(CyFalse);
//初始化串口日志功能
CyFx3Status = CyU3PDebugInit(CY_U3P_LPP_SOCKET_UART_CONS,8);
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
return CY_U3P_SUCCESS;
}
void I2cThread(uint32_t arg)
{
CyU3PReturnStatus_t CyFx3Status;
uint8_t WriteData[] = "m24m02 i2c read write test!!!";
uint8_t ReadData[50] = {0};
CyFx3I2cInit();
CyFx3LogInit();
CyFx3Status = M24M02WriteBytes(0x00,WriteData,sizeof(WriteData));
if(CyFx3Status != CY_U3P_SUCCESS)
{
CyU3PDebugPrint(6,"Write Error\r\n");
}
CyU3PThreadSleep(50);
CyFx3Status = M24M02ReadBytes(0x00,ReadData,sizeof(WriteData));
if(CyFx3Status != CY_U3P_SUCCESS)
{
CyU3PDebugPrint(6,"Read Error\r\n");
}
CyU3PDebugPrint(6,"result:%s\r\n",ReadData);
while(1)
{
CyU3PThreadSleep(1000);
}
}
void CyFxApplicationDefine(void)
{
void *I2cStackStart = CyU3PMemAlloc(I2C_THREAD_STACK_SIZE);
if(I2cStackStart != NULL)
{
CyU3PThreadCreate((CyU3PThread* ) &I2cThreadHandler,
(char* ) "21:I2c Thread 1",
(CyU3PThreadEntry_t) I2cThread,
(uint32_t ) 0,
(void* ) I2cStackStart,
(uint32_t ) I2C_THREAD_STACK_SIZE,
(uint32_t ) I2C_THREAD_PRIORITY,
(uint32_t ) I2C_THREAD_PRIORITY,
(uint32_t ) CYU3P_NO_TIME_SLICE,
(uint32_t ) CYU3P_AUTO_START);
}
}
int main(void)
{
CyU3PReturnStatus_t CyFx3Status;
//初始化FX3设备
CyFx3Status = CyU3PDeviceInit(NULL);
if(CyFx3Status != CY_U3P_SUCCESS)
{
goto HandleFatalError;
}
//初始化FX3内核缓存
CyFx3Status = CyU3PDeviceCacheControl(CyTrue,CyTrue,CyTrue);
if(CyFx3Status != CY_U3P_SUCCESS)
{
goto HandleFatalError;
}
//配置IO矩阵(16位数据总线 + UART + GPIO)
CyU3PIoMatrixConfig_t MatrixcConfig = {
.isDQ32Bit = CyFalse,
.s0Mode = CY_U3P_SPORT_INACTIVE,
.s1Mode = CY_U3P_SPORT_INACTIVE,
.useUart = CyTrue,
#if I2C_HARDWARE
.useI2C = CyTrue,
.gpioComplexEn[0] = 0,
#else
.useI2C = CyFalse,
#endif
.useI2S = CyFalse,
.useSpi = CyFalse,
.lppMode = CY_U3P_IO_MATRIX_LPP_UART_ONLY,
.gpioSimpleEn[0] = 0,
.gpioSimpleEn[1] = 0,
.gpioComplexEn[0] = 0,
.gpioComplexEn[1] = 0,
};
CyFx3Status = CyU3PDeviceConfigureIOMatrix(&MatrixcConfig);
if (CyFx3Status != CY_U3P_SUCCESS)
{
goto HandleFatalError;
}
//初始化RTOS内核
CyU3PKernelEntry();
HandleFatalError:
//复位
CyU3PDeviceReset(CyTrue);
}
三、验证
验证I2C读写开发板上的M24M02芯片,芯片地址为A0