SPI
目录
[1.6.1. 8 bit](#1.6.1. 8 bit)
[1.6.2. 16 bit](#1.6.2. 16 bit)
[5.3.1. 2线全双工](#5.3.1. 2线全双工)
[5.3.2. 2线只读](#5.3.2. 2线只读)
[5.3.3. 1线发送](#5.3.3. 1线发送)
[5.3.4. 1线接收](#5.3.4. 1线接收)
一、电路结构和通信协议
1.1.SPI总线的电路结构

**MOSI(Master Output Slave Input):**主发从收
**MISO(Master Input Slave Output):**主收从发
**SCK(Serial Clock):**串行时钟
**NSS(Negative Slave Select):**从机选择(低电压有效)
1.2.通信流程及波形

1.2.1.建立通信
主机通过IO引脚
向通信从机的NSS引脚发送低电压
向其他从机的NSS引脚发送高电压
1.2.2.发送时钟
通信时,主机通过SCK引脚
向通信从机的SCK引脚发送时钟信号
1.2.3.发送数据
每一个时钟周期在MOSI和MISO上各传输一个bit位
低电平表示0,高电平表示1
主机在发送一个bit位的同时,从机也会返回一个bit位
1.3.参数1:时钟信号的极性和相位
1.3.1.极性

**低极性:**空闲状态下,SCK引脚保持低电平

**高极性:**空闲状态下,SCK引脚保持高电平
1.3.2.相位
**第1边沿:**空闲状态后,第一个出现的边沿
**第2边沿:**空闲状态后,第二个出现的边沿

1.4.SPI的4种模式
1.4.1.模式0
低极性第一边沿采集

1.4.2.模式1
低极性第二边沿采集

1.4.3.模式2
高极性第一边沿采集

1.4.4.模式3
高极性第二边沿采集

1.5.参数2:传输顺序
每个字节比特位的传输顺序
1.5.1.低位先行
先传最低有效位(LSB First)

1.5.2.高位先行
先传最高有效位(MSB First)

1.6.参数3:数据宽度
每次传输比特位的个数
1.6.1. 8 bit

1.6.2. 16 bit

二、按钮驱动程序编写
使用按钮切换板载LED亮灭状态
2.1.连接电路
按钮接法:

PA0 输入上拉模式
- 按钮按下:0
- 按钮松开:1
板载LED接法:

PC13 输出开漏模式
- 写1:LED熄灭
- 写0:LED点亮

2.2.初始化板载LED和按钮
cpp
#include "stm32f10x.h"
void App_Button_Init(void);
void App_OnBoardLED_Init(void);
int main(void)
{
App_Button_Init();//按钮初始化
App_OnBoardLED_Init();//LED初始化
while(1)
{
}
}
void App_Button_Init(void)
{
/*启动GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA0引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA0引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
void App_OnBoardLED_Init(void)
{
/*启动GPIOC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PC13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*输出开漏模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA0引脚*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
2.3.按钮程序的基本原理

松开按钮时为高电平1
按下按钮时为低电平0
创建变量prev和cur,prev记录上次引脚电平,cur记录当前引脚电平
当上次为1,当前为0时:按下按钮
当上次为0,当前为1时:松开按钮
cpp
int main(void)
{
App_Button_Init();//按钮初始化
App_OnBoardLED_Init();//LED初始化
uint8_t previous = Bit_SET;
uint8_t current = Bit_SET;
while(1)
{
previous = current;//保存上次状态
current = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);//更新当前状态
if(previous != current)//如果上次与当前不等
{
if(current == Bit_SET)//按钮松开
{
//改变LED的亮灭状态
}
else//按钮按下
{
}
}
}
}
改变LED亮灭状态

cpp
void GPIO_WriteBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin,BitAction BitVal);
**作用:**向输出数据寄存器写0/1
cpp
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
**作用:**读取输出数据寄存器的一个比特位
cpp
int main(void)
{
App_Button_Init();//按钮初始化
App_OnBoardLED_Init();//LED初始化
uint8_t previous = Bit_SET;
uint8_t current = Bit_SET;
while(1)
{
previous = current;//保存上次状态
current = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);//更新当前状态
if(previous != current)//如果上次与当前不等
{
if(current == Bit_SET)//按钮松开
{
//读取输出数据寄存器的值
if(GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13) == Bit_SET)//如果是1
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//写0
}
else//如果是0
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//写1
}
}
else//按钮按下
{
}
}
}
}
2.4.按钮消抖的原理


cpp
#include "stm32f10x.h"
#include "delay.h"
void App_Button_Init(void);
void App_OnBoardLED_Init(void);
int main(void)
{
App_Button_Init();//按钮初始化
App_OnBoardLED_Init();//LED初始化
uint8_t previous = Bit_SET;
uint8_t current = Bit_SET;
while(1)
{
previous = current;//保存上次状态
current = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);//更新当前状态
if(previous != current)//如果上次与当前不等
{
if(current == Bit_SET)//按钮松开
{
//读取输出数据寄存器的值
if(GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13) == Bit_SET)//如果是1
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//写0
}
else//如果是0
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//写1
}
}
else//按钮按下
{
}
Delay(10);
}
}
}
三、按钮代码的封装
3.1.搭建电路

3.2.串口的初始化

cpp
#include "stm32f10x.h"
#include "usart.h"//引入串口头文件
/*声明串口初始化函数*/
void App_USART1_Init(void);
int main(void)
{
/*初始化串口*/
App_USART1_Init();
/*测试串口*/
My_USART_SendString(USART1,"Hello world\r\n");
while(1)
{
}
}
/*创建串口初始化函数*/
void App_USART1_Init(void)
{
//#1:初始化IO引脚 PA9 Tx PA10 Rx
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:初始化USART1
/*开启USART1时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
/*声明USART1结构变量*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合USART1总开关*/
USART_Cmd(USART1,ENABLE);
}
3.3.按钮的初始化
cpp
void My_Button_Init(Button_TypeDef* Button,Button_InitTypeDef* Button_InistStruct);
解析:
- 参数1:按钮结构地址
- 参数2:初始化的参数结构体地址
**作用:**初始化按钮,配置按钮的各种参数(包括IO引脚)
**补充:**Button_InitTypeDef结构(按钮参数菜单)
cpp
struct GPIO_InitTypeDef
{
GPIO_TypeDef* GPIOx;
uint16_t GPIO_Pin;
uint32_t LongPressTime;
uint32_t LongPressInterval;
uint32_t ClickInterval;
void(*button_pressed_cb)(void);
void(*button_released_cb)(void);
void(*button_clicked_cb)(uint8_t clicks);
void(*button_Long_pressed_cb)(uint8_t clicks);
};
分析:
**1.GPIOx:**IO引脚的端口号
**2.GPIO_Pin:**IO引脚的引脚编号
**3.LongPressTime:**长按的时间阈值(单位:毫秒,0表示默认1000)
**4.LongPressInterval:**长按后持续触发的时间间隔(0表示默认100)
**5.ClickInterval:**连击的最大间隔(0表示默认200)
**6.button_pressed_cb:**按钮按下
**7.button_released_cb:**按钮抬起
**8.button_clicked_cb:**按钮点击
**9.button_Long_pressed_cb:**按钮长按
**注:**最后四个回调函数没有就填0
cpp
#include "stm32f10x.h"
#include "usart.h"//引入串口头文件
#include "button.h"//引入按钮头文件
/*声明按钮结构变量*/
Button_TypeDef button1;
/*声明串口初始化函数*/
void App_USART1_Init(void);
/*声明按钮初始化函数*/
void App_Button_Init(void);
int main(void)
{
/*初始化串口*/
App_USART1_Init();
/*初始化按钮1*/
App_Button_Init();
while(1)
{
/*按钮进程函数*/
My_Button_Proc(&button1);
}
}
/*创建串口初始化函数*/
void App_USART1_Init(void)
{
//#1:初始化IO引脚 PA9 Tx PA10 Rx
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:初始化USART1
/*开启USART1时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
/*声明USART1结构变量*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合USART1总开关*/
USART_Cmd(USART1,ENABLE);
}
/*创建按钮初始化函数*/
void App_Button_Init(void)
{
/*声明按钮初始化结构变量*/
Button_InitTypeDef Button_InitStruct;
/*IO引脚的端口号设为GPIOA*/
Button_InitStruct.GPIOx = GPIOA;
/*IO引脚的引脚编号设为0*/
Button_InitStruct.GPIO_Pin = GPIO_Pin_0;
/*连击的最大间隔设为0*/
Button_InitStruct.ClickInterval = 0;
/*长按的时间阈值设为0*/
Button_InitStruct.LongPressTime = 0;
/*长按后持续触发的时间间隔设为0*/
Button_InitStruct.LongPressTickInterval = 0;
/*按钮点击*/
Button_InitStruct.button_clicked_cb = 0;
/*按钮按下*/
Button_InitStruct.button_pressed_cb = 0;
/*按钮抬起*/
Button_InitStruct.button_released_cb = 0;
/*按钮长按*/
Button_InitStruct.button_long_pressed_cb = 0;
/*初始化按钮1*/
My_Button_Init(&button1,&Button_InitStruct);
}
3.4.进程函数
cpp
void My_Button_Proc(Button_TypeDef *Button);
**注:**该方法需要在main函数的while循环中调用

3.5.回调函数
3.5.1.按钮点击函数
cpp
void button_clicked_cb(uint8_t clicks);
解析:
**参数:**点击按钮的次数
示例:
cpp
/*创建按钮点击函数*/
void button_clicked_cb(uint8_t clicks)
{
/*单击*/
if(clicks == 1)
{
cnt++;//计次加一
}
else if(clicks == 2)//双击
{
cnt = 0;//计次清零
}
}
3.5.2.按钮长按函数
cpp
void button_long_pressed_cb(uint8_t ticks);
解析:
**参数:**长按触发的次数
示例:
cpp
/*创建按钮长按函数*/
void button_long_pressed_cb(uint8_t ticks)
{
cnt++;//计次加1
}
将设置好的回调函数赋给按钮结构体的成员变量
cpp
/*按钮点击*/
Button_InitStruct.button_clicked_cb = button_clicked_cb;
/*按钮按下*/
Button_InitStruct.button_pressed_cb = 0;
/*按钮抬起*/
Button_InitStruct.button_released_cb = 0;
/*按钮长按*/
Button_InitStruct.button_long_pressed_cb = button_long_pressed_cb;
总代码:
cpp
#include "stm32f10x.h"
#include "usart.h"//引入串口头文件
#include "button.h"//引入按钮头文件
/*声明按钮结构变量*/
Button_TypeDef button1;
/*实验计数变量*/
uint32_t cnt = 0;
/*声明串口初始化函数*/
void App_USART1_Init(void);
/*声明按钮初始化函数*/
void App_Button_Init(void);
/*声明按钮点击函数*/
void button_clicked_cb(uint8_t clicks);
/*声明按钮长按函数*/
void button_long_pressed_cb(uint8_t ticks);
int main(void)
{
/*初始化串口*/
App_USART1_Init();
/*初始化按钮1*/
App_Button_Init();
while(1)
{
My_Button_Proc(&button1);
}
}
/*创建串口初始化函数*/
void App_USART1_Init(void)
{
//#1:初始化IO引脚 PA9 Tx PA10 Rx
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:初始化USART1
/*开启USART1时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
/*声明USART1结构变量*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合USART1总开关*/
USART_Cmd(USART1,ENABLE);
}
/*创建按钮初始化函数*/
void App_Button_Init(void)
{
/*声明按钮结构变量*/
Button_InitTypeDef Button_InitStruct;
/*IO引脚的端口号设为GPIOA*/
Button_InitStruct.GPIOx = GPIOA;
/*IO引脚的引脚编号设为0*/
Button_InitStruct.GPIO_Pin = GPIO_Pin_0;
/*连击的最大间隔设为0*/
Button_InitStruct.ClickInterval = 0;
/*长按的时间阈值设为0*/
Button_InitStruct.LongPressTime = 0;
/*长按后持续触发的时间间隔设为0*/
Button_InitStruct.LongPressTickInterval = 0;
/*按钮点击*/
Button_InitStruct.button_clicked_cb = button_clicked_cb;
/*按钮按下*/
Button_InitStruct.button_pressed_cb = 0;
/*按钮抬起*/
Button_InitStruct.button_released_cb = 0;
/*按钮长按*/
Button_InitStruct.button_long_pressed_cb = button_long_pressed_cb;
/*初始化按钮1*/
My_Button_Init(&button1,&Button_InitStruct);
}
/*创建按钮点击函数*/
void button_clicked_cb(uint8_t clicks)
{
/*单击*/
if(clicks == 1)
{
cnt++;//计次加一
My_USART_Printf(USART1,"%d",cnt);
}
else if(clicks == 2)//双击
{
cnt = 0;//计次清零
My_USART_Printf(USART1,"%d",cnt);
}
}
/*创建按钮长按函数*/
void button_long_pressed_cb(uint8_t ticks)
{
cnt++;//计次加1
My_USART_Printf(USART1,"%d",cnt);
}
四、IO引脚初始化
4.1.SPI模块简介

SPI模块是单片机上的片上外设,给单片机通过SPI通信接口
4.2.W25Q64简介

单片机内部Flash:类似于内部硬盘,存放编写的程序
W25Q64的Flash:类似于移动硬盘,存放单独的数据
通过SPI总线与单片机通信

**VCC:**接电源+
**GND:**接地
**DI(Slave Data Input MOSI):**接主机的MOSI
**DO(Slave Data Output MISO):**接主机的MISO
**CLK(Serial Clock SCK):**接主机的SCK
4.3.定位SPI的引脚位置



4.4.选择IO引脚的模式

4.5.选择IO最大输出速度

4.6.初始化IO引脚

cpp
#include "stm32f10x.h"
//声明SPI1初始化函数
void App_SPI1_Init(void);
int main(void)
{
App_SPI1_Init();
while(1)
{
}
}
//创建SPI1初始化函数
void App_SPI1_Init(void)
{
//#1:初始化IO引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*开启SPI1重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*PB3 SCK AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB3引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB4 MISO IPU*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB4引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PB4*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB5 MOSI AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PA15 普通IO Out_PP 2MHz*/
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*选择PA15引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
/*通用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA15*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
五、SPI模块的初始化
5.1.SPI模块的基本工作原理

**MOSI:**主发从收
**MISO:**主收从发
**SCK:**串行时钟
**NSS(只在从机模式使用):**从机选择
**发送数据:**将数据写入发送数据寄存器,再由移位寄存器逐个bit位通过MOSI向外发送
**接收数据:**通过MISO接收波形,逐个bit位进行解析,移位寄存器填满后写入接收数据寄存器
**时钟信号:**单片机内部产生的时钟信号经过分频器产生不同速度的波特率
**时钟控制:**控制时钟的极性、相位、波特率在SCK引脚输出相应的波形
**NSS:**作主机时NSS引脚也需配置
**SPI标志位:**TxE,RxNE,ORV
5.2.SPI_Init
cpp
void SPI_Init(SPI_TypeDef* SPIx,SPI_InitTypeDef* SPI_InitStruct);
解析:
- 参数1:SPI的名称 SPI1,SPI2
- 参数2:初始化的参数结构体地址
作用: 初始化SPI,配置SPI的各种参数
**补充:**SPI_InitTypeDef结构(SPI参数菜单)
cpp
struct SPI_InitTypeDef
{
uint16_t SPI_Direction;
uint16_t SPI_Mode;
uint16_t SPI_DataSize;
uint16_t SPI_CPOL;
uint16_t SPI_CPHA;
uint16_t SPI_NSS;
uint16_t SPI_BaudRatePrescaler;
uint16_t SPI_FirstBit;
};
分析:
**SPI_Direction:**选择SPI通信方向
- SPI_Direction_2Lines_FullDuplex:2线全双工
- SPI_Direction_2Lines_ReadOnly:2线只读
- SPI_Direction_1Lines_Rx:单线接收
- SPI_Direction_1Lines_Tx:单线发送
**SPI_Mode:**选择SPI模式
- SPI_Master:主机模式
- SPI_Slave:从机模式
**SPI_DataSize:**数据宽度
- SPI_DataSize_8b:8bit
- SPI_DataSize_16b:16bit
**SPI_CPOL:**时钟的极性
**SPI_CPHA:**时钟的相位
**SPI_NSS:**软件NSS/硬件NSS
**SPI_BaudRatePrescaler:**波特率分频器的分频系数
**SPI_FirstBit:**比特位的传输顺序
- SPI_FirstBit_MSB:高位先行
- SPI_FirstBit_LSB:低位先行
5.3.选择数据通信方向
5.3.1. 2线全双工

标准的SPI总线格式
主机MOSI发送数据,从机MOSI接收数据
主机MISO接收数据,从机MISO发送数据
**全双工:**通信接口既能发送数据、也能接收数据,并且数据收发同时进行
5.3.2. 2线只读

一般用于从机
主机MOSI发送数据,从机MOSI接收数据
但从机不会通过MISO发送数据(主机向从机广播)
5.3.3. 1线发送

单线、方向是发送
主机单线发送、从机单线接收
5.3.4. 1线接收

单向、方向是接收
主机单线接收、从机单线发送
5.4.数据宽度、极性、相位和比特位传输顺序
5.4.1.数据宽度
8bit(一个字节):

16bit(一个字长):

5.4.2.极性和相位

5.4.3.比特位传输顺序

5.5.设置波特率

由于使用面包板作实验,只需将波特率设为1MHz

分频系数设置为64
5.6.NSS的配置方式
5.6.1.多主机模式

NSS被拉低时主机身份变为从机
5.6.2.NSS配置
作主机时NSS应输入高电压
作从机时NSS作为片选输入
主机通过普通IO引脚向从机NSS输入低电压表示选中
主机的NSS需要外接高电压

硬件NSS:外接3.3V高电压
软件NSS:向内部NSS写0为低电压,写1为高电压

配置软件NSS
cpp
SPI_NSSInternalSoftwareConfig(SPI1,SPI_NSSInternalSoft_Set);
SET:写1、为高电压
RESET:写0、为低电压
5.7.总代码
cpp
#include "stm32f10x.h"
//声明SPI1初始化函数
void App_SPI1_Init(void);
int main(void)
{
App_SPI1_Init();
while(1)
{
}
}
//创建SPI1初始化函数
void App_SPI1_Init(void)
{
//#1:初始化IO引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*开启SPI1重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*PB3 SCK AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB3引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB4 MISO IPU*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB4引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PB4*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB5 MOSI AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PA15 普通IO Out_PP 2MHz*/
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*选择PA15引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
/*通用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA15*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2:初始化SPI
/*开启SPI1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
/*声明SPI结构变量*/
SPI_InitTypeDef SPI_InitStruct;
/*选择主机模式*/
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
/*2线全双工*/
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
/*8bit数据宽度*/
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
/*高极性*/
SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
/*第二边沿采集*/
SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
/*高位先行*/
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
/*分频系数为64*/
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
/*选择软件NSS*/
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
/*初始化SPI1*/
SPI_Init(SPI1,&SPI_InitStruct);
/*配置软件NSS*/
SPI_NSSInternalSoftwareConfig(SPI1,SET);
}
六、数据收发
6.1.SPI数据收发的特点

SPI数据收发是双向的、同时的
主机每次发送一个bit位给从机
必然接收到从机发送一个bit位
6.2.声明数据收发的编程接口
cpp
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size);
解析:
- 参数1:SPI的名称 SPI1 SPI2
- 参数2:要发送的数据
- 参数3:接收到的数据
- 参数4:收发数据的数量
**作用:**使用SPI总线收发数据
6.3.SPI数据收发过程简介

- 闭合总开关、开启时钟
- 将第一个字节提前写入发送数据寄存器
- 等待发送数据寄存器空标志位为1(无数据)
- 向发送数据寄存器写下一个字节(i+1)
- 等待接收数据寄存器非空标志位为1(有数据)
- 从接收数据寄存器读当前的字节(i)
- 等待接收数据寄存器非空标志位为1(有数据)
- 读取最后一个字节
- 断开总开关、关闭时钟

6.4.代码编写
cpp
//创建SPI收发数据函数
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size)
{
//#1:闭合总开关
SPI_Cmd(SPIx,ENABLE);
//#2:发送第一个字节
SPI_I2S_SendData(SPIx,pDataTx[0]);
//#3:循环发送数据
for(uint16_t i = 0;i < Size - 1;i++)
{
/*等待发送数据寄存器空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET);
/*发送一个字节*/
SPI_I2S_SendData(SPIx,pDataTx[i+1]);
/*等待接收数据寄存器非空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET);
/*接收一个字节*/
pDataRx[i] = SPI_I2S_ReceiveData(SPIx);
}
//#4:读取最后一个字节
/*等待接收数据寄存器非空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET);
/*读取最后一个字节*/
pDataRx[Size - 1] = SPI_I2S_ReceiveData(SPIx);
//#5:断开总开关
SPI_Cmd(SPIx,DISABLE);
}
七、W25Q64实验1
7.1.PA15的重映射

由于PA15引脚上电后默认的功能是JTDI(调试)
所以刚上电时,这个引脚会被调试接口占用
需要对PA15进行重映射,才能对普通PA15进行使用
cpp
/*开启PA15重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);

初始状态下还需要向PA15输出高电压:

时钟信号需要选择模式0:
低极性第一边沿采集
修改后的代码:
cpp
//创建SPI1初始化函数
void App_SPI1_Init(void)
{
//#1:初始化IO引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*开启SPI1重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);
/*开启PA15重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*PB3 SCK AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB3引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB4 MISO IPU*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB4引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PB4*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB5 MOSI AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PA15 普通IO Out_PP 2MHz*/
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*选择PA15引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
/*通用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA15*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*将PA15设置为高电平*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#2:初始化SPI
/*开启SPI1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
/*声明SPI结构变量*/
SPI_InitTypeDef SPI_InitStruct;
/*选择主机模式*/
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
/*2线全双工*/
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
/*8bit数据宽度*/
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;
/*分频系数为64*/
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
/*选择软件NSS*/
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
/*初始化SPI1*/
SPI_Init(SPI1,&SPI_InitStruct);
/*配置软件NSS*/
SPI_NSSInternalSoftwareConfig(SPI1,SET);
}
7.2.W25Q64的存储结构

64:存储容量为64MBit(8MByte)
Flash存储数据:先擦除原有数据,再编写存储数据

- 以扇区为单位擦除数据
- 以页为单位编程数据
写使能:
打开Flash模块的锁(避免数据遭到损坏、维护数据安全),执行完数据操作后自动锁上
等待空闲:
擦除需要50ms,编程需要10ms,执行数据操作后要等待操作完成
当SR1寄存器中BUSY标志位从1变成0时,说明数据操作完成,处于空闲状态
7.3.使用W25Q64存储数据
7.3.1.写使能


单片机通过SPI总线向W25Q64发送0x60(指令)
代码编写:
cpp
//#1:写使能
/*声明存储数据数组*/
uint8_t buffer[10];
/*存放指令0x06*/
buffer[0] = 0x06;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
7.3.2.扇区擦除


单片机通过SPI总线向W25Q64发送0x20(指令)+24位地址(3个0x00)
cpp
//#2:扇区擦除
/*存放指令0x20*/
buffer[0] = 0x20;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
7.3.3.等待空闲



单片机通过SPI总线向W25Q64发送0x05(指令),再发送0xff,保证MOSI上的电平一直为高电平
使读取更加稳定,然后读取一个字节,等待BUSY标志位从1变成0,操作结束,退出循环
cpp
//#3:等待空闲
while(1)
{
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x05*/
buffer[0] = 0x05;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*等待读取结束*/
if((buffer[0] & 0x01) == 0) break;
}
7.3.4.页编程


单片机通过SPI总线向W25Q64发送0x02(指令)+24位地址+要写的数据(最多256字节)
cpp
//#5:页编程
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x02*/
buffer[0] = 0x02;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*存放发送数据*/
buffer[4] = Byte;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,5);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
7.3.5.总代码
cpp
//创建W25Q64存储数据函数
void App_W25Q64_SaveByte(uint8_t Byte)
{
//#1:写使能
/*声明存储数据数组*/
uint8_t buffer[10];
/*存放指令0x06*/
buffer[0] = 0x06;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#2:扇区擦除
/*存放指令0x20*/
buffer[0] = 0x20;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#3:等待空闲
while(1)
{
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x05*/
buffer[0] = 0x05;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*等待读取结束*/
if((buffer[0] & 0x01) == 0) break;
}
//#4:写使能
/*存放指令0x06*/
buffer[0] = 0x06;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#5:页编程
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x02*/
buffer[0] = 0x02;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*存放发送数据*/
buffer[4] = Byte;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,5);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#6:等待空闲
while(1)
{
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x05*/
buffer[0] = 0x05;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*等待读取结束*/
if((buffer[0] & 0x01) == 0) break;
}
}
7.4.使用W25Q64读取数据

单片机通过SPI总线向W25Q64发送0x03(指令)+24位地址,然后读取数据
cpp
//创建W25Q64读取数据函数
uint8_t App_W25Q64_LoadByte(void)
{
/*声明存储数据数组*/
uint8_t buffer[10];
/*存放指令0x03*/
buffer[0] = 0x03;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*返回接收的数据*/
return buffer[0];
}
7.5.测试代码
cpp
#include "stm32f10x.h"
//声明SPI1初始化函数
void App_SPI1_Init(void);
//声明SPI数据收发函数
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size);
//声明W25Q64存储数据函数
void App_W25Q64_SaveByte(uint8_t Byte);
//声明W25Q64读取数据函数
uint8_t App_W25Q64_LoadByte(void);
uint8_t a = 0;
int main(void)
{
App_SPI1_Init();
/*将字节0x12保存在W25Q64的0x000000*/
App_W25Q64_SaveByte(0x12);
/*将保存的字节读出来*/
a = App_W25Q64_LoadByte();
while(1)
{
}
}
//创建SPI1初始化函数
void App_SPI1_Init(void)
{
//#1:初始化IO引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*开启SPI1重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);
/*开启PA15重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*PB3 SCK AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB3引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB4 MISO IPU*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB4引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PB4*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB5 MOSI AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PA15 普通IO Out_PP 2MHz*/
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*选择PA15引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
/*通用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA15*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*将PA15设置为高电平*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#2:初始化SPI
/*开启SPI1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
/*声明SPI结构变量*/
SPI_InitTypeDef SPI_InitStruct;
/*选择主机模式*/
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
/*2线全双工*/
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
/*8bit数据宽度*/
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;
/*分频系数为64*/
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
/*选择软件NSS*/
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
/*初始化SPI1*/
SPI_Init(SPI1,&SPI_InitStruct);
/*配置软件NSS*/
SPI_NSSInternalSoftwareConfig(SPI1,SET);
}
//创建SPI收发数据函数
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size)
{
//#1:闭合总开关
SPI_Cmd(SPIx,ENABLE);
//#2:发送第一个字节
SPI_I2S_SendData(SPIx,pDataTx[0]);
//#3:循环发送数据
for(uint16_t i = 0;i < Size - 1;i++)
{
/*等待发送数据寄存器空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET);
/*发送一个字节*/
SPI_I2S_SendData(SPIx,pDataTx[i+1]);
/*等待接收数据寄存器非空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET);
/*接收一个字节*/
pDataRx[i] = SPI_I2S_ReceiveData(SPIx);
}
//#4:读取最后一个字节
/*等待接收数据寄存器非空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET);
/*读取最后一个字节*/
pDataRx[Size - 1] = SPI_I2S_ReceiveData(SPIx);
//#5:断开总开关
SPI_Cmd(SPIx,DISABLE);
}
//创建W25Q64存储数据函数
void App_W25Q64_SaveByte(uint8_t Byte)
{
//#1:写使能
/*声明存储数据数组*/
uint8_t buffer[10];
/*存放指令0x06*/
buffer[0] = 0x06;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#2:扇区擦除
/*存放指令0x20*/
buffer[0] = 0x20;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#3:等待空闲
while(1)
{
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x05*/
buffer[0] = 0x05;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*等待读取结束*/
if((buffer[0] & 0x01) == 0) break;
}
//#4:写使能
/*存放指令0x06*/
buffer[0] = 0x06;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#5:页编程
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x02*/
buffer[0] = 0x02;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*存放发送数据*/
buffer[4] = Byte;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,5);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#6:等待空闲
while(1)
{
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x05*/
buffer[0] = 0x05;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*等待读取结束*/
if((buffer[0] & 0x01) == 0) break;
}
}
//创建W25Q64读取数据函数
uint8_t App_W25Q64_LoadByte(void)
{
/*声明存储数据数组*/
uint8_t buffer[10];
/*存放指令0x03*/
buffer[0] = 0x03;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*返回接收的数据*/
return buffer[0];
}
八、W25Q64实验2
8.1.初始化板载LED

将PC13引脚设置为通用输出开漏模式
cpp
//创建板载LED初始化函数
void App_OnBoardLED_Init(void)
{
/*启动GPIOC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PC13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*输出开漏模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA0引脚*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
8.2.初始化按钮

cpp
#include "stm32f10x.h"
#include "button.h"//引入按钮头文件
/*声明按钮结构变量*/
Button_TypeDef button;
//声明SPI1初始化函数
void App_SPI1_Init(void);
//声明SPI数据收发函数
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size);
//声明W25Q64存储数据函数
void App_W25Q64_SaveByte(uint8_t Byte);
//声明W25Q64读取数据函数
uint8_t App_W25Q64_LoadByte(void);
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
//声明按钮初始化函数
void App_Button_Init(void);
int main(void)
{
App_SPI1_Init();
App_OnBoardLED_Init();
App_Button_Init();
while(1)
{
/*按钮进程函数*/
My_Button_Proc(&button);
}
}
//创建按钮初始化函数
void App_Button_Init(void)
{
/*声明按钮初始化结构变量*/
Button_InitTypeDef Button_InitStruct = {0};
/*IO引脚的端口号设为GPIOA*/
Button_InitStruct.GPIOx = GPIOA;
/*IO引脚的引脚编号设为0*/
Button_InitStruct.GPIO_Pin = GPIO_Pin_0;
/*初始化按钮*/
My_Button_Init(&button,&Button_InitStruct);
}
注:
按钮初始化结构变量要先将它初始化为0,因为其他的回调函数并没有创建,否则实验会失败
8.3.使用按钮切换LED的亮灭
cpp
#include "stm32f10x.h"
#include "button.h"//引入按钮头文件
/*声明按钮结构变量*/
Button_TypeDef button;
//声明SPI1初始化函数
void App_SPI1_Init(void);
//声明SPI数据收发函数
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size);
//声明W25Q64存储数据函数
void App_W25Q64_SaveByte(uint8_t Byte);
//声明W25Q64读取数据函数
uint8_t App_W25Q64_LoadByte(void);
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
//声明按钮初始化函数
void App_Button_Init(void);
//声明按钮点击回调函数
void button_clicked_cb(uint8_t clicks);
int main(void)
{
App_SPI1_Init();
App_OnBoardLED_Init();
App_Button_Init();
while(1)
{
/*按钮进程函数*/
My_Button_Proc(&button);
}
}
//创建按钮初始化函数
void App_Button_Init(void)
{
/*声明按钮初始化结构变量*/
Button_InitTypeDef Button_InitStruct = {0};
/*IO引脚的端口号设为GPIOA*/
Button_InitStruct.GPIOx = GPIOA;
/*IO引脚的引脚编号设为0*/
Button_InitStruct.GPIO_Pin = GPIO_Pin_0;
/*按钮点击*/
Button_InitStruct.button_clicked_cb = button_clicked_cb;
/*初始化按钮*/
My_Button_Init(&button,&Button_InitStruct);
}
//创建按钮点击回调函数
void button_clicked_cb(uint8_t clicks)
{
/*如果是单击*/
if(clicks == 1)
{
/*如果上次输出的是1*/
if(GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13) == Bit_SET)
{
/*这次就输出0*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
else/*如果上次输出的是0*/
{
/*这次就输出1*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
}
}
8.4.保存LED的当前状态
cpp
#include "stm32f10x.h"
#include "button.h"//引入按钮头文件
/*声明按钮结构变量*/
Button_TypeDef button;
//声明SPI1初始化函数
void App_SPI1_Init(void);
//声明SPI数据收发函数
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size);
//声明W25Q64存储数据函数
void App_W25Q64_SaveByte(uint8_t Byte);
//声明W25Q64读取数据函数
uint8_t App_W25Q64_LoadByte(void);
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
//声明按钮初始化函数
void App_Button_Init(void);
//声明按钮点击回调函数
void button_clicked_cb(uint8_t clicks);
int main(void)
{
App_SPI1_Init();
App_OnBoardLED_Init();
App_Button_Init();
while(1)
{
/*按钮进程函数*/
My_Button_Proc(&button);
}
}
//创建按钮点击回调函数
void button_clicked_cb(uint8_t clicks)
{
/*如果是单击*/
if(clicks == 1)
{
/*如果上次输出的是1*/
if(GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13) == Bit_SET)
{
/*这次就输出0*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
/*保存0x01,表示LED点亮*/
App_W25Q64_SaveByte(0x01);
}
else/*如果上次输出的是0*/
{
/*这次就输出1*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
/*保存0x00,表示LED熄灭*/
App_W25Q64_SaveByte(0x00);
}
}
}
8.5.恢复LED状态
cpp
int main(void)
{
App_SPI1_Init();
App_OnBoardLED_Init();
App_Button_Init();
uint8_t byte = App_W25Q64_LoadByte();
/*如果断电前读的是0*/
if(byte == 0)
{
/*熄灭LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
else/*如果断电前读的是1*/
{
/*点亮LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
while(1)
{
/*按钮进程函数*/
My_Button_Proc(&button);
}
}
8.6.总代码
cpp
#include "stm32f10x.h"
#include "button.h"//引入按钮头文件
/*声明按钮结构变量*/
Button_TypeDef button;
//声明SPI1初始化函数
void App_SPI1_Init(void);
//声明SPI数据收发函数
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size);
//声明W25Q64存储数据函数
void App_W25Q64_SaveByte(uint8_t Byte);
//声明W25Q64读取数据函数
uint8_t App_W25Q64_LoadByte(void);
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
//声明按钮初始化函数
void App_Button_Init(void);
//声明按钮点击回调函数
void button_clicked_cb(uint8_t clicks);
int main(void)
{
App_SPI1_Init();
App_OnBoardLED_Init();
App_Button_Init();
uint8_t byte = App_W25Q64_LoadByte();
/*如果断电前读的是0*/
if(byte == 0)
{
/*熄灭LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
else/*如果断电前读的是1*/
{
/*点亮LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
while(1)
{
/*按钮进程函数*/
My_Button_Proc(&button);
}
}
//创建按钮点击回调函数
void button_clicked_cb(uint8_t clicks)
{
/*如果是单击*/
if(clicks == 1)
{
/*如果上次输出的是1*/
if(GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13) == Bit_SET)
{
/*这次就输出0*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
/*保存0x01,表示LED点亮*/
App_W25Q64_SaveByte(0x01);
}
else/*如果上次输出的是0*/
{
/*这次就输出1*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
/*保存0x00,表示LED熄灭*/
App_W25Q64_SaveByte(0x00);
}
}
}
//创建按钮初始化函数
void App_Button_Init(void)
{
/*声明按钮初始化结构变量*/
Button_InitTypeDef Button_InitStruct = {0};
/*IO引脚的端口号设为GPIOA*/
Button_InitStruct.GPIOx = GPIOA;
/*IO引脚的引脚编号设为0*/
Button_InitStruct.GPIO_Pin = GPIO_Pin_0;
/*按钮点击*/
Button_InitStruct.button_clicked_cb = button_clicked_cb;
/*初始化按钮*/
My_Button_Init(&button,&Button_InitStruct);
}
//创建SPI1初始化函数
void App_SPI1_Init(void)
{
//#1:初始化IO引脚
/*开启AFIO的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/*开启SPI1重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);
/*开启PA15重映射*/
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*PB3 SCK AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB3引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB4 MISO IPU*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB4引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
/*输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PB4*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PB5 MOSI AF_PP 2MHz*/
/*开启GPIOB的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*选择PB5引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
/*复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PB3*/
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*PA15 普通IO Out_PP 2MHz*/
/*开启GPIOA的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*选择PA15引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
/*通用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA15*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*将PA15设置为高电平*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#2:初始化SPI
/*开启SPI1的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
/*声明SPI结构变量*/
SPI_InitTypeDef SPI_InitStruct;
/*选择主机模式*/
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
/*2线全双工*/
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
/*8bit数据宽度*/
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;
/*分频系数为64*/
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
/*选择软件NSS*/
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
/*初始化SPI1*/
SPI_Init(SPI1,&SPI_InitStruct);
/*配置软件NSS*/
SPI_NSSInternalSoftwareConfig(SPI1,SET);
}
//创建SPI收发数据函数
void App_SPI_MasterTransmitReceive(SPI_TypeDef* SPIx,const uint8_t* pDataTx,uint8_t *pDataRx,uint16_t Size)
{
//#1:闭合总开关
SPI_Cmd(SPIx,ENABLE);
//#2:发送第一个字节
SPI_I2S_SendData(SPIx,pDataTx[0]);
//#3:循环发送数据
for(uint16_t i = 0;i < Size - 1;i++)
{
/*等待发送数据寄存器空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET);
/*发送一个字节*/
SPI_I2S_SendData(SPIx,pDataTx[i+1]);
/*等待接收数据寄存器非空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET);
/*接收一个字节*/
pDataRx[i] = SPI_I2S_ReceiveData(SPIx);
}
//#4:读取最后一个字节
/*等待接收数据寄存器非空标志位为1*/
while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET);
/*读取最后一个字节*/
pDataRx[Size - 1] = SPI_I2S_ReceiveData(SPIx);
//#5:断开总开关
SPI_Cmd(SPIx,DISABLE);
}
//创建W25Q64存储数据函数
void App_W25Q64_SaveByte(uint8_t Byte)
{
//#1:写使能
/*声明存储数据数组*/
uint8_t buffer[10];
/*存放指令0x06*/
buffer[0] = 0x06;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#2:扇区擦除
/*存放指令0x20*/
buffer[0] = 0x20;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#3:等待空闲
while(1)
{
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x05*/
buffer[0] = 0x05;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*等待读取结束*/
if((buffer[0] & 0x01) == 0) break;
}
//#4:写使能
/*存放指令0x06*/
buffer[0] = 0x06;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#5:页编程
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x02*/
buffer[0] = 0x02;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*存放发送数据*/
buffer[4] = Byte;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,5);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#6:等待空闲
while(1)
{
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*存放指令0x05*/
buffer[0] = 0x05;
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*等待读取结束*/
if((buffer[0] & 0x01) == 0) break;
}
}
//创建W25Q64读取数据函数
uint8_t App_W25Q64_LoadByte(void)
{
/*声明存储数据数组*/
uint8_t buffer[10];
/*存放指令0x03*/
buffer[0] = 0x03;
/*存放24位地址*/
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
/*通过主机PA15引脚向从机NSS引脚发送0*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);
/*向从机收发数据*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
/*存放数据0xff*/
buffer[0] = 0xff;
/*读取一个字节*/
App_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
/*通过主机PA15引脚向从机NSS引脚发送1*/
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
/*返回接收的数据*/
return buffer[0];
}
//创建板载LED初始化函数
void App_OnBoardLED_Init(void)
{
/*启动GPIOC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PC13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*输出开漏模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA0引脚*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
