esp8266 I2C 实例解析及源码分析
一 前言
作为一个方案商兼芯片开发者,研究芯片和功能实现除了基本的工作需要,还有一层就是也变成了一种职业习惯。从芯片到方案,发现很多方案公司的人水平都比较堪忧,只会调用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 实例解析及源码分析的更多相关文章
- ReentrantLock解析及源码分析
本文结构 Tips:说明一部分概念及阅读源码需要的基础内容 ReentrantLock简介 公平机制:对于公平机制和非公平机制进行介绍,包含对比 实现:Sync源码解析额,公平和非公平模式的加锁.解锁 ...
- Django(49)drf解析模块源码分析
前言 上一篇分析了请求模块的源码,如下: def initialize_request(self, request, *args, **kwargs): """ Retu ...
- Django 之 restframework 解析器源码分析
解析器分类: 1. JSONPaser ----> 解析 JSON-serialized data (解析JSON序列化的数据) 2.FormParser ---->解析form 表单中 ...
- 实例解析Collections源码,Iterator和ListIterator
比如一个视频或文章有多个页面标签设置,我们在看一篇文章或一个视频时,底部有为你推荐栏目. 如何根据这个文章或视频的标签,来实现这个推荐栏目呢. public List<VideoInfoVo&g ...
- layoutInflater参数解析与源码分析
关于LayoutInflater方法,无论是在listview的适配器中,还是在动态添加view的时候,都会出现它的身影,最开始我在看<第一行代码>时,不知道这个方法实际的参数到底指的是什 ...
- Java Collections 源码分析
Java Collections API源码分析 侯捷老师剖析了不少Framework,如MFC,STL等.侯老师有句名言: 源码面前,了无秘密 这句话还在知乎引起广泛讨论. 我对教授程序设计的一点想 ...
- Android应用层View绘制流程与源码分析
1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...
- Retrofit源码分析(一)
1.基本用法 创建接口 public interface GitHubService { @GET("users/{user}/repos") Observable<List ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
随机推荐
- 【Linux】常用基本指令大汇总系列(篇一)【超详细的图解保姆教程】
常用基本指令大汇总系列(篇一) 大家好,欢迎大家来到我的博客.从今天开始,博主就要开启一个全新的系列了!Linux操作系统常用基本指令汇总系列,当大家按顺序学完这个系列,在Linux上编写基本的C语言 ...
- public private protected 的辨析
一. public 1.作为类内成员的访问修饰符时,由public修饰的成员数据或者成员函数可以在类外(即派生类内以及实例化的对象后)以及类内进行随意访问 可以看到public成员Data在类外是可访 ...
- 深入浅出 Application Insights--学习笔记
摘要 介绍如何将 Application Insights 用于生产上实践,并透过它发现/诊断问题.同时也会介绍如何将 Application Insighs 与其他体系相集成实现 Devops(与发 ...
- MySQL执行函数时报错:Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation 'find_in_set'
执行函数时报错: Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) f ...
- [数据结构] 串与KMP算法详解
写在前面 今天是农历大年初三,祝大家新年快乐! 尽管新旧交替只是一个瞬间,在大家互祝新年快乐的瞬间,在时钟倒计时数到零的瞬间,在烟花在黑色幕布绽放的瞬间,在心底默默许下愿望的瞬间--跨入新的一年,并不 ...
- Power BI 7 DAY
DAX 表达式(Data Analysis Expressions) DAX表达式的结果应用在数据透视表中 DAX表达式的结果作用于整列或者表中所有行 还需注意以下几点: a. 表名用"'' ...
- JS leetcode 删除排序数组中的重复项 题解分析
壹 ❀ 引 一日一题,今天的题目来自于leetcode26. 删除排序数组中的重复项,其实在之前我们已经做了一道类似的题目,可参考JS leetcode 移除元素 题解分析,关于本题描述如下: 给定一 ...
- 【Unity3D】Shader常量、变量、结构体、函数
1 源码路径 Unity Shader 常量.变量.结构体.函数一般可以在 Unity Editor 安装目录下面的[Editor\Data\CGIncludes\UnityShader]目录下查 ...
- el-dialog关闭后重置表单和校验提示
问题说明 最近测试反馈操作某新增/修改表单,点击[取消]或[关闭]窗口后再次点击[新增]或[修改]发现校验提示仍然存在! 问题原因 项目采用Vue+ElementUI,修改表单的窗口控件采用el-di ...
- java: -source 1.5 中不支持 diamond 运算符
1.问题说明 平常在用idea编译spring boot多模块项目时,老是无端提示: Error:(107, 55) java: -source 1.5 中不支持 diamond 运算符 (请使用 - ...