本节为STM32内部硬件外设—SPI。通过stm32内部SPI 硬件读写W25Q64.
- STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担
- 可配置8位/16位数据帧、高位先行/低位先行
- 时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256) 支持多主机模型、主或从操作
- 可精简为半双工/单工通信
- 支持DMA 兼
- 容I2S协议
SPI1、SPI2挂载在不同的APB总线。
一、SPI硬件介绍
1.1、SPI硬件框图
其中LSBFIRST 是用于配置高位先行还是低位先行。
1.2、SPI硬件传输模式
1.2.1、非连续传输
在TNX标志位置1,TDR为空,软件写入数据至SPI_DR,随后TDR(SPI_DR)值为1,TNX值为0,此时发送缓存数据区也为空,数据立即转入发送数据寄存器进行发送;波形产生,TNX值重新置为1(表示下一个数据可以写入TDR等待),但此时并不急于写入数据,而是等待第一个字节时序结束(意味接收一个字节也完成了),此时RXNE=1,读取一个字节完成后,在写入下一个数据。
二、SPI相关代码简介
2.1、SPI_Init()*
//根据SPI_InitStruct中指定的参数初始化SPIx外围设备。
SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
2.2、SPI_StructInit()*
//用默认值填充每个SPI_InitStruct成员
SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct);
2.3、SPI_Cmd()*
//启用或禁用指定的SPI外围设备。
SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
2.4、SPI_I2S_ITConfig()
SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
//启用或禁用指定的SPI/I2S中断
SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
2.5、SPI_I2S_DMACmd()
//启用或禁用SPIx/I2Sx DMA接口。
SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
2.6、SPI_I2S_SendData()*
//通过SPIx/I2Sx外围设备传输数据
SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
2.7、SPI_I2S_ReceiveData()*
//返回SPIx/I2Sx外围设备最近接收到的数据。
SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
2.8、SPI_I2S_GetFlagStatus()*
//检查指定的SPI/I2S标志是否已设置
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);
2.9、SPI_I2S_ClearFlag()*
//清除SPIx CRC错误(CRCERR)标志。
SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
2.10、SPI_I2S_GetITStatus()
//检查指定的SPI/I2S中断是否已发生
SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
三、现象代码
这是STM32 SPI的配置流程图。主要配置内容有以下几点:
【1】开启GPIO、SPI 的时钟
【2】初始化 GPIO、SPI ,
【3】开关控制 Cmd
3.1、MySPI.c文件
由于 SPI 硬件读取 W25Q64 功能代码相较于 软件读写,主要差异体现在SPI 通信底层代码的不同,软件主要通过手动翻转引脚电平,而硬件主要通过自身硬件电路实现电平自由 翻转,因此以下代码主要是MySPI.C文件的更改,其余文件代码无变动 。
void MySPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 ;//SS
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//MISO
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;//SCLK&MOSI
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStrycture;
SPI_InitStrycture.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_128;
SPI_InitStrycture.SPI_CPHA=SPI_CPHA_1Edge; //CPHA=0
SPI_InitStrycture.SPI_CPOL=SPI_CPOL_Low; //CPOL=0
SPI_InitStrycture.SPI_CRCPolynomial=7;
SPI_InitStrycture.SPI_DataSize=8;//8为数据帧
SPI_InitStrycture.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//全双工通信
SPI_InitStrycture.SPI_FirstBit=SPI_FirstBit_MSB;//高位输出
SPI_InitStrycture.SPI_Mode=SPI_Mode_Master;//主设备
SPI_InitStrycture.SPI_NSS=SPI_NSS_Soft;//软件控制
SPI_Init(SPI1,&SPI_InitStrycture);
SPI_Cmd(SPI1,ENABLE );
}
由于SCLK 与MOSI SHI 是有硬件外设控制输出信号,因此 引脚配置为复用推挽输出 AF_PP
【1】SPI_BaudRatePrescaler:指定波特率预分频器值,用于配置发送和接收SCK时钟
【2】SPI_CPHA:指定位捕获的时钟活动边沿,即选择CPHA的值(CPHA=0 or CPHA=1)
【3】.SPI_CPOL:指定串行时钟稳定状态。即选择 CLOP值 (CLOP=0 or CLOP=1)
【4】SPI_CRCPolynomial:指定用于CRC计算的多项式
【5】SPI_DataSize:用于配置SPI通信中每个数据帧的数据位数的参数。即决定收发数据是8位还是16位
【6】SPI_Direction:用于配置SPI通信的数据传输方向的参数。
【7】SPI_FirstBit:指定数据传输是从MSB位开始还是从LSB位开始。即LSBFIRST 位觉得高位先行还是低位先行。
【8】SPI_Mode:用于配置SPI通信的工作模式,即选择设备室主设备还是从设备
【9】SPI_NSS 指定NSS信号是由硬件(NSS引脚)管理还是由使用SSI位的软件管理
3.2、 引脚控制&基本时序
void MySPI_W_SS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA ,GPIO_Pin_4,(BitAction)BitValue);
}
void MySPI_Star(void)
{
MySPI_W_SS(0);
}
void MySPI_Stop(void)
{
MySPI_W_SS(1);
}
uint8_t MySPI_WriteRead_0(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);
}
【1】相较于上节 内容,本文件实例代码删除了 MySPI_W_SCLK()、MySPI_W_MOSI()、MySPI_R_MISO(),如下图所示。
【2】MySPI_WriteRead_0 函数考虑使用 if 语句实现,但是发现其逻辑与 while 语句逻辑存在不同,且无法实现设想功能,因此使用while 语句。
更新:可以使用以下语句
uint8_t MySPI_WriteRead_0(uint8_t Bytesend)
{
uint8_t i=0x00;
if(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);;
}
本节内容到此结束,如有补充将及时更新。