通用SPI有4根线:
①时钟线SCK。
②主机输出从机输入MOSI (master output slave in)。
③主机输入从机输出MISO。
④片选CS(chip select)。建议推挽上拉。
由以上四根线定义可见,SPI是区分主机、从机的。片选线的作用:主机通过拉低某个从设备的CS,来选中这个从设备。
时钟SCK只能由主机发出,从机被这个SCK驱动。根据SCK的极性(CPOL,Clock POLarity),和接收方采样的相位(CPHA,Clock PHAse)),SPI可以分为4种模式:
----------CPOL ---CPHA
模式0 0 0 高脉冲(SCK空闲时为低),第1个边沿(上升沿)采样
模式1 0 1 高脉冲(SCK空闲时为低),第2个边沿(下降沿)采样
模式2 1 0 低脉冲(SCK空闲时为高),第1个边沿(下降沿)采样
模式3 1 1 低脉冲(SCK空闲时为高),第2个边沿(上升沿)采样
所谓SCK的极性,极性=0时,意即:SCK空闲时为低电平,发出的脉冲均为高脉冲(低-高-低)。
所谓MISO/MOSI的相位,相位=0,意即:发送方在脉冲的第1个边沿保持数据稳定,供接收方采样。发送方在脉冲的第2个边沿改变数据。
STM32F407的一段例程
/*SPI接口定义-开头****************************/
#define ADIS_SPI SPI1
#define ADIS_SPI_CLK RCC_APB2Periph_SPI1
#define ADIS_SPI_CLK_INIT RCC_APB2PeriphClockCmd
//SPI-CS A4
#define ADIS_CS_PIN GPIO_Pin_4
#define ADIS_CS_GPIO_PORT GPIOA
#define ADIS_CS_GPIO_CLK RCC_AHB1Periph_GPIOA
//SPI-SCK A5
#define ADIS_SPI_SCK_PIN GPIO_Pin_5
#define ADIS_SPI_SCK_GPIO_PORT GPIOA
#define ADIS_SPI_SCK_GPIO_CLK RCC_AHB1Periph_GPIOA
#define ADIS_SPI_SCK_PINSOURCE GPIO_PinSource5
#define ADIS_SPI_SCK_AF GPIO_AF_SPI1
//SPI-MISO A6
#define ADIS_SPI_MISO_PIN GPIO_Pin_6
#define ADIS_SPI_MISO_GPIO_PORT GPIOA
#define ADIS_SPI_MISO_GPIO_CLK RCC_AHB1Periph_GPIOA
#define ADIS_SPI_MISO_PINSOURCE GPIO_PinSource6
#define ADIS_SPI_MISO_AF GPIO_AF_SPI1
//SPI-MOSI A7
#define ADIS_SPI_MOSI_PIN GPIO_Pin_7
#define ADIS_SPI_MOSI_GPIO_PORT GPIOA
#define ADIS_SPI_MOSI_GPIO_CLK RCC_AHB1Periph_GPIOA
#define ADIS_SPI_MOSI_PINSOURCE GPIO_PinSource7
#define ADIS_SPI_MOSI_AF GPIO_AF_SPI1
#define SPI_ADIS_CS_LOW() {ADIS_CS_GPIO_PORT->BSRRH=ADIS_CS_PIN;}
#define SPI_ADIS_CS_HIGH() {ADIS_CS_GPIO_PORT->BSRRL=ADIS_CS_PIN;}
void spi1_init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能 ADIS_SPI 及GPIO 时钟 */
/*!< SPI_ADIS_SPI_CS_GPIO, SPI_ADIS_SPI_MOSI_GPIO,
SPI_ADIS_SPI_MISO_GPIO,SPI_ADIS_SPI_SCK_GPIO 时钟使能 */
RCC_AHB1PeriphClockCmd (ADIS_SPI_SCK_GPIO_CLK | ADIS_SPI_MISO_GPIO_CLK|ADIS_SPI_MOSI_GPIO_CLK|ADIS_CS_GPIO_CLK, ENABLE);
/*!< SPI_ADIS_SPI 时钟使能 */
RCC_APB2PeriphClockCmd(ADIS_SPI_CLK, ENABLE);
//设置引脚复用
GPIO_PinAFConfig(ADIS_SPI_SCK_GPIO_PORT, ADIS_SPI_SCK_PINSOURCE,ADIS_SPI_SCK_AF);
GPIO_PinAFConfig(ADIS_SPI_MISO_GPIO_PORT, ADIS_SPI_MISO_PINSOURCE,ADIS_SPI_MISO_AF);
GPIO_PinAFConfig(ADIS_SPI_MOSI_GPIO_PORT, ADIS_SPI_MOSI_PINSOURCE,ADIS_SPI_MOSI_AF);
/*!< 配置 SPI_ADIS_SPI 引脚: CS*/
GPIO_InitStructure.GPIO_Pin = ADIS_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(ADIS_CS_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置 SPI_ADIS_SPI 引脚: SCK */
GPIO_InitStructure.GPIO_Pin = ADIS_SPI_SCK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_NOPULL;//GPIO_PuPd_NOPULL; GPIO_PuPd_UP//SCK需要上拉,MI MO均不上拉也不下拉
GPIO_Init(ADIS_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置 SPI_ADIS_SPI 引脚: MISO */
GPIO_InitStructure.GPIO_PuPd =GPIO_PuPd_UP;// GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = ADIS_SPI_MISO_PIN;
GPIO_Init(ADIS_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置 SPI_ADIS_SPI 引脚: MOSI */
GPIO_InitStructure.GPIO_Pin = ADIS_SPI_MOSI_PIN;
GPIO_InitStructure.GPIO_PuPd =GPIO_PuPd_DOWN;// GPIO_PuPd_UP;
GPIO_Init(ADIS_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
/* 停止信号 ADIS: CS引脚高电平*/
SPI_ADIS_CS_HIGH();
//Delay_us(1000 * 500);
/* ADIS_SPI 模式配置 */
// ADIS芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//对PCLK2总线做256分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 10;
SPI_Init(ADIS_SPI, &SPI_InitStructure);
/* 使能 SPI */
SPI_Cmd(ADIS_SPI, ENABLE);
}
其中:
SPI_Direction_2Lines_FullDuplex指双线全双工,
SPI_Mode_Master指工作在主机模式,
SPI_DataSize_8b指的是,每次调用库函数void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)发送数据时,总是只发出实参的低8位(8个SCK+8个MOSI比特),如果设置为SPI_DataSize_16b,则该函数会一下子发出16个SCK+16个MOSI的实参的16个bits。
SPI_CPOL_High设置SCK的极性,空闲时为高。
SPI_CPHA_2Edge设置MOSI和MISO的采样时机为SCK的第2个边沿。
SPI_NSS_Soft指片选信号不由STM32的硬件SPI模块来控制,也即由程序员自己写GPIO来控制。
SPI_BaudRatePrescaler_256,分频因子,决定了SCK的频率。由于SPI1工作在APB2总线上,SCK的频率就=PCLK2的频率/这个分频因子。
SPI_FirstBit_MSB,先发高bit位,(Most Significant Bit,与之相反的是 least significant bit)
SPI_CRCPolynomial 指的是使用多少阶的CRC多项式进行校验,一般的SPI设备都不具备此功能,随便填个值即可。