分类: LINUX
2015-07-02 19:23:08
原文地址:s5pc100之SPI 作者:草根老师
一、SPI介绍
SPI英文名叫Serial Peripheral Interface,中文名叫串行外围设备接口。是由Motorola公司开发,用来在微控制器和外围设备芯片之间提供一个低成本、易使用的接口。这种接口可以用来连接存储器(存储数据)、AD转换器、DA转换器、实时时钟、LCD驱动器、传感器、音频芯片、甚至其他处理器。
与标准的串行端口不同,SPI是一个同步协议接口,所有的传输都参照一个共同的时钟,这个同步时钟信号由主机(处理器)产生。接收数据的外设(从设备)使用时钟对串行比特流的接收进行同步化。可能会有许多芯片连接到主机的同一个SPI接口上,这时主机通过触发从设备的芯片的片选输入引脚来选择接收数据的从设备,没有被选中的外设将不会参与SPI传输。
SPI主要使用4个信号:主机输输出/从机输入(MOSI: Master Output Save Input)、主机输入/从机输出(MISO:Master Input Save Output)、串行时钟(SCLK或SCK)和外设片选(CS)。
MOSI信号由主机产生、从机接收。在有些芯片上,MOSI只被简单标为串行输入(SI),或者串行数据输入(SDI)。MISO信号由从机产生,不过还是在主机的控制下产生。在一些芯片上,MISO有时被称为串行输出(SO),或者串行数据输出(SDO)。外设片选信号通常只是由主机的备用I/O引脚产生。例如下图所示的微处理器通过SPI和外设进行连接的示意图:
接下来我们在来看看,SPI主机和从机是怎么传输的?
主机和从机都包含一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节发起一次传输。寄存器通过MOSI信号线将字节传送给从机,从机也将自己的移位寄存器中的内容通过MISO信号线返回给主机。这样两个移位寄存器中的内容就被交换了。从机的写操作和读操作时同步完成的,因此SPI成为一个很有效的协议。
注意:如果只是进行写操作,主机只需忽略收到的字节;反过来,如果主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
通过SPI总线,在主机和从机之间传输数据,必须保证主机发出的时序和从机所要求的时序一致才可以。在这里,常常我们会说到两个概念:时钟极性和时钟相位。
(1)时钟极性:Clock Polarity,简称CPOL或POL,指SPICLK空闲时的电平,是0还是1
(2)时钟相位:Clock Phase,简称CPHA或PHA,指数据采样的时候,是CLK的第一个边沿还是第二个边沿。CPHA=0,CLK的第一个边沿采样,CPHA=1时,时钟的第二个边沿采样。
将CPOL和CPHA进行组合,可以得到SPI主要有四种模式的时序。常用的是第一个和第三个。
对应到时序图的关系如下:
接下来我们以s5pc100 SPI控制器提供的SPI时序。这里分享一个简单的经验:
(1)如果在数据传输之前和数据传输之后,CLK = 0,表示SPICLK空闲时CPOL=0;CLK = 1,表示SPICLK空闲时CPOL=1
(2)如果采样数据是在CLK的第一个边沿进行的,表示CPHA=0,如果采样数据是在第二个边沿进行的,表示CPHA=1
SPICLK空闲时为低电平,所以CPOL=0,CLK的第一个边沿采样,所以CPHA=0
SPICLK空闲时为低电平,所以CPOL=0,CLK的第二个边沿采样,所以CPHA=1
SPICLK空闲时为高电平,所以CPOL=1,CLK第一个边沿采样,所以CPHA=0
SPICLK空闲时为高电平,所以CPOL=1,CLK第二个边沿采样,所以CPHA=1
你应该知道如何去判断CPOL和CPHA了,下面来看一个实例,来自己判断一下CPOL和CPHA。
M25P10(一个FLASH型的存储器件)读其ID的SPI时序图:
其中C就是我们的CLK。细心的读者会发现,怎么CLK空闲的时候,有高有低呀?呵呵,这就表示它支持的CPOL可以是
0也可以是1呀。为了偷懒,将两个时序图放在一起画了。
先看CPOL = 0时,数据采样是在 CLK的第一个边沿进行的,所以此时CPHA=0。
如果CPOL =1时,数据采样是在CLK的第二个边沿进行的,所以此时CPHA=1.。
所以M25P10支持的SPI时序为:CPOL=0,CPHA=0和CPOL=1,CPHA=1两种模式。
是的你只要设置好S5PC100的控制器,让它发出以上时序,M25P10A就可以正常工作了。怎么设置呢?就是设置 CPOL = 0,CPHA=0或者CPOL=1,CPHA=1。
二、通过SPI总线操作 M25P10
先来看看M25P10硬件上的连线,如下图所示:
总共四根线,连接到S5PC100的I/O 引脚。我们只需要将那些引脚设置为对应的工作模式即可。
这里以读取M25P10设备ID为例,来讲解一下操作步骤:
(1)设置GPIO引脚
(2)设置SPI控制器
(3)按照M25p10读取ID的时序操作
核心代码如下:
void init_spi()
{
/*设置GPIO*/
SET_GPIO_MODE(GPB.GPBCON,0,2);
SET_GPIO_MODE(GPB.GPBCON,1,2);
SET_GPIO_MODE(GPB.GPBCON,2,2);
SET_GPIO_MODE(GPB.GPBCON,3,2);
/*Master,CPOL = 0,CPHA=0*/
SPI0.CH_CFG = (1 << 1) | (1 << 0);
/*CLK:PCLK 66MHZ,SPI_CLK = 66MHZ/2 * (0 + 1)=33MHZ*/
SPI0.CLK_CFG = (1 << 8);
//CH_WIDTH:00(byte),BUS_WIDTH:00(byte)
SPI0.MODE_CFG = 0x0;
//manual select chip,disconnect slave device
SPI0.CS_REG = (1 << 0);
return;
}
M25P10(一个FLASH型的存储器件)读其ID的SPI时序图:
/*接收数据*/
uint8 recv_data()
{
int flag;
software_reset();
do{
//发送空字节,以触发接收
send_data(0);
flag = SPI0.SPI_STATUS & (0x7f << 13);
}while(!flag);
return SPI0.SPI_RX_DATA;
}
void read_m25p10_id()
{
int i = 0;
uint8 cmd[3];
DRIVE_CS_LOW();
//发送读ID命令
send_data(RDID);
//读取ID
for(i = 0;i < 3;i ++)
{
cmd[i] = recv_data();
}
DRIVE_CS_HIGH();
//打印ID
for(i = 0;i < 3;i ++)
{
printf("%#x ",cmd[i]);
}
printf("\r\n");
return;
}
经验:在操作SPI设备之前将/CS信号拉低,按照时序图操作完之后,将/CS信号拉高。M25P10的很多操作都是在将/CS信号拉高后,其内部才进行干活的。例如:发送擦除命令和地址后,只有在将/CS信号拉高后,其内部才擦除对应地址的数据。这一点手册上也有说明.