赞
踩
目录
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola(摩托罗拉)首先在其MC68HCXX系列处理器上定义的。SPI是一种高速的,全双工,同步的通信总线,可以在同一时间发送和接收数据,并且没有定义速度限制,通常能达到甚至超过10M/bps。在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
SPI总线包括4条逻辑线,定义如下:
MISO:Master input slave output 主机输入,从机输出(数据来自从机);
MOSI:Master output slave input 主机输出,从机输入(数据来自主机);
SCLK :Serial Clock 串行时钟信号,由主机产生发送给从机;
SS:Slave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号。
其他制造商可能会遵循其他命名规则,但是最终他们指的相同的含义。以下是一些常用术语;
MISO也可以是SIMO,DOUT,DO,SDO或SO(在主机端);
MOSI也可以是SOMI,DIN,DI,SDI或SI(在主机端);
NSS也可以是CE,CS或SSEL;
SCLK也可以是SCK;
本文将按照以下命名进行讲解[MISO, MOSI, SCK,CS]
SPI是一个同步的数据总线,它是用一条单独的数据线和一条单独的时钟信号来保证发送端和接收端的完美同步。
时钟是一个振荡信号,它告诉接收端在确切的时机对数据线上的信号进行采样。
SPI有主、从两种模式,通常由一个主模块(主机)和一个或多个从模块(丛机)组成(SPI不支持多主机),主模块选择一个从模块进行同步通信,从而完成数据的交换。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起,当存在多个从设备时,通过各自的片选信号进行管理。
数据的采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低)。
整体的传输大概可以分为以下几个过程:
1、主设备发起信号,将CS/SS拉低,启动通信。
2、主设备通过发送时钟信号,来告诉从设备进行写数据或者读数据操作(采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低),因为SPI有四种模式,后面会讲到),它将立即读取数据线上的信号,这样就得到了一位数据(1bit)。
3、主机(Master)将要发送的数据写到发送数据缓存区(Menory),缓存区经过移位寄存器(缓存长度不一定,看单片机配置),串行移位寄存器通过MOSI信号线将字节一位一位的移出去传送给从机,同时MISO接口接收到的数据经过移位寄存器一位一位的移到接收缓存区。
4、从机(Slave)也将自己的串行移位寄存器(缓存长度不一定,看单片机配置)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。
例如,下图示例中简单模拟SPI通信流程,主机拉低CS片选信号,启动通信,并且产生时钟信号,上升沿触发边沿信号,主机在MOSI线路一位一位发送数据0X53,在MISO线路一位一位接收数据0X46,如下图所示:
SPI是“全双工”(具有单独的发送和接收线路), SPI只有主模式和从模式之分,没有读和写的说法,可以在同一时间发送和接收数据。另外SPI的接收硬件可以是一个简单的移位寄存器。这比异步串行通信所需的完整UART要简单得多,并且更加便宜;
如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。
数据在传输中,高位在先还是低位在先,SPI协议并无明确规定,但是数据要在主从机中正确传输,自然双方要先约定好,一般会采用高位在先(MSB)方式传输。
除了配置串行时钟速率(频率)外,SPI主设备还需要配置时钟极性和时钟相位。
时钟极性 CKP/Clock Polarit
根据硬件制造商的命名规则不同,时钟极性通常写为CKP或CPOL。时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据;
CKP可以配置为1或0。这意味着您可以根据需要将时钟的默认状态(IDLE)设置为高或低。极性反转可以通过简单的逻辑逆变器实现。您必须参考设备的数据手册才能正确设置CKP和CKE。CKP = 0:时钟空闲IDLE为低电平 0;
CKP = 1:时钟空闲IDLE为高电平1;
时钟相位 CKE /Clock Phase (Edge)
除配置串行时钟速率和极性外,SPI主设备还应配置时钟相位(或边沿)。根据硬件制造商的不同,时钟相位通常写为CKE或CPHA;
顾名思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿;
CKE = 0:在时钟信号SCK的第一个跳变沿采样;
CKE = 1:在时钟信号SCK的第二个跳变沿采样;
此处附上一组软件模拟SPI 通信传输数据的代码,方便各位理解其实现的方法。
- //SPI发送数据,val为要发送的数据
- void SPI_Send(u8 val)
- {
-
- u8 recv_data = 0, i = 0;//将接受数据清零方便直接或运算接受数据
-
- SCK = 0;//时钟线拉低
-
- for(i=0; i<8; i++) //传输数据,先发高位
- {
- //准备数据
- if(val & (1<<(7-i))) //通过1左移7位然后相与,判断数据最高位是1还是0
- {
- MOSI = 1; //数据为1
- }
- else
- {
- MOSI = 0; //数据为0
- }
- delay_us(5);
-
- SCK = 1; //时钟线拉高准备接收数据
- delay_us(5);
-
- //高电平区间接收数据,此处不重要但必须有
- if(MISO == 1) //收到的为1
- {
- recv_data |= (1<<(7-i));//将数据位通过或运算保存。先保持高位
- }
- SCK = 0;//时钟线拉低
- }
- }
-
- //SPI接收数据
- u8 SPI_Receive(void)
- {
- u8 recv_data = 0, i = 0;
-
- // u8 val = 0x00;
-
- RC522_SCK = 0;
- for(i=0; i<8; i++) //发送数据,此处不重要但必须有
- {
- //准备数据
- // if(val & (1<<(7-i))) //1
- // {
- // MOSI = 1; //数据为1
- // }
- // else
- // {
- // MOSI = 0; //数据为0
- // }
-
- MOSI = 0; //输出线清零
- delay_us(5);
-
- SCK = 1; //时钟线拉高准备接收数据
- delay_us(5);
-
- //高电平区间接收数据
- if(MISO == 1) //收到的为1
- {
- recv_data |= (1<<(7-i));//将数据位通过或运算保存。先保持高位
- }
-
- SCK = 0;
- }
- return recv_data; //返回接收到的数据
- }
首先主机和从机都选择同一传输模式。然后主机片选拉低,选中从机。接着在时钟的驱动下, MOSI发送数据,同时MISO读取接收数据。最后完成传输,取消片选。
- /*
- * 函数名: void SPI_WriteByte(uint8_t data)
- * 输入参数: data -> 要写的数据
- * 输出参数:无
- * 返回值:无
- * 函数作用:模拟 SPI 写一个字节
- */ SPI写1 Byte,循环8次,每次发送1 Bit;
- void SPI_WriteByte(uint8_t data) {
- uint8_t i = 0;
- uint8_t temp = 0;
- for(i=0; i<8; i++) {
- temp = ((data&0x80)==0x80)? 1:0; //将data最高位保存到temp;
- data = data<<1; //data左移一位,将次高位变为最高位,用于下次取最高位;
- SPI_CLK(0); //CPOL=0 //拉低时钟,即空闲时钟为低电平, CPOL=0;
- SPI_MOSI(temp); //根据temp值,设置MOSI引脚的电平;
- SPI_Delay(); //简单延时,可以定时器或延时函数实现
- SPI_CLK(1); //CPHA=0 //拉高时钟, W25Q64只支持SPI模式0或1,即会在时钟上升沿采样MOSI数据;
- SPI_Delay();
- }
- SPI_CLK(0); //最后SPI发送完后,拉低时钟,进入空闲状态;
- }
-
- /*
- * 函数名: uint8_t SPI_ReadByte(void)
- * 输入参数:
- * 输出参数:无
- * 返回值:读到的数据
- * 函数作用:模拟 SPI 读一个字节
- */ SPI读1 Byte,循环8次,每次接收1 Bit;
- uint8_t SPI_ReadByte(void) {
- uint8_t i = 0;
- uint8_t read_data = 0xFF;
- for(i=0; i<8; i++) {
- read_data = read_data << 1; //“腾空” read_data最低位,8次循环后,read_data将高位在前;
- SPI_CLK(0); //拉低时钟,即空闲时钟为低电平;
- SPI_Delay();
- SPI_CLK(1);
- SPI_Delay();
- if(SPI_MISO()==1) {
- read_data = read_data + 1;
- }
- }
- SPI_CLK(0); //最后SPI读取完后,拉低时钟,进入空闲状态
- return read_data;
- }
前面提到SPI传输可以看作一个虚拟的环形拓扑结构,即输入和输出同时进行。在前面“ SPI_WriteByte()”函数里,发送了1 Byte,也应该接收1 Byte,只是代码中忽略了接收引脚MISO的状态; 在前面“ SPI_ReadByte()”函数里,接收了1 Byte,也应该发送1 Byte,只是代码中忽略了发送引脚MOSI的内容。有些场景, SPI需要同时读写,因此还需要编写SPI同时读写函数。
- /*
- * 函数名: uint8_t SPI_WriteReadByte(uint8_t data)
- * 输入参数: data -> 要写的一个字节数据
- * 输出参数:无
- * 返回值:读到的数据
- * 函数作用:模拟 SPI 读写一个字节
- */SPI读和写1 Byte,循环8次,每次发送和接收1 Bit;
- uint8_t SPI_WriteReadByte(uint8_t data) {
- uint8_t i = 0;
- uint8_t temp = 0;
- uint8_t read_data = 0xFF;
- for(i=0;i<8;i++) {
- temp = ((data&0x80)==0x80)? 1:0; //将data最高位保存到temp;
- data = data<<1; //data左移一位,将次高位变为最高位,用于下次取最高位;
- read_data = read_data<<1; //“腾空” read_data最低位,8次循环后,read_data将高位在前;
- SPI_CLK(0);
- SPI_MOSI(temp);
- SPI_Delay();
- SPI_CLK(1);
- SPI_Delay();
- if(SPI_MISO()==1) { //读取MISO上的数据,保存到当前read_data最低位;
- read_data = read_data + 1;
- }
- }
- SPI_CLK(0);
- return read_data;
- }
IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。
在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI
IIC只有两个总线: 一条是双向的串行数据线SDA,一条是串行时钟线SCL
SDA(Serial data)是数据线,D代表Data也就是数据,Send Data也就是用来传输数据。
SCL(Serial clock line)是时钟线,C代表Clock也就是时钟,用来控制数据发送的时序。
所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。
通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。
IIC主设备功能:主要产生时钟,产生起始信号和停止信号
IIC从设备功能:可编程的IIC地址检测,停止位检测
IIC的一个优点是它支持多主控(multimastering), 其中任何一个能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。支持不同速率的通讯速度,标准速度(最高速度100kHZ),快速(最高400kHZ)。
SCL和SDA都需要接上拉电阻 (大小由速度和容性负载决定一般在3.3K-10K之间) 保证数据的稳定性,减少干扰。
IIC是半双工,而不是全双工 ,同一时间只可以单向通信
为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。
IIC的高阻态
漏极开路(Open Drain)即高阻状态,适用于输入/输出,其可独立输入/输出低电平和高阻状态,若需要产生高电平,则需使用外部上拉电阻
高阻状态:高阻状态是三态门电路的一种状态。逻辑门的输出除有高、低电平两种状态外,还有第三种状态——高阻状态的门电路。电路分析时高阻态可做开路理解。
我们知道IIC的所有设备是接在一根总线上的,那么我们进行通信的时候往往只是几个设备进行通信,那么这时候其余的空闲设备可能会受到总线干扰,或者干扰到总线,怎么办呢?
为了避免总线信号的混乱,IIC的空闲状态只能有外部上拉, 而此时空闲设备被拉到了高阻态,也就是相当于断路, 整个IIC总线只有开启了的设备才会正常进行通信,而不会干扰到其他设备。
Start: IIC开始信号,表示开始传输。
DEVICE_ADDRESS:: 从设备地址,就是7位从机地址。
R/W: W(write)为写,R(read)为读。
ACK: 应答信号。
WORD_ADDRESS : 从机中对应的寄存器地址 比方说访问 OLED中的 某个寄存器。
DATA: 发送的数据。
STOP: 停止信号。结束IIC通信。
流程介绍:
1、主机首先产生START信号;
2、然后发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方向位(R/W),“0”表示主机向从机发送数据(写),“1”表示主机从从机接收数据(读);
3、主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/W位将自己确定为发送器和接收器;
4、这时候主机等待从机的应答信号(ACK);
5、当主机收到应答信号时,发送要访问从机的那个地址, 继续等待从机的应答信号;
6、当主机收到应答信号时,发送N个字节的数据,继续等待从机的N次应答信号;
7、主机产生停止信号,结束传输过程。还有一种情况是从机发送非应答信号表示接受完毕后主机产生停止信号结束传输过程。
1、主机首先产生START信号;
2、然后紧跟着发送一个从机设备地址,注意此时该地址的第8位为0,表明是向从机写命令;
3、这时候主机等待从机的应答信号(ACK);
4、当主机收到应答信号时,发送要访问的寄存器地址,继续等待从机的应答信号;
5、当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设置成接收模式开始读取数据;
6、这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收1个字节的数据,当接收完成后,主机发送非应答信号(NACK),表示不再接收数据;
7、主机进而产生停止信号,结束传送过程。
开漏Pin不连接外部的上拉电阻,则只能输出低电平。当输出电平为低时,N沟道MOS管是导通的,这样在Vcc和GND之间有一个持续的电流流过上拉电阻R和三极管Q1。这会影响整个系统的功耗。采用较大值的上拉电阻可以减小电流。但是大的阻值会使输出信号的上升时间变慢。即上拉电阻R pull-up的阻值 决定了逻辑电平转换的沿的速度。阻值越大,速度越低功耗越小。反之亦然。
防止短路
如果不设为开漏,而设为推挽,几个设备连在同一条总线上,这时某一设备的某个IO输出高电平,另有一台设备的某一个IO输出低电平,这时你会发现这两个IO的VCC和GND短路了;但是开漏就不会有这个问题。
增强端口扇出能力、降低功耗
IC为增强端口扇出能力而设计为漏极开路样式,使用时将该端口设为低电平有效的灌电流模式,能得到最大输出电流同时IC功耗最低。此类端口当输出高电平则需要外接上拉电阻。
利用“线与”判断总线占用状态
可以将多个开漏输出的Pin脚,连接到一条线上,形成“与逻辑”关系,即“线与”功能,任意一个变低后,开漏线上的逻辑就为0了。这也是I2C,SMBus等总线判断总线占用状态的原理。
如果总线上的一个A设备将SDA拉高,这时总线上另一个B设备已将SDA拉低,这时由于1&0=0,所以A设备检查SDA的时候会发现不是高电平而是低电平,这就表明总线上已经有其他设备占用总线了,A只好放弃,如果检测是高电平那就可以使用。
增加驱动能力
如果在漏极drain_output接上拉电阻,则可以进行电平转换,且驱动能力较强。
利用外部电路的驱动能力,减少IC内部的驱动。当IC内部MOSFET导通时,驱动电流是从外部的VCC流经R pull-up ,MOSFET到GND。IC内部仅需很下的栅极驱动电流。
控制输出高电平大小
可以利用改变上拉电源的电压,改变传输电平。
软件IIC:软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式。
硬件IIC:一块硬件电路,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,硬件(固件)I2C是直接调用内部寄存器进行配置。
硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。
STM32硬件I2C与软件模拟I2C超详解_stm32 i2c-CSDN博客
UART的全称是通用异步收发器(Universal Asynchronous Receiver/Transmitter)
Universal 通用性体现在UART使用范围广上,作为一个通用的接口协议,UART广泛的应用在各类MCU和SOC产品上。
Asynchronous 异步性体现在“不需要额外的时钟线进行数据的同步传输”即只要信号拉低,即可开始传送数据,而另一些通讯协议,需要引入时钟信号来进行操作,如SPI需要在时钟的边沿发送数据。
Receiver/Transmitter 收发器则更好理解,即一个数据的发送方和一个数据的接收方,也意味着在数字IC设计中需要分别设计Receiver和Transmitter。
UART的一帧由起始位、数据位、校验位和停止位组成。数据逐位传输,如下图所示
因为UART没有控制线,要让接收方知道什么时候开始接收数据,需要一些手段,UART数据的传输中,只有一根线,所以在发送数据之前,先发一位逻辑“0”作为数据发送的起始标志,接收方在空闲时,当检测到有一个低电平,则开始接逐位接收数据。
(3)为什么UART的数据位可变?
因为UART是一种低速总线,每多发一位都占用不少的时间(由传输波特率决定),所以可以根据传输数据的特点,采用不同位宽以节约数据传输的时间。
如果从更高的level审视UART传输协议,如嵌入式开发者的角度,我们会发现,在使用具体的UART协议前,我们需要对发送端和接收端进行波特率的同步,以此来确保发射端的数据可以在接收端得到正确的采集。常用的波特率可以是300,1200,2400,9600,19200,38400,115200,这些数意味着什么呢?别着急,我们接下来要讨论这个内容。
波特率等于每秒钟传输的数据位数,假如我们的全局时钟频率为100MHz,波特率设置为9600,那么意味着每秒该UART传输协议可以传输9600bits的数据,换句话说传输1比特需时间约为:10^9(ns)/9600=104166(ns)。
书接上文,时钟频率假如为100MHz,这意味着我们的时钟周期为10ns,因此10416个时钟周期我们就可以传输1bit数据,换言之我们需要一个大小为10416的分频电路来对100MHz时钟进行处理,因此在设计UART的过程中,我们需要使用分频电路依据波特率处理全局时钟,依据分频后的时钟节奏来发送数据和接收数据。
UART只是对信号的时序进行了定义,而未定义接口的电气特性;
UART通信时一般直接使用处理器使用的电平,即TTL电平,但不同的处理器使用的电平存在差异,所以不同的处理器使用UART通信时一般不能直接相连;
UART没有规定不同器件连接时连接器的标准,所以不同器件间通过UART通信时连接很不方便。
UART一般直接使用TTL信号来表示0和1,但TTL信号的抗干扰能力较差,数据在传输过程中很容易出错。
因为TTL信号的抗干扰能力较差,所以其通信距离也很短,一船只能用于一个电路板上的两个不芯片之间的通信。
缺点:
接口的信号电平值较高,易损坏接口电路的芯片,又因为与TTL电平不兼容,所以需要使用电平转换芯片才能与TTL电路连接
通信速度较低。
易产生共模干扰,抗噪声干扰性弱。
传输距离较短(15m)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。