【RTOS训练营】I2C和UART知识和预习安排 + 晚课提问

一、I2C

对于I2C,在芯片内部有I2C控制器。

他的结构如下图:

我们配置好I2C控制器之后,去读写寄存器就可以了。

但是在我们的视频里,我们使用GPIO引脚来模拟I2C。

我们先来看看 I2C的协议,这是硬件连接图:

一个主芯片,多个从芯片。

假设我们的arm是主芯片,他要去访问这条i2c总线上面的多个设备,他要怎么做呢?

我讲i2c协议时,举了一个体育老师发球的例子:

我们来看一下i2c总线上的数据是怎么传输的:

首先提醒对方:发出S信号(S为Start,开始信号)

怎么规定S信号呢?

你看平时这两条引脚都是高电平,然后SDA从高变低,这就是S信号。

我们使用i2c控制器来写程序的话,就是:写某个寄存器的某一位,他就会自动的帮你发出S信号。

我们用gpio来模拟I2C的话怎么做呢?

请添加图片描述

针对这段代码,我们来画个图:

在i2c协议里面, S信号的低电平维持多长时间并没有规定,

只要知道两个引脚一开始的时候都是高电平,然后SDA来一个低电平,这就是S信号。

这个体育老师要发球给某一个学生,他得点名,这就是发出地址。

老师是想把球发给他,而不是让这学生传回球,所以还有个方向。

S信号之后,主设备要发出设备地址,并且发出数据的传输方向

设备地址是7位,数据传输方向是1位,加起来就是8位。

主设备就像老师一样,老师点名学生要回答:到。

主设备发出设备地址、发出数据的读写方向,如果有这个设备存在的话,他要回应。怎么回应呢?

主设备发出7位的地址、1位的方向之后,马上释放SDA,也就是说这个时候SDA是高电平。

如果对方是被存在的话,他就会把SDA拉低。主设备会检测sda,他发现:哦,有人把这个SDA拉低了,那么这个设备肯定是存在的。

讲到这里,这就涉及到I2C传输的精妙之处了。

大家可以看到在这个传输过程中,发送方控制SDA的时间有8个时钟,在第9个时钟是由接收方来控制的。

也就是说这个SDA他一会由发送方控制,一会由接收方控制。

如果大家的时间都管理得很精确,这个时间你控制;另外一个时间,我控制。

大家的时间没有交叉,这不会有任何问题。

但是万一大家的时间有交叉怎么办?

就比如说发送方把这个引脚设置成高电平,
接收方同一时间我把这个引脚设置成低电平,
同一个引脚,一方写高,另一方写低,它不就冲突了吗?搞不好会烧坏引脚。

怎么办呢?

这就引入了开漏电路,我来画一个图:

怎么避免冲突呢?

那就是如果有一个人想把这个引脚设置成高电平的话,他就什么都不做,把决定权交给别人。

什么意思呢?

发送方想让这个引脚发出低电平的时候,在发送方内部,它就相当于把这个引脚接到地。

那发送方想让这个引脚发出高电平的时候,他怎么做?

再看这图,我在发送方,我想让这个引脚发出高电平。

我有两种方法:

1.让这引脚在内部接到电源

但是这种方法我们刚才说过了,有可能会跟别人发生冲突。

2.就像上面的图一样,使用第2种方法,
如果他想发出高电平,他就把这个引脚和芯片内部的模块给断开。

他说:我想发出高电平,你又怕我跟别人冲突,那我就不管了,爱怎么滴就怎么滴

他不管了,那么这个电平是高还是低?

上拉电阻决定。

请添加图片描述

发送方想发出高电平,又怕他跟别人冲突,
于是他就啥都不管了,这时候电平由上拉电阻决定。

发送方想发出高电平,接收方突然想发出低电平,会发生什么事呢?

我们画个图:

图里面蓝色的就是接收方,他想发出低电平,
在芯片内部,它就相当于把这个引脚接到地。

这个引脚,一边要发出1,另外一边要发出0,它最终的结果是啥?

就是低电平。

