你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32F103标准库开发---SPI实验---读写 W25Q128 外部 Flash

[复制链接]
STMCU小助手 发布时间:2022-9-6 21:44
一、实验前期准备
本次实验的 MCU 是 STM32F103C8T6 芯片,通过 SPI 通信实现 W25Q128 的读写操作。

1. 原理图

28aa85ae89f54d22a0ac7f859f06dffb.png

2. 引脚连接

6(AG%P$K@2WYM7L28}Z~2YC.png

二、SPI 底层驱动
SPI.c
  1. #include "SPI.h"

  2. /*
  3.         SPI引脚初始化配置
  4.         **PA4------CS
  5.         **PA5------SCLK
  6.         **PA6------MISO
  7.         **PA7------MOSI
  8. */

  9. static void SPI1_GPIO_Config(void)        
  10. {
  11.         GPIO_InitTypeDef GPIO_InitStructure;
  12.         
  13.         RCC_APB2PeriphClockCmd(        RCC_APB2Periph_GPIOA, ENABLE );//PORTA时钟使能

  14.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_7;
  15.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PA5/6/7复用推挽输出
  16.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  17.         GPIO_Init(GPIOA, &GPIO_InitStructure);                                                //初始化GPIOA
  18.         
  19.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  20.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //PA4推挽输出
  21.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  22.         GPIO_Init(GPIOA, &GPIO_InitStructure);                                                //初始化GPIOA

  23.         GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); //初始上拉输出

  24. }

  25. //SPI1初始化函数
  26. void SPI1_Init(void)
  27. {
  28.         SPI1_GPIO_Config();//SPI引脚初始化配置        

  29.         SPI_InitTypeDef  SPI_InitStructure;
  30.         
  31.         RCC_APB2PeriphClockCmd(        RCC_APB2Periph_SPI1,  ENABLE );//SPI1时钟使能         
  32.         
  33.         SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;          //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
  34.         SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                                                                                                        //设置SPI工作模式:设置为主SPI
  35.         SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                                                                                        //设置SPI的数据大小:SPI发送接收8位帧结构
  36.         SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                                                                                                                //串行同步时钟的空闲状态为高电平
  37.         SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                                                                                                        //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
  38.         SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                                                                                                                        //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
  39.         SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //定义波特率预分频的值:波特率预分频值为256
  40.         SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                                                                                //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
  41.   SPI_InitStructure.SPI_CRCPolynomial = 7;        //CRC值计算的多项式
  42.         SPI_Init(SPI1, &SPI_InitStructure);                          //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
  43.         
  44.         SPI_Cmd(SPI1, ENABLE); //使能SPI外设
  45.         SPI1_ReadWriteByte(0xFF);//启动传输
  46. }

  47. /*
  48.         SPI的读写操作
  49.         **TxData:要写入的字节
  50.         **返回值:读取到的字节
  51. */
  52. uint8_t SPI1_ReadWriteByte(uint8_t TxData)
  53. {                                 
  54.         while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
  55.         {
  56.                 //等待发送完成
  57.         }                          
  58.         SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据
  59.         while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
  60.         {
  61.                 //等待接收完成
  62.         }                                                              
  63.         return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据                                            
  64. }
复制代码

SPI.h
  1. #ifndef __SPI_H
  2. #define        __SPI_H

  3. #include "stm32f10x.h"

  4. #define SPI_CS(a)                if (a)        \
  5.                                                 GPIO_SetBits(GPIOA,GPIO_Pin_4);\
  6.                                                 else                \
  7.                                             GPIO_ResetBits(GPIOA,GPIO_Pin_4)

  8. void SPI1_Init(void);//SPI1初始化函数
  9. uint8_t SPI1_ReadWriteByte(uint8_t TxData);//SPI1读写函数

  10. #endif
复制代码

三、读取 W25Q128 设备 ID
本次实验是:读取 W25Q128 设备 ID,然后通过串口1打印出来。
通过简单的实验来调试,有利于发现问题。
通过W25Q128的数据手册可知其设备ID如下图所示:

0429d107819242b99ac4b1572581e79f.png

W25Q128.c
  1. #include "W25Q128.h"
  2. #include "SPI.h"


  3. uint8_t W25Q128_ReadWriteByte(uint8_t TxData)//函数包装一下
  4. {     
  5.            return SPI1_ReadWriteByte(TxData);                 
  6. }

  7. uint16_t W25Q128_ReadID(void)//读取芯片ID
  8. {
  9.         uint16_t Temp = 0;         
  10.         W25Q128_CS(0);                                    
  11.         W25Q128_ReadWriteByte(W25X_ManufactDeviceID);//发送读取ID命令            
  12.         W25Q128_ReadWriteByte(0x00);            
  13.         W25Q128_ReadWriteByte(0x00);            
  14.         W25Q128_ReadWriteByte(0x00);                                    
  15.         Temp|=W25Q128_ReadWriteByte(0xFF)<<8;  
  16.         Temp|=W25Q128_ReadWriteByte(0xFF);         
  17.         W25Q128_CS(1);                                    
  18.         return Temp;
  19. }
复制代码

W25Q128.h
  1. #ifndef __W25Q128_H
  2. #define        __W25Q128_H

  3. #include "stm32f10x.h"

  4. //操作指令表
  5. #define W25X_ManufactDeviceID         0x90       //制造商+设备ID

  6. #define W25Q128_CS(a) SPI_CS(a)        

  7. uint8_t W25Q128_ReadWriteByte(uint8_t TxData);//函数包装一下
  8. uint16_t W25Q128_ReadID(void);//读取芯片ID

  9. #endif
复制代码

main.c
  1. #include <stdio.h>
  2. #include "Uart1.h"
  3. #include "delay.h"
  4. #include "SPI.h"
  5. #include "W25Q128.h"

  6. uint16_t W25Q128_ID=0;

  7. int main (void)
  8. {
  9.         Uart1_init();
  10.         SPI1_Init();
  11.         
  12.         W25Q128_ID = W25Q128_ReadID();
  13.         while(1)
  14.         {
  15.                 printf("\nW25Q128_ID=0x%X\n",W25Q128_ID);
  16.                 delay_ms(500);
  17.         }
  18. }
复制代码

实验结果
440efd4c84a849299dc4a781b30b43e4.png

接收到 W25Q128 设备ID 为0xEF17

四、读写 W25Q128 外部 Flash
W25Q128.c
  1. #include "W25Q128.h"
  2. #include "SPI.h"


  3. uint8_t W25Q128_ReadWriteByte(uint8_t TxData)//函数包装一下
  4. {     
  5.          return SPI1_ReadWriteByte(TxData);                 
  6. }


  7. uint16_t W25Q128_ReadID(void)//读取芯片ID
  8. {
  9.         uint16_t Temp = 0;         
  10.         W25Q128_CS(0);                                    
  11.         W25Q128_ReadWriteByte(W25X_ManufactDeviceID);//发送读取ID命令            
  12.         W25Q128_ReadWriteByte(0x00);            
  13.         W25Q128_ReadWriteByte(0x00);            
  14.         W25Q128_ReadWriteByte(0x00);                                    
  15.         Temp|=W25Q128_ReadWriteByte(0xFF)<<8;  
  16.         Temp|=W25Q128_ReadWriteByte(0xFF);         
  17.         W25Q128_CS(1);                                    
  18.         return Temp;
  19. }

  20. //读取W25Q128的状态寄存器
  21. //BIT7  6   5   4   3   2   1   0
  22. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
  23. //SPR:默认0,状态寄存器保护位,配合WP使用
  24. //TB,BP2,BP1,BP0:FLASH区域写保护设置
  25. //WEL:写使能锁定
  26. //BUSY:忙标记位(1,忙;0,空闲)
  27. //默认:0x00
  28. uint8_t W25Q128_ReadSR(void)//读取状态寄存器
  29. {
  30.     uint8_t byte=0;
  31.     W25Q128_CS(0);                               //使能器件
  32.     W25Q128_ReadWriteByte(W25X_ReadStatusReg1);  //发送读取状态寄存器命令
  33.     byte=W25Q128_ReadWriteByte(0Xff);            //读取一个字节
  34.     W25Q128_CS(1);                               //取消片选
  35.     return byte;
  36. }

  37. //写W25Q128状态寄存器
  38. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
  39. void W25Q128_WriteSR(uint8_t sr)//写状态寄存器
  40. {
  41.     W25Q128_CS(0);                                           //使能器件
  42.     W25Q128_ReadWriteByte(W25X_WriteStatusReg1);        //发送写取状态寄存器命令
  43.     W25Q128_ReadWriteByte(sr);                               //写入一个字节
  44.     W25Q128_CS(1);                                            //取消片选
  45. }

  46. void W25Q128_Write_Enable(void) //写使能
  47. {
  48.            W25Q128_CS(0);        
  49.         W25Q128_ReadWriteByte(W25X_WriteEnable);
  50.         W25Q128_CS(1);        
  51. }

  52. void W25Q128_Write_Disable(void) //禁止写入        
  53. {
  54.            W25Q128_CS(0);        
  55.         W25Q128_ReadWriteByte(W25X_WriteDisable);
  56.         W25Q128_CS(1);        
  57. }

  58. void W25Q128_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)   
  59. {                                                     
  60.         W25Q128_CS(0);                                  //使能器件   
  61.     W25Q128_ReadWriteByte(W25X_ReadData);      //发送读取命令  
  62.     W25Q128_ReadWriteByte((uint8_t)((ReadAddr)>>16));   //发送24bit地址   
  63.     W25Q128_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
  64.     W25Q128_ReadWriteByte((uint8_t)ReadAddr);   
  65.     for(uint16_t i=0;i<NumByteToRead;i++)
  66.         {
  67.        pBuffer<i>=W25Q128_ReadWriteByte(0XFF);    //循环读数  
  68.     }
  69.         W25Q128_CS(1);                                                  
  70. }  


  71. void W25Q128_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
  72. {

  73.     W25Q128_Write_Enable();                  //SET WEL
  74.         W25Q128_CS(0);                            //使能器件   
  75.     W25Q128_ReadWriteByte(W25X_PageProgram);   //发送写页命令   

  76.     W25Q128_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址   
  77.     W25Q128_ReadWriteByte((uint8_t)((WriteAddr)>>8));   
  78.     W25Q128_ReadWriteByte((uint8_t)WriteAddr);   
  79.     for(uint16_t i=0;i<NumByteToWrite;i++)
  80.         {
  81.                 W25Q128_ReadWriteByte(pBuffer<i>);//循环写数  
  82.         }
  83.         W25Q128_CS(1);                            //取消片选
  84.         W25Q128_Wait_Busy();                                           //等待写入结束
  85. }

  86. //无检验写SPI FLASH
  87. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
  88. //具有自动换页功能
  89. void W25Q128_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)   
  90. {                                          
  91.         uint16_t pageremain=256-WriteAddr%256; //单页剩余的字节数                             
  92.         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
  93.         while(1)
  94.         {           
  95.                 W25Q128_Write_Page(pBuffer,WriteAddr,pageremain);
  96.                 if(NumByteToWrite==pageremain)        break;//写入结束了
  97.                  else
  98.                 {
  99.                         pBuffer+=pageremain;
  100.                         WriteAddr+=pageremain;        
  101.                         NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
  102.                         if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
  103.                         else pageremain=NumByteToWrite;           //不够256个字节了
  104.                 }
  105.         }            
  106. }

  107. //写SPI FLASH  
  108. //在指定地址开始写入指定长度的数据
  109. //该函数带擦除操作!
  110. //pBuffer:数据存储区
  111. //WriteAddr:开始写入的地址(24bit)                                                
  112. //NumByteToWrite:要写入的字节数(最大65535)   
  113. uint8_t W25Q128_BUFFER[4096];                 
  114. void W25Q128_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)   
  115. {
  116.          uint16_t i;   
  117.         uint8_t * W25Q128_BUF;         
  118.     W25Q128_BUF=W25Q128_BUFFER;            
  119.          uint32_t secpos = WriteAddr/4096;//扇区地址  
  120.         uint16_t secoff = WriteAddr%4096;//在扇区内的偏移
  121.         uint16_t secremain = 4096-secoff;//扇区剩余空间大小   

  122.          if(NumByteToWrite<=secremain)                secremain=NumByteToWrite;//不大于4096个字节
  123.         while(1)
  124.         {        
  125.                 W25Q128_Read(W25Q128_BUF,secpos*4096,4096);//读出整个扇区的内容
  126.                 for(i=0;i<secremain;i++)//校验数据
  127.                 {
  128.                         if(W25Q128_BUF[secoff+i]!=0XFF)        break;//需要擦除            
  129.                 }
  130.                 if(i<secremain)//需要擦除
  131.                 {
  132.                         W25Q128_Erase_Sector(secpos*4096);//擦除这个扇区
  133.                         for(i=0;i<secremain;i++)           //复制
  134.                         {
  135.                                 W25Q128_BUF[i+secoff]=pBuffer<i>;         
  136.                         }
  137.                         W25Q128_Write_NoCheck(W25Q128_BUF,secpos*4096,4096);//写入整个扇区  

  138.                 }else W25Q128_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
  139.                 if(NumByteToWrite==secremain)        break;//写入结束了
  140.                 else//写入未结束
  141.                 {
  142.                         secpos++;//扇区地址增1
  143.                         secoff=0;//偏移位置为0         

  144.                     pBuffer+=secremain;  //指针偏移
  145.                         WriteAddr+=secremain;//写地址偏移           
  146.                     NumByteToWrite-=secremain;                                //字节数递减
  147.                         if(NumByteToWrite>4096)        secremain=4096;        //下一个扇区还是写不完
  148.                         else         secremain=NumByteToWrite;                //下一个扇区可以写完了
  149.                 }         
  150.         }         
  151. }

  152. //擦除一个扇区
  153. //Dst_Addr:扇区地址 根据实际容量设置
  154. //擦除一个扇区的最少时间:150ms

  155. void W25Q128_Erase_Sector(uint32_t Dst_Addr)   
  156. {  
  157.     W25Q128_Write_Enable();                  //SET WEL         
  158.     W25Q128_Wait_Busy();   
  159.           W25Q128_CS(0);                            //使能器件   
  160.     W25Q128_ReadWriteByte(W25X_SectorErase);   //发送扇区擦除指令
  161.     W25Q128_ReadWriteByte((uint8_t)((Dst_Addr)>>16));  //发送24bit地址   
  162.     W25Q128_ReadWriteByte((uint8_t)((Dst_Addr)>>8));   
  163.     W25Q128_ReadWriteByte((uint8_t)Dst_Addr);  
  164.         W25Q128_CS(1);                                    //取消片选                  
  165.     W25Q128_Wait_Busy();                                               //等待擦除完成
  166. }  

  167. //擦除整个芯片                  
  168. //等待时间超长...
  169. void W25Q128_Erase_Chip(void)   
  170. {                                   
  171.     W25Q128_Write_Enable();                  //SET WEL
  172.     W25Q128_Wait_Busy();   
  173.           W25Q128_CS(0);                           //使能器件   
  174.     W25Q128_ReadWriteByte(W25X_ChipErase);   //发送片擦除命令  
  175.         W25Q128_CS(1);                           //取消片选                  
  176.         W25Q128_Wait_Busy();                                              //等待芯片擦除结束
  177. }


  178. //等待空闲
  179. void W25Q128_Wait_Busy(void)   
  180. {   
  181.         while((W25Q128_ReadSR()&0x01)==0x01);   // 等待BUSY位清空
  182. }  

  183. //进入掉电模式
  184. void W25Q128_PowerDown(void)   
  185. {
  186.           W25Q128_CS(0);                            //使能器件   
  187.     W25Q128_ReadWriteByte(W25X_PowerDown);    //发送掉电命令  
  188.         W25Q128_CS(1);                            //取消片选                  
  189. }   
  190. //掉电唤醒
  191. void W25Q128_WAKEUP(void)   
  192. {  
  193.           W25Q128_CS(0);                                //使能器件   
  194.     W25Q128_ReadWriteByte(W25X_ReleasePowerDown);   
  195.         W25Q128_CS(1);                                //取消片选                  
  196. }   </i></i></i>
复制代码

W25Q128.h
#ifndef __W25Q128_H
#define        __W25Q128_H

#include "stm32f10x.h"

//操作指令表
#define W25X_WriteEnable                           0x06         //写使能
#define W25X_WriteDisable                           0x04         //写禁止
#define W25X_ReadStatusReg1                         0x05         //读状态寄存器1
#define W25X_ReadStatusReg2                         0x35         //读状态寄存器2
#define W25X_ReadStatusReg3                         0x15         //读状态寄存器3
#define W25X_WriteStatusReg1                   0x01         //写状态寄存器1
#define W25X_WriteStatusReg2                   0x31         //写状态寄存器2
#define W25X_WriteStatusReg3                   0x11         //写状态寄存器3
#define W25X_ReadData                                0x03         //读数据
#define W25X_FastReadData                           0x0B         //快读
#define W25X_FastReadDual                           0x3B    //双输出快读
#define W25X_PageProgram                           0x02         //页编程
#define W25X_BlockErase                                   0xD8         //块擦除(64K)
#define W25X_SectorErase                           0x20    //扇区擦除(4K)
#define W25X_ChipErase                                   0xC7    //芯片擦除
#define W25X_PowerDown                                   0xB9    //掉电
#define W25X_ReleasePowerDown                 0xAB    //释放掉电
#define W25X_DeviceID                            0xAB    //器件ID
#define W25X_ManufactDeviceID                 0x90    //制造商+设备ID
#define W25X_JedecDeviceID                         0x9F         //电子元件ID


#define W25Q128_CS(a) SPI_CS(a)        


uint8_t W25Q128_ReadWriteByte(uint8_t TxData);//函数包装一下

uint16_t W25Q128_ReadID(void);//读取芯片ID

uint8_t W25Q128_ReadSR(void);//读取状态寄存器
void W25Q128_WriteSR(uint8_t sr);//写状态寄存器

void W25Q128_Write_Enable(void);//写使能
void W25Q128_Write_Disable(void);//禁止写入        

void W25Q128_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead); //读取数据
void W25Q128_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//页写
void W25Q128_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//无检验写数据,可自动翻页
void W25Q128_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//写入数据,带擦写功能

void W25Q128_Erase_Sector(uint32_t Dst_Addr);//擦除扇区
void W25Q128_Erase_Chip(void);//擦除整个芯片        

void W25Q128_Wait_Busy(void);//等待空闲
void W25Q128_PowerDown(void); //进入掉电模式
void W25Q128_WAKEUP(void);//掉电唤醒

#endif

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
main.c
#include <stdio.h>
#include "Uart1.h"
#include "delay.h"
#include "SPI.h"
#include "W25Q128.h"

uint16_t W25Q128_ID=0;
uint8_t Write_data[20]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
uint8_t Read_data[100];

int main (void)
{
        Uart1_init();
        SPI1_Init();
        
        W25Q128_ID=W25Q128_ReadID();
        W25Q128_Write_Page(Write_data,0x01,20);
        
        while(1)
        {
                W25Q128_Read(Read_data,0x00,100);
                for(int i=0;i<100;i++)
                {
                        printf(" 0x%X ",Read_data);
                }
                printf("\nW25Q128_ID=0x%X\n",W25Q128_ID);
                delay_ms(500);
        }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
实验结果

————————————————
版权声明:根号五


d2e3010e09354c1a831211d35a40522f.png
1 收藏 1 评论0 发布时间:2022-9-6 21:44

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版