嵌入式-SPI的IO引脚初始化、模块初始化、数据的收发-Day16
目录
一、IO引脚的初始化
1.SPI模块简介
2.W25Q64简介
3.定位SPI的引脚位置
4.选择IO引脚的模式
5.选择IO的最大输出速度
6.代码实现IO引脚的初始化
二、模块初始化
1.SPI模块的基本工作原理
2.选择数据通信方向
3.数据宽度、极性、相位和比特位传输顺序
4.设置波特率
5.NSS的配置方式
6.SPl Init
7.初始化的代码
三、数据的收发
1.SPI数据收发的特点(全双工一定是收发'同时'的)
2.声明数据收发的编程接口
3.SPI数据收发过程简介(核心:收发同时)
4.代码编写
一、IO引脚的初始化
1.SPI模块简介
2.W25Q64简介
Flash 芯片:一种非易失性存储器,就像小U盘,掉电后数据也不会丢,用来存程序、固件、配置等。
Flash:指的就是这种存储技术,常见在单片机、路由器、手机里。
W25Q64:是 华邦(Winbond) 出的一款 串行Flash存储芯片,容量是 64Mbit(8MB),通过 SPI接口 和单片机通信,常用来存放程序代码或数据。
👉 总结一句:
Flash 就是掉电不丢的存储器(硬盘),W25Q64 是一款常见的 8MB 大小的 SPI Flash 芯片。

