一  前言

作为一个方案商兼芯片开发者,研究芯片和功能实现除了基本的工作需要,还有一层就是也变成了一种职业习惯。从芯片到方案,发现很多方案公司的人水平都比较堪忧,只会调用api,根本不会看底层的代码实现逻辑。这次调试I2C挂载传感器之后。

作为一个课题,笔者就好好地研究了一下ESP8266的I2C的源码,没想到的是,还收获挺大的,具体什么收获,请看完代码分析再说吧。

二 实例分析

1 和很多主控芯片一样,esp8266的I2C接口也只是开放了master的底层,slave的底层没有代码实现部分。

这个也许是需求考量,因为这种芯片一般的都是挂载传感器,传感器都是I2C slave的,也为了方便挂载多个传感器。

2 主函数:

初始化: i2c_example_master_init() 这里主要是clk和sda的初始化和选择。

写函数: i2c_example_master_mpu6050_write 该函数主要是负责往特定寄存器中写入数据。

读函数: i2c_example_master_mpu6050_read 该函数主要负责从slave中读取数据。

特定传感器初始化函数:i2c_example_master_mpu6050_init 该函数主要负责传感器寄存器的初始化。

简简单单的四个函数,就把I2C的所有功能囊括了,真是惊叹乐鑫的代码整洁啊。

这个只要按照例子操作,硬件ok的情况下,一般都能读到数据了。挂载多个传感器的也只需要启动多个线程即可。

三 底层源码分析

其实,假如要想深刻理解I2C的协议的话,最好看一下底层代码,幸运的是,esp8266 的I2C的底层代码是提供了的。我简单的阅读之后,发现了所有的核心就在一个函数中:

该函数如下所示:

static void i2c_master_cmd_begin_static(i2c_port_t i2c_num)
{
i2c_obj_t *p_i2c = p_i2c_obj[i2c_num];
i2c_cmd_t *cmd;
uint8_t dat;
uint8_t len;
int8_t i, k;
uint8_t retVal; // This should never happen
if (p_i2c->mode != I2C_MODE_MASTER) {
return;
} while (p_i2c->cmd_link.head) {
cmd = &p_i2c->cmd_link.head->cmd; switch (cmd->op_code) {
case (I2C_CMD_RESTART): {
i2c_master_set_dc(i2c_num, 1, i2c_last_state[i2c_num]->scl);
i2c_master_set_dc(i2c_num, 1, 1);
i2c_master_wait(1); // sda 1, scl 1
i2c_master_set_dc(i2c_num, 0, 1);
i2c_master_wait(1); // sda 0, scl 1
}
break; case (I2C_CMD_WRITE): {
p_i2c->status = I2C_STATUS_WRITE; for (len = 0; len < cmd->byte_num; len++) {
dat = 0;
retVal = 0;
i2c_master_set_dc(i2c_num, i2c_last_state[i2c_num]->sda, 0); for (i = 7; i >= 0; i--) {
if (cmd->byte_num == 1 && cmd->data == NULL) {
dat = (cmd->byte_cmd) >> i;
} else {
dat = ((uint8_t) * (cmd->data + len)) >> i;
} i2c_master_set_dc(i2c_num, dat, 0);
i2c_master_wait(1);
i2c_master_set_dc(i2c_num, dat, 1);
i2c_master_wait(2); if (i == 0) {
i2c_master_wait(1); // wait slaver ack
} i2c_master_set_dc(i2c_num, dat, 0);
} i2c_master_set_dc(i2c_num, i2c_last_state[i2c_num]->sda, 0);
i2c_master_set_dc(i2c_num, 1, 0);
i2c_master_set_dc(i2c_num, 1, 1);
i2c_master_wait(1);
retVal = i2c_master_get_dc(i2c_num);
i2c_master_wait(1);
i2c_master_set_dc(i2c_num, 1, 0); if (cmd->ack.en == 1) {
if ((retVal & 0x01) != cmd->ack.exp) {
p_i2c->status = I2C_STATUS_ACK_ERROR;
return ;
}
}
}
}
break; case (I2C_CMD_READ): {
p_i2c->status = I2C_STATUS_READ; for (len = 0; len < cmd->byte_num; len++) {
retVal = 0;
i2c_master_set_dc(i2c_num, i2c_last_state[i2c_num]->sda, 0); for (i = 0; i < 8; i++) {
i2c_master_set_dc(i2c_num, 1, 0);
i2c_master_wait(2);
i2c_master_set_dc(i2c_num, 1, 1);
i2c_master_wait(1); // sda 1, scl 1
k = i2c_master_get_dc(i2c_num);
i2c_master_wait(1); if (i == 7) {
i2c_master_wait(1);
} k <<= (7 - i);
retVal |= k;
} i2c_master_set_dc(i2c_num, 1, 0);
memcpy((uint8_t *)(cmd->data + len), (uint8_t *)&retVal, 1);
i2c_master_set_dc(i2c_num, i2c_last_state[i2c_num]->sda, 0);
i2c_master_set_dc(i2c_num, cmd->ack.val, 0);
i2c_master_set_dc(i2c_num, cmd->ack.val, 1);
i2c_master_wait(4); // sda level, scl 1
i2c_master_set_dc(i2c_num, cmd->ack.val, 0);
i2c_master_set_dc(i2c_num, 1, 0);
i2c_master_wait(1);
}
}
break; case (I2C_CMD_STOP): {
i2c_master_wait(1);
i2c_master_set_dc(i2c_num, 0, i2c_last_state[i2c_num]->scl);
i2c_master_set_dc(i2c_num, 0, 1);
i2c_master_wait(2); // sda 0, scl 1
i2c_master_set_dc(i2c_num, 1, 1);
i2c_master_wait(2); // sda 1, scl 1
}
break;
} p_i2c->cmd_link.head = p_i2c->cmd_link.head->next;
} p_i2c->status = I2C_STATUS_DONE;
return;
}

