串行外设接口SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线。
在某些芯片上,SPI接口可以配置为支持SPI协议或者支持I2S音频协议。 SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式,具体需参考操作手册
串行外设接口(SPI)允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。接口还能以多主配置方式工作。
它可用于多种用途,包括使用一条双向数据线的双线单工同步传输,还可使用CRC校验的可靠通信。
I2S也是一种3引脚的同步串行接口通讯协议。它支持四种音频标准,包括飞利浦I2S标准, MSB和LSB对齐标准,以及PCM标准。它在半双工通讯中,可以工作在主和从2种模式下。当它作为主设备时,通过接口向外部的从设备提供时钟信号。
起始条件:SS从高电平切换到地点哦
终止条件:SS从低电平切换到高电平
SPI有四种模式通过CPOL时钟极性和CPHA时钟相位来定义
数据采样==移入数据
以模式0为例,只需对相位和极性修改即可得到其他模式
//SPI写SS引脚电平 void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);//根据BitValue,设置SS引脚的电平 } //SPI写SCK引脚电平 void MySPI_W_SCK(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);//根据BitValue,设置SCK引脚的电平 } //SPI写MOSI引脚电平 void MySPI_W_MOSI(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue); //根据BitValue,设置MOSI引脚的电平,BitValue要实现非0即1的特性 } //SPI读MISO引脚电平 uint8_t MySPI_R_MISO(void) { return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6); //读取MISO电平并返回 } //SPI初始化 void MySPI_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA4、PA5和PA7引脚初始化为推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入 /*设置默认电平*/ MySPI_W_SS(1); //SS默认高电平 MySPI_W_SCK(0); //SCK默认低电平 } //SPI起始 void MySPI_Start(void) { MySPI_W_SS(0); //拉低SS,开始时序 } //SPI终止 void MySPI_Stop(void) { MySPI_W_SS(1); //拉高SS,终止时序 } //SPI交换传输一个字节,使用SPI模式0 uint8_t MySPI_SwapByte(uint8_t ByteSend) { uint8_t i, ByteReceive = 0x00; //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到 for (i = 0; i < 8; i ++) //循环8次,依次交换每一位数据 { MySPI_W_MOSI(ByteSend & (0x80 >> i)); //使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线 MySPI_W_SCK(1); //拉高SCK,上升沿移出数据 if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);} //读取MISO数据,并存储到Byte变量 //当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0 MySPI_W_SCK(0); //拉低SCK,下降沿移入数据 } return ByteReceive; //返回接收到的一个字节数据 // for(i=0;i<8;i++) // { // MySPI_W_MOSI(ByteSend & 0x80); // ByteSend<<=1; // MySPI_W_SCK(1); // if(MySPI_R_MISO()==1){ByteSend|=0x01;} // MySPI_W_SCK(0); // } // return ByteSend; }
SPI采用高位先行的模式
数据寄存器DR分为TDR和RDR
1.当需要发送数据时,第一个数据写入TDR,当移位寄存器没有数据进行移位时,TDR数据会立刻转入移位寄存器开始移位。
转入时刻会置状态寄存器的TXE位1,表示发送寄存器空,当检查TXE为1后,下一个数据便可写入TDR等候。
2.移位寄存器检测到有数据也会自动产生时钟将数据移出,在移出的过程中MISO的数据也会移入。
3.一旦数据移出完成,数据移入也完成了,这是移位寄存器就会将数据整体转出到RDR,
这时会置状态寄存器RXNE为1,表示接收寄存器非空。当检测到RXNE为1就需在下一个数据到来之前将数据读出来,不然数据会被覆盖。
模式三
#include "stm32f10x.h" // Device header //SPI写SS引脚电平,SS仍由软件模拟 void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);//根据BitValue,设置SS引脚的电平 } //SPI初始化 void MySPI_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //开启SPI1的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA4引脚初始化为推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA5和PA7引脚初始化为复用推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入 /*SPI初始化*/ SPI_InitTypeDef SPI_InitStructure; //定义结构体变量 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //模式,选择为SPI主模式 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //方向,选择2线全双工 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据宽度,选择为8位 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //先行位,选择高位先行 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //波特率分频,选择128分频 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //SPI极性,选择低极性 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS,选择由软件控制 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC多项式,暂时用不到,给默认值7 SPI_Init(SPI1, &SPI_InitStructure); //将结构体变量交给SPI_Init,配置SPI1 /*SPI使能*/ SPI_Cmd(SPI1, ENABLE); //使能SPI1,开始运行 /*设置默认电平*/ MySPI_W_SS(1); //SS默认高电平 } //SPI起始 void MySPI_Start(void) { MySPI_W_SS(0); //拉低SS,开始时序 } //SPI终止 void MySPI_Stop(void) { MySPI_W_SS(1); //拉高SS,终止时序 } //SPI交换传输一个字节,使用SPI模式0 uint8_t MySPI_SwapByte(uint8_t ByteSend) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET); //等待发送数据寄存器空 SPI_I2S_SendData(SPI1, ByteSend); //写入数据到发送数据寄存器,开始产生时序 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//等待接收数据寄存器非空 return SPI_I2S_ReceiveData(SPI1); //读取接收到的数据并返回 }