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

【经验分享】STM32使用HAL库模拟SPI方式驱动W25Q128存芯片

[复制链接]
STMCU小助手 发布时间:2022-4-11 10:16
1、模拟SPI方式的IO配置 20200622094245892.png 2、驱动代码2.1 头文件
  1. #ifndef __W25QXX_H
  2. #define __W25QXX_H                           
  3. #include "main.h"
  4. //W25X系列/Q系列芯片列表           
  5. //W25Q80  ID  0XEF13
  6. //W25Q16  ID  0XEF14
  7. //W25Q32  ID  0XEF15
  8. //W25Q64  ID  0XEF16        
  9. //W25Q128 ID  0XEF17        
  10. #define W25Q80         0XEF13         
  11. #define W25Q16         0XEF14
  12. #define W25Q32         0XEF15
  13. #define W25Q64         0XEF16
  14. #define W25Q128        0XEF17
  15. extern uint16_t W25QXX_TYPE;                                        //定义W25QXX芯片型号                  
  16. extern uint32_t W25QXX_SIZE;                    //容量
  17. extern uint8_t  W25QXX_UID[];                   //唯一ID
  18. //
  19. //指令表
  20. #define W25X_WriteEnable                0x06
  21. #define W25X_WriteDisable                0x04
  22. #define W25X_ReadStatusReg                0x05
  23. #define W25X_WriteStatusReg                0x01
  24. #define W25X_ReadData                        0x03
  25. #define W25X_FastReadData                0x0B
  26. #define W25X_FastReadDual                0x3B
  27. #define W25X_PageProgram                0x02
  28. #define W25X_BlockErase                        0xD8
  29. #define W25X_SectorErase                0x20
  30. #define W25X_ChipErase                        0xC7
  31. #define W25X_PowerDown                        0xB9
  32. #define W25X_ReleasePowerDown        0xAB
  33. #define W25X_DeviceID                        0xAB
  34. #define W25X_ManufactDeviceID        0x90
  35. #define W25X_JedecDeviceID                0x9F
  36. void W25QXX_Init(void);
  37. uint16_t  W25QXX_ReadID(void);                              //读取FLASH ID
  38. void W25QXX_ReadUniqueID(uint8_t UID[8]);       //读取唯一ID
  39. uint32_t W25QXX_ReadCapacity(void);             //读取容量
  40. uint8_t         W25QXX_ReadSR(void);                        //读取状态寄存器
  41. void W25QXX_Write_SR(uint8_t sr);                          //写状态寄存器
  42. void W25QXX_Write_Enable(void);                  //写使能
  43. void W25QXX_Write_Disable(void);                //写保护
  44. void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
  45. void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);   //读取flash
  46. void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//写入flash
  47. void W25QXX_Erase_Chip(void);                      //整片擦除
  48. void W25QXX_Erase_Sector(uint32_t Dst_Addr);        //扇区擦除
  49. void W25QXX_Wait_Busy(void);                   //等待空闲
  50. void W25QXX_PowerDown(void);                //进入掉电模式
  51. void W25QXX_WAKEUP(void);                                //唤醒
  52. #endif