有没有冲突呀?发送方你啥都不管了。
引脚电平:由上拉电阻 和 另外一方决定。

在这种操作下,不会出现烧坏电路的情况。

我们在图里面,用红色叉号表示说断开这个引脚。

在芯片内部它是使用三极管来实现的,我们再来画一个图:

再看看图里面左边。
当他想发出高电平的时候,经过一个反向器进入到三极管,三极管截止,就相当于这个SDA跟左边芯片断开。

当主芯片想发出低电平的时候,经过一个反向器进入到三极管,三极管导通,就相当于SDA接地。

主机发出S信号,发出设备地址和方向,得到回应之后:就可以来发出、或者读取数据了。

发出什么数据,读取什么数据,每个芯片的含义都不一样。

我们随便选一个存储芯片来看一下:

既然是存储芯片,我们就可以写数据,也可以从里面读到数据。

我要写数据时,写存储空间的哪个地址?写什么数据?

所以在图里面你可以看到:红色1的地方就是存储地址,红色2的地方就是数据。

对于写操作,地址值和数据值,都是主设备备发给存储设备。

对于读操作呢?你要读哪一个存储地址?这个地址应该由主设备发给从设备。

读到的数据,应该由从设备返回给主设备。

所以读存储芯片的数据,会涉及两个I2C操作,一个是写地址,另外一个是读数据。

在上面的图里你可以看到读操作,会涉及两个S信号。

第1个S信号之后是写操作,第2个S信号之后是读操作。

我们再来看看oled:

S信号之后是设备地址,然后是一个控制字节,接下来就是数据。

这个在视频里面都讲得比较清楚了,简单举几个例子:

对oled,我们可以给他发命令,就比如说启动它,关闭它:
在上面这个函数发的那个0x00就表示说我要发一个命令给你了,发的命令到底是什么?就是cmd参数。

那要发数据的话怎么发?在下面这个函数,它要先发出0x40这个值,然后才是数据本身data。

这些设备的i2c数据含义, 都要去看这些设备的芯片手册。

二、UART

现在我们来讲UART,UART大家经常用。

我们画一个图里面只有一条线:

左边怎么给右边,怎么通过一条线发出数据?

关键在于每一位维持的时间,都是双方事先约定好,也就是波特率

一开始的时候这个引脚是高电平,他想发送数据了,他把这个引脚设置为低电平维持一段时间。这就是通知对方,说:喂,我准备发出数据了

发送方这边,就接下来在每一段时间里发出一位数据。

接收方这边,就是在每一段时间里来读取引脚,得到一位数据

UART的协议比较简单,因为两边都要约定好非常精确的时间。

所以一般来说,不可能使用引脚来模拟串口。

假设你用引脚来模拟串口的话,有可能会被中断程序打断呀,这样时间不就乱套了吗。

所以对于串口,都会使用芯片里面的串口模块。

我们配置好串口模块之后,想发送数据的话,把数据写入某个寄存器就可以了。

串口模块会把这些数据一位一位地发送出去。

串口模块会从接收引脚上检测信号,把那些数据一位一位的读进来,组合成8位数据之后,你就可以去读寄存器了得到数据了。

对于串口章节,比较有意思的就是环形缓冲区了

三:预习安排

布置一下预习的视频和文档:

请添加图片描述

就像今天有个同学提的建议一样,预习的时候不仅要有视频,也要有文档。

文档和源码在这里:

请添加图片描述

对于项目一, 主要是给大家打基础,前面有C语言基础,有HAL开发的基础,
后面我就要讲怎么设计出一个比较优秀的程序,这个程序的框架怎样做才可以比较容易扩展,比较容易移植。

星期天的时候我们来做一个全天的视频!

直播, 把C语言彻底地掌握了。

大家关注一下公众号,公众号没写好,然后会有一些转发奖励。

因为我们的直播会持续差不多6个小时,大家最好来现场参与,否则你第2天想去看的时候,你不可能看6个小时的

四、 晚课学员提问

1. 问: I2C开始信号中,SCL低电平时间最短多少?

