[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

目录

9. 软件SPI读写W25Qx

0. 博主调侃:

1. 实验内容及步骤:

2. 硬件说明

3. 步骤详细讲解

       3.1 配置GPIO

       3.2 软件SPI读写Byte(模式0或模式3)

       3.3 读取W25Q16的制造商ID和设备ID

       3.4 通过标准SPI对W25Q16擦除-写入-读取     

4. 程序设计(寄存器)

5. 程序设计(标准库)

6. 程序设计(HAL库)

7. 实验结果

8. 源码下载


9. 软件SPI读写W25Qx

0. 博主调侃:

       本实验使用软件模拟标准的SPI读写W25Q16JV,这个W25Qx通常用来存储程序,可在W25Qx内部直接运行程序,无需移动到单片机的内部RAM中,当然用来存储数据也行的。

       这里W25Qx的芯片类型有点多,大体操作都相同,每种类型都有一丢丢区别,建议用哪一块就去找对应的芯片(本人就被坑了一次,啊哈哈哈哈~)。

1. 实验内容及步骤:

       1. 配置GPIO,MOSI(推挽输出)、MISO(浮空输入)、CS(输出)、SCK(输出);

       2. 软件SPI读写Byte(模式0或模式3);

       3. 读取W25Q16的制造商ID和设备ID;

       4. 通过标准SPI对W25Q16擦除-写入-读取;

2. 硬件说明

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       CS   ->   PB12

       CLK ->   PB13

       MISO     ->   PB14

       MOSI     ->   PB15

3. 步骤详细讲解

       3.1 配置GPIO

       MOSI(推挽输出)、MISO(浮空输入)、CS(输出)、SCK(输出),详细的代码见源码哈。

       3.2 软件SPI读写Byte(模式0或模式3)

       见基于STM32F1的软件SPI讲解。

       左图为软件SPI模式0的读写Byte,右图为软件SPI模式3的读写Byte

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       3.3 读取W25Q16的制造商ID和设备ID

       详细见W25Q16芯片讲解,源码如下所示。

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       3.4 通过标准SPI对W25Q16擦除-写入-读取     

       写使能:(在擦除和写入的时候都要进行写使能)

       写使能指令宏定义如下图所示

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       写使能操作如下图所示

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       擦除:(擦除操作后会清除写使能,并且busy位置1)

       擦除指令宏定义如下图所示

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库) 

       擦除操作如下图所示

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       等待忙(BUSY位):

       程序如下所示,读取状态寄存器1的值,并且等待BUSY为清零。

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       写入数据:

       注意:W25Qx的写操作只能一次性连续写256个位置,超过256个位置后,就会从1开始重新覆盖,这种写256 Byte操作成为页操作。

       这种页操作不是随便的写256Byte,如从0-255称为一个页,就是你从100开始写也只能写到255,下一个就从0开始;256-511称为下一个页,一次类推。

       写操作程序如下图所示。由于有点长,这里就只截取一点。

       这个程序会判断是否到结尾,并且跳转到另一页。

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       普通读操作:

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       快速读操作:

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

4. 程序设计(寄存器)

       在W25Q16芯片的第0x20000位置开始存储,存储15000个数据,并且读取出来进行对比。

/*
    通过标准SPI对W25Q16擦除-写入-读取
输入:
    addr        读取的地址
    len         读数据长度
    writebuf    发送缓冲器
    readbuf     接收缓冲器
*/
void W25Q_test_V2(u32 addr,u32 len,u8* writebuf,u8* readbuf)
{
    u32 i=0;
    u8 j=0;
    u16 ID=0;
    //ID输出
    ID = W25Qx_Read_ID(); 
    printf("制造商ID:0X%0X  设备ID:0X%0X\r\n",(ID>>8)&0xFF,ID&0xFF); 
    printf("寄存器1:0X%0X  寄存器2:0X%0X\r\n",W25Qx_Read_Reg1(),W25Qx_Read_Reg2()); 
    printf("存储起始地址:0X%0X\r\n",addr);    
    for(i=0,j=1;i<len-1;i++,j++)
    {
        if(j==0)j=1;
        writebuf[i]=j;
    }
    writebuf[i]='\0';   
    W25QX_CHIP_ERASE();       //擦除整个芯片(函数内部有等待busy了)
    W25Qx_write_DATA(addr,len,writebuf);    //写入(函数内部有等待busy了)
    W25Qx_Read_DATA(addr,len,readbuf);  //读取数据
    //数据比较
    if(strcmp((char*)writebuf,(char*)readbuf)==0)
        printf("普通读取:存储正确\r\n");
    else
        printf("普通读取:存储出错\r\n");

    W25Qx_Fast_Read_DATA(addr,len,readbuf);	//快速读取
    //数据比较
    if(strcmp((char*)writebuf,(char*)readbuf)==0)
        printf("快速读取:存储正确\r\n");
    else
        printf("快速读取:存储出错\r\n");    
}


