一、概述
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 一个字节数据传输示例
二、程序
源文件
#include "bsp_iic.h"
#include "esp_log.h"
#include "unistd.h"
static const char *TAG = "I2C";
void I2cInit(void)
{
const gpio_config_t io_config = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pin_bit_mask = (1ULL << I2C_SCL_PIN)
| (1ULL << I2C_SDA_PIN)
};
gpio_config(&io_config);
I2C_SDA_HIGH;
I2C_SCL_HIGH;
}
esp_err_t I2cSdaIn(void)
{
const gpio_config_t io_config = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pin_bit_mask = (1ULL << I2C_SDA_PIN)
};
return gpio_config(&io_config);
}
esp_err_t I2cSdaOut(void)
{
const gpio_config_t io_config = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pin_bit_mask = (1ULL << I2C_SDA_PIN)
};
return gpio_config(&io_config);
}
void I2cStart(void)
{
I2cSdaOut();
I2C_SDA_HIGH;
I2C_SCL_HIGH;
usleep(4);
I2C_SDA_LOW;
usleep(4);
I2C_SCL_LOW;
}
void I2cStop(void)
{
I2cSdaOut();
I2C_SCL_LOW;
I2C_SDA_LOW;
usleep(4);
I2C_SCL_HIGH;
I2C_SDA_HIGH;
usleep(4);
}
uint8_t I2cWaitAck(void)
{
uint8_t ErrTime = 0;
I2C_SDA_HIGH;
usleep(1);
I2cSdaIn();
I2C_SCL_HIGH;
usleep(1);
while(I2C_SDA == 1)
{
ErrTime++;
if(ErrTime > 200)
{
I2cStop();
return 1;
}
}
I2C_SCL_LOW;
return 0;
}
void I2cAck(void)
{
I2cSdaOut();
I2C_SCL_LOW;
I2C_SDA_LOW;
usleep(2);
I2C_SCL_HIGH;
usleep(2);
I2C_SCL_LOW;
}
void I2cNack(void)
{
I2cSdaOut();
I2C_SCL_LOW;
I2C_SDA_HIGH;
usleep(2);
I2C_SCL_HIGH;
usleep(2);
I2C_SCL_LOW;
}
void I2cWriteByte(uint8_t Data)
{
uint8_t i;
I2cSdaOut();
I2C_SCL_LOW;
for(i = 0;i < 8;i++)
{
if((Data & 0x80) >> 7)
{
I2C_SDA_HIGH;
}
else
{
I2C_SDA_LOW;
}
Data <<= 1;
usleep(2);
I2C_SCL_HIGH;
usleep(2);
I2C_SCL_LOW;
usleep(2);
}
}
uint8_t I2cReadByte(uint8_t isAck)
{
uint8_t i;
uint8_t Data = 0x0;
I2cSdaIn();
for(i = 0;i < 8;i++)
{
I2C_SCL_LOW;
usleep(2);
I2C_SCL_HIGH;
Data <<= 1;
if(I2C_SDA == 1)
{
Data++;
}
usleep(1);
}
if(isAck)
{
I2cNack();
}
else
{
I2cAck();
}
return Data;
}
void EspI2cWriteByte(uint8_t dev_addr,uint8_t reg_addr,uint8_t write_data)
{
I2cStart();
I2cWriteByte((dev_addr << 1) | I2C_MASTER_WRITE);
I2cWaitAck();
I2cWriteByte(reg_addr);
I2cWaitAck();
I2cWriteByte(write_data);
I2cWaitAck();
I2cStop();
}
void EspI2cReadByte(uint8_t dev_addr,uint8_t reg_addr,uint8_t *read_data)
{
I2cStart();
I2cWriteByte((dev_addr << 1) | I2C_MASTER_WRITE);
I2cWaitAck();
I2cWriteByte(reg_addr);
I2cWaitAck();
I2cStart();
I2cWriteByte((dev_addr << 1) | I2C_MASTER_READ);
*read_data = I2cReadByte(NACK_VAL);
I2cStop();
}
void EspI2cWriteBytes(uint8_t dev_addr,uint8_t reg_addr,uint8_t *write_data,uint8_t write_len)
{
uint8_t i;
I2cStart();
I2cWriteByte((dev_addr << 1) | I2C_MASTER_WRITE);
I2cWaitAck();
I2cWriteByte(reg_addr);
I2cWaitAck();
for(i = 0;i < write_len;i++)
{
I2cWriteByte(write_data[i]);
I2cWaitAck();
}
I2cStop();
}
void EspI2cReadBytes(uint8_t dev_addr,uint8_t reg_addr,uint8_t *read_data,uint8_t read_len)
{
uint8_t i = 0;
I2cStart();
I2cWriteByte((dev_addr << 1) | I2C_MASTER_WRITE);
I2cWaitAck();
I2cWriteByte(reg_addr);
I2cWaitAck();
I2cStart();
I2cWriteByte((dev_addr << 1) | I2C_MASTER_READ);
I2cWaitAck();
if(read_len > 1)
{
for(i = 0;i < read_len - 1;i++)
{
read_data[i] = I2cReadByte(ACK_VAL);
}
}
read_data[i] = I2cReadByte(NACK_VAL);
I2cStop();
}
void I2cDetect(void)
{
uint8_t i;
uint8_t j;
uint8_t address;
uint8_t ret = 0;
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
for(i = 0;i < 128;i += 16)
{
printf("%02x: ", i);
for (j = 0;j < 16;j++)
{
fflush(stdout);
address = i + j;
I2cStart();
I2cWriteByte((address << 1) | I2C_MASTER_WRITE);
ret = I2cWaitAck();
I2cStop();
if(ret == 0)
{
printf("%02x ",address);
}
else
{
printf("-- ");
}
}
printf("\r\n");
}
}
头文件
#ifndef _BSP_IIC_H_
#define _BSP_IIC_H_
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#define ACK_VAL 0x0
#define NACK_VAL 0x1
#define ACK_CHECK_EN 0x1
#define ACK_CHECK_DIS 0x0
#define ESP32_GPIO_OUT_W1TS(gpio_num) GPIO.out_w1ts = (0x01 << gpio_num) //gpio_num:0 ~ 31 GPIO OUT HIGH
#define ESP32_GPIO_OUT_W1TC(gpio_num) GPIO.out_w1tc = (0x01 << gpio_num) //gpio_num:0 ~ 31 GPIO OUT LOW
#define ESP32_GPIO_OUT1_W1TS(gpio_num) GPIO.out1_w1ts.data = (0x01 << (gpio_num - 32)) //gpio_num:32 ~ 39 GPIO OUT HIGH
#define ESP32_GPIO_OUT1_W1TC(gpio_num) GPIO.out1_w1tc.data = (0x01 << (gpio_num - 32)) //gpio_num:32 ~ 39 GPIO OUT LOW
#define ESP32_GPIO_IN(gpio_num) ((GPIO.in >> gpio_num) & 0x1) //gpio_num:0 ~ 31 GPIO IN VALUE
#define ESP32_GPIO_IN1(gpio_num) ((GPIO.in1.data >> gpio_num) & 0x1) //gpio_num:32 ~ 39 GPIO IN VALUE
#define GpioReadBit(gpio_num) ESP32_GPIO_IN(gpio_num)
#define GpioSetBit(gpio_num) if(gpio_num < 32)\
ESP32_GPIO_OUT_W1TS(gpio_num);\
else\
ESP32_GPIO_OUT1_W1TS(gpio_num);
#define GpioResetBit(gpio_num) if(gpio_num < 32)\
ESP32_GPIO_OUT_W1TC(gpio_num);\
else\
ESP32_GPIO_OUT1_W1TC(gpio_num);
#define I2C_SCL_PIN GPIO_NUM_4
#define I2C_SDA_PIN GPIO_NUM_5
#define I2C_SDA_HIGH GpioSetBit (I2C_SDA_PIN)
#define I2C_SDA_LOW GpioResetBit(I2C_SDA_PIN)
#define I2C_SCL_HIGH GpioSetBit (I2C_SCL_PIN)
#define I2C_SCL_LOW GpioResetBit(I2C_SCL_PIN)
#define I2C_SDA GpioReadBit (I2C_SDA_PIN)
// #define I2C_SDA gpio_get_level(I2C_SDA_PIN)
void I2cInit(void);
void I2cStart(void);
void I2cStop(void);
uint8_t I2cWaitAck(void);
void I2cAck(void);
void I2cNack(void);
void I2cWriteByte(uint8_t Data);
uint8_t I2cReadByte(uint8_t isAck);
void EspI2cWriteByte(uint8_t dev_addr,
uint8_t reg_addr,
uint8_t write_data);
void EspI2cReadByte(uint8_t dev_addr,
uint8_t reg_addr,
uint8_t *read_data);
void EspI2cWriteBytes(uint8_t dev_addr,
uint8_t reg_addr,
uint8_t *write_data,
uint8_t write_len);
void EspI2cReadBytes(uint8_t dev_addr,
uint8_t reg_addr,
uint8_t *read_data,
uint8_t read_len);
void I2cDetect(void);
#endif