I2C配置及使用分享
本帖最后由 Afanx 于 2022-8-24 18:59 编辑工程代码:
一、I2C初始化相关配置
1、I2C的GPIO配置
N32G45x按组进行复用和重映射。具体引脚参考用户手册7.2.5.12 I2C复用功能重映射章节。
以I2C1为例子,使用默认分组,只需要把PB6和PB7配置为GPIO_Mode_AF_OD模式即可。如果需要使用PB8和PB9,调用库函数GPIO_ConfigPinRemap()配置分组1即可。
void I2C1_GPIO_Config(void)
{
GPIO_InitType GPIO_InitStructure;
/* Enable the GPIO Clock */
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO | RCC_APB2_PERIPH_GPIOB, ENABLE);
GPIO_InitStruct(&GPIO_InitStructure);
/* SCL--PB6/PB8SDA--PB7/PB9 */
GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7; // 使用默认分组0管脚
// GPIO_InitStructure.Pin = GPIO_PIN_8 | GPIO_PIN_9;// 使用分组1 (I2C1_RMP = 01)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);
// GPIO_ConfigPinRemap(GPIO_RMP_I2C1, ENABLE); // 打开重映射功能,使用分组1(PB8 PB9)
}关于GPIO_ConfigPinRemap参数说明:
库文件中参数定义如下,I2C1只有2个分组,所以默认为分组0,调用函数可配置分组1。I2C2有3个分组,默认为分组0,调用函数可配置分组1、分组3。
#define GPIO_RMP_I2C1 ((uint32_t)0x00000002) /*!< I2C1 Alternate Function mapping */ //I2C1使用分组1
#define GPIO_RMP1_I2C2 ((uint32_t)0x40160040) /*!< I2C2 Alternate Function mapping */ //I2C2使用分组1
#define GPIO_RMP3_I2C2 ((uint32_t)0x401600C0) /*!< I2C2 Alternate Function mapping */ //I2C2使用分组3
#define GPIO_RMP2_I2C3 ((uint32_t)0x40180200) /*!< I2C3 Alternate Function mapping */ //I2C3使用分组2
#define GPIO_RMP3_I2C3 ((uint32_t)0x40180300) /*!< I2C3 Alternate Function mapping */ //I2C3使用分组3
#define GPIO_RMP1_I2C4 ((uint32_t)0x401A0400) /*!< I2C4 Alternate Function mapping */ //I2C4使用分组1
#define GPIO_RMP3_I2C4 ((uint32_t)0x401A0C00) /*!< I2C4 Alternate Function mapping */ //I2C4使用分组3
2、I2C外设寄存器配置
以7位地址为例,首先定义I2C模块自身地址:
#define I2C1_OwnAddr 0x10
#define I2C2_OwnAddr 0x20
#define I2C3_OwnAddr 0x30
#define I2C4_OwnAddr 0x40外设寄存器初始化代码:void I2C_Config(I2C_Module* I2Cx)
{
I2C_InitType I2C_InitStructure;
if (I2Cx == I2C1) {
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C1, ENABLE);
I2C_InitStructure.OwnAddr1 = I2C1_OwnAddr;
} else if (I2Cx == I2C2) {
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C2, ENABLE);
I2C_InitStructure.OwnAddr1 = I2C2_OwnAddr;
} else if (I2Cx == I2C3) {
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_I2C3, ENABLE);
I2C_InitStructure.OwnAddr1 = I2C3_OwnAddr;
} else if (I2Cx == I2C4) {
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_I2C4, ENABLE);
I2C_InitStructure.OwnAddr1 = I2C4_OwnAddr;
}
I2C_DeInit(I2Cx);
I2C_InitStructure.BusMode = I2C_BUSMODE_I2C;
I2C_InitStructure.FmDutyCycle = I2C_FMDUTYCYCLE_2;
I2C_InitStructure.AckEnable = I2C_ACKEN;
I2C_InitStructure.AddrMode = I2C_ADDR_MODE_7BIT;
I2C_InitStructure.ClkSpeed = 100000;// 100K
I2C_Init(I2Cx, &I2C_InitStructure); // Initial and Enable I2Cx
}调用I2C_Init()会自动使能I2C模块,不需要额外使能。
3、I2C中断配置(如果使用中断收发,与轮询收发冲突)
定义是否开启中断:
#define I2C1_UseIT ENABLE
#define I2C2_UseIT DISABLE
#define I2C3_UseIT DISABLE
#define I2C4_UseIT DISABLEI2C一共三个中断开关:EVTINTEN、BUFINTEN、ERRINTEN。分为两个中断入口:EV_IRQHandler、ER_IRQHandler。
中断配置代码:
void I2C_NVIC_Config(I2C_Module* I2Cx)
{
NVIC_InitType NVIC_InitStructure;
if (I2Cx == I2C1) {
NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = I2C1_UseIT;
} else if (I2Cx == I2C2) {
NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = I2C2_UseIT;
} else if (I2Cx == I2C3) {
NVIC_InitStructure.NVIC_IRQChannel = I2C3_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = I2C3_UseIT;
} else if (I2Cx == I2C4) {
NVIC_InitStructure.NVIC_IRQChannel = I2C4_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = I2C4_UseIT;
}
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel++;// 配置I2Cx_ER_IRQn
NVIC_Init(&NVIC_InitStructure);
I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, NVIC_InitStructure.NVIC_IRQChannelCmd); //打开I2C三个中断使能
}4、I2C初始化以及复位调用以上三个初始化函数进行I2C配置:
void I2C_Initial(void)
{
I2C1_GPIO_Config();
I2C_Config(I2C1);
I2C_NVIC_Config(I2C1);// #define I2Cx_UseIT
}硬件I2C在使用过程中容易被干扰进入错误状态,此时硬件无法自动恢复,需要手动复位。通信错误会导致I2C状态卡死,因此I2C的软件复位非常重要!
最简单的复位方式就是重新初始化,GPIO不需要重新配置,重新初始化I2C寄存器以及中断配置即可。另外,当出现总线处于空闲状态,但I2C的Busy位置“1”,此时无法再开启新的传输,只能通过SWRESET位清除Busy位。I2C软件复位代码:void I2C_ResetInit(I2C_Module* I2Cx)
{
I2Cx->CTRL1 |= 0x8000;// Reset Busy
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
I2Cx->CTRL1 &= ~0x8000;
I2C_Config(I2Cx);
I2C_NVIC_Config(I2Cx);// #define I2Cx_UseIT
}
本帖最后由 Afanx 于 2022-7-19 18:02 编辑
二、I2C做主机,轮询读写
1、I2C主机轮询发送
EV5:STARTBF=1,起始位产生。EV6:ADDRF=1,从机响应地址。EV8_1/EV8:TXDATE=1,发送寄存器为空,写DAT寄存器。EV8_2:TXDATE =1,BSF=1,请求设置停止位。
主机发送步骤:
(1)保证I2C不在使用中,等待BUSY=0;(2)发送START信号,等待EV5 (STARTBF=1);(3)写从机地址,等待EV6 (ADDRF=1);(4)发送数据,发送之前判断TXDATE标志,保证DAT为空开始写入数据;(5)等待最后一个字节位发送完成;(6)发送STOP信号;
注意在发送从机地址后写DAT后,数据会立即发送至移位缓冲器中,所以此时TXDATE不会清0(EV8_1),可以继续写下一个数据。在程序中只需要关注在TXDATE置1时写DAT即可。写完第N个数据后,停止写数据,此时移位寄存器正在发送第N-1个数据。等移位寄存器发送第N个数据时可以不关心TXDATE置1,发送完成后DAT寄存器和移位寄存器同时为空时,BSF置1。此时发送STOP,等待BUSY清0,结束一次通信。定义超时时间和错误状态:#define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT ((uint32_t)(3 * I2CT_FLAG_TIMEOUT))
typedef enum {
MASTER_OK = 0,
MASTER_BUSY,
MASTER_MODE,
MASTER_TXMODE,
MASTER_RXMODE,
MASTER_SENDING,
MASTER_SENDED,
MASTER_RECVD,
MASTER_BYTEF,
MASTER_BUSERR,
MASTER_UNKNOW,
SLAVE_OK = 20,
SLAVE_BUSY,
SLAVE_MODE,
SLAVE_BUSERR,
SLAVE_UNKNOW,
} ErrCode_t;主机轮询发送函数:int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
{
uint32_t I2CTimeout;
if (regAddr < 0 && len == 0) return 0; /* 至少发送一个数据 */
/* 1.保证I2C不在使用中 */
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BUSY;
}
}
/* 2.发送START信号 */
I2C_GenerateStart(I2Cx, ENABLE);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) {// EV5
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_MODE;
}
}
/* 3.写从机地址 */
I2C_SendAddr7bit(I2Cx, slaveAddr, I2C_DIRECTION_SEND);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG)) {// EV6
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_TXMODE;
}
}
/* 4.写寄存器地址 */
if (regAddr >= 0) { /* 寄存器写 */
I2C_SendData(I2Cx, (uint8_t)regAddr);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) {// EV8
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_SENDING;
}
}
}
/* 4.发送数据 */
while (len--) {
I2C_SendData(I2Cx, *datPtr++);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) {// EV8
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_SENDING;
}
}
}
/* 5.等待最后一个字节发送完成 */
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED)) {// EV8_2
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_SENDED;
}
}
/* 6.发送STOP信号 */
I2C_GenerateStop(I2Cx, ENABLE);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BUSY;
}
}
/* 7.发送完成 */
return MASTER_OK;
}int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);参数说明:uint8_tslaveAddr:Bit为从机地址,不关心最低位。short regAddr:发送从器件寄存器地址(8bit),如果不需要发送,填-1 。【寄存器写】uint8_t* datPtr:发送数据Buf的地址。
返回值:MASTER_OK表示通信正常,其他表示错误。
2、I2C主机轮询接收
EV5:STARTBF=1,起始位产生。EV6:ADDRF=1,从机响应地址。EV7/EV7_1: RXDATNE=1,读 DAT 寄存器。
主机接收步骤:
(1)保证I2C不在使用中,等待BUSY=0;(2)发送START信号,等待EV5 (STARTBF=1);(3)写从机地址,等待EV6 (ADDRF=1);(4)打开接收数据应答ACK,等待接收数据完成,判断RXDATNE标志读DAT,在接收最后一个字节完成前设置NACK和STOP信号。(5)等待STOP发送,等待BUSY信号;
主机轮询接收函数:int I2C_Read(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
{
uint32_t I2CTimeout;
int ret;
if (len == 0) return 0; /* 至少读取一个数据 */
if (regAddr >= 0) { /* 寄存器读 */
ret = I2C_Write(I2Cx, slaveAddr, regAddr, datPtr, 0);
if (ret != MASTER_OK) return ret;
}
/* 1.保证I2C不在使用中 */
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BUSY;
}
}
/* 2.发送START信号 */
I2C_GenerateStart(I2Cx, ENABLE);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) {// EV5
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_MODE;
}
}
/* 3.写从机地址 */
I2C_SendAddr7bit(I2Cx, slaveAddr, I2C_DIRECTION_RECV);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_RXMODE_FLAG)) {// EV6
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_TXMODE;
}
}
/* 4.接收数据 */
I2C_ConfigAck(I2Cx, ENABLE); /* 开启应答 */
while (len--) {
if (len == 0) { /* 接收最后一个数据前关闭应答,准备STOP */// EV7_1
I2C_ConfigAck(I2Cx, DISABLE);
I2C_GenerateStop(I2Cx, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_RXDATNE)) {// EV7
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BYTEF;
}
}
*datPtr++ = I2C_RecvData(I2Cx);
}
/* 5.等待BUSY */
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BUSY;
}
}
/* 6.接收完成 */
return MASTER_OK;
}如果需要【寄存器读】,在读之前写一个数据。设置regAddr值>=0,否则直接读,设置regAddr为-1即可。
主机轮询读写函数声明:int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
int I2C_Read(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
本帖最后由 Afanx 于 2022-7-19 18:07 编辑
三、I2C做主机/从机,中断读写/收发当I2C发送START信号时,自身作为主机,直到通信完成BUSY清0。其余时刻I2C作为从机,可以响应总线上的预设自身地址,地址匹配后作为从机收发。在程序上主从模式可以共用同一套配置和处理。
I2C中断请求表:
状态寄存器1:
状态寄存器2:
1、中断处理函数
声明I2C中断相关变量:typedef struct {
uint8_t dir;// I2C_DIRECTION_SEND or I2C_DIRECTION_RECV
uint8_t slaveAddr;
short regAddr;// -1 : no reg
uint16_t dataLen;
uint16_t dataCnt;
uint8_t* datPtr;
} _iicMaster;
typedef struct {
uint8_t mem;
uint8_t ptr;
uint8_t ptrUpdate;
} _iicSlave;
volatile _iicMaster iicMaster;
volatile _iicSlave iicSlave;
EV_IRQ中断处理函数:
void I2C_EV_IRQ_CallBack(I2C_Module* I2Cx, volatile _iicMaster* iicMaster, volatile _iicSlave* iicSlave)
{
uint32_t last_event = I2C_GetLastEvent(I2Cx);
// GPIO_LOG(GPIOA, GPIO_PIN_8, last_event);
if ((last_event & I2C_ROLE_MASTER) == I2C_ROLE_MASTER) { /* MASTER Mode */
switch (last_event) {
case I2C_EVT_MASTER_MODE_FLAG:// 0x00030001.EV5 (STARTBF)
I2C_SendAddr7bit(I2Cx, iicMaster->slaveAddr, iicMaster->dir);
break;
/* Master Tx */
case I2C_EVT_MASTER_TXMODE_FLAG:// 0x00070082.EV6.EV8_1 (ADDRF TXDATE)
if (iicMaster->regAddr < 0)
I2C_SendData(I2Cx, iicMaster->datPtr);
else
I2C_SendData(I2Cx, (uint8_t)iicMaster->regAddr);
break;
case I2C_EVT_MASTER_DATA_SENDING:// 0x00070080.EV8 (TXDATE)
case I2C_EVT_MASTER_DATA_SENDED: // 0x00070084.EV8_2 (TXDATE BSF)
if (iicMaster->dataCnt < iicMaster->dataLen) {
I2C_SendData(I2Cx, iicMaster->datPtr);
} else {
I2C_SendData(I2Cx, 0);// clear TXDATE
I2C_GenerateStop(I2Cx, ENABLE);
}
break;
/* Master Rx */
case I2C_EVT_MASTER_RXMODE_FLAG:// 0x00030002.EV6 (ADDRF)
if (iicMaster->dataLen == 1) {
I2C_ConfigAck(I2Cx, DISABLE);
I2C_GenerateStop(I2Cx, ENABLE);
} else {
I2C_ConfigAck(I2Cx, ENABLE);
}
break;
case I2C_EVT_MASTER_DATA_RECVD_FLAG: // 0x00030040.EV7 (RXDATNE)
case I2C_EVT_MASTER_SFT_DATA_RECVD_FLAG:// 0x00030044.EV7 (RXDATNE BSF)
iicMaster->datPtr = I2C_RecvData(I2Cx);
if (iicMaster->dataCnt == (iicMaster->dataLen - 1)) {
I2C_ConfigAck(I2Cx, DISABLE);
I2C_GenerateStop(I2Cx, ENABLE);
}
break;
default:
I2C_ResetInit(I2Cx);
break;
}
} else { /* Slave Mode */
switch (last_event) {
/* Slave Tx */
case I2C_EVT_SLAVE_SEND_ADDR_MATCHED: // 0x00060082.EV1.EV3_1 (ADDRF TXDATE)
case I2C_EVT_SLAVE_DATA_SENDING: // 0x00060080.EV3 (TXDATE)
case I2C_EVT_SLAVE_DATA_SENDED: // 0x00060084.EV3_2 (TXDATE BSF)
I2C_SendData(I2Cx, iicSlave->mem);// clear TXDATE
break;
/* Slave Rx */
case I2C_EVT_SLAVE_RECV_ADDR_MATCHED:// 0x00020002.EV1 (ADDRF)
iicSlave->ptrUpdate = 1;
break;
case I2C_EVT_SLAVE_DATA_RECVD:// 0x00020040.EV2 (RXDATNE)
if (iicSlave->ptrUpdate) {
iicSlave->ptrUpdate = 0;
iicSlave->ptr = I2C_RecvData(I2Cx);// clear RXDATNE
} else {
iicSlave->mem = I2C_RecvData(I2Cx);// clear RXDATNE
}
break;
case I2C_EVT_SLAVE_STOP_RECVD:// 0x00000010.EV4 (STOPF)
I2C_Enable(I2Cx, ENABLE); // clear STOPF
break;
default:
I2C_ResetInit(I2Cx);
break;
}
}
}void I2C1_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C1, &iicMaster, &iicSlave); }
void I2C2_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C2, &iicMaster, &iicSlave); }
void I2C3_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C3, &iicMaster, &iicSlave); }
void I2C4_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C4, &iicMaster, &iicSlave); }
ER_IRQ中断处理函数:
void I2C_ER_IRQ_CallBack(I2C_Module* I2Cx)
{
if ((I2C_GetLastEvent(I2Cx) & I2C_EVT_SLAVE_ACK_MISS) == I2C_EVT_SLAVE_ACK_MISS) {
I2C_ClrFlag(I2Cx, I2C_FLAG_ACKFAIL);
} else {
I2C_ResetInit(I2Cx);
}
}void I2C1_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C1); }
void I2C2_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C2); }
void I2C3_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C3); }
void I2C4_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C4); }
2、I2C主机中断读写
开启中断写:
void I2C_Write_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
{
if (regAddr < 0 && len == 0) return; /* 至少发送一个数据 */
Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
iicMaster.dir = I2C_DIRECTION_SEND;
iicMaster.slaveAddr = slaveAddr;
iicMaster.regAddr = regAddr;
iicMaster.dataLen = len;
iicMaster.dataCnt = 0;
iicMaster.datPtr = datPtr;
I2C_GenerateStart(I2Cx, ENABLE);
}开启中断读:
void I2C_Read_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
{
if (len == 0) return; /* 至少读取一个数据 */
if (regAddr >= 0) { /* 寄存器读 */
Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
iicMaster.dir = I2C_DIRECTION_SEND;
iicMaster.slaveAddr = slaveAddr;
iicMaster.regAddr = regAddr;
iicMaster.dataLen = 0;
iicMaster.dataCnt = 0;
iicMaster.datPtr = datPtr;
I2C_GenerateStart(I2Cx, ENABLE);
Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
}
Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
iicMaster.dir = I2C_DIRECTION_RECV;
iicMaster.slaveAddr = slaveAddr;
iicMaster.regAddr = -1;
iicMaster.dataLen = len;
iicMaster.dataCnt = 0;
iicMaster.datPtr = datPtr;
I2C_GenerateStart(I2Cx, ENABLE);
}
等待BUSY信号清0:
void Wait_IIC_Busy(I2C_Module* I2Cx, uint32_t timeOut)
{
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((timeOut--) == 0) {
I2C_ResetInit(I2Cx);
return;
}
}
}
声明中断读写函数以及等待通信完成:
void I2C_Write_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
void I2C_Read_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
void Wait_IIC_Busy(I2C_Module* I2Cx, uint32_t timeOut);
3、I2C从机中断收发
I2C器件主从模式在配置上没有区别,只是主动发送时作为主机(标志位MSMODE置1),响应自身地址时作为从机(标志位MSMODE置1)。因此主从模式可以共用同一套代码,只需要判断MSMODE标志位做区分即可。在上面的例程中,主机读写之余,将自身模拟成一个256字节的EEPROM器件。总线上的其他主机可以用读写EEPROM的方式访问本机的256字节RAM数据。
本帖最后由 Afanx 于 2022-8-11 16:08 编辑
主机模式:
中断发送波形图分析:
中断接收波形图分析:
从机模式:
从机中断发送波形图分析:
从机中断接收波形图分析:
感谢大佬的分享,太牛了,对学习硬件I2C帮助很大 请教一下大佬,我主机发送最后一个数据后频繁触发中断,这个是为啥 小黄鸭 发表于 2022-8-24 18:56
请教一下大佬,我主机发送最后一个数据后频繁触发中断,这个是为啥
应该是程序发送最后一个字节后没有清除TXDATE,写完需要发送的数据后,在最后一个字节发送过程中产生的TXDATE中断写任意值清除标志即可,这个值不会发送出去,因为发送最后一个字节后就产生STOP信号了。
页:
[1]