对于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的协议比较简单,因为两边都要约定好非常精确的时间。
所以一般来说,不可能使用引脚来模拟串口。
假设你用引脚来模拟串口的话,有可能会被中断程序打断呀,这样时间不就乱套了吗。
所以对于串口,都会使用芯片里面的串口模块。
我们配置好串口模块之后,想发送数据的话,把数据写入某个寄存器就可以了。
串口模块会把这些数据一位一位地发送出去。
串口模块会从接收引脚上检测信号,把那些数据一位一位的读进来,组合成8位数据之后,你就可以去读寄存器了得到数据了。
对于串口章节,比较有意思的就是环形缓冲区了
布置一下预习的视频和文档:
就像今天有个同学提的建议一样,预习的时候不仅要有视频,也要有文档。
文档和源码在这里:
对于项目一, 主要是给大家打基础,前面有C语言基础,有HAL开发的基础,
后面我就要讲怎么设计出一个比较优秀的程序,这个程序的框架怎样做才可以比较容易扩展,比较容易移植。
星期天的时候我们来做一个全天的视频!
直播, 把C语言彻底地掌握了。
大家关注一下公众号,公众号没写好,然后会有一些转发奖励。
因为我们的直播会持续差不多6个小时,大家最好来现场参与,否则你第2天想去看的时候,你不可能看6个小时的
答: 最短多长没看过资料,但是肯定不能太短,太短的话对方都检测不到这个信号。低电平最长多长,具体数值我也没去看,只要对方芯片不会自动进入休眠状态就可以。在I2C协议里面,这个时钟它并不需要很准确,根本就不需要准确。通信的双方,只要有一个说我还没准备好,他就可以把SCL拉低让对方等他一会。
答: 肯定会受到影响,delay时间容易被其他任务、容易被中断 延长。如果对方的设备,对于时序要求非常高的话,那就不能够用gpio来模拟。我们使用gpio模拟的时候,主要是因为没有I2C控制器,或者不够用。
答: 不加delay也可以的原因在于:
答: 10K左右。你要问我原因,我也说不上准确的原因,100K我也试过,1K、2K,10K、100K都行。只要是上拉就可以,这主要跟内部的晶体管,那个三极管或者MOS管有关系: 能让他导通就可以。
答: 有I2C控制器,就使用I2C控制器;没有的话才使用GPIO来模拟。
答: 实际上有硬件就用硬件,没硬件才用gpio来模拟,以前说是难用,应该是某一款芯片的I2C硬件有BUG。然后大家以讹传讹,一直以为有bug。我们去访问某些I2C设备时,如果不追求效率的话,用引脚来模拟,主要是因为懒,懒得去看I2C的寄存器操作。就比如我移植rt-smart到IMX6ULL的时候,要去操作触摸屏,一开始我们也懒得去看那些寄存器,直接用GPIO模拟好了。
答: 这个问题我暂时没法回答,在时序图里面,他并没有监测SDA。但是I2C,它有总线冲突的检测机制,现在我也没有办法去查资料,他肯定有回检的功能的。
答: 这个地址由程序来决定。温湿度传感器芯片内部那个程序,不是我们写的,是厂家写的,他也会去判断SDA线上传输的地址值是不是他的
答: 是的, I2C模块能够使用外部引脚,就几个,这要看芯片手册确认。
答: NACK 就是不回应。
就比如说你要写数据给从设备,从设备每收到一个数据都会给你回应。
但从机有时可能不会回应,比如回应时刚好发生了中断,此时主设备设计程序就需要考虑不理会ACK信号。
答: 送完第8位数据的时候,主机方一定要设置SDA为1,然后在第9个时钟读取引脚。
答: 看芯片手册。
答: 看我同事录制的视频里面讲的很清楚:
答: 意思是:Scl为低电平的时候,大家都暂停i2c操作。
也就是说我发送着发送着,唉,发现我有些忙不过来,我可以把scl拉低:让对方的从设备等待一会。
也可以反过来:接收方得到数据之后,发现,唉,这个数据比较难处理,我得花很长时间。他就可以把scl拉低让发送方等待一会。
答: 一个引脚被配置成输入时,他就是高阻状态 ,不会影响到外面的电路
答: 可以在第9个时钟,发送方不要去影响到别人,设置成高阻状态是可以的。
答: 你可以用环形缓冲区来接收字符串,这字符串要有一个分隔符。一般就是回车换行,用回车换行来区分:上一个字符串、下一个字符串。
答: I2C使用TTL电平时,就是在电路板上传输,也就厘米量级吧(比如10cm),TTL电平也是厘米量级的。将串口转化成RS232电平,则是米量级比如5米。
答: 在视频里面说指针只是一个习惯性的说法,应该就像你说的就是一个计数器。
答:
UART是一个很慢的设备。
我们使用窗口时,很多时候是突发地要传很多数据,比如:printf("hello, world");
。
为了提高效率,串口模块内部有一个FIFO缓冲区。
你可以一次性的写入多个字节的数据,然后就不管了,串口模块会从FIFO缓冲区里面把那些数据一个一个的取出来发送出去。
接收缓冲区也是类似的,别人可能一次性的给你发来很多个串口数据,串口模块把这些数据收起来之后,也放入FIFO缓冲区。
我们应用程序去读串口的时候,就可以一次性的读出多个数据。
回调函数,在串口中断里面用到:
你不想去修改中断的处理函数,但是你又想在发生中断时去做一些事情,
就可以提供一个回调函数,在串口的中断服务程序里面,它会去调用你提供的这个函数。
答: 串口的波特率最大也就是兆级别的。传出一个位需要200纳秒,波特率就是:1/200ns = 1000,000,000/200 = 5,000,000 波特率。一秒钟可以传输500万位。
问一个问题,波特率设置为115200,起始位是一位,数据位是8位,停止位是一位,没有校验位。115200的波特率,1秒钟可以传多少字节?
传输一个字节。一个字节是8位,但是需要一个起始位,一个停止位,也就是说传输一个字节需要10位。所以就是:115200/10 = 11520。
答: At指令很快,纯操作而已,没有问题的话10分钟就过了。
HAL库智能家居,主要是锻炼大家编程的习惯,怎么抽象出一个结构体,怎么让程序更容易移植、扩展。
可能大家会反馈的问题比较多,所以这个我暂且认为是4次课
Freeros入门大概各讲解多少课时? 写的参考书有11、12章,估计要讲7、8次课
答: 当然可以,环形缓冲区只是用来接收数据而已,防止数据丢失,你当然可以用来解析协议了。
答: I2C最大问题就是总线被占用, 就是scl老是为低电平。
解决这个问题的方法就是:我先用gpio来模拟,我就看看谁把这个引脚拉低了。
对于串口一般来说不会有什么问题,主要就是处理不过来导致数据丢失。
答: 中断程序里面要尽可能快地退出,所以我们一般来说就在串口中断程序里面,把数据放入环形缓冲区。
答: 一条指令它以回车换行结束,所以你接收到一个一个字符之后,判断一下是回车换行的话就表明接收到了完整的指令。
答: 在上一次课里面我们讲了,你可以去定义一个很大的数组,
然后去管理这个数组,你看这个堆不就在数据段里了吗。
答: 别被“重定向”给唬住了,就是:printf里调用到fputc函数。
这个函数你写串口的话就是打印到串口。
这个函数你写屏幕的话,就是打印到屏幕。
答: 自己写的:
答: 看个人喜欢,驼峰式 好看一点。
答: 应该是weak属性,你提供自己的代码的话,就用你自己的。
答: 我同事对LVGL了解比较深,我还没有去看LVGL。
我大概的看了一下,lvgl里面,他已经实现了"事件"驱动
能不能这样:lvgl本身作为一个任务,其他不涉及界面操作的,也拆分为任务。
答: 不应该这么做的,不建议直接去访问全局变量,而是放到一个函数里面去访问。
答: 在一个项目里面,底层驱动的占比不会很大的,所以你不用担心。
答: 这种你要去多练习,故意找一个不一样的模块来写程序,多写几个就明白。
答: 如果要在ESP上开发程序,用IDF。
答: 一般来说都不要暴露出变量,除非说这个变量很重要,是只读的 。就比如linux中的jiffies。
答: 双方要有一个参考的电位,就是共地。
答: 这模块要尽可能独立,不要去引用别人的东西。
答: 单片机是一个web服务器,通过浏览器来访问单片机。
答: 操作系统原理、编译原理 ,学的话是有帮助,但是停下来去学习他的话代价太大。