1. SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线:SCLK,MISO,MOSI,CS
2. SPI结构简图:

可以看出,SPI主从设备两端都有一个位移寄存器,数据在位移寄存器上通过逐位移动来实现同步双工通信,在主机发送数据的同时也会收到从机发来的数据。类似一个循环。(图片不好编辑,将就着看),需要注意的是传输过程是通过主机写入一个需要发送的数据来开始的。
如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。发送结束可以设置中断。
3. 时钟极性(POL)和相位(PHA),通过配置极性及相位为0或1,可配置成为4种不同的传输时序:极性为0,时钟空闲为低电平,反之为高;相位为0,据在第一个时钟跳变沿被采集,为1的话在第二个跳变沿被采集。如图:


4. SPI 时钟最多可以到 18Mhz,支持 DMA,可以配置为 SPI 协议或者 I2S (一种音频传输总线,集成电路内置音频总线)。
5. NSS引脚:主从模式选择,简单了解,用到时再具体了解。
7. 数据帧格式:可软件设置MSB或LSB哪个在先(SPI_CR1寄存器中LSBFIRST位),也可设置输入输出数据帧是8位或者16位(SPI_CR1寄存器DFF位)。
8. 状态标志位:① 发送缓冲器空闲标志(TXE),为1时表明发送缓冲为空,可写入数据,写入后自动置0;
② 接受缓冲器非空(RXNE),为1表示接收缓冲器中有有效接收数据,读数据自动置0;
③ Busy位,由硬件管理。
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG); void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG); ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT); void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
9. SPI中断:

10. 时钟配置最大为(Fpclk/2),这里的PCLK为PCLK1(SPI为APB1的外设),PCLK1最大为系统时钟的1/2。
(贴一个刚才百度的:SYSCLK 系统时钟,最大72MHzHCLK :AHB总线时钟,由系统时钟SYSCLK 分频得到,一般不分频,等于系统钟经过总线桥AHB--APB,通过设置分频,可由HCLK得到 PCLK1与PCLK2时钟不过PCLK2时钟最高可达72MHz,而PCLK1最大36MHz。PCLK2对应APB2外设。PCLK1对应APB1外设。)
下次整理一下关于时钟配置!!!
11. CRC校验(可配置中断):在发送模式下CRC位可被作为最后一个值发送;
全双工模式下对接收到的最后一个字节自动进行CRC校验。
12. SPI一般配置步骤:
配置相关引脚的复用功能,使能SPIx时钟。调用函数:void GPIO_Init();
初始化SPIx,设置SPIx工作模式。调用函数:void SPI_Init();
使能SPIx。调用函数:void SPI_Cmd();
SPI传输数据。调用函数:void SPI_I2S_SendData();uint16_t SPI_I2S_ReceiveData();
查看SPI传输状态。调用函数:SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE
13. 相关代码(以STM32F103x为例):
1 #include "spi.h"
2
3 void SPI2_Init(void)
4 {
5 GPIO_InitTypeDef GPIO_InitStructure;
6 SPI_InitTypeDef SPI_InitStructure;
7
8 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能
9 RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2时钟使能
10
11 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
12 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15¸复用推挽输出
13 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
14 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
15
16 GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉
17
18 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:双向双线全双工
19 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式为主SPI
20 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI数据帧大小为8位
21 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟空闲为高电平
22 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //时钟极性为1,第二个跳变沿采集数据
23 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件或者硬件管理,软件通过SSI位
24 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频值位256
25 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从最高位开始
26 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式,因该是说7位数据之后校验,标记下,我去找找再补回来
27 SPI_Init(SPI2, &SPI_InitStructure); //根据指定参数初始化SPI
28
29 SPI_Cmd(SPI2, ENABLE); //使能SPI
30
31 SPI2_ReadWriteByte(0xff);//启动传输发送0xff,可随意赋值
32
33
34 }
35 //SPI 速度设置函数
36 //SpeedSet:
37 //SPI_BaudRatePrescaler_2 2分频
38 //SPI_BaudRatePrescaler_8 8分频
39 //SPI_BaudRatePrescaler_16 16分频
40 //SPI_BaudRatePrescaler_256 256分频
41
42 void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
43 {
44 assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
45 SPI2->CR1&=0XFFC7;
46 SPI2->CR1|=SPI_BaudRatePrescaler; //根据传入参数设置分频系数
47 SPI_Cmd(SPI2,ENABLE);
48
49 }
50
51 //SPIx 读写一个字节
52 //TxData:要写入的字节
53 //返回值:要读取的字节
54 u8 SPI2_ReadWriteByte(u8 TxData)
55 {
56 u8 retry=0;
57 while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查发送缓冲器空标志位是否为1
58 {
59 retry++;
60 if(retry>200)return 0;
61 }
62 SPI_I2S_SendData(SPI2, TxData); //通过SPI2发送一个数据
63 retry=0;
64
65 while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查接收缓存是否非空
66 {
67 retry++;
68 if(retry>200)return 0;
69 }
70 return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
71 }