一  前言

作为一个方案商兼芯片开发者,研究芯片和功能实现除了基本的工作需要,还有一层就是也变成了一种职业习惯。从芯片到方案,发现很多方案公司的人水平都比较堪忧,只会调用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. 使用JAAS文件登陆kerberos(zookeeper)

    Kerberos 5 Configuration Since the SPNEGO mechanism will call JGSS, which in turns calls the Kerbero ...

  2. uni-app接口请求封装

    首先根目录下新建文件夹取名随意,这里我取名common(意为:常见的.共有的) 然后新建request.js文件,贴入以下代码 let server_url = ''; //请求根路径(服务器地址) ...

  3. 看New Bing回答世纪难题:女友和妈妈掉水里先救谁

    1.女友和妈妈掉水里先救谁 今天好奇想看看New Bing怎么回答这种世纪难题 结果New Bing非常聪明,反手建议我不要直接回答这个问题,而是换个角度哄女朋友,带着点不甘心,我继续追问它 New ...

  4. 洛谷P1045 麦森数。 快速幂算法以及固定位数的高精度乘法的优化

    P1045 [NOIP2003 普及组] 麦森数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 想法很简单,我们要做的就是两件事,求2^P-1的位数,求出2^P-1的最后500位数 ...

  5. 21.1 动态TLS--《Windows核心编程》

    应用程序通过调用一组4个函数来使用动态 TLS,这些函数实际上最经常为 DLL 所使用. 通常情况下,如果DLL使用 TLS,那么当它用 DLL_PROCESS_ATTACH 标志调用它的 DllMa ...

  6. 用superxmlparser.pas的XMLParseString----XML转Json注意

    了解XML转成Json时候用的时候多了个#号: ---------------------------------------------------------------------------- ...

  7. ASP.NET Core分布式项目实战(第三方ClientCredential模式调用)--学习笔记

    任务10:第三方ClientCredential模式调用 创建一个控制台程序 dotnet new console --name ThirdPartyDemo 添加 Nuget 包:IdentityM ...

  8. Python Rich:美化终端显示效果

    Rich库的功能就像它的名字一样,使Python编程更加丰富(rich),它帮助开发者在控制台(命令行)输出中创建丰富.多彩和具有格式化的文本. 本篇总结了如何使用Rich库让我们的命令行工具更加美观 ...

  9. ASCII编码的诞生:解决字符标准化与跨平台通信的需求

    在计算机的发展过程中,字符的表示和传输一直是一个重要的问题.为了实现字符的标准化和跨平台通信,ASCII(American Standard Code for Information Intercha ...

  10. JS Leetcode 179. 最大数 题解分析,sort a-b与b-a的区别,sort排序原理解析

    壹 ❀ 引 今天的题目来自LeetCode179. 最大数,题目描述如下: 给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数. 注意:输出结果可能非常大,所以你 ...