仔细阅读这段代码你就会发现,这个函数是esp8266的I2C的全部精华部分。 通过四个命令:restart,write read  stop 很清楚的列出了I2C的时序。假如这个时候,你对着示波器查看这些指令,再修改一下延时值,估计很快你就明白了I2C是怎么的工作模式。

从这段代码来看,esp8266的I2C是使用软件模拟的。

四 总结

通过分析I2C的代码,很惊叹乐鑫的工程师的代码水平,笔者也在几个芯片公司待过,说实在的,感觉代码规范程度,只有st才能和乐鑫一决高下。这整洁代码的背后,是工程师静下心来日复一日的努力的完善的结果,中间经过多少次迭代,估计只有做这件事情的工程师才清楚。唯有心平气和,不急不躁的高手才能做到。真心地认为,想学习嵌入式的同学,可以把乐鑫的代码当做模仿的对象了。绝对是一份非常好的教材。

esp8266 I2C 实例解析及源码分析的更多相关文章

  1. ReentrantLock解析及源码分析

    本文结构 Tips:说明一部分概念及阅读源码需要的基础内容 ReentrantLock简介 公平机制:对于公平机制和非公平机制进行介绍,包含对比 实现:Sync源码解析额,公平和非公平模式的加锁.解锁 ...

  2. Django(49)drf解析模块源码分析

    前言 上一篇分析了请求模块的源码,如下: def initialize_request(self, request, *args, **kwargs): """ Retu ...

  3. Django 之 restframework 解析器源码分析

    解析器分类: 1. JSONPaser ----> 解析 JSON-serialized data (解析JSON序列化的数据) 2.FormParser ---->解析form 表单中 ...

  4. 实例解析Collections源码,Iterator和ListIterator

    比如一个视频或文章有多个页面标签设置,我们在看一篇文章或一个视频时,底部有为你推荐栏目. 如何根据这个文章或视频的标签,来实现这个推荐栏目呢. public List<VideoInfoVo&g ...

  5. layoutInflater参数解析与源码分析

    关于LayoutInflater方法,无论是在listview的适配器中,还是在动态添加view的时候,都会出现它的身影,最开始我在看<第一行代码>时,不知道这个方法实际的参数到底指的是什 ...

  6. Java Collections 源码分析

    Java Collections API源码分析 侯捷老师剖析了不少Framework,如MFC,STL等.侯老师有句名言: 源码面前,了无秘密 这句话还在知乎引起广泛讨论. 我对教授程序设计的一点想 ...

  7. Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

  8. Retrofit源码分析(一)

    1.基本用法 创建接口 public interface GitHubService { @GET("users/{user}/repos") Observable<List ...

  9. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  10. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

随机推荐

  1. CF1764H Doremy's Paint 2 题解

    题目链接:CF 或者 洛谷 高分题,感觉挺有意思的题,值得一提的是这个题的 \(1\) 和 \(3\) 版本却是两个基础题. 一开始以为跟这道差不多:P8512 [Ynoi Easy Round 20 ...

  2. P3730 曼哈顿交易 题解

    题目链接:曼哈顿交易 比较容易想的题,观察下首先不带修改,考虑维护的东西:次数作为权值,这玩意很显然很难在线维护,考虑下离线算法.看到这种和次数有关的权值,典型的单点加入和删除是非常好找到变化的,那么 ...

  3. 2024-02-03:用go语言,你有 k 个背包。给你一个下标从 0 开始的整数数组 weights, 其中 weights[i] 是第 i 个珠子的重量。同时给你整数 k, 请你按照如下规则将所有

    2024-02-03:用go语言,你有 k 个背包.给你一个下标从 0 开始的整数数组 weights, 其中 weights[i] 是第 i 个珠子的重量.同时给你整数 k, 请你按照如下规则将所有 ...

  4. 单片机 IAP 功能基础开发篇之APP升级(一)

    引言 目的 主要介绍单片机 IAP 开发的设计思路,如何不使用下载烧录器的方式对单片机的程序进行升级,升级区域包括 bootloader 和用户程序的升级,升级方式有 UASRT 通信.CAN 通信和 ...

  5. Iot学习笔记记录

    前言 2024.1.13 沙青图书馆 甚至一开始打成了2023年.各位新年快乐.有时间会写下2023的年度总结.不过在此要提前开一个博客,记录一下接下来学习Iot安全的记录了.实在是再不学就要被学弟学 ...

  6. Nginx 简介 转载:https://www.cnblogs.com/wztshine/p/16162640.html

    Nginx 安装环境 安装 gcc 安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,则需要安装: yum install gcc-c++ 安装 PCR ...

  7. 静态RMQ处理方式合辑

    这里汇集了所有我知道的静态区间最大值做法. \(O(n)\) 预处理,\(O(n)\) 回答. 每一次询问暴力处理即可. \(O(n^2)\) 预处理,\(O(1)\) 回答. 预处理出所有的答案. ...

  8. 蔚来杯2022牛客暑期多校训练营3 AC

    比赛链接 A 题解 知识点:LCA. 队友写的,俺不会qwq.预处理出关键点序列的在树A B上的前缀LCA和后缀LCA,枚举去掉的关键节点并使用前后缀LCA算出剩余节点的LCA比较权值即可. 时间复杂 ...

  9. 让 K8s 更简单!8款你不得不知的 AI 工具-Part 2

    在 part 1 中,我们探讨了目前比较流行的四种 OpenAI 开源工具.在今天的 part 2 中我们将探究另外三种不同的 OpenAI 开源工具并介绍一些与 Appilot 相关的内容. Kub ...

  10. win32 - ReadDirectoryChangesW的使用

    任务:创建一个进程,并在进程内创建一个文本,再创建另一个进程来监控第一个进程内的文本变化 //Process 1 #include <windows.h> #include <ios ...