目录
前言
针对GPIO、I2C、SPI的讲解,对这些通讯方式有个大致框架,对日后的开发会有帮助,了解因为所以然。
一. GPIO
1. 什么是GPIO?
GPIO全拼叫General Purpose Input Output(通用输入输出)简称IO口也叫总线扩展器,GPIO口是由引脚,功能寄存器组成,不同的架构中的GPIO封装不同,所使用的引脚数与寄存器数不同,具体可以参考芯片手册里的GPIO篇。
GPIO的作用是用来控制连接在此GPIO口上的外设,我们一般通过观察原理图找到当前板子的GPIO口引出在哪个口上或者排针上,我们把我们的外设接到上面去就可以通过GPIO与这个外设进行交互控制,在驱动层我们通过读写GPIO口中的功能寄存器来改变连接在此GPIO上的外设状态。
2. GPIO组成原理
GPIO原理图如下:
取自STM32F7系列,不同架构的GPIO口封装不同,这里仅用于当前示例
从上面原理图可以看出STM32F7系列GPIO是由三个寄存器以及“TTL肖特基触发器、二极管、P-MOS管和N-MOS管”组成
3. GPIO工作原理
电子器件的作用:
器件名 | 作用 |
---|---|
输出数据寄存器 | 此寄存器的值会通过输出驱动器输出,其中指定位用于决定使用P-MOS还是N-MOS,有些位表示输出高低电平,如寄存器是4位的,其中0-2位代表输出模式,第3位表示输出电平信号,数字信号1则高电平,数字信号0则低电平 |
P-MOS | Vgs(电荷)小于一定的值就会导通,利用这个特性和实现推挽与开漏输出 |
N-MOS | Vgs(电荷)大于一定的值就会导通,利用这个特性和实现推挽与开漏输出 |
保护二极管 | IO引脚上下两边两个二极管用于防止引脚外部过高、过低的电压输入。当引脚电压高于VDD时,上方的二极管导通;当引脚电压低于VSS时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁,即只有电流不高于VDD也不低于VSS时才能正常通过 |
TTL肖特基触发器 | 用于将电流信号转化为模拟数字信号0或1,当使用模拟输入时不会通过触发器,模拟输入是指不通过内部转换器,需要外部外接adc进行ad采样转换 |
每个GPIO口都有一组引脚连接,并且这组引脚,如PA-PG(引脚编号)连接到GPIOA中,其中有一个引脚的作用是输出,说的直接一点GPIO口是一个可编程的引脚,我们通过对GPIO口里的寄存器进行配置,让CPU对特定的引脚输出高电平。
上面涉及到一个知识点,推挽与开漏输出,这两个模式是使用P-MOS与N-MOS两个晶体管实现的
P-MOS与N-MOS的区别
P-MOS是接了VDD(正极电源),所以它拥有输出高电流的能力,所以给P-MOS高电平就可以导通P-MOS管,因为接了电源线的原因一旦导通了就可以让电源线里的工作电压流通,而N-MOS上面接了VSS,若给高电平是无法导通的,因为它没有接电源线是无法输出高电平的,如果给了N-MOS高电平即便流通了因为没有VDD电源线它也无法正常输出电流,若给了低电平则会流向VSS接地处,所以N-MOS只能给低电平才有效,若想N-MOS驱动可以外接上拉电阻。
所以这里输出低电流的话输出到N-MOS晶体管的引脚上,那么始终输出的是接地部分,可以有效降低材料的消耗率。
这里说一下高电平与低电平在电路里的区分:
这里以TTL电路为列,高电平>3.4V,低电平<0.2V,所以即便N-MOS没有输出任何电流,它也是低电平。
使用这两个器件能够在输出高低电平时保证材料的消耗率降低,如果使用一个线路输出高低电平的话会损耗会提升,N-MOS会一直接地,所以输出端其实一点电流都没有,这会极大降低消耗率。
MOS是场效应管,它有两种类型,P沟道与M沟道,至于哪种类型取决于MOS源接在VDD还是VSS,接在VDD就是P沟道,接在VSS就是M沟道
推挽输出
连接在输出控制电路里的数字器件,可以输出高电平与低电平,输出数字信号0时N-MOS导通,P-MOS高阻,输出数字信号1时P-MOS导通,N-MOS高阻抗
开漏输出
只能输出低电平,输出数字0时N-MOS导通,P-MOS高阻,无法输出高电平,若需要输出高电平需要在N-MOS晶体管后的引脚上接一个上拉电阻,将所有不确定的信号变成高电平,它只能从N-MOS这条线路上流过,所以就导致了它不能输出高电平
输出电路会获取输出数据寄存器里当前的模式位是什么,如推挽或开漏,若推挽则用电路比较器进行电流比较来确定输出到P-MOS还是N-MOS,若是开漏模式则根据N-MOS导通特性以及当前数字信号的值0/1,来输出对应的电流导通,同时电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。
开漏负载能力较强,因为有上拉电阻,所以一般长时间的设备会用开漏输出
二. I2C
1. 什么是I2C?
I2C全拼Inter Integrated Circuit,简称IIC或I2C,是由Philips公司开发的两线时串行总线,用于SOC与外设的连接通讯,它只需要两根线就能实现I2C的通讯,采用主从模式,主的一方可以读写数据,而从的一方只能等待被读写。从的一方没有主动权。
I2C是双向通讯的,由两根线完成,分别是:SDA(串行数据线)、SCL(串行时钟线),接口输出模式为开漏输出,其总线接口已经集成到SOC内部,我们只需要通过原理图找到它的接口,在用外设的杜邦线或者其它方法连接到此接口上就可以实现I2C的通讯。
SDA与SCL都外接了上拉电阻,所以当SDA空闲时刻输出的永远是高电平,它对外设也有一定要求,要求外设的输出模式也是开漏输出,因为这跟它本身的电路实现有关,若两个电路接口模式不一则是无法完成正常通讯的。
其中I2C里的上拉电阻也不是随便用的,因为电阻值越高意味着信号拉高周期越长,那么通讯周期时间就越高,速率就下来了。
I2C为每个设备提供了一个地址,可以通过这个地址找到不同的设备,来表明不同的设备,只有从设备收到是自己的地址时才响应。
因为连接在I2C上的可能有很多个设备,从设备需要设置自己的地址,主设备不用,因为主设备是不会被从设备读写的,主设备是负责读写其它设备的。
通讯时会有一个起始数据,这个数据是9个bit位,前7位是从设备地址,最后一位是方向(0/1(读或写))
这也就表明从设备的地址不能超过7个bit
2. I2C组成原理
架构图:
SMBA线用于SMBUS 的警告信号,I2C 通讯没有使用,可以忽略
I2C内部是有自己的控制单元的,用于处理I2C的通讯,可以说是一个小芯片,它不属于CPU,它只是被集成SOC里(看架构,有的架构是没有的)
3. I2C的特性
- 仅需两根线
- 接口集成在PCB内部
- 因为是开漏输出,电流消耗较小(高电流由上拉电阻完成,通常情况下器件不需要输出高电平所以这块功耗较小)
- 世界级标准,大多数开发板都支持
- 一对多,一根线接多跟外设,通过地址区分,节省排版空间
4. I2C的通讯模式
双向传输总线:
模式 | 速率(kbit/s) |
---|---|
标准模式(Standard-mode) | 100 |
快速模式(Fast-mode) | 400 |
快速模式+(Fast-mode Plus) | 1M |
高速模式(High-speed mode) | 3.4M |
单向传输总线:
模式 | 速率(kbit/s) |
---|---|
超快速模式(Ultra Fast-mode) | 5M |
这些模式是根据速率来决定的,也就是说我们将I2C通讯速率设置成与上面对应的速率那么就是处于这个模式当中,其中说是这么快其实是有延迟的,因为要流过上拉电阻,这个取决于上拉电阻的阻值,因为信号在经过上拉电阻时会有一个周期变化,这个周期变化区别于阻值。
设置方法:
我们需要根据当前的PCLK时钟频率来计算的,这和设置PWM占空比类似,都需要经过计算然后设置I2C里的CCR时钟控制寄存器的值,因为如果频率不同是无法正常工作的。
如:时钟频率是66MHZ,预分频值是65MHZ,系数是1/2那么计算公式如下:
时钟频率=PCLK/((预分频值+1))/分频系数
PCLK我们得知是66,分频值我们设置的是65,分频系数是1/2也就是除于2
所以公式:66/(65+1)/2 =0.5
在将0.5转化为HZ的单位:0.5*1000=500KHZ,这里还有一个公式,就是算HZ转秒,赫兹的倒数就是它的秒这个公式:(1/500)=0.002ms,1毫秒=1000微秒,0.002毫秒等于2微秒,通过这样的算法公式就可以得知我们现在的频率是每2微秒工作一次
我们想让它每0.5毫秒工作一次,所以TCNTB的值=500毫秒/当前微秒频率2
500/2=250,所以TCNTB的值应该为250,当然你也可以这也算,2微秒一次,1毫秒=1000微秒,0.5毫秒就等于500微秒,500微秒/2微秒=250,就得出经过250次2微秒后就到达了500微秒,而500微秒就等于0.5毫秒
5. I2C的通讯过程
通讯步骤:
- 开始信号:SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件
- 结束信号:SCL为高电平而SDA由低到高的跳变,表示产生一个 停止条件
- 应答信号:当发送完信号之后则拉低
总线在空闲状态时,SCL和SDA都保持着高电平
这三种信号里,起始信号是必须需要的,而结束信号和应答信号都可以视情况不要。
在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备 将释放总线,总线再次处于空闲状态
传输过程:
- 每次传输时的数据/地址以9位bit进行传输,前8位是数据,最后一位是应答位
- 传输时高位在前,低位在后
- 主设备在SCL和SDA都处于高电平状态时,先将SDA拉到低电平,然后在将SCL也拉入低电平,这样SDA产生了一个下降沿的信号,从设备检测到之后便知道主设备发来了起始信号,那么从设备需要主动拉高SDA线告诉主设备我准备好了
- 这个电平变化不是说一个时间点的变化,比如上一秒是高电平下一秒是低电平这不算变化,时间点变化是要求在一个周期里某一时间段处于高电平,某一时间段处于低电平,整个周期里完成这样一个动作才属于上升沿或下降沿变化,这个周期取决于I2C的时钟频率
- 如下图的周期变化,假设一个周期是1.2毫秒,那么这个毫秒里的周期变化如下
- T1到T2之间处于高电平,T3到T4之间处于低电平,然后整个周期就变成了下降沿的信号
- 然后主设备会在SCL线上产生时钟周期的脉冲信号,即发高电平,每产生一个脉冲信号都会从SDA线上发送一个数据出去,然后从设备检测到脉冲信号了就从SDA线上去读一个BIT位,在产生脉冲信号之后,SCL是处于低电平的,那么此时SDA电平会根据传输的数字信号进行翻转(为了防止数据不稳定,要求在SCl处于低电平时SDA才允许改变电平值,SCL发送一次脉冲信号后会要求拉低电平)
- 首先最开始发送的是地址信息,每次有效位为8位,当主设备发送8位之后就不会在发了,且发送完八位以后会主动让SDA线一直处于高电平(因为SDA线发送BIT可能是高或低电平,可能第8位是低电平所以需要拉高回来),然后就会一直等待从设备拉低,当从设备拉低以后那么主设备监听到之后就会认为此次通讯是正确的
- 然后接下来就可以发送数据位了,与地址信息一样,SCL产生脉冲之后SDA进行翻转,从设备进行读取,当发送完成之后就需要产生停止位,这个是由主设备完成的,会拉高SCL电平使其一直处于高电平,然后将SDA进行周期变化:上升沿,即在一定周期里拉低电平然后在拉高电平,然后此时SCL与SDA都处于高电平模式,则代表总线处于空闲状态可以被其它设备占用
从设备是需要实时监听SCL的电平变化,来完成对应的动作
以下是发送170(十六进制:0xAA; 二进制:1010 1010)到从设备的传输时序:
总结理解起始与结束信号就是:
- 起始条件:SCL线是高电平时,SDA线从高电平向低电平切换。
- 停止条件:SCL线是高电平时,SDA线从低电平向高电平切换。
这其中还有重复起始条件,即不用停止符号,只需要发送一个重复起始条件后续可以发送任意字节的数据,当全部发送完成之后在发送停止条件
- 即在不释放总线的情况下给从设备发送Sr信号,然后重复上一次的传输,这样做的好处在于不会出现在传输期间被别的设备抢占的情况,因为每次传输结束都会有一定的空闲时间,如果这个时间周期被别的设备占用会出现一些数据不完整的情况,因为每次传输只能传输一个字节的有效数据位,每次通讯都要产生起始和结束信号,所以会有一定的空闲时间
具体的通讯步骤可以参考I2C官方文档
6. I2C接口工作模式
I2C接口有四种工作模式
工作模式 | 介绍 |
---|---|
从发送器模式 | 从设备收到主设备发来的ADDR地址后,将内部的数据寄存器里的值通过数据控制模块,发送给SDA总线,这种模式一般应用于主设备读取从设备里的数据 |
从接收器模式 | 从设备收到主设备发来的ADDR地址后,通过数据控制模块将SDA总线的数据存于到数据寄存器中 |
主发送器模式 | 在发送ADDR地址后且得到从设备响应后将数据寄存器里的值发送到SDA总线上 |
主接收器模式 | 在发送ADDR地址后且得到从设备响应后将SDA总线上的数据写入到数据寄存器中 |
数据寄存器一般是DR寄存器(数据缓存寄存器),这块寄存器是等待内部程序将其读走的
其中因为是高位到地位发送的,所以内部会有一个位移寄存器的过程才能写入到DR寄存器
7. 硬件拉高拉低的过程
I2C的SDA与SCL都外接了上拉电阻,如果一直处于拉高状态那么只能输出高电平无法输出低电平,那么数字逻辑信号时钟是1,这样结果是不对的,所以需要有一个拉低的过程才能保证传输的bit位是有效且正确的。
I2C内部有一个逻辑控制器,负责控制这一块,当需要上拉时候会控制FET(场效应管,主要作用是控制半导体器件,控制电阻值),来拉低上拉电阻值使其电流固定在低电平值范围,产生低电平信号
拉低:
拉高:
S3C2400的物理层的拓扑结构(仅供参考):
8. 一对多
I2C给每个从设备设置地址,然后通过发送地址位来选择要操控哪个从设备。
在每个从设备连接后需要设置自己的地址,便于I2C主设备查找。
每个设备连接到I2C的SDA与SCL上,然后I2C主设备对这条线发送地址当从设备收到以后来确定是否是寻找自己
下图的连接就是将所有设备的SCL与SDA连接到I2C的主设备上,也就是说将I2C主设备的SDA与SCL引出来,然后从设备接到这根线上去,然后发送数据时接在这跟线上的所有设备都能监听到电平变化,就像socket的广播模式一样。
主设备是不需要设置自身地址的,因为不会有人找它,当然如果要与其它外设进行合作开发,可以设置一个自己的地址,并编写监听代码让自己做主或从
9. 开发流程
以下是我在资料里找到的开发流程,若裸机编程可以用作参考
主机接收模式:
主机发送模式:
三. SPI
1. 什么是SPI?
SPI全拼Serial Peripheral interface(串行外围设备接口),是由Motorola(摩托罗拉)在MC68HCXX系列处理器上定义的,主要应用于EEPROM(带电可擦可编程只读存储器),FLASH,RTC(实时时钟),AD转换器,还有数字信号处理器和数字信号解码器。
SPI,是一种高速的,全双工,同步的通信总线,且只需要四根引脚线,与I2C一样都大大简化了PCB板子的布局空间,SPI依然是一种世界级的流行标准,大多数板子都内置了SPI的接口。
Motorola设计它的初衷也是为了节约排版空间。
2. SPI优与缺点
优点
- 支持全双工通信
- 通信简单
- 数据传输速率块
缺点
- 没有指定的读写方向控制
- 没有应答机制确认是否接收到数据
- 跟I2C总线协议比较在数据,可靠性上有一定的缺陷
3. SPI组成原理
SPI模块框架图
SPI结构
上图结构中的SCK、SS、SDO、SDI是比较重要的几根线,其作用如下:
名称 | 作用 |
---|---|
SCK | 串行时钟信号,主设备产生 |
SS | 片选线,主要用于发送片选CS信号,即选择从设备 |
SDO | 主设备输入,从设备输出 |
SDI | 主设备输出,从设备输入 |
对于SS片选信号这里在多解释一下,在SPI中可能存在许多设备,但是只能有一个主设备,主设备通过SS线来控制选择哪个设备,默认情况下SS线是低电平状态,主设备通过选择连接在对应SS线下的设备,然后使其发送高电平,当连接在对应SS线上的设备收到高电平时就会做出响应,开始接收SDO或SDI的数据,SS线会有多个,可以外接许多设备,SCK与SDO、SDI只有一个,其它设备都接在上面。
其内部是有DR寄存器与位移寄存器,用来存数据与写数据
4. SPI通讯模式
SPI有四种通讯模式,分别是:
- Mode0
- Mode1
- Mode2
- Mode3
SPI官方没有给定名字,只给了模式0-3,其作用如下:
模式 | 作用 |
---|---|
Mode0 | SCLK输出数字信号0时处于空闲状态,SCLK输出数字信号为1时则代表有效状态 |
Mode1 | SCLK输出数字信号0时处于空闲状态,SCLK输出数字信号为1时则代表有效状态,数据采样是在第2个边沿,数据发送在第1个边沿 |
Mode2 | SCLK输出数字信号0时处于有效状态,SCLK输出数字信号1时处于空闲状态 |
Mode3 | SCLK输出数字信号0时处于有效状态,SCLK输出数字信号1时处于空闲状态,数据采样是在第1个边沿,数据发送在第2个边沿 |
这些模式我们需要通过设置SCLK的”CPOL(时钟极性)和CPHA(时钟相位)”模式来完成,你可以把CPOL与CPHA理解为一个标志位,它们标志位不同会使SCLK的输出电平状态不同,也会改变数据输出的边沿
SPI工作模式与CPOL和CPHA的对应关系以及它俩的作用:
模式 | CPOL | CPHA |
---|---|---|
Mode0 | 0 | 0 |
Mode1 | 0 | 1 |
Mode2 | 1 | 0 |
Mode3 | 1 | 1 |
边沿
这里说一下在SPI中什么是边沿,这里的第1个边沿与第2个边沿表示的是电平变化
边沿表示的是上升沿或下降沿
上升沿:即在一定周期时由低电平变化到高电平,然后此时在进行数据采样,即电平信号到数字信号转换
下降沿:即在一定周期时由高电平变化到低电平,然后此时在进行数据采样,即电平信号到数字信号转换
CPOL
用来决定SCL空闲时应该是怎样的电平状态,若为0,SCLK空闲时输出低电平,若为1空闲时输出高电平
CPHA
它用来表示该SDI有效还是SDO有效,若它的值为0则代表第0个边沿数据采样,即SDI上可以进行数据采样,若它为1时则SDO数据可以进行采样
同时它与CPOL是关联的:
若CPHA为0,CPOL为0时:
若CPOL为0,则第一个边沿有效,即SCLK空闲时是低电平,那么边沿就等于上升沿,同时SDI输出信号,SDO可以采样了
若CPHA为0,CPOL为1时:
若CPOL为1,则第一个边沿有效,即SCLK空闲时是高电平,那么边沿就等于下降沿,同时SDO输出信号,SDI可以采样了
SDI与SDO线对主设备和从设备方向不同,SDI线在主设备上是输出,从设备上是输入,所以它们是相反的,所以当SDI输出信号时,对于从设备来说,它就可以读数据了所以主设备与从设备两根线SDO与SDI两根线作用是不一样的,所以上面的状态对于不同的设备是相反的,也就是刚好实现了主设备写时,从设备读,主设备读时,从设备写
采样意思是设备可以去某个线上去读数据了
SPI是没有控制信号的,只能通过SCLK时钟的状态来告诉从设备现在的流方向,从设备需要实时检测当前的SCLK状态
工作时需要保证从设备与主设备一致的工作模式,其次许多从设备在出厂时就已经设定好了工作模式,是无法修改的,有些从设备是可以修改的。
5. 通讯过程
- 首先拉高对应设备的SS线,选择对应的从设备
- 通过SLCk产生时钟边沿变化,来告诉从设备读写
- 主设备里的控制逻辑通过位移寄存器从高到低的方式移动给SDI总线上
- 从设备接收到之后也通过位移寄存器的方式按照顺序移到DR寄存器中
- 这里给大家看一下SCLK产生时钟周期时做了什么
- 如下图,当SCLK周期开始变成低电平时则代表数据有输出了,当变成高电平时则代表数据可以采样(读)了,即输出→输出完成,这是整个周期做的事情,下位机只需要等待周期变化完成之后根据当前模式去到指定的线上读或写数据就可以了
- SPI是双向的,主机发的时候一样可以收从机的数据,在一个周期里完成数据交换,但是只有一个DR寄存器,所以SPI的方法是位移,主机高到低,从机低到高的交换,当主机高位移出去,低位就空一个出来,而从机低位移出一位,高位就空出一位
在通讯之前需要保证双方时钟频率是一致的,一般情况下从设备是无法修改时钟频率的,只有主设备能够修改时钟频率,将主设备的时钟频率修改与从设备一致,默认情况下是不需要修改这些的,这个可以根据芯片手册中SPI上的使能时钟接的是谁,如果提供了GPIO口进行修改那么就支持可编程时钟,若没有则代表当前的MCU不支持编程SPI的时钟频率