3.定位SPI的引脚位置
同理,在操作手册里面的引脚章节存储着这个引脚的位置以及重映射的位置
4.选择IO引脚的模式
5.选择IO的最大输出速度
根据官方的操作手册来说,最大高达80MHz,这里实际因为接的是面包板,不稳定,所以选择2MHz就行。
6.代码实现IO引脚的初始化
这里想随便选择一个IO引脚,结果选择了PA15,结果正好这个PA15比较特殊,其默认的功能是不是PA15,所以这里可以换一个其他普通引脚,或者像下面一样使用重映射,将引脚映射到PA15
#include "stm32f10x.h"
#include "Delay.h"
void My_SPI_IOInit(void);int main(void)
{My_SPI_IOInit();while(1){}
}
void My_SPI_IOInit(void){//首先IO引脚,这里选择的是重映射的RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);//PA15的重映射GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//IO引脚初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct1={0};GPIO_InitStruct1.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_5;GPIO_InitStruct1.GPIO_Speed=GPIO_Speed_2MHz;GPIO_InitStruct1.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_Init(GPIOB,&GPIO_InitStruct1);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct2={0};GPIO_InitStruct2.GPIO_Pin=GPIO_Pin_15;GPIO_InitStruct2.GPIO_Speed=GPIO_Speed_2MHz;GPIO_InitStruct2.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_Init(GPIOA,&GPIO_InitStruct2);//这里很容易忘记,因为这个是要发往从机的NSS,所以需要默认给高电压GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct3={0};GPIO_InitStruct3.GPIO_Pin=GPIO_Pin_4;GPIO_InitStruct3.GPIO_Mode=GPIO_Mode_IPU;GPIO_Init(GPIOB,&GPIO_InitStruct3);}
二、模块初始化
1.SPI模块的基本工作原理
从上往下看,上面最右边的就是状态寄存器,Tx的发送,RX的接收状态寄存器,ORV后面再说
之后就是左边的发送数据需要的发送寄存器和移位寄存器。之后就是接收数据的同理。
再下来就是时钟信号的传输,首先总开关,之后经过一个分频器,通过该分频器可以得到想要的波特率,在右边的时钟控制电路里面还有两极性和相位。
之后在下面就是模块通信方向的选择,是选择主/从,以及通信的模式
最后就是NSS,一般单片机作为主机是基本不使用NSS的。虽然不使用,但是会设置默认的为其连接默认的高电压(硬件)或者(软件)为其写1,使其高电压(因为NSS如果低电压了说明自己被当别人的从机了,所以需要设置一个默认的高电压,来恒定自己的主机身份)
在模块初始化的时候一般进行配置参数的时候,也要考虑从机的配置(配置手册)从而配置合适的参数
2.选择数据通信方向
3.数据宽度、极性、相位和比特位传输顺序
发现这些配置都有多种,而具体选择哪种,需要去连接的设备的操作手册选择合适的模式
从手册发现。W25Q64支持8b,低极性的1相位,MSB的下的配置,注意不支持高级性和2相位

4.设置波特率
5.NSS的配置方式
6.SPl Init
7.初始化的代码
#include "stm32f10x.h"
#include "Delay.h"
void My_SPI_IOInit(void);int main(void)
{My_SPI_IOInit();while(1){}
}
void My_SPI_IOInit(void){//首先IO引脚,这里选择的是重映射的RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);//PA15的重映射GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//IO引脚初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct1={0};GPIO_InitStruct1.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_5;GPIO_InitStruct1.GPIO_Speed=GPIO_Speed_2MHz;GPIO_InitStruct1.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_Init(GPIOB,&GPIO_InitStruct1);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct2={0};GPIO_InitStruct2.GPIO_Pin=GPIO_Pin_15;GPIO_InitStruct2.GPIO_Speed=GPIO_Speed_2MHz;GPIO_InitStruct2.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_Init(GPIOA,&GPIO_InitStruct2);//这里很容易忘记,因为这个是要发往从机的NSS,所以需要默认给高电压GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct3={0};GPIO_InitStruct3.GPIO_Pin=GPIO_Pin_4;GPIO_InitStruct3.GPIO_Mode=GPIO_Mode_IPU;GPIO_Init(GPIOB,&GPIO_InitStruct3);//SPI模块的初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);SPI_InitTypeDef SPI_InitStruct={0};SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;SPI_InitStruct.SPI_Mode=SPI_Mode_Master;SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_64;SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;SPI_Init(SPI1,&SPI_InitStruct);//设置软件的NSS的高电压模式SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);}
三、数据的收发
1.SPI数据收发的特点(全双工一定是收发'同时'的)
2.声明数据收发的编程接口
3.SPI数据收发过程简介(核心:收发同时)
首先,闭合总开关
之后,先向发送数据寄存器写入一个字节;
之后开始Size-1次的循环,因为首先是发了一个字节,当发送寄存器空了,就开始传入下一个要发送的字节,而在移位寄存器里面的前一个字节一位位发送的时候,相应的也是在接收数据的移位寄存器里面也是在一位位比特位的接收,因为是收发同时的。
最后接收最后一个字节然后断开开关
4.代码编写
这里自带的SPI_I2S_SendData()的I2S是涉及音频相关的,因为和SPI的内容逻辑差不多就写为同一个方法
#include "stm32f10x.h"
#include "Delay.h"
void My_SPI_IOInit(void);
void App_SPI_MasterTransmitReceiv(SPI_TypeDef *SPIx,const uint8_t *pDataTx,uint8_t *pDataRx,uint16_t Size);
int main(void)
{My_SPI_IOInit();uint8_t pDataTx[]={0,1,2,3};uint8_t pDataRx[100];App_SPI_MasterTransmitReceiv(SPI1,pDataTx,pDataRx,4);while(1){}
}
void My_SPI_IOInit(void){//首先IO引脚,这里选择的是重映射的RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);//PA15的重映射GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//IO引脚初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct1={0};GPIO_InitStruct1.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_5;GPIO_InitStruct1.GPIO_Speed=GPIO_Speed_2MHz;GPIO_InitStruct1.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_Init(GPIOB,&GPIO_InitStruct1);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct2={0};GPIO_InitStruct2.GPIO_Pin=GPIO_Pin_15;GPIO_InitStruct2.GPIO_Speed=GPIO_Speed_2MHz;GPIO_InitStruct2.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_Init(GPIOA,&GPIO_InitStruct2);//这里很容易忘记,因为这个是要发往从机的NSS,所以需要默认给高电压GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct3={0};GPIO_InitStruct3.GPIO_Pin=GPIO_Pin_4;GPIO_InitStruct3.GPIO_Mode=GPIO_Mode_IPU;GPIO_Init(GPIOB,&GPIO_InitStruct3);//SPI模块的初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);SPI_InitTypeDef SPI_InitStruct={0};SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;SPI_InitStruct.SPI_Mode=SPI_Mode_Master;SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_64;SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;SPI_Init(SPI1,&SPI_InitStruct);//设置软件的NSS的高电压模式SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);}void App_SPI_MasterTransmitReceiv(SPI_TypeDef *SPIx,const uint8_t *pDataTx,uint8_t *pDataRx,uint16_t Size){SPI_Cmd(SPIx,ENABLE);//发送第一个数据SPI_I2S_SendData(SPIx,pDataTx[0]);//开始发送Size-1个数据for(int i=0;i<Size-1;i++){while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE)==RESET);SPI_I2S_SendData(SPIx,pDataTx[i+1]);while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE)==RESET);pDataRx[i]=SPI_I2S_ReceiveData(SPIx);}//接收最后一个数据while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE)==RESET);pDataRx[Size-1]=SPI_I2S_ReceiveData(SPIx);SPI_Cmd(SPIx,DISABLE);}