答: 最短多长没看过资料,但是肯定不能太短,太短的话对方都检测不到这个信号。低电平最长多长,具体数值我也没去看,只要对方芯片不会自动进入休眠状态就可以。在I2C协议里面,这个时钟它并不需要很准确,根本就不需要准确。通信的双方,只要有一个说我还没准备好,他就可以把SCL拉低让对方等他一会。

2. 问: 在使用GPIO模拟I2C中,延时会不会受到其他中断任务的影响?

答: 肯定会受到影响,delay时间容易被其他任务、容易被中断 延长。如果对方的设备,对于时序要求非常高的话,那就不能够用gpio来模拟。我们使用gpio模拟的时候,主要是因为没有I2C控制器,或者不够用。

3. 问: 在使用GPIO模拟I2C中, 不要delay可以吗?

答: 不加delay也可以的原因在于:

请添加图片描述

4. 问: 在使用GPIO模拟I2C中,上拉电阻多大合适?

答: 10K左右。你要问我原因,我也说不上准确的原因,100K我也试过,1K、2K,10K、100K都行。只要是上拉就可以,这主要跟内部的晶体管,那个三极管或者MOS管有关系: 能让他导通就可以。

5. 问: GPIO模拟和芯片内部控制哪个好?实际工作中用哪种?

答: 有I2C控制器,就使用I2C控制器;没有的话才使用GPIO来模拟。

6. 问: 为什么现在好多芯片都有硬件iic 大家都不用 ,说不稳定,非要用gpio模拟?

答: 实际上有硬件就用硬件,没硬件才用gpio来模拟,以前说是难用,应该是某一款芯片的I2C硬件有BUG。然后大家以讹传讹,一直以为有bug。我们去访问某些I2C设备时,如果不追求效率的话,用引脚来模拟,主要是因为懒,懒得去看I2C的寄存器操作。就比如我移植rt-smart到IMX6ULL的时候,要去操作触摸屏,一开始我们也懒得去看那些寄存器,直接用GPIO模拟好了。

7. 问: I2C中,主机发完S信号,要回检吗? 如果有设备没有准备好,是不是还不能立即发送地址?

答: 这个问题我暂时没法回答,在时序图里面,他并没有监测SDA。但是I2C,它有总线冲突的检测机制,现在我也没有办法去查资料,他肯定有回检的功能的。

8. 问: 如果2个进行IIC通讯,怎么确定设备的地址?

答: 这个地址由程序来决定。温湿度传感器芯片内部那个程序,不是我们写的,是厂家写的,他也会去判断SDA线上传输的地址值是不是他的

9. 问: 硬件I2C是不是限制管脚的?

答: 是的, I2C模块能够使用外部引脚,就几个,这要看芯片手册确认。

10. 问: 为什么有的需要NACK 有的需要 ACK?

答: NACK 就是不回应。
就比如说你要写数据给从设备,从设备每收到一个数据都会给你回应。
但从机有时可能不会回应,比如回应时刚好发生了中断,此时主设备设计程序就需要考虑不理会ACK信号。

11. 问: 如果最后一位发的是0,确认也是0,那么主设备怎么判断从设备到底有没有确认?

答: 送完第8位数据的时候,主机方一定要设置SDA为1,然后在第9个时钟读取引脚。

12. 问: 怎么确定有没有开漏?

答: 看芯片手册。

13. 问: Pushpull会讲到吗?

答: 看我同事录制的视频里面讲的很清楚:

请添加图片描述

14. 问: 图里的Clock line held low by receiver and/or transmitter是什么意思?

答: 意思是:Scl为低电平的时候,大家都暂停i2c操作。

也就是说我发送着发送着,唉,发现我有些忙不过来,我可以把scl拉低:让对方的从设备等待一会。

也可以反过来:接收方得到数据之后,发现,唉,这个数据比较难处理,我得花很长时间。他就可以把scl拉低让发送方等待一会。

15. 问: Input是用的什么电路不影响sda的?

答: 一个引脚被配置成输入时,他就是高阻状态 ,不会影响到外面的电路