//主函数
int main(void)
{
  SET_NVIC_GROUP(NVIC_PriorityGroup_4); //中断组4
  Systick_Config(); 
  USART1_Config(115200);      //串口1配置115200
  W25Qx_GPIO_Config();  //W25Qx初始化
    W25Q_test_V2(0x020000,15000,W25Q_write,W25Q_read);
  while (1)
  {
  }
}

5. 程序设计(标准库)

       软件SPI的标准库,除了GPIO配置、时钟配置与寄存器不同,其它部分都与寄存器一致。

       源码:(GPIO配置部分)


//CS
#define SOFT_CS_PIN_NUM      12
#define SOFT_CS_PIN          GPIO_Pin_12
#define SOFT_CS_Mode         GPIO_Mode_Out_PP   
#define SOFT_CS_RCC_CLK      RCC_APB2Periph_GPIOB
#define SOFT_CS_GPIOX        GPIOB
#define SOFT_CS_OUT          BPB_OUT(SOFT_CS_PIN_NUM)
#define SOFT_CS_IN           BPB_IN(SOFT_CS_PIN_NUM)

//SCK
#define SOFT_SCK_PIN_NUM     13
#define SOFT_SCK_PIN         GPIO_Pin_13
#define SOFT_SCK_Mode        GPIO_Mode_Out_PP 
#define SOFT_SCK_RCC_CLK     RCC_APB2Periph_GPIOB
#define SOFT_SCK_GPIOX       GPIOB
#define SOFT_SCK_OUT         BPB_OUT(SOFT_SCK_PIN_NUM)
#define SOFT_SCK_IN          BPB_IN(SOFT_SCK_PIN_NUM)

//MISO
#define SOFT_MISO_PIN_NUM      14
#define SOFT_MISO_PIN          GPIO_Pin_14
#define SOFT_MISO_Mode         GPIO_Mode_IN_FLOATING 
#define SOFT_MISO_RCC_CLK      RCC_APB2Periph_GPIOB
#define SOFT_MISO_GPIOX        GPIOB
#define SOFT_MISO_OUT          BPB_OUT(SOFT_MISO_PIN_NUM)
#define SOFT_MISO_IN           BPB_IN(SOFT_MISO_PIN_NUM)
#define SOFT_MISO_MODE_OUT()        GPIOX_PIN_Mode_Turn(SOFT_MISO_GPIOX,SOFT_MISO_PIN_NUM,GPIOX_MODE_OUT);   //配置MISO为输出模式
#define SOFT_MISO_MODE_IN()         GPIOX_PIN_Mode_Turn(SOFT_MISO_GPIOX,SOFT_MISO_PIN_NUM,GPIOX_MODE_IN);   //配置MISO为输入模式

//MOSI
#define SOFT_MOSI_PIN_NUM      15
#define SOFT_MOSI_PIN          GPIO_Pin_15
#define SOFT_MOSI_Mode         GPIO_Mode_Out_PP
#define SOFT_MOSI_RCC_CLK      RCC_APB2Periph_GPIOB
#define SOFT_MOSI_GPIOX        GPIOB
#define SOFT_MOSI_OUT          BPB_OUT(SOFT_MOSI_PIN_NUM)
#define SOFT_MOSI_IN           BPB_IN(SOFT_MOSI_PIN_NUM)
#define SOFT_MOSI_MODE_OUT()        GPIOX_PIN_Mode_Turn(SOFT_MOSI_GPIOX,SOFT_MOSI_PIN_NUM,GPIOX_MODE_OUT);   //配置MOSI为输出模式
#define SOFT_MOSI_MODE_IN()         GPIOX_PIN_Mode_Turn(SOFT_MOSI_GPIOX,SOFT_MOSI_PIN_NUM,GPIOX_MODE_IN);   //配置MOSI为输入模式


