[A133]全志u-boot中的I2C驱动分析

[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坂田民工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值