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

news2024/11/19 8:39:26

[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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1882190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GA自动点击器(Auto Clicker)v1.0.83高级版

其能够在用户指定的任何应用程序中&#xff0c;根据设定的时间间隔和位置&#xff0c;自动进行重复的点击或滑动动作. 链接&#xff1a;https://pan.baidu.com/s/11AV4Zxg6y9364kADDDSFYQ?pwdbo6q 提取码&#xff1a;bo6q

深入解读一下`android.os.CountDownTimer`

简介 在 Android 开发中&#xff0c;CountDownTimer 是一个非常有用的类&#xff0c;它可以用于倒计时任务&#xff0c;比如倒计时器、限时活动等。CountDownTimer 提供了一个简单的方式来实现定时操作&#xff0c;无需我们手动管理线程和计时器。 本文将深入解析 CountDownT…

HiBit Uninstaller:软件批量卸载,一触即得

名人说&#xff1a;莫道谗言如浪深&#xff0c;莫言迁客似沙沉。 ——刘禹锡《浪淘沙》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、软件介绍1、HiBit Uninstaller2、核心功能 二、下载安装1、下载2、安装 …

文件操作与管理

程序经常需要访问文件和目录&#xff0c;读取文件信息或写入文件信息&#xff0c;在Python语言中对文件的读写是通过文件对象&#xff08;file object&#xff09;实现的。Python的文件对象也称为类似文件对象或流&#xff08;stream&#xff09;&#xff0c;因为Python提供一种…

13_网络安全

目录 网络安全协议 网络安全协议 PGP协议 网络安全技术 防火墙技术 入侵检测系统 入侵防御系统 杀毒软件 蜜罐系统 计算机病毒与木马 网络安全协议 网络安全协议 物理层主要使用物理手段隔离、屏蔽物理设备等&#xff0c;其他层都是靠协议来保证传输的安全&#xff…

【python刷题】蛇形方阵

题目描述 给出一个不大于 99 的正整数n&#xff0c;输出n*n的蛇形方阵。从左上角填上1开始&#xff0c;顺时针方向依次填入数字&#xff0c;如同样例所示。注意每个数字有都会占用3个字符&#xff0c;前面使用空格补齐。 输入 输入一个正整数n,含义如题所述 输出 输出符合…

ITK-二值阈值分割

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 什么是二值阈值分割&#xff1f; 二值阈值分割是一种常见的图像处理技术&#xff0c;用于将图像的像素值分成两个类别&#xff1…

基于GTX的64B66B编码的自定义协议发送模块(高速收发器二十一)

点击进入高速收发器系列文章导航界面 1、64B66B组帧原理 前文讲解64B66B编码原理时&#xff0c;已经讲解过组帧的原理&#xff0c;包括数据帧和控制帧两种&#xff0c;区别在于同步码不同。 下图是802.3的以太网控制协议&#xff0c;其中S表示起始位&#xff0c;T表示停止位。为…

LINUX系统编程:多线程互斥

目录 1.铺垫 2.线程锁接口的认识 静态锁分配 动态锁的分配 互斥量的销毁 互斥量加锁和解锁 3.加锁版抢票 4.互斥的底层实现 1.铺垫 先提一个小场景&#xff0c;有1000张票&#xff0c;现在有4个进程&#xff0c;这四个进程疯狂的去抢这1000张票&#xff0c;看看会发生什…

熊猫烧香是什么?

熊猫烧香&#xff08;Worm.WhBoy.cw&#xff09;是一种由李俊制作的电脑病毒&#xff0c;于2006年底至2007年初在互联网上大规模爆发。这个病毒因其感染后的系统可执行文件图标会变成熊猫举着三根香的模样而得名。熊猫烧香病毒具有自动传播、自动感染硬盘的能力&#xff0c;以及…

简单爬虫案例——爬取快手视频

网址&#xff1a;aHR0cHM6Ly93d3cua3VhaXNob3UuY29tL3NlYXJjaC92aWRlbz9zZWFyY2hLZXk9JUU2JThCJTg5JUU5JTlEJUEy 找到视频接口&#xff1a; 视频链接在photourl中 完整代码&#xff1a; import requestsimport re url https://www.kuaishou.com/graphql cookies {did: web_…

C语言 | Leetcode C语言题解之第207题课程表

题目&#xff1a; 题解&#xff1a; bool canFinish(int numCourses, int** prerequisites, int prerequisitesSize, int* prerequisitesColSize) {int** edges (int**)malloc(sizeof(int*) * numCourses);for (int i 0; i < numCourses; i) {edges[i] (int*)malloc(0);…

一点连接千家银行,YonSuite让“银企对账”一键确认

在当今数智化浪潮下&#xff0c;成长型企业面临着前所未有的机遇与挑战。特别是在与银行的对接以及银企对账等方面&#xff0c;传统的手动操作模式已难以满足企业高效、安全的金融管理需求。用友YonSuite作为一款全场景SaaS应用服务&#xff0c;凭借其强大的银企直联功能&#…

【C++ 初阶路】--- C++内存管理

目录 一、C/C内存分布二、C内存管理方式2.1 new/delete操作内置类型2.2 new和delete操作自定义类型 三、operator new与operator delete函数四、new和delete的实现原理4.1 内置类型4.2 自定义类型 一、C/C内存分布 int globalVar 1; static int staticGlobalVar 1; void Tes…

树立行业标杆,林清轩获“以油养肤开创者”市场地位认证

从0到1的创造&#xff0c;才能快速实现从1到100的裂变&#xff0c;这是亘古不变的商业逻辑。 6月25日&#xff0c;知名美妆国货品牌林清轩&#xff0c;获得了CIC灼识的市场地位确认书&#xff0c;确定“以油养肤开创者” 的地位。 近两年&#xff0c;以油养肤的概念逐渐兴起&am…

【Python】入门Python,你必须了解这些事

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言一、认识Python什么是Python&#xff1f;!Python的起源Python的特点简洁易读跨平台&#xff0c;可移植拥有强大的库和框架面向对象 Python的优缺点优点缺点 Python的应用环境扩…

【第六节】C/C++静态查找算法

目录 前言 一、搜索查找 二、查找算法 1. 线性查找&#xff08;Linear Search&#xff09; 2. 二分查找&#xff08;Binary Search&#xff09; 3. 插值查找&#xff08;Interpolation Search&#xff09; 4. 哈希查找&#xff08;Hash Search&#xff09; 5. Fibonacc…

气膜足球馆需要投资多少—轻空间

随着足球运动的普及和人们对健康生活方式的追求&#xff0c;建设高质量的足球场地成为许多城市和社区的需求。在众多建设方案中&#xff0c;气膜足球馆因其独特的优势&#xff0c;逐渐成为一种受欢迎的选择。轻空间将探讨建设气膜足球馆所需的投资情况&#xff0c;并分析其成本…

InnoDB 表空间2---系统表空间

系统表空间 了解完了独立表空间的基本结构&#xff0c;系统表空间的结构也就好理解多了&#xff0c;系统表空间的结构和独立表空间基本类似&#xff0c;只不过由于整个MySQL进程只有一个系统表空间&#xff0c;在系统表空间中会额外记录一些有关整个系统信息的页&#xff0c;所…

MySQL之主从同步、分库分表

1、主从同步的原理 MySQL主从复制的核心是二进制日志 二进制日志&#xff08;binlog&#xff09;记录了所有DDL语句和DML语句&#xff0c;但不包括数据查询&#xff08;select、show&#xff09;语句。 1.1、复制分三步 master主库在事务提交时&#xff0c;会把数据变更记录…