//软件SPI GPIO配置
void soft_spi_gpio_config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    //时钟
    RCC_APB2PeriphClockCmd(SOFT_CS_RCC_CLK,ENABLE);     //CS时钟
    RCC_APB2PeriphClockCmd(SOFT_SCK_RCC_CLK,ENABLE);    //SCK时钟   
    RCC_APB2PeriphClockCmd(SOFT_MISO_RCC_CLK,ENABLE);   //MISO时钟
    RCC_APB2PeriphClockCmd(SOFT_MOSI_RCC_CLK,ENABLE);   //MOSI时钟
    
    //CS(推挽输出、速度10MHz)
    GPIO_InitStruct.GPIO_Mode   =   SOFT_CS_Mode;
    GPIO_InitStruct.GPIO_Pin    =   SOFT_CS_PIN;
    GPIO_InitStruct.GPIO_Speed  =   GPIO_Speed_10MHz;
    GPIO_Init(SOFT_CS_GPIOX,&GPIO_InitStruct);        
    //SCK(推挽输出、速度10MHz)
    GPIO_InitStruct.GPIO_Mode   =   SOFT_SCK_Mode;
    GPIO_InitStruct.GPIO_Pin    =   SOFT_SCK_PIN;
    GPIO_InitStruct.GPIO_Speed  =   GPIO_Speed_10MHz;
    GPIO_Init(SOFT_SCK_GPIOX,&GPIO_InitStruct);     
    //MISO(输入 浮空模式)
    GPIO_InitStruct.GPIO_Mode   =   SOFT_MISO_Mode;
    GPIO_InitStruct.GPIO_Pin    =   SOFT_MISO_PIN;
    GPIO_Init(SOFT_MISO_GPIOX,&GPIO_InitStruct);     
    //MOSI(推挽输出、速度10MHz)
    GPIO_InitStruct.GPIO_Mode   =   SOFT_MOSI_Mode;
    GPIO_InitStruct.GPIO_Pin    =   SOFT_MOSI_PIN;
    GPIO_InitStruct.GPIO_Speed  =   GPIO_Speed_10MHz;
    GPIO_Init(SOFT_MOSI_GPIOX,&GPIO_InitStruct);     
}

6. 程序设计(HAL库)

       软件SPI的HAL库,除了GPIO配置、时钟配置与寄存器不同,其它部分都与寄存器一致。

       源码:(GPIO配置部分)

/*============================================= SPI宏定义 ================================================*/
//CS
#define SOFT_CS_CLK_ENABLE   __HAL_RCC_GPIOB_CLK_ENABLE
#define SOFT_CS_PIN_NUM      12
#define SOFT_CS_PIN          GPIO_PIN_12
#define SOFT_CS_Mode         GPIO_MODE_OUTPUT_PP   
#define SOFT_CS_RCC_CLK      RCC_APB2Periph_GPIOB
#define SOFT_CS_GPIOX        GPIOB
#define SOFT_CS_OUT          BPB_OUT(SOFT_CS_PIN_NUM)
#define SOFT_CS_IN           BPB_IN(SOFT_CS_PIN_NUM)

//SCK
#define SOFT_SCK_CLK_ENABLE   __HAL_RCC_GPIOB_CLK_ENABLE
#define SOFT_SCK_PIN_NUM     13
#define SOFT_SCK_PIN         GPIO_PIN_13
#define SOFT_SCK_Mode        GPIO_MODE_OUTPUT_PP 
#define SOFT_SCK_RCC_CLK     RCC_APB2Periph_GPIOB
#define SOFT_SCK_GPIOX       GPIOB
#define SOFT_SCK_OUT         BPB_OUT(SOFT_SCK_PIN_NUM)
#define SOFT_SCK_IN          BPB_IN(SOFT_SCK_PIN_NUM)

