I2C通讯总结:
- 确定I2C地址;一般i2c有7位跟10位的区分,8位地址是7位地址左移1位,或者读写位,读位1 写为0;
I2C设备的写地址 = I2C设备地址 << 1
I2C设备的读地址 = (I2C设备地址 << 1) + 1
假设一个i2c 7位地址位0x50 ,则它的读地址为(0x50<<1)| 0x1=0xA1,写地址为0xA0;
10位地址暂时不谈; - 通讯时序
开始信号:
停止信号:
读数据:SCL拉低是SDA可以改变,SCL 高的时候,SDA数据要稳定,SCL高这个时候读SDA数据;
回读reg数据:(包含ack,nack是传完最后一个字节时会回应NACK, NACK SDA为高)
- 通讯流程
通讯过程步骤:
- 某个从机A发送start 信号,A变成了主机模式,A发送通讯从机地址B的写地址,如果有ack,表明A已发送地址并收到B回应;
- A 发送需要写入/读取的寄存器地址reg_addr,收到ack后,发送restart信号;
- A 发送从机B地址(读就发送读地址,写就发送写地址),有ack后,发送要读/写的数据.如果是多个数据,则中间每个数据都会有ack,最后一个字节发送完,会有NACK;
- 主机A发送停止信号;
单片机硬件I2C通讯流程:
寄存器介绍:
硬件i2c初始化
void i2c1_config(void)
{
unsigned char val;
i2c_cfg[1].i2c_peri = I2C1;
i2c_cfg[1].addr = DEFAULT_BACKPLANE_ADDR;
i2c_cfg[1].i2c_rd = rngCreate(I2C_RNG_SIZE);
i2c_cfg[1].i2c_wr = rngCreate(I2C_RNG_SIZE);
if(!i2c_cfg[1].i2c_rd || !i2c_cfg[1].i2c_wr)
return ;
i2c_cfg[1].mutex = xSemaphoreCreateMutex();
i2c_cfg[1].xfer.mode = MODE_MASTER;
rcu_periph_clock_enable(RCU_I2C1);
/* I2C1 SDA/PB11 SCL/PB10 SMBA/PB12*/
gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
i2c_deinit(I2C1);
/* configure I2C clock */
i2c_clock_config(I2C1, I2C1_SPEED, I2C_DTCY_2);
#ifdef I2C_SMBUS
/* configure I2C address */
i2c_mode_addr_config(I2C1, I2C_SMBUSMODE_ENABLE, I2C_ADDFORMAT_7BITS, i2c_cfg[1].addr);
/* configure SMBUS HOST/DEVICE */
i2c_smbus_type_config(I2C1, I2C_SMBUS_HOST);
#endif
i2c_interrupt_enable(I2C1, I2C_INT_EV);
i2c_interrupt_enable(I2C1, I2C_INT_ERR);
i2c_interrupt_enable(I2C1, I2C_INT_BUF);
//i2c_interrupt_enable(I2C1, I2C_INT_STPSEND);
nvic_irq_enable(I2C1_EV_IRQn, 0, 2);
nvic_irq_enable(I2C1_ER_IRQn, 0, 2);
/* enable I2C1 */
i2c_enable(I2C1);
i2c_ack_config(I2C1, I2C_ACK_ENABLE);
}
中断处理函数:
//i2c_cfg结构体
struct I2C_Config {
RING_ID i2c_rd;
RING_ID i2c_wr;
unsigned int i2c_peri;
unsigned char addr;
struct i2c_transfer_t xfer;
SemaphoreHandle_t mutex;
unsigned char support_eeprom;
};
static struct I2C_Config i2c_cfg[2];
void i2c_ev_isr(unsigned int index)
{
unsigned char val=0;
unsigned int i2c_peri = i2c_cfg[index].i2c_peri;
if(i2c_cfg[index].xfer.mode == MODE_SLAVE){
if(i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_ADDSEND) == SET){
i2c_interrupt_flag_clear(i2c_peri, I2C_INT_FLAG_ADDSEND);
//从机发送
if(i2c_cfg[index].xfer.flag & FLAG_START){
SMBusCommandProcess(index);
} else{
i2c_cfg[index].xfer.flag |= FLAG_START;
}
}
if(i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_RBNE) == SET){
i2c_interrupt_flag_clear(i2c_peri, I2C_INT_FLAG_RBNE);
val = i2c_data_receive(i2c_peri);
if(rngBufPut(i2c_cfg[index].i2c_rd, &val, 1) != 1){ printf("i2c data save error \n");;}
//printf("I2C%x recv lost %x\r\n", index, val);
++i2c_cfg[index].xfer.rd_len;
printf("MODE_SLAVE 0x%x\n", val);
}
if(i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_TBE) == SET){
rngBufGet(i2c_cfg[index].i2c_wr, &val, 1);
i2c_data_transmit(i2c_peri, val);
printf("send 0x%x\n", val);
//当移位寄存器与DATA都为空,可以再写一个字节
if(i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_TBE) == SET){
rngBufGet(i2c_cfg[index].i2c_wr, &val, 1);
i2c_data_transmit(i2c_peri, val);
}
}
//slave 读结束flag
if(SET == i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_STPDET)){
i2c_enable(i2c_cfg[index].i2c_peri);
//从机接收
SMBusCommandProcess(index);
//清空buffer
rngFlush(i2c_cfg[index].i2c_rd);
rngFlush(i2c_cfg[index].i2c_wr);
i2c_cfg[index].xfer.rd_len = 0;
i2c_cfg[index].xfer.wr_len = 0;
}
} else {
//主机模式下 判断是否发送了地址并收到了ack
if(i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_ADDSEND) == SET){
//收到发送了地址并收到了ack,清除掉这个标志位
i2c_interrupt_flag_clear(i2c_peri, I2C_INT_FLAG_ADDSEND);
i2c_cfg[index].xfer.flag |= FLAG_START;
}
if(i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_RBNE) == SET){
printf("i2c_cfg[index].xfer.rd_len:%d\n",i2c_cfg[index].xfer.rd_len);
if(i2c_cfg[index].xfer.rd_len ==0)
{
i2c_interrupt_flag_clear(i2c_peri, I2C_INT_FLAG_RBNE);
val = i2c_data_receive(i2c_peri);//当接收长度为0时,为了消除I2C_INT_FLAG_RBNE标志位,需要对I2C_DATA进行对操作;
printf("i2c:len :0x%x rev val 0x%x\n", i2c_cfg[index].xfer.rd_len,val);
}
/* 接收到len-1长度的数据,发送NACK和stop并接收下一个数据 */
else if(i2c_cfg[index].xfer.rd_len == 1){
i2c_ack_config(i2c_peri, I2C_ACK_DISABLE);
i2c_stop_on_bus(i2c_peri);
i2c_cfg[index].xfer.flag |= FLAG_STOP;
val = i2c_data_receive(i2c_peri);
printf("i2c:len :0x%x rev val 0x%x\n", i2c_cfg[index].xfer.rd_len,val);
--i2c_cfg[index].xfer.rd_len ;
if(rngBufPut(i2c_cfg[index].i2c_rd, &val, 1) != 1)
;
}
else
{
val = i2c_data_receive(i2c_peri);
if(rngBufPut(i2c_cfg[index].i2c_rd, &val, 1) != 1)
;
--i2c_cfg[index].xfer.rd_len ;
}
;
}
//判断是否发送了start信号
if(SET == i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_SBSEND)){
//判断传输方向
if(i2c_cfg[index].xfer.direction == DIR_RD){
//方向为接收 此处写I2C_DATA
i2c_master_addressing(i2c_peri, i2c_cfg[index].xfer.xfer_addr, I2C_RECEIVER);
} else {
//方向为发送 此处写I2C_DATA
i2c_master_addressing(i2c_peri, i2c_cfg[index].xfer.xfer_addr, I2C_TRANSMITTER);
}
} //I2C_DATA是否为空 1为空
if(i2c_interrupt_flag_get(i2c_peri, I2C_INT_FLAG_TBE) == SET){
/* write完成,发送restart */
if(i2c_cfg[index].xfer.wr_len == 0 && i2c_cfg[index].xfer.rd_len != 0){
i2c_cfg[index].xfer.direction = DIR_RD;
i2c_start_on_bus(i2c_peri);
/* 收发完成,stop */
} else if(i2c_cfg[index].xfer.wr_len == 0 && i2c_cfg[index].xfer.rd_len == 0){
#ifdef I2C_PEC
i2c_pec_transfer(i2c_peri);
#endif
i2c_stop_on_bus(i2c_peri);
i2c_cfg[index].xfer.flag |= FLAG_STOP;
/* write */
} else if(!rngIsEmpty(i2c_cfg[index].i2c_wr)){
rngBufGet(i2c_cfg[index].i2c_wr, &val, 1);
i2c_data_transmit(i2c_peri, val);
--i2c_cfg[index].xfer.wr_len;
}
}
}
}
读写:
int SMBusBlockRead(unsigned int index, unsigned char slaveAddr, unsigned char cmd, unsigned char *buffer, unsigned int len)
{
int i=0;
unsigned char val;
struct I2C_Config *pI2C = &i2c_cfg[index];
while(i2c_flag_get(pI2C->i2c_peri, I2C_FLAG_I2CBSY) && (i++<SMBUS_TIMEOUT_US))udelay(1);
if(i>=SMBUS_TIMEOUT_US)
return ACK_SMBUS_TIMEOUT;
i2c_prepare_transfer(index);
MUTEX_TAKE(pI2C->mutex);
pI2C->xfer.mode = MODE_MASTER;
pI2C->xfer.xfer_addr = slaveAddr;
pI2C->xfer.direction = DIR_RDWR;
//cmd+buffer
pI2C->xfer.wr_len = 1;
pI2C->xfer.rd_len = len;
rngBufPut(pI2C->i2c_wr, (unsigned char *)&cmd, 1);
i2c_start_on_bus(pI2C->i2c_peri);
#if 0
//cmd+buffer
while((rngNBytes(pI2C->i2c_rd)<len) && (i++<SMBUS_TIMEOUT_US))udelay(1);
if(i>=SMBUS_TIMEOUT_US)
return -1;
#endif
//等待传输结束后取数据
while(!(i2c_cfg[index].xfer.flag & FLAG_STOP)){
if(i2c_cfg[index].xfer.flag & FLAG_TIMEOUT){
MUTEX_GIVE(pI2C->mutex);
return ACK_SMBUS_TIMEOUT;
}
}
rngBufGet(pI2C->i2c_rd, buffer, len);
i2c_stop_transfer(index);
MUTEX_GIVE(pI2C->mutex);
return ACK_OK;
}
int SMBusBlockWrite(unsigned int index, unsigned char slaveAddr, unsigned char cmd, unsigned char *buffer, unsigned int len)
{
int i=0;
struct I2C_Config *pI2C = &i2c_cfg[index];
while(i2c_flag_get(pI2C->i2c_peri, I2C_FLAG_I2CBSY) && (i++<SMBUS_TIMEOUT_US))udelay(1);
if(i>=SMBUS_TIMEOUT_US)
return ACK_SMBUS_TIMEOUT;
i2c_prepare_transfer(index);
MUTEX_TAKE(pI2C->mutex);
pI2C->xfer.mode = MODE_MASTER;
pI2C->xfer.xfer_addr = slaveAddr;
pI2C->xfer.direction = DIR_WR;
//cmd+buffer
pI2C->xfer.wr_len = len+1;
pI2C->xfer.rd_len = 0;
rngBufPut(pI2C->i2c_wr, (unsigned char *)&cmd, 1);
rngBufPut(pI2C->i2c_wr, buffer, len);
i2c_start_on_bus(pI2C->i2c_peri);
while(!(i2c_cfg[index].xfer.flag & FLAG_STOP)){
if(i2c_cfg[index].xfer.flag & FLAG_TIMEOUT){
MUTEX_GIVE(pI2C->mutex);
return ACK_SMBUS_TIMEOUT;
}
}
i2c_stop_transfer(index);
MUTEX_GIVE(pI2C->mutex);
return ACK_OK;
}
ACK_STATUS SMBusBlockRW(unsigned int index, unsigned char slaveAddr, unsigned char cmd, unsigned char *rbuffer, unsigned int rlen, unsigned char *wbuffer, unsigned int wlen)
{
int i=0;
struct I2C_Config *pI2C = &i2c_cfg[index];
while(i2c_flag_get(pI2C->i2c_peri, I2C_FLAG_I2CBSY) && (i++<SMBUS_TIMEOUT_US))udelay(1);
if(i>=SMBUS_TIMEOUT_US)
return ACK_SMBUS_TIMEOUT;
i2c_prepare_transfer(index);
MUTEX_TAKE(pI2C->mutex);
pI2C->xfer.mode = MODE_MASTER;
pI2C->xfer.xfer_addr = slaveAddr;
pI2C->xfer.direction = DIR_RDWR;
//cmd+buffer
pI2C->xfer.wr_len = 1+wlen;
pI2C->xfer.rd_len = rlen;
rngBufPut(pI2C->i2c_wr, (unsigned char *)&cmd, 1);
rngBufPut(pI2C->i2c_wr, (unsigned char *)wbuffer, wlen);
i2c_start_on_bus(pI2C->i2c_peri);
//等待传输结束后取数据
while(!(i2c_cfg[index].xfer.flag & FLAG_STOP)){
if(i2c_cfg[index].xfer.flag & FLAG_TIMEOUT){
MUTEX_GIVE(pI2C->mutex);
return ACK_SMBUS_TIMEOUT;
}
}
rngBufGet(pI2C->i2c_rd, rbuffer, rlen);
i2c_stop_transfer(index);
MUTEX_GIVE(pI2C->mutex);
return ACK_OK;
}
参考:https://www.cnblogs.com/BitArt/archive/2013/05/28/3103917.html
参考: 《GD32E507用户手册》