注:本博文大部分内容由华清远见彭丹老师整理!
2018-08-06补充:
在主设备模式下,一般需要先拉高片选端,然后配置主设备写函数,一般需要配置时钟极性,时钟相位,这两个配置参数与从设备一致。还需要配置主设备和从设备一次交换多少个字符。虽然SPI含有CLCK, 但是从设备的时钟完全由主设备控制,所以不需要配置波特率。
CPOL:时钟极性选择,为0时SPI总线空闲为低电平,为1时SPI总线空闲为高电平
CPHA:时钟相位选择,为0时在SCK第一个跳变沿采样,为1时在SCK第二个跳变沿采样
一、SPI总线协议
1. SPI特点
1.1 采用主-从模式(Mater-Slave)
SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). 一个 Master 设备可以通过提供 Clock 以及对 Slave 设备进行片选 (Slave Select) 来控制多个 Slave 设备, SPI 协议还规定Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备,Slave 设备本身不能产生或控制 Clock, 没有 Clock 则 Slave 设备不能正常工作。
1.2 采用同步方式(Synchronous)传输数据
Master 设备会根据将要交换的数据来产生相应的时钟脉冲(Clock Pulse), 时钟脉冲组成了时钟信 (Clock Signal) , 时钟信 通过时钟极性 (CPOL) 和 时钟相位 (CPHA) 控制着两个 SPI 设备间何时数据交换以及何时对接收到的数据进行采样, 来保证数据在两个设备之间是同步传输的.
关于时钟极性, 时钟相位名词含义下面会有解释。
1.3 数据交换(DataExchanges)
SPI 设备间的数据传输之所以又被称为数据交换, 是因为 SPI 协议规定一个 SPI设备不能在数据通信过程中仅仅只充当一个 “发送者(Transmitter)”或者 “接收者(Receiver)”. 在每个 Clock 周期内, SPI 设备都会发送并接收一个 bit 大小的数据, 相当于该设备有一个 bit 大小的数据被交换了. 一个 Slave设备要想能够接收到 Master 发过来的控制信 , 必须在此之前能够被 Master 设备进行访问 (Access). 所以, Master 设备必须首先通过 SS/CS pin 对 Slave 设备进行片选, 把想要访问的 Slave 设备选上. 在数据传输的过程中, 每次接收到的数据必须在下一次数据传输之前被采样. 如果之前接收到的数据没有被读取, 那么这些已经接收完成的数据将有可能会被丢弃, 导致 SPI 物理模块最终失效. 因此, 在程序中一般都会在 SPI 传输完数据后, 去读取 SPI设备里的数据, 即使这些数据(Dummy Data)在我们的程序里是无用的。
1.4 四种传输模式
上升沿、下降沿、前沿(第一个边沿)、后沿(第二个边沿)触发。当然也有MSB和LSB传输方式.
1.5 SPI只有主模式和从模式之分
没有读和写的说法,因为实质上每次SPI是主从设备在交换数据。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。
2. 工作机制
2.1 概述
上图只是对 SPI 设备间通信的一个简单的描述, 下面就来解释一下图中所示的几个组件(Module):
SSPBUF
SynchronousSerial Port Buffer, 泛指 SPI 设备里面的内部缓冲区, 一般在物理上是以 FIFO 的形式,保存传输过程中的临时数据;
SSPSR,
SynchronousSerial Port Register, 泛指 SPI 设备里面的移位寄存器(Shift Regitser), 它的作用是根据设置好的数据位宽(bit-width)把数据移入或者移出 SSPBUF;
Controller,
泛指 SPI 设备里面的控制寄存器, 可以通过配置它们来设置 SPI 总线的传输模式。
通常情况下, 我们只需要对上图所描述的四个管脚(pin) 进行编程即可控制整个 SPI 设备之间的数据通信:
SCK
SerialClock, 主要的作用是 Master 设备往Slave 设备传输时钟信 , 控制数据交换的时机以及速率;
SS/CS,
SlaveSelect/Chip Select, 用于 Master 设备片选 Slave 设备, 使被选中的Slave 设备能够被 Master 设备所访问;
SDO/MOSI,
SerialData Output/Master Out Slave In, 在 Master 上面也被称为 Tx-Channel, 作为数据的出口, 主要用于 SPI 设备发送数据;
SDI/MISO
SerialData Input/Master In Slave Out, 在 Master 上面也被称为 Rx-Channel, 作为数据的入口, 主要用于SPI 设备接收数据;
SPI设备在进行通信的过程中, Master 设备和 Slave 设备之间会产生一个数据链路回环(Data Loop), 就像上图所画的那样, 通过 SDO 和 SDI 管脚, SSPSR控制数据移入移出 SSPBUF, Controller 确定SPI 总线的通信模式, SCK 传输时钟信 。
2.2 Timing
注:在Mode 1,1下,只能在SCK下降沿改变数据,在SCK上升沿读取数据。
2.2.1 SPI相关的缩写或说法
SPI的极性Polarity和相位Phase,最常见的写法是CPOL和CPHA,不过也有一些其他写法,简单总结如下:
(1)CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
(2)CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位
(3)SCK=SCLK=SPI的时钟
(4)Edge=边沿,即时钟电平变化的时刻,即上升沿(rising edge)或者下降沿(falling edge)
对于一个时钟周期内,有两个edge,分别称为:
Leadingedge=前一个边沿=第一个边沿,对于开始电压是1,那么就是1变成0的时候,对于开始电压是0,那么就是0变成1的时候;
Trailingedge=后一个边沿=第二个边沿,对于开始电压是1,那么就是0变成1的时候(即在第一次1变成0之后,才可能有后面的0变成1),对于开始电压是0,那么就是1变成0的时候;
2.2.2 SPI的相位和极性
CPOL和CPHA,分别都可以是0或时1,对应的四种组合就是:
Mode0 CPOL=0, CPHA=0
Mode1 CPOL=0, CPHA=1
Mode2 CPOL=1, CPHA=0
Mode3 CPOL=1, CPHA=1
3.2.3 CPOL极性
先说什么是SCLK时钟的空闲时刻,其就是当SCLK在数发送8个bit比特数据之前和之后的状态,于此对应的,SCLK在发送数据的时候,就是正常的工作的时候,有效active的时刻了。
先说英文,其精简解释为:Clock Polarity = IDLE state of SCK。
再用中文详解:
SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:
CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high;
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low;
3.2.4 CPHA相位
首先说明一点,capturestrobe = latch = read = sample,都是表示数据采样,数据有效的时刻。相位,对应着数据采样是在第几个边沿(edge),是第一个边沿还是第二个边沿,0对应着第一个边沿,1对应着第二个边沿。
对于:
CPHA=0,表示第一个边沿:
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;
CPHA=1,表示第二个边沿:
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从低变到高,所以是上升沿;
还是上图大家更容易看懂
2.2.5 软件中如何设置SPI的极性和相位
SPI分主设备和从设备,两者通过SPI协议通讯。
而设置SPI的模式,是从设备的模式,决定了主设备的模式。
所以要先去搞懂从设备的SPI是何种模式,然后再将主设备的SPI的模式,设置和从设备相同的模式,即可正常通讯。
对于从设备的SPI是什么模式,有两种:
3.2.5.1固定的,有SPI从设备硬件决定的
SPI从设备,具体是什么模式,相关的datasheet中会有描述,需要自己去datasheet中找到相关的描述,即:
关于SPI从设备,在空闲的时候,是高电平还是低电平,即决定了CPOL是0还是1;
然后再找到关于设备是在上升沿还是下降沿去采样数据,这样就是,在定了CPOL的值的前提下,对应着可以推算出CPHA是0还是1了。
3.2.5.2 可配置的,由软件自己设定
从设备也是一个SPI控制器,4种模式都支持,此时只要自己设置为某种模式即可。
然后知道了从设备的模式后,再去将SPI主设备的模式,设置为和从设备模式一样,即可。
对于如何配置SPI的CPOL和CPHA的话,不多细说,多数都是直接去写对应的SPI控制器中对应寄存器中的CPOL和CPHA那两位,写0或写1即可。
3.3 SSPSR与SSPBUF
SSPSR 是 SPI 设备内部的移位寄存器(Shift Register). 它的主要作用是根据 SPI 时钟信 状态, 往 SSPBUF 里移入或者移出数据,每次移动的数据大小由 Bus-Width 以及Channel-Width 所决定。
Bus-Width的作用是指定地址总线到 Master 设备之间数据传输的单位.
例如, 我们想要往 Master 设备里面的 SSPBUF 写入 16 Byte 大小的数据: 首先, 给 Master 设备的配置寄存器设置 Bus-Width 为 Byte; 然后往 Master 设备的 Tx-Data 移位寄存器在地址总线的入口写入数据, 每次写入 1 Byte 大小的数据(使用writeb 函数); 写完 1 Byte 数据之后, Master 设备里面的 Tx-Data 移位寄存器会自动把从地址总线传来的1 Byte 数据移入 SSPBUF 里;上述动作一共需要重复执行 16 次.
Channel-Width的作用是指定 Master 设备与 Slave 设备之间数据传输的单位. 与 Bus-Width 相似,Master 设备内部的移位寄存器会依据 Channel-Width 自动地把数据从 Master-SSPBUF 里通过 Master-SDO 管脚搬运到 Slave 设备里的 Slave-SDI 引脚, Slave-SSPSR 再把每次接收的数据移入 Slave-SSPBUF里.通常情况下,Bus-Width 总是会大于或等于 Channel-Width, 这样能保证不会出现因 Master 与 Slave 之间数据交换的频率比地址总线与 Master 之间的数据交换频率要快, 导致 SSPBUF 里面存放的数据为无效数据这样的情况.
3.4 Controller
Master 设备里面的 Controller 主要通过时钟信 (Clock Signal)以及片选信 (Slave Select Signal)来控制 Slave 设备. Slave 设备会一直等待, 直到接收到 Master 设备发过来的片选信 , 然后根据时钟信 来工作.
Master设备的片选操作必须由程序所实现. 例如: 由程序把 SS/CS 管脚的时钟信 拉低电平, 完成 SPI 设备数据通信的前期工作; 当程序想让 SPI 设备结束数据通信时, 再把SS/CS 管脚上的时钟信 拉高电平.
二、Exynos4412-SPI控制器
1. SPI控制器时钟源控制,配置时钟源分频
2. SPI控制寄存器
2.1 SPI传输控制寄存器(CH_CFGn n = 0-2),配置参数如下:接收使能、发送使能、主从模式配置、传输方式相位配置、极性配置、软件复位等
2.2 SPI模式配置寄存器(MODE_CFGn n = 0-2), 配置参数如下:FIFO、DMA、通道宽度、总线宽度等。
2.3从机选择信 配置寄存器(CS_CFG寄存器 n = 0-2),用来对SPI控制器上的CS引脚选择方式配置
2.4 SPI状态寄存器(SPI_STATUSn n = 1-2), 用来查看当前SPI寄存器的状态,比如移位寄存器是否为空。
2.5 SPI数据发送寄存器
SPI_TX_DATA 发送的数据填充到该寄存器
SPI_RX_DATA 接收的数据会自动发送到该寄存器
三、示例部分代码
int main(void)
{
GPC1.CON =(GPC1.CON & ~0xffff0) | 0x55550;//设置IO引脚为SPI模式
/*spi clockconfig*/
CLK_SRC_PERIL1= (CLK_SRC_PERIL1 & ~(0xF
CLK_DIV_PERIL2= 19
soft_reset(); // 软复位SPI控制器
SPI2.CH_CFG&= ~( (0x1
SPI2.MODE_CFG&= ~((0x3
SPI2.CS_REG&= ~(0x1
delay(10); //延时
}
/*
* 片选从机
*/
void slave_enable(void)
{
SPI2.CS_REG&= ~0x1; //enable salve
delay(3);
}
/*
* 取消片选从机
*/
void slave_disable(void)
{
SPI2.CS_REG|= 0x1; //disable salve
delay(1);
}
/*
* 功能:向SPI总线发送一个字节
*/
void send_byte(unsigned char data)
{
SPI2.CH_CFG|= 0x1; // enable Tx Channel
delay(1);
SPI2.SPI_TX_DATA= data;
while(!(SPI2.SPI_STATUS & (0x1
SPI2.CH_CFG&= ~0x1; // disable Tx Channel
}
/*
* 功能:从SPI总线读取一个字节
*/
unsigned char recv_byte()
{
unsigned chardata;
SPI2.CH_CFG|= 0x1
delay(1);
data =SPI2.SPI_RX_DATA;
delay(1);
SPI2.CH_CFG&= ~(0x1
return data;
}
/*
* 复位spi控制器
*/
void soft_reset(void)
{
SPI2.CH_CFG|= 0x1
delay(1); //延时
SPI2.CH_CFG&= ~(0x1
}
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!