一、SPI外设
SPI外设简介:
SPI框图:
此处配置的为低位先行的模式,由LSBFIRST控制位进行控制;MOSI和MISO部分交叉相连因为STM32可以做主机也可以做从机,可以进行主从模式转换, 从机模式下两引脚功能翻转,MOSI作为输入走交叉路线数据进入寄存器;全双工的通信发送和接收寄存器是分离的,半双工是一体的;NSS引脚的作用更倾向于多主机模型的实现,所以我们在实验中不直接使用NSS来指定从机,而是指定一个GPIO口来执行SS的作用
SPI基本结构:
传输时序
主模式全双工连续传输
此模式借助缓冲区进行连续传输,但是相对复杂不方便封装,如果对性能没有极致要求不使用,所以本节使用非连续传输的方法
此时序并不是常规的发送数据1-接收数据1-发送数据二-接收数据二,而是发送数据一--发送数据二--接收数据一--发送数据三--接收数据二
非连续传输:
此时序条例更清晰,在发送一个数据后会等待回传的数据读取完成后再发送下一个数据
1.等待TXE为1,写入TDR
2.等待RXNE为1,读取RDR数据
不断重复上述过程就是非连续传输的时序 ,因此方便封装在一个函数中需要使用时就调用,但是相应的,此时序会导致字节间产生间隙,拖慢整体节奏,在SCK频率低的时候影响不大,但在频率非常高的时候会严重的拖后腿,因此想要更高的效率则需要使用连续传输或者直接引入DMA进行数据的转运
二、硬件SPI通信读写W25Q64实验
此实验就是将上一小节的软件SPI读写的底层函数实现方式由软件改为硬件
配置步骤
1.开启SPI时钟,初始化GPIO,其中SCK、MOSI是硬件外设控制的输出引脚,配置为复用推挽输出,MISO为上拉输入
2.结构体配置SPI外设,使能SPI
3.参考时序进行数据交换
部分库函数解释
//将数据写入TDR中
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
//将RDR中的数据读出
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
//8或16位数据帧格式选择
void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize);
代码部分
因为只改变了底层代码的部分所以此处只放置MySPI底层部分代码
#include "stm32f10x.h" // Device header
//封装指定设备线的电平改变函数
void MySPI_W_SS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
void MySPI_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//通用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//外设控制的复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_5|GPIO_Pin_7;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitTypeDef SPI_S;
SPI_S.SPI_Mode=SPI_Mode_Master;//主机模式
SPI_S.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//全双工双向通信
SPI_S.SPI_DataSize=SPI_DataSize_8b;//8位数据帧
SPI_S.SPI_FirstBit=SPI_FirstBit_MSB;//高位现行
SPI_S.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_128;//128分频
SPI_S.SPI_CPOL=SPI_CPOL_Low;//默认高电平
SPI_S.SPI_CPHA=SPI_CPHA_1Edge;//第一边沿采样
SPI_S.SPI_NSS=SPI_NSS_Soft;//软件NSS
SPI_S.SPI_CRCPolynomial=7;//CRC校验多项式,默认7
SPI_Init(SPI1,&SPI_S);
SPI_Cmd(SPI1,ENABLE);
MySPI_W_SS(1);//SS设置默认高电平
}
//时序基本单元:起始信号
void MySPI_START(void)
{
MySPI_W_SS(0);
}
//终止信号
void MySPI_STOP(void)
{
MySPI_W_SS(1);
}
//硬件根据时序控制数据交换
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=SET);//等待TXE
SPI_I2S_SendData(SPI1,ByteSend);//ByteSend写入TDR,然后再自动转入移位寄存器,写入数据后TXE自动清除
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=SET);//等待RXNE
return SPI_I2S_ReceiveData(SPI1);//返回收到的数据,读取数据后RXNE自动清除
}