复制代码
2.2 源文件
  1. #include "w25qxx.h"
  2. uint16_t W25QXX_TYPE = 0;
  3. uint32_t W25QXX_SIZE = 0;
  4. uint8_t  W25QXX_UID[8] = {0};
  5. ///
  6. ///            移植修改区
  7. ///
  8. ///
  9. #define W25QXX_CS_H()         HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET)
  10. #define W25QXX_CS_L()         HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET)
  11. #define W25QXX_SCK_H()        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET)
  12. #define W25QXX_SCK_L()        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET)
  13. #define W25QXX_MOSI_H()       HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET)
  14. #define W25QXX_MOSI_L()       HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET)
  15. #define W25QXX_MISO_GET()      (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) == GPIO_PIN_SET ? 1 : 0)
  16. static void delay_us(uint32_t us)
  17. {
  18.     uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
  19.     while (delay--)
  20.     {
  21.         ;
  22.     }
  23. }
  24. static uint8_t W25QXX_SPI_ReadWriteByte(uint8_t TxData)
  25. {
  26.     int i = 0;
  27.     uint8_t RxData = 0;
  28.     W25QXX_SCK_L();
  29.     for(i = 7; i >= 0; i--)
  30.     {
  31.         W25QXX_SCK_L();
  32.         if(TxData & (1 << i))
  33.         {
  34.             W25QXX_MOSI_H();
  35.         }
  36.         else
  37.         {
  38.             W25QXX_MOSI_L();
  39.         }
  40.         delay_us(1);
  41.         W25QXX_SCK_H();
  42.         RxData <<= 1;
  43.         RxData |= W25QXX_MISO_GET();
  44.         delay_us(1);
  45.     }
  46.     W25QXX_SCK_L();
  47.     return RxData;
  48. }
  49. static void W25QXX_SPI_Init(void)
  50. {
  51.         // 这里添加初始化IO的代码
  52. }
  53. ///
  54. ///            驱动代码
  55. ///
  56. ///
  57. //4Kbytes为一个Sector
  58. //16个扇区为1个Block
  59. //W25Q128
  60. //容量为16M字节,共有128个Block,4096个Sector
  61. //初始化SPI FLASH的IO口
  62. void W25QXX_Init(void)
  63. {
  64.         W25QXX_SPI_Init();
  65.         W25QXX_CS_H();                // 取消片选
  66.         W25QXX_SCK_L();               // 时钟空闲为低电平
  67.     W25QXX_CS_L();
  68.     W25QXX_SPI_ReadWriteByte(0XFF);
  69.     W25QXX_CS_H();
  70.     W25QXX_TYPE=W25QXX_ReadID();             //读取FLASH ID
  71.         W25QXX_SIZE=W25QXX_ReadCapacity();       //读取容量
  72.         W25QXX_ReadUniqueID(W25QXX_UID);         //读取唯一ID
  73. }
  74. uint32_t W25QXX_ReadCapacity(void)
  75. {
  76.         int i = 0;
  77.         uint8_t arr[4] = {0,0,0,0};
  78.     W25QXX_CS_L();
  79.     W25QXX_SPI_ReadWriteByte(0x5A);
  80.     W25QXX_SPI_ReadWriteByte(0x00);
  81.     W25QXX_SPI_ReadWriteByte(0x00);
  82.     W25QXX_SPI_ReadWriteByte(0x84);
  83.         W25QXX_SPI_ReadWriteByte(0x00);
  84.         for(i = 0; i < sizeof(arr); i++)
  85.         {
  86.                 arr<i> = W25QXX_SPI_ReadWriteByte(0xFF);
  87.         }
  88.     W25QXX_CS_H();
  89.     return ((((*(uint32_t *)arr)) + 1) >> 3);
  90. }
  91. void W25QXX_ReadUniqueID(uint8_t UID[8])
  92. {
  93.         int i = 0;
  94.         W25QXX_CS_L();
  95.     W25QXX_SPI_ReadWriteByte(0x4B);
  96.     W25QXX_SPI_ReadWriteByte(0x00);
  97.     W25QXX_SPI_ReadWriteByte(0x00);
  98.     W25QXX_SPI_ReadWriteByte(0x00);
  99.         W25QXX_SPI_ReadWriteByte(0x00);
  100.     for(i = 0; i < 8; i++)
  101.         {
  102.                 UID<i> = W25QXX_SPI_ReadWriteByte(0xFF);
  103.         }
  104.         W25QXX_CS_H();
  105. }
  106. //读取W25QXX的状态寄存器
  107. //BIT7  6   5   4   3   2   1   0
  108. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
  109. //SPR:默认0,状态寄存器保护位,配合WP使用
  110. //TB,BP2,BP1,BP0:FLASH区域写保护设置
  111. //WEL:写使能锁定
  112. //BUSY:忙标记位(1,忙;0,空闲)
  113. //默认:0x00
  114. uint8_t W25QXX_ReadSR(void)
  115. {
  116.     uint8_t byte=0;
  117.     W25QXX_CS_L();                            //使能器件
  118.     W25QXX_SPI_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令
  119.     byte=W25QXX_SPI_ReadWriteByte(0Xff);          //读取一个字节
  120.     W25QXX_CS_H();                            //取消片选
  121.     return byte;
  122. }
  123. //写W25QXX状态寄存器
  124. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
  125. void W25QXX_Write_SR(uint8_t sr)
  126. {
  127.     W25QXX_CS_L();                            //使能器件
  128.     W25QXX_SPI_ReadWriteByte(W25X_WriteStatusReg);//发送写取状态寄存器命令
  129.     W25QXX_SPI_ReadWriteByte(sr);                       //写入一个字节
  130.     W25QXX_CS_H();                            //取消片选
  131. }
  132. //W25QXX写使能
  133. //将WEL置位
  134. void W25QXX_Write_Enable(void)
  135. {
  136.     W25QXX_CS_L();                                  //使能器件
  137.     W25QXX_SPI_ReadWriteByte(W25X_WriteEnable);         //发送写使能
  138.     W25QXX_CS_H();                                   //取消片选
  139. }
  140. //W25QXX写禁止
  141. //将WEL清零
  142. void W25QXX_Write_Disable(void)
  143. {
  144.     W25QXX_CS_L();                            //使能器件
  145.     W25QXX_SPI_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令
  146.     W25QXX_CS_H();                            //取消片选
  147. }
  148. //读取芯片ID
  149. //返回值如下:
  150. //0XEF13,表示芯片型号为W25Q80
  151. //0XEF14,表示芯片型号为W25Q16
  152. //0XEF15,表示芯片型号为W25Q32
  153. //0XEF16,表示芯片型号为W25Q64
  154. //0XEF17,表示芯片型号为W25Q128
  155. uint16_t W25QXX_ReadID(void)
  156. {
  157.     uint16_t Temp = 0;
  158.     W25QXX_CS_L();
  159.     W25QXX_SPI_ReadWriteByte(0x90);//发送读取ID命令
  160.     W25QXX_SPI_ReadWriteByte(0x00);
  161.     W25QXX_SPI_ReadWriteByte(0x00);
  162.     W25QXX_SPI_ReadWriteByte(0x00);
  163.     Temp|=W25QXX_SPI_ReadWriteByte(0xFF)<<8;
  164.     Temp|=W25QXX_SPI_ReadWriteByte(0xFF);
  165.     W25QXX_CS_H();
  166.     return Temp;
  167. }
  168. //读取SPI FLASH
  169. //在指定地址开始读取指定长度的数据
  170. //pBuffer:数据存储区
  171. //ReadAddr:开始读取的地址(24bit)
  172. //NumByteToRead:要读取的字节数(最大65535)
  173. void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
  174. {
  175.     uint16_t i;
  176.     W25QXX_CS_L();                                    //使能器件
  177.     W25QXX_SPI_ReadWriteByte(W25X_ReadData);                 //发送读取命令
  178.     W25QXX_SPI_ReadWriteByte((uint8_t)((ReadAddr)>>16));          //发送24bit地址
  179.     W25QXX_SPI_ReadWriteByte((uint8_t)((ReadAddr)>>8));
  180.     W25QXX_SPI_ReadWriteByte((uint8_t)ReadAddr);
  181.     for(i=0; i<NumByteToRead; i++)
  182.     {
  183.         pBuffer<i>=W25QXX_SPI_ReadWriteByte(0XFF);           //循环读数
  184.     }
  185.     W25QXX_CS_H();
  186. }
  187. //SPI在一页(0~65535)内写入少于256个字节的数据
  188. //在指定地址开始写入最大256字节的数据
  189. //pBuffer:数据存储区
  190. //WriteAddr:开始写入的地址(24bit)
  191. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
  192. void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
  193. {
  194.     uint16_t i;
  195.     W25QXX_Write_Enable();                          //SET WEL
  196.     W25QXX_CS_L();                                    //使能器件
  197.     W25QXX_SPI_ReadWriteByte(W25X_PageProgram);              //发送写页命令
  198.     W25QXX_SPI_ReadWriteByte((uint8_t)((WriteAddr)>>16));         //发送24bit地址
  199.     W25QXX_SPI_ReadWriteByte((uint8_t)((WriteAddr)>>8));
  200.     W25QXX_SPI_ReadWriteByte((uint8_t)WriteAddr);
  201.     for(i=0; i<NumByteToWrite; i++)W25QXX_SPI_ReadWriteByte(pBuffer<i>); //循环写数
  202.     W25QXX_CS_H();                                    //取消片选
  203.     W25QXX_Wait_Busy();                                                           //等待写入结束
  204. }
  205. //无检验写SPI FLASH
  206. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
  207. //具有自动换页功能
  208. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
  209. //pBuffer:数据存储区
  210. //WriteAddr:开始写入的地址(24bit)
  211. //NumByteToWrite:要写入的字节数(最大65535)
  212. //CHECK OK
  213. void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
  214. {
  215.     uint16_t pageremain;
  216.     pageremain=256-WriteAddr%256; //单页剩余的字节数
  217.     if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
  218.     while(1)
  219.     {
  220.         W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
  221.         if(NumByteToWrite==pageremain)break;//写入结束了
  222.         else //NumByteToWrite>pageremain
  223.         {
  224.             pBuffer+=pageremain;
  225.             WriteAddr+=pageremain;
  226.             NumByteToWrite-=pageremain;                          //减去已经写入了的字节数
  227.             if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
  228.             else pageremain=NumByteToWrite;           //不够256个字节了
  229.         }
  230.     };
  231. }
  232. //写SPI FLASH
  233. //在指定地址开始写入指定长度的数据
  234. //该函数带擦除操作!
  235. //pBuffer:数据存储区
  236. //WriteAddr:开始写入的地址(24bit)
  237. //NumByteToWrite:要写入的字节数(最大65535)
  238. uint8_t W25QXX_BUFFER[4096];
  239. void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
  240. {
  241.     uint32_t secpos;
  242.     uint16_t secoff;
  243.     uint16_t secremain;
  244.     uint16_t i;
  245.     uint8_t * W25QXX_BUF;
  246.     W25QXX_BUF=W25QXX_BUFFER;
  247.     secpos=WriteAddr/4096;//扇区地址
  248.     secoff=WriteAddr%4096;//在扇区内的偏移
  249.     secremain=4096-secoff;//扇区剩余空间大小
  250.     if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
  251.     while(1)
  252.     {
  253.         W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
  254.         for(i=0; i<secremain; i++) //校验数据
  255.         {
  256.             if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除
  257.         }
  258.         if(i<secremain)//需要擦除
  259.         {
  260.             W25QXX_Erase_Sector(secpos);                //擦除这个扇区
  261.             for(i=0; i<secremain; i++)                           //复制
  262.             {
  263.                 W25QXX_BUF[i+secoff]=pBuffer<i>;
  264.             }
  265.             W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区
  266.         }
  267.         else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain); //写已经擦除了的,直接写入扇区剩余区间.
  268.         if(NumByteToWrite==secremain)break;//写入结束了
  269.         else//写入未结束
  270.         {
  271.             secpos++;//扇区地址增1
  272.             secoff=0;//偏移位置为0
  273.             pBuffer+=secremain;                                  //指针偏移
  274.             WriteAddr+=secremain;                                //写地址偏移
  275.             NumByteToWrite-=secremain;                        //字节数递减
  276.             if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
  277.             else secremain=NumByteToWrite;                //下一个扇区可以写完了
  278.         }
  279.     };
  280. }
  281. //擦除整个芯片
  282. //等待时间超长...
  283. void W25QXX_Erase_Chip(void)
  284. {
  285.     W25QXX_Write_Enable();                                  //SET WEL
  286.     W25QXX_Wait_Busy();
  287.     W25QXX_CS_L();                                    //使能器件
  288.     W25QXX_SPI_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令
  289.     W25QXX_CS_H();                                    //取消片选
  290.     W25QXX_Wait_Busy();                                                      //等待芯片擦除结束
  291. }
  292. //擦除一个扇区
  293. //Dst_Addr:扇区地址 根据实际容量设置
  294. //擦除一个山区的最少时间:150ms
  295. void W25QXX_Erase_Sector(uint32_t Dst_Addr)
  296. {
  297.     //监视falsh擦除情况,测试用
  298.     Dst_Addr*=4096;
  299.     W25QXX_Write_Enable();                          //SET WEL
  300.     W25QXX_Wait_Busy();
  301.     W25QXX_CS_L();                                    //使能器件
  302.     W25QXX_SPI_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令
  303.     W25QXX_SPI_ReadWriteByte((uint8_t)((Dst_Addr)>>16));          //发送24bit地址
  304.     W25QXX_SPI_ReadWriteByte((uint8_t)((Dst_Addr)>>8));
  305.     W25QXX_SPI_ReadWriteByte((uint8_t)Dst_Addr);
  306.     W25QXX_CS_H();                                    //取消片选
  307.     W25QXX_Wait_Busy();                                                      //等待擦除完成
  308. }
  309. //等待空闲
  310. void W25QXX_Wait_Busy(void)
  311. {
  312.     while((W25QXX_ReadSR()&0x01)==0x01);                  // 等待BUSY位清空
  313. }
  314. //进入掉电模式
  315. void W25QXX_PowerDown(void)
  316. {
  317.     W25QXX_CS_L();                                            //使能器件
  318.     W25QXX_SPI_ReadWriteByte(W25X_PowerDown);        //发送掉电命令
  319.     W25QXX_CS_H();                                    //取消片选
  320.     delay_us(3);                               //等待TPD
  321. }
  322. //唤醒
  323. void W25QXX_WAKEUP(void)
  324. {
  325.     W25QXX_CS_L();                                    //使能器件
  326.     W25QXX_SPI_ReadWriteByte(W25X_ReleasePowerDown);        //  send W25X_PowerDown command 0xAB
  327.     W25QXX_CS_H();                                    //取消片选
  328.     delay_us(3);                                    //等待TRES1
  329. }
  330. </i></i></i></i></i>
复制代码
收藏 评论0 发布时间:2022-4-11 10:16

举报

0个回答

所属标签

相似分享

官网相关资源

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