16. 问: 老师,第九个时钟设成高阻态可以吗?

答: 可以在第9个时钟,发送方不要去影响到别人,设置成高阻状态是可以的。

17. 问: 有没有讲解串口接收字符串的?

答: 你可以用环形缓冲区来接收字符串,这字符串要有一个分隔符。一般就是回车换行,用回车换行来区分:上一个字符串、下一个字符串。

18. 问: 串口通信和IIC通信的距离一般多长?

答: I2C使用TTL电平时,就是在电路板上传输,也就厘米量级吧(比如10cm),TTL电平也是厘米量级的。将串口转化成RS232电平,则是米量级比如5米。

19. 问: 环行缓冲区定义的不是指针,为什么视频里按地址算,更像计数器。

答: 在视频里面说指针只是一个习惯性的说法,应该就像你说的就是一个计数器。

20. 问: 老师,讲解下FIFO和回调函数?

答:

UART是一个很慢的设备。

我们使用窗口时,很多时候是突发地要传很多数据,比如:printf("hello, world");

为了提高效率,串口模块内部有一个FIFO缓冲区。

你可以一次性的写入多个字节的数据,然后就不管了,串口模块会从FIFO缓冲区里面把那些数据一个一个的取出来发送出去。

接收缓冲区也是类似的,别人可能一次性的给你发来很多个串口数据,串口模块把这些数据收起来之后,也放入FIFO缓冲区。

我们应用程序去读串口的时候,就可以一次性的读出多个数据。

回调函数,在串口中断里面用到:

你不想去修改中断的处理函数,但是你又想在发生中断时去做一些事情,

就可以提供一个回调函数,在串口的中断服务程序里面,它会去调用你提供的这个函数。

21. 问: 串口里面,双发约定时间,例如传输1个位的时间是200ns,波特率是多少,硬件串口波特率最大支持多大?

答: 串口的波特率最大也就是兆级别的。传出一个位需要200纳秒,波特率就是:1/200ns = 1000,000,000/200 = 5,000,000 波特率。一秒钟可以传输500万位。

问一个问题,波特率设置为115200,起始位是一位,数据位是8位,停止位是一位,没有校验位。115200的波特率,1秒钟可以传多少字节?

传输一个字节。一个字节是8位,但是需要一个起始位,一个停止位,也就是说传输一个字节需要10位。所以就是:115200/10 = 11520。

22. 问: 老师,两个假期想好好学习单片机裸机程序,用103那款开发板 1、预告下国庆前的课程安排 2、AT 、HAL库智能家居,Freeros入门大概各讲解多少课时?

答: At指令很快,纯操作而已,没有问题的话10分钟就过了。

HAL库智能家居,主要是锻炼大家编程的习惯,怎么抽象出一个结构体,怎么让程序更容易移植、扩展。
可能大家会反馈的问题比较多,所以这个我暂且认为是4次课

Freeros入门大概各讲解多少课时? 写的参考书有11、12章,估计要讲7、8次课

23. 问: 老师,环形缓冲区可以用来协议解析吗?

答: 当然可以,环形缓冲区只是用来接收数据而已,防止数据丢失,你当然可以用来解析协议了。

24. 问: 能讲讲你IIC和串口调试过程中常遇到的问题吗?这些问题的调试思路是什么?遇到问题了怎么划分是硬件问题还是软件问题??

答: I2C最大问题就是总线被占用, 就是scl老是为低电平。

解决这个问题的方法就是:我先用gpio来模拟,我就看看谁把这个引脚拉低了。

对于串口一般来说不会有什么问题,主要就是处理不过来导致数据丢失。

25. 问: 请教老师一个问题,如果进入串口中断后,进入中断服务函数的话,然后在中断服务函数中加入接收数据处理函数的话,会不会影响其他的函数,或者跑飞。

答: 中断程序里面要尽可能快地退出,所以我们一般来说就在串口中断程序里面,把数据放入环形缓冲区。

26. 问: ESP8266本来是一个MCU,用AT固件方便开发。那这样我能自己写一个类似AT固件一样的程序,然后处理指令吗,一般数据是怎么处理的呢,因为串口是一个个位这样收发数据的?