//MISO
#define SOFT_MISO_CLK_ENABLE   __HAL_RCC_GPIOB_CLK_ENABLE
#define SOFT_MISO_PIN_NUM      14
#define SOFT_MISO_PIN          GPIO_PIN_14
#define SOFT_MISO_Mode         GPIO_MODE_INPUT 
#define SOFT_MISO_RCC_CLK      RCC_APB2Periph_GPIOB
#define SOFT_MISO_GPIOX        GPIOB
#define SOFT_MISO_OUT          BPB_OUT(SOFT_MISO_PIN_NUM)
#define SOFT_MISO_IN           BPB_IN(SOFT_MISO_PIN_NUM)
#define SOFT_MISO_MODE_OUT()        GPIOX_PIN_Mode_Turn(SOFT_MISO_GPIOX,SOFT_MISO_PIN_NUM,GPIOX_MODE_OUT);   //配置MISO为输出模式
#define SOFT_MISO_MODE_IN()         GPIOX_PIN_Mode_Turn(SOFT_MISO_GPIOX,SOFT_MISO_PIN_NUM,GPIOX_MODE_IN);   //配置MISO为输入模式

//MOSI
#define SOFT_MOSI_CLK_ENABLE   __HAL_RCC_GPIOB_CLK_ENABLE
#define SOFT_MOSI_PIN_NUM      15
#define SOFT_MOSI_PIN          GPIO_PIN_15
#define SOFT_MOSI_Mode         GPIO_MODE_OUTPUT_PP
#define SOFT_MOSI_RCC_CLK      RCC_APB2Periph_GPIOB
#define SOFT_MOSI_GPIOX        GPIOB
#define SOFT_MOSI_OUT          BPB_OUT(SOFT_MOSI_PIN_NUM)
#define SOFT_MOSI_IN           BPB_IN(SOFT_MOSI_PIN_NUM)
#define SOFT_MOSI_MODE_OUT()        GPIOX_PIN_Mode_Turn(SOFT_MOSI_GPIOX,SOFT_MOSI_PIN_NUM,GPIOX_MODE_OUT);   //配置MOSI为输出模式
#define SOFT_MOSI_MODE_IN()         GPIOX_PIN_Mode_Turn(SOFT_MOSI_GPIOX,SOFT_MOSI_PIN_NUM,GPIOX_MODE_IN);   //配置MOSI为输入模式

//软件SPI GPIO配置
void soft_spi_gpio_config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    //时钟
    SOFT_CS_CLK_ENABLE();     //CS时钟
    SOFT_SCK_CLK_ENABLE();    //SCK时钟   
    SOFT_MISO_CLK_ENABLE();   //MISO时钟
    SOFT_MOSI_CLK_ENABLE();   //MOSI时钟
    
    //CS(推挽输出、速度10MHz)
    GPIO_InitStruct.Mode    =   SOFT_CS_Mode;
    GPIO_InitStruct.Pin     =   SOFT_CS_PIN;
    GPIO_InitStruct.Speed   =   GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(SOFT_CS_GPIOX,&GPIO_InitStruct);      
    //SCK(推挽输出、速度10MHz)
    GPIO_InitStruct.Mode    =   SOFT_SCK_Mode;
    GPIO_InitStruct.Pin     =   SOFT_SCK_PIN;
    GPIO_InitStruct.Speed   =   GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(SOFT_SCK_GPIOX,&GPIO_InitStruct);    
    //MISO(输入 浮空模式)
    GPIO_InitStruct.Mode    =   SOFT_MISO_Mode;
    GPIO_InitStruct.Pin     =   SOFT_MISO_PIN;
    GPIO_InitStruct.Pull    =   GPIO_NOPULL;
    HAL_GPIO_Init(SOFT_MOSI_GPIOX,&GPIO_InitStruct);      
    //MOSI(推挽输出、速度10MHz)
    GPIO_InitStruct.Mode    =   SOFT_MOSI_Mode;
    GPIO_InitStruct.Pin     =   SOFT_MOSI_PIN;
    GPIO_InitStruct.Speed   =   GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(SOFT_MOSI_GPIOX,&GPIO_InitStruct);    
}

7. 实验结果

       可以看出,写入的起始地址为0x20000,最后通过普通读取和快速读取,分别读出写入的数据,并对比。结果都为存储正确。

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

       HAL,标准库源码运行结果:

[学习笔记]STM32F1软件SPI读写W25Qx(寄存器、标准库、HAL库)

8. 源码下载

        源码下载

上一篇:SAPD:FSAF升级版,合理的损失值加权以及金字塔特征选择 | ECCV 2020


下一篇:SAPD:FSAF升级版,合理的损失值加权以及金字塔特征选择 | ECCV 2020