[A133]全志u-boot中的I2C驱动分析
hongxi.zhu
2024-6-27
一、IIC标准读写时序
IIC是高位(MSB)先传输
二、代码流程
2.1主机写数据
brandy/brandy-2.0/u-boot-2018/drivers/i2c/sunxi_i2c.c
static int sunxi_i2c_write(struct i2c_adapter *adap, uint8_t chip,
uint32_t addr, int alen, uint8_t *buffer, int len)
{
int ret;
ret = twi_start(adap->hwadapnr); // 主机发开始信号
if (ret) {
I2C_ERR("twi_write start error\n");
goto i2c_write_err_occur;
}
ret = twi_send_slave_addr(adap->hwadapnr, chip, I2C_WRITE); // 主机发从机设备地址
if (ret) {
I2C_ERR("twi_write send slave addr error!\n");
goto i2c_write_err_occur;
}
ret = twi_send_addr(adap->hwadapnr, addr, alen); // 主机发送从机寄存器地址
if (ret) {
I2C_ERR("twi_write send addr error!\n");
goto i2c_write_err_occur;
}
ret = twi_send_data(adap->hwadapnr, buffer, len); //主机发送数据到从机
if (ret) {
I2C_ERR("twi_write send data error!\n");
goto i2c_write_err_occur;
}
i2c_write_err_occur:
twi_stop(adap->hwadapnr); // 主机发送Stop信号
return ret;
}
2.1.1 主机发开始信号
static int twi_start(int bus_num)
{
u32 timeout = MAX_SUNXI_I2C_TIMEOUT;
twi_soft_reset(bus_num); //重置TWI_EFR[1:0]寄存器和TWI_SRST[0]寄存器实现软复位
twi_set_start(bus_num); //发送START信号,设置TWI_CNTR[5]为1
//发送START信号成功会触发中断,判断TWI_CNTR[3]寄存器的INT_FLAG位是否为1
if (twi_wait_irq_flag(bus_num, timeout)) {
I2C_ERR("START can't sendout!\n");
return SUNXI_I2C_FAIL;
}
// 判断START信号是否正常发出,读取TWI_STAT[7:0]是否为0x08(START condition transmitted)
if (twi_wait_status(bus_num, I2C_START_TRANSMIT, timeout))
return SUNXI_I2C_FAIL;
return SUNXI_I2C_OK;
}
2.1.2 主机发从机设备地址
static int twi_send_slave_addr(int bus_num, u32 saddr, u32 rw)
{
u32 timeout = MAX_SUNXI_I2C_TIMEOUT;
struct sunxi_twi_reg *i2c = sunxi_i2c[bus_num];
rw &= 1;
i2c->data = ((saddr & 0xff) << 1) | rw; //将7-bit地址写到高7位, 并将读写位设为I2C_WRITE(0)
twi_clear_irq_flag(bus_num); //清除当前总线的中断标志, TWI_CNTR[3] INT_FLAG = 0 (全志这里写的有问题)
if (twi_wait_irq_flag(bus_num, timeout)) // 等待从机地址发送完成中断
return SUNXI_I2C_TOUT;
if ((rw == I2C_WRITE) && (twi_wait_status(bus_num, I2C_ADDRWRITE_ACK, timeout)))
return SUNXI_I2C_FAIL;
else if ((rw == I2C_READ) && (twi_wait_status(bus_num, I2C_ADDRREAD_ACK, timeout)))
return SUNXI_I2C_FAIL;
return SUNXI_I2C_OK;
}
2.1.3 主机发送从机寄存器地址
static int twi_send_addr(int bus_num, uint addr, int alen)
{
int i, ret, addr_len;
char *slave_reg;
if (alen >= 3)
addr_len = 2;
else if (alen <= 1)
addr_len = 0;
else
addr_len = 1;
slave_reg = (char *)&addr;
for (i = addr_len; i >= 0; i--) {
// 1. 发送寄存器地址数据
// 2. 等待中断
// 3. 判断状态寄存器值 == 0x28,即发送成功且收到从机ACK
ret = twi_send_byte_addr(bus_num, slave_reg[i] & 0xff);
if (ret != SUNXI_I2C_OK)
goto twi_send_addr_err;
}
twi_send_addr_err:
return ret;
}
2.1.4 主机发送数据到从机
static int twi_send_data(int bus_num, u8 *data_addr, u32 data_count)
{
u32 timeout = MAX_SUNXI_I2C_TIMEOUT;
u32 i;
struct sunxi_twi_reg *i2c = sunxi_i2c[bus_num];
for (i = 0; i < data_count; i++) {
i2c->data = data_addr[i];
twi_clear_irq_flag(bus_num); //清中断
if (twi_wait_irq_flag(bus_num, timeout)) //等待发送完成中断
return SUNXI_I2C_TOUT;
if (twi_wait_status(bus_num, I2C_DATAWRITE_ACK, timeout)) //等待状态寄存器值变为0x28,即发送成功且收到从机ACK
return SUNXI_I2C_FAIL;
}
return SUNXI_I2C_OK;
}
2.1.5主机发送Stop信号
static int twi_stop(int bus_num)
{
u32 timeout = MAX_SUNXI_I2C_TIMEOUT;
struct sunxi_twi_reg *i2c = sunxi_i2c[bus_num];
i2c->ctl |= (0x01 << 4); //清除TWI_CNTR[4]位 (全志这里的操作有点问题)
i2c->ctl |= (0x01 << 3); //清中断 (全志这里的操作有点问题)
twi_set_stop(bus_num); // 设置TWI_CNTR[4]寄存器,发出stop信号
twi_clear_irq_flag(bus_num); //清中断 (全志这里的操作有点问题)
if (twi_wait_status(bus_num, TWI_STAT_IDLE, timeout)) //等待总线进入idle状态
return SUNXI_I2C_TFAIL;
return SUNXI_I2C_OK;
}
2.2 主机读数据
brandy/brandy-2.0/u-boot-2018/drivers/i2c/sunxi_i2c.c
static int sunxi_i2c_read(struct i2c_adapter *adap, uint8_t chip,
uint32_t addr, int alen, uint8_t *buffer, int len)
{
int ret;
ret = twi_start(adap->hwadapnr); // 主机发开始信号
if (ret) {
I2C_ERR("twi_read start error\n");
goto i2c_read_err_occur;
}
ret = twi_send_slave_addr(adap->hwadapnr, chip, I2C_WRITE); // 主机发从机设备地址
if (ret){
I2C_ERR("twi_read send slave addr error!\n");
goto i2c_read_err_occur;
}
ret = twi_send_addr(adap->hwadapnr, addr, alen); // 主机发送从机寄存器地址
if (ret) {
I2C_ERR("twi_read send addr error\n");
goto i2c_read_err_occur;
}
ret = twi_restart(adap->hwadapnr); // 主机发送restart信号(或者start信号)
if (ret) {
I2C_ERR("twi_restart error\n");
goto i2c_read_err_occur;
}
ret = twi_send_slave_addr(adap->hwadapnr, chip, I2C_READ); // 主机发从机设备地址
if (ret) {
I2C_ERR("twi_read send slave addr error!\n");
goto i2c_read_err_occur;
}
ret = twi_get_data(adap->hwadapnr, buffer, len); // 主机读取从机发送的数据
if (ret) {
I2C_ERR("twi_get_data error\n");
goto i2c_read_err_occur;
}
i2c_read_err_occur:
twi_stop(adap->hwadapnr); // 主机发送Stop信号
return ret;
}
读操作除了下面的两个操作,其他与写一样
2.2.1 主机发送restart信号(或者start信号)
static int twi_start(int bus_num)
{
u32 timeout = MAX_SUNXI_I2C_TIMEOUT;
twi_soft_reset(bus_num);
twi_set_start(bus_num);
if (twi_wait_irq_flag(bus_num, timeout)) {
I2C_ERR("START can't sendout!\n");
return SUNXI_I2C_FAIL;
}
if (twi_wait_status(bus_num, I2C_START_TRANSMIT, timeout))
return SUNXI_I2C_FAIL;
return SUNXI_I2C_OK;
}
static int twi_restart(int bus_num)
{
u32 timeout = MAX_SUNXI_I2C_TIMEOUT;
twi_set_start(bus_num);
twi_clear_irq_flag(bus_num);
if (twi_wait_irq_flag(bus_num, timeout)) {
I2C_ERR("Restart can't sendout!\n");
return SUNXI_I2C_FAIL;
}
if (twi_wait_status(bus_num, I2C_RESTART_TRANSMIT, timeout))
return SUNXI_I2C_FAIL;
return SUNXI_I2C_OK;
}
restart信号实际上就是start信号,只是restart不会reset总线
start: 重置SCL和SDA到IDLE状态,即两者都为高,此时只需要操作SDA拉低,做出一个SDA下降沿就完成start信号
restart:不重置SCL和SDA,但是主动拉高SDA到高电平,直到下一个SCL高电平时钟周期到来后做出下降沿
2.2.2 主机读取从机数据
static int twi_get_data(int bus_num, u8 *data_addr, u32 data_count)
{
u32 timeout = MAX_SUNXI_I2C_TIMEOUT;
u32 i;
struct sunxi_twi_reg *i2c = sunxi_i2c[bus_num];
// 最后一个字节不需要发送ACK到从机, 而是发送NACK
if (data_count == 1) { //如果只有一个字节,那它就是最后一个字节。
/* no need ack */
twi_clear_irq_flag(bus_num);
if (twi_wait_irq_flag(bus_num, timeout))
return SUNXI_I2C_TOUT;
if (twi_wait_status(bus_num, I2C_DATAREAD_NACK, timeout)) // 等待TWI_STAT[7:0]的状态位变为I2C_DATAREAD_NACK
return SUNXI_I2C_FAIL;
*data_addr = i2c->data; //从TWI_DATA[7:0]读取一个字节的数据
} else {
for (i = 0; i < data_count - 1; i++) { //非最后一个字节需要向从机发ACK
/* need ack */
twi_enable_ack(bus_num); // 设TWI_CNTR[2]为1
twi_clear_irq_flag(bus_num);
if (twi_wait_irq_flag(bus_num, timeout))
return SUNXI_I2C_TOUT;
if (twi_wait_status(bus_num, I2C_DATAREAD_ACK, timeout)) 等待TWI_STAT[7:0]的状态位变为I2C_DATAREAD_ACK
return SUNXI_I2C_FAIL;
data_addr[i] = i2c->data; //从TWI_DATA[7:0]读取一个字节的数据
}
/* received the last byte */
twi_disable_ack(bus_num); // 设TWI_CNTR[2]为0
twi_clear_irq_flag(bus_num);
if (twi_wait_irq_flag(bus_num, timeout))
return SUNXI_I2C_TOUT;
if (twi_wait_status(bus_num, I2C_DATAREAD_NACK, timeout)) // 等待TWI_STAT[7:0]的状态位变为I2C_DATAREAD_NACK
return SUNXI_I2C_FAIL;
data_addr[data_count - 1] = i2c->data; //从TWI_DATA[7:0]读取一个字节的数据
}
return SUNXI_I2C_OK;
}