目录
前言
一、相关库函数介绍
1.初始化
2.写入数据
3.接收数据
4.获取标志位
二、软件SPI读写W25Q64
前言
上一期我们学习了stm32的SPI外设(上一期链接:stm32入门-----硬件SPI外设-CSDN博客),那么我们本期就来通过stm32的SPI外设来去实现之前软件读写W25Q64的功能。(视频:[11-5] 硬件SPI读写W25Q64_哔哩哔哩_bilibili)
本期代码我已上传至百度网盘,需要的可以自己下载。
通过百度网盘分享的文件:硬件SPI读写W25Q64(1).rar
链接:https://pan.baidu.com/s/1JZiDtAC9tOKsidcS_BGesA?pwd=0721
提取码:0721
一、相关库函数介绍
这里我们点开项目的library库,然后看到下面这两个文件,这两个文件就是存储SPI的底层功能的函数。
下面这些就是关于SPI的相关函数,但是I2S的也是跟SPI弄到一起了,不过我们不需要用到,这里本期要用到的我已经在下面画出来了。
1.初始化
这个函数我们已经再熟悉不过了,也就是定义结构体,对结构体进行配置,然后放入到下面这个函数去初始化。
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
下面看一个示例:
//SPI结构体初始化
SPI_InitTypeDef SPI_initstruct;
SPI_initstruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//波特率预分频器,对SPI总线上的时钟进行分频,这里是SPI1对应的APB2时钟为72MHz
SPI_initstruct.SPI_CPHA = SPI_CPHA_1Edge;//配置模式,第几个边沿采样(模式0第一个边沿采样)
SPI_initstruct.SPI_CPOL = SPI_CPOL_Low;//配置模式,SCK默认情况下电平(模式0是低电平)
SPI_initstruct.SPI_CRCPolynomial = 7;//CRC校验,这里选默认值0x0007就行了
SPI_initstruct.SPI_DataSize = SPI_DataSize_8b;//配置数据帧,当前选择一个字节为一个数据,8位
SPI_initstruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//选择工作模式,当前选择双线全双工
SPI_initstruct.SPI_FirstBit = SPI_FirstBit_MSB;//选择低位先行还是高位先行,这里选择高位先行
SPI_initstruct.SPI_Mode = SPI_Mode_Master; //选择SPI的模式,当前设备为主机还是从机,这里选择主机
SPI_initstruct.SPI_NSS = SPI_NSS_Soft; //选择SS SPI设备,这里使用软件NSS模式
SPI_Init(SPI1, &SPI_initstruct);
2.写入数据
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
把数据写入到发送数据寄存器DR中。
3.接收数据
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
读取接收数据寄存器DR里面的数据,返回出来。
4.获取标志位
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
这个函数的参数选择如下,我们可以去通过这个函数来去获取到TEX和RNEX的标志位,从而去判断数据是否能继续写入或者读取操作。
二、软件SPI读写W25Q64
现象:
电路连接图:
工程文件如下,这里我们只需要在软件SPI读写W25Q64的代码进行修改就行了,这里要修改的部分实际上就是底层部分,之前是软件底层,那这里我们只需要改MySPI.c文件的底层即可。
MySPI.c代码如下:
#include "stm32f10x.h" // Device header
//对SS的写入
void SPI_W_SS(uint8_t val) {
GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)val);
}
void SPI_init() {
/*开启时钟*/
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; //SS
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; //MOSI和SCK
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);
//SPI结构体初始化
SPI_InitTypeDef SPI_initstruct;
SPI_initstruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//波特率预分频器,对SPI总线上的时钟进行分频,这里是SPI1对应的APB2时钟为72MHz
SPI_initstruct.SPI_CPHA = SPI_CPHA_1Edge;//配置模式,第几个边沿采样(模式0第一个边沿采样)
SPI_initstruct.SPI_CPOL = SPI_CPOL_Low;//配置模式,SCK默认情况下电平(模式0是低电平)
SPI_initstruct.SPI_CRCPolynomial = 7;//CRC校验,这里选默认值0x0007就行了
SPI_initstruct.SPI_DataSize = SPI_DataSize_8b;//配置数据帧,当前选择一个字节为一个数据,8位
SPI_initstruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//选择工作模式,当前选择双线全双工
SPI_initstruct.SPI_FirstBit = SPI_FirstBit_MSB;//选择低位先行还是高位先行,这里选择高位先行
SPI_initstruct.SPI_Mode = SPI_Mode_Master; //选择SPI的模式,当前设备为主机还是从机,这里选择主机
SPI_initstruct.SPI_NSS = SPI_NSS_Soft; //选择SS SPI设备,这里使用软件NSS模式
SPI_Init(SPI1, &SPI_initstruct);
//使能
SPI_Cmd(SPI1, ENABLE);
SPI_W_SS(1);//SS为高电平表示不选中从机,
}
//1.起始
void SPI_Start() {
//SS置为低电平
SPI_W_SS(0);
}
// 2.终止
void SPI_Stop() {
// SS置回高电平
SPI_W_SS(1);
}
// 交换字节 4步搞定
uint8_t SPI_SwapByte(uint8_t byteSend) {
//先发送再有接收
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);//等待TXE标志位,表示是否可以写入数据到发送寄存器TDR
SPI_I2S_SendData(SPI1, byteSend);//写入数据到发送数据寄存器
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//等待RXNE标志位,表示是否可以从接收数据寄存器RDR读取数据
return SPI_I2S_ReceiveData(SPI1);//返回读取到的数据
}
main.c文件代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"
uint8_t MID;
uint16_t DID;
uint8_t arrWrite[] = { 0x01,0x02,0x03,0x04 };
uint8_t arrRead[4];
int main(void)
{
OLED_Init();
W25Q64_init();
OLED_ShowString(1, 1, "MID: DID:");
OLED_ShowString(2, 1, "W:");
OLED_ShowString(3, 1, "R:");
W25Q64_ReadID(&MID, &DID);
OLED_ShowHexNum(1, 5, MID, 2);
OLED_ShowHexNum(1, 13, DID, 4);
W25Q64_SectorErase(0x000000);//扇区的地址取决于前面三位,后面三位无论怎么变都是表示同一个扇区
W25Q64_PagePro(0x0000FF, arrWrite, 4);//写入
W25Q64_ReadData(0x0000FF, arrRead, 4);//读取
// 展示发送的数据
for (uint8_t i = 0;i < 4;i++) {
OLED_ShowHexNum(2, 3+3*i, arrWrite[i],2);
}
// 展示读取的数据
for (uint8_t i = 0;i < 4;i++) {
OLED_ShowHexNum(3, 3+3*i, arrRead[i],2);
}
while (1) {
}
}
以上就是本期的全部内容了,我们下次见!
今日壁纸: