一、SPI电路结构及通信协议
1.1.1SPI电路总线的电路结构

MOSI:主机给从机发送地址
MISO:从机给主机发送地址
SCK:时钟线
NSS:低电压代表选中,高电压代表没有选中
2.1.1通信流程及波形

NSS1低电压代表从机被选中;
SCK控制运行速度,高电平代表高速度
MOSI向MISO发送数据,MISO向MOSI返回数据
3.1.1参数1时钟信号的极性

低极性:空闲的时候默认为低电压,与高极性的时候相反
3.2.1参数1时钟信号的相位

第一边沿采集:在数字为1的边缘进行采集(低极性)
第二边沿采集:在数字为2的边缘进行采集(高极性)
4.1.1SPI的四种采集模式

5.1.1比特位的传输顺序

两种模,第一种LSB First:先传最低有效位(0~7)
第二种MSB First:先传最高有效位(7~0)
6.1.1数据宽度

就有是每次传输比特位的个数,例如一次传输8个bit,一次传输16个bit
二、SPI IO模块引脚初始化
1.1.1W25Q64模块简介

2.1.1定位SPI引脚内部结构

3.1.1查询参考手册第8.3.10

SPI2没有重映射
4.1.1接线

5.1.1选择SPI工作模式

6.1.1选择W25Q64 IO引脚的最大输出速度

选择符合要求的最低频率
7.1.1选择SPI IO引脚输入输出模式及速率

8.1.1SPI引脚初始化代码(main.c)
#include "stm32f10x.h"
#include "delay.h"
void APP_SPI_Init(void);
int main(void)
{
APP_SPI_Init();
while(1)
{
}
}
void APP_SPI_Init(void)
{
//#1初始化IO引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
//PB3 SCK AF_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_3;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PB4 MISO IPU
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_IPU;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PB5 MOSI AF_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PA15 普通IO OUT_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_15;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOA,&GPIO_InitStruct);
}
三、IO模块初始化
1.1.1SPI模块的基本工作原理

TXE:发送数据寄存器为空
RxNE:接收数据寄存器非空
NSS:作为从机的时候使用
先闭合总开关,写入第一个字,标志位TXE=1,读取第字节宽度-1个数据位,开始向TDR(发送数据寄存器)写数据,标志位TXE=0,然后读数据标志位RxNE=0,再断开总开关。
2.1.1选择数据通信方向

这里我们选择全双工模式
3.1.1数据宽度、极性、相位和比特位的传输顺序
相位:什么时候对信号进行采集
根据图示,以发送0x04(0000 0100)为例
相位:1/2
数据位:8b(0~7)
极性:高/低(Model3/Model0)
比特位传输顺序:MSB(先传最高位)
4.1.1设置波特率

时钟的频率是72MHZ,假设我们想得到18MZ,我们可以在分频器中选择4分频,72/4=18MHZ
5.1.1NSS引脚配置方式
软件NSS:向内部NSS输入1,相当于外接了电源

硬件NSS:

对从机外接一个3.3v电压的方式
6.1.1代码部分(main.c部分)
#include "stm32f10x.h"
#include "delay.h"
void APP_SPI_Init(void);
int main(void)
{
APP_SPI_Init();
while(1)
{
}
}
void APP_SPI_Init(void)
{
//#1初始化IO引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
//PB3 SCK AF_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_3;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PB4 MISO IPU
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_IPU;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PB5 MOSI AF_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PA15 普通IO OUT_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_15;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOA,&GPIO_InitStruct);
//#2对SPI本身进行初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex ;//2线全双工模式
SPI_InitStruct.SPI_DataSize =SPI_DataSize_8b;//字节宽度
SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;//高极性
SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;//第2边沿采集
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//先传最高位
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;//设置波特率
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//选择软NSS
SPI_Init(SPI1,&SPI_InitStruct);
SPI_NSSInternalSoftwareConfig(SPI1,SPI_NSSInternalSoft_Set);//向内部NSS写1
}
四、数据收发
1.1.1SPI数据收发的特点

2.1.1SPI数据收发的过程简介

TDR:发送数据寄存器
RDR:接收数据寄存器
TXE:发送数据寄存器为空
RxNE:接收数据寄存器不为空
3.1.1代码示例
void My_SPI_MasterTransmitReceive(SPI_TypeDef *SPIx, const uint8_t *pDataTx, uint8_t *pDataRx, uint16_t Size)
{
if(Size == 0) return;
// #1. 闭合总开关
SPI_Cmd(SPIx, ENABLE);
// #2. 写入第一个字节
SPI_I2S_SendData(SPIx, pDataTx[0]);
// #3. 读写Size-1个字节
for(uint16_t i=0; i<Size-1; i++)
{
// 向TDR写数据
while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPIx, pDataTx[i+1]);
// 从RDR读数据
while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
pDataRx[i] = SPI_I2S_ReceiveData(SPIx);
}
// #4. 读取最后一个字节
while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
pDataRx[Size-1] = SPI_I2S_ReceiveData(SPIx);
// #5. 断开总开关
SPI_Cmd(SPIx, DISABLE);
}
五、W25Q64实验
1.1.1W25Q64储存结构
这是一个flash存储结构

2.1.1使用W25Q64模块存储数据

3.1.1存储代码示例
void APP_W25Q64_Init(uint8_t Byte)
{
uint8_t buffer[10];
//#1写使能
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
//#2扇区擦除
buffer[0]=0x20;
buffer[1]=0x00;
buffer[2]=0x00;
buffer[3]=0x00;
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
//#3等待空闲
while(1)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
//写0x05
buffer[0]=0x05;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
//读寄存器状态
buffer[0]=0xff;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
if((buffer[0]&0x01)==0)break;
}
//#4写使能
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
//#5页编程
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
buffer[0]=0x20;
buffer[1]=0x00;
buffer[2]=0x00;
buffer[3]=0x00;
buffer[4]=Byte;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,5);
//#6等待空闲
while(1)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
//写0x05
buffer[0]=0x05;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
//读寄存器状态
buffer[0]=0xff;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
if((buffer[0]&0x01)==0)break;
}
}
4.1.1使用W25Q64读数据
查询W25Q64参考手册第11.2.8节

5.1.1代码示例(main.c部分)
#include "stm32f10x.h"
#include "delay.h"
#include "spi.h"
void APP_SPI_Init(void);
void APP_W25Q64_Init(uint8_t Byte);
uint8_t APP_W25Q64_LoadByte(void);
uint8_t a =0;
int main(void)
{
APP_SPI_Init();
APP_W25Q64_Init(0x12);
a=APP_W25Q64_LoadByte();
while(1)
{
APP_W25Q64_Init(0x12);
a=APP_W25Q64_LoadByte();
}
}
void APP_SPI_Init(void)
{
//#1初始化IO引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
//PB3 SCK AF_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_3;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PB4 MISO IPU
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_IPU;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PB5 MOSI AF_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOB,&GPIO_InitStruct);
//PA15 普通IO OUT_PP 2MHZ
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_15;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_2MHz;
GPIO_Init (GPIOA,&GPIO_InitStruct);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);
//#2对SPI本身进行初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex ;//2线全双工模式
SPI_InitStruct.SPI_DataSize =SPI_DataSize_8b;//字节宽度
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;//低极性
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;//第1边沿采集
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//先传最高位
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;//设置波特率
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//选择软NSS
SPI_Init(SPI1,&SPI_InitStruct);
SPI_NSSInternalSoftwareConfig(SPI1,SPI_NSSInternalSoft_Set);//向内部NSS写1
}
void APP_W25Q64_Init(uint8_t Byte)
{
uint8_t buffer[10];
//#1写使能
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
//#2扇区擦除
buffer[0]=0x20;
buffer[1]=0x00;
buffer[2]=0x00;
buffer[3]=0x00;
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
//#3等待空闲
while(1)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
//写0x05
buffer[0]=0x05;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
//读寄存器状态
buffer[0]=0xff;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
if((buffer[0]&0x01)==0)break;
}
//#4写使能
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
//#5页编程
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
buffer[0]=0x20;
buffer[1]=0x00;
buffer[2]=0x00;
buffer[3]=0x00;
buffer[4]=Byte;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,5);
//#6等待空闲
while(1)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
//写0x05
buffer[0]=0x05;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
//读寄存器状态
buffer[0]=0xff;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
if((buffer[0]&0x01)==0)break;
}
}
uint8_t APP_W25Q64_LoadByte(void)
{
uint8_t buffer[10];
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_RESET);//选中
buffer[0]=0x03;
buffer[1]=0x00;
buffer[2]=0x00;
buffer[3]=0x00;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,4);
buffer[0]=0xff;
My_SPI_MasterTransmitReceive(SPI1,buffer,buffer,1);
GPIO_WriteBit(GPIOA,GPIO_Pin_15,Bit_SET);//取消选中
return buffer[0];
}
2693

被折叠的 条评论
为什么被折叠?