答: 一条指令它以回车换行结束,所以你接收到一个一个字符之后,判断一下是回车换行的话就表明接收到了完整的指令。

27. 问: C语言malloc创建的只能在堆么?C++中new出来的对象能通过重载new的技巧使对象创建在静态区或者栈区,C语言有什么技巧可以这样做么? 如果有的话,可以在周日直播讲讲技巧?

答: 在上一次课里面我们讲了,你可以去定义一个很大的数组,
然后去管理这个数组,你看这个堆不就在数据段里了吗。

28. 问: 老师,printf重定向那个是什么原理?

答: 别被“重定向”给唬住了,就是:printf里调用到fputc函数。

这个函数你写串口的话就是打印到串口。

这个函数你写屏幕的话,就是打印到屏幕。

29. 问: 这个fputc哪里指定的?C库吗?换一个平台之后还一样是fputc吗??

答: 自己写的:

30. 问: 老师,我想问下变量和函数命名的规范,驼峰式和下划线式哪个好?是不是针对不同的场景会采用不同的方式??

答: 看个人喜欢,驼峰式 好看一点。

31. 问: fputc 是不是类似于HAL库里的回调,自己写同名的就会用自己写的?

答: 应该是weak属性,你提供自己的代码的话,就用你自己的。

32. 问: 老师,如果智能家居显示不使用OLED,而是电容触摸屏使用LVGL来管理,实时性更强了,那工程是怎么管理,项目是怎么实现的 我能裸机移植LVGL使用,但是我不知道怎么结合FreeRTOS,一堆东西脑子乱成一团了

答: 我同事对LVGL了解比较深,我还没有去看LVGL。
我大概的看了一下,lvgl里面,他已经实现了"事件"驱动

能不能这样:lvgl本身作为一个任务,其他不涉及界面操作的,也拆分为任务。

33. 问: 另外,教学视频里面,环形缓冲区extern 了结构体test_buffer,这个变量很多地方都被调用了哦,如果我要改点东西,直接牵一发而动全身了,有什么好办法吗?

答: 不应该这么做的,不建议直接去访问全局变量,而是放到一个函数里面去访问。

34. 问: 如果裸机层面的底层驱动没有弄的很清楚的话。比如can,网口等。直接学操作系统会不会有点更不上产品级的项目?

答: 在一个项目里面,底层驱动的占比不会很大的,所以你不用担心。

35. 问: 老师,视频hal库的i2c和oled驱动程序看的很吃力怎么办,看是能看懂但是建立不起体系?

答: 这种你要去多练习,故意找一个不一样的模块来写程序,多写几个就明白。

36. 问: 老师,对于ESP的wifi/bt二合一产品,是用AT开发比较多还是IDF?另外使用IDF开发是不是很难?

答: 如果要在ESP上开发程序,用IDF。

37. 问: 老师,如何真正做到模块化,每个模块化功能移植下就能用,每次移植的时候会有很多的其他变量都要屏蔽掉,是不是要少用extern?

答: 一般来说都不要暴露出变量,除非说这个变量很重要,是只读的 。就比如linux中的jiffies。

38. 问: 老师,有一个基本概念不懂,用ttl转usb模块接电脑的时候,为什么单片机的地要和ttl转usb模块接一起。

答: 双方要有一个参考的电位,就是共地。

39. 问: 老师,如何真正做到模块化,每个模块化功能移植下就能用,每次移植的时候会有很多的其他变量都要屏蔽掉,是不是要少用extern?

答: 这模块要尽可能独立,不要去引用别人的东西。

40. 问: 想请问一下,网页控制单片机怎们实现的,是HTTP转串口吗? 纯小白,想先去学习一下

答: 单片机是一个web服务器,通过浏览器来访问单片机。

41. 问: 如何 提高 数据结构 算法 设计模式 操作系统原理 编译原理 软件架构 这些内功?

答: 操作系统原理、编译原理 ,学的话是有帮助,但是停下来去学习他的话代价太大。