学习目标

  1. 掌握w25q128的移植
  2. 熟悉驱动开发学习方式

    学习内容

    原理图

    122.png

    W25Q128介绍

    W25Q128是一种常见的串行闪存器件,它采用SPI(Serial Peripheral Interface)接口协议,具有高速读写和擦除功能,可用于存储和读取数据。W25Q128芯片容量为128 M-bit(16 M-byte),其中名称后的数字代表不同的容量选项。不同的型号和容量选项可以满足不同应用的需求,比如W25Q16、W25Q32、W25Q128等。通常被用于嵌入式设备、存储设备、路由器等高性能电子设备中。
    W25Q128闪存芯片的内存分配是按照扇区(Sector)和块(Block)进行的,每个扇区的大小为4KB,每个块包含16个扇区,即一个块的大小为64KB。
    123.png

    实现参考

    https://lceda001.feishu.cn/wiki/Yl6mwgNQiiwolrkqBbscxk45nFb

    封装改造

    官方示例中,耦合太强,进行一些修改,修改后如下: ```c

    ifndef BSPW25Q64_H

    define BSPW25Q64_H

include “gd32f4xx.h”

include “SPI.h”

define W25Q_CS_PORT_RCU RCU_GPIOF

define W25Q_CS_PORT GPIOF

define W25Q_CS_PIN GPIO_PIN_6

define W25Q_CS_SELECT() gpio_bit_write(W25Q_CS_PORT, W25Q_CS_PIN, RESET)

define W25Q_CS_UNSELECT() gpio_bit_write(W25Q_CS_PORT, W25Q_CS_PIN, SET)

define W25Q_SPI_RD_WR(data) SPI4_read_write(data)

void W25Q64_init(void); uint16_t W25Q64_readID(void); void W25Q64_write(uint8_t buffer, uint32_t addr, uint16_t numbyte); void W25Q64_read(uint8_t buffer,uint32_t read_addr,uint16_t read_length) ;

endif

  1. ```c
  2. #include "bsp_w25q64.h"
  3. void W25Q64_init(void)
  4. {
  5. //开启CS引脚时钟
  6. rcu_periph_clock_enable(W25Q_CS_PORT_RCU);
  7. //配置CS引脚模式
  8. gpio_mode_set(W25Q_CS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, W25Q_CS_PIN);
  9. //配置CS输出模式
  10. gpio_output_options_set(W25Q_CS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, W25Q_CS_PIN);
  11. //W25Q64不选中
  12. W25Q_CS_UNSELECT();
  13. }
  14. /******************************************************************
  15. * 函 数 名 称:spi_read_write_byte
  16. * 函 数 说 明:硬件SPI的读写
  17. * 函 数 形 参:dat=发送的数据
  18. * 函 数 返 回:读取到的数据
  19. * 作 者:LC
  20. * 备 注:无
  21. ******************************************************************/
  22. static uint8_t spi_read_write_byte(uint8_t dat)
  23. {
  24. return W25Q_SPI_RD_WR(dat);
  25. }
  26. /******************************************************************
  27. * 函 数 名 称:W25Q64_readID
  28. * 函 数 说 明:读取W25Q64的厂商ID和设备ID
  29. * 函 数 形 参:无
  30. * 函 数 返 回:设备正常返回EF16
  31. * 作 者:LC
  32. * 备 注:无
  33. ******************************************************************/
  34. uint16_t W25Q64_readID(void)
  35. {
  36. uint16_t temp = 0;
  37. gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);
  38. spi_read_write_byte(0x90);//发送读取ID命令
  39. spi_read_write_byte(0x00);
  40. spi_read_write_byte(0x00);
  41. spi_read_write_byte(0x00);
  42. //接收数据
  43. temp |= spi_read_write_byte(0xFF)<<8;
  44. temp |= spi_read_write_byte(0xFF);
  45. gpio_bit_write(GPIOF, GPIO_PIN_6, SET);
  46. return temp;
  47. }
  48. /**********************************************************
  49. * 函 数 名 称:W25Q64_wait_busy
  50. * 函 数 功 能:判断W25Q64是否忙
  51. * 传 入 参 数:无
  52. * 函 数 返 回:无
  53. * 作 者:LC
  54. * 备 注:无
  55. **********************************************************/
  56. static void W25Q64_wait_busy(void)
  57. {
  58. unsigned char byte = 0;
  59. do
  60. {
  61. gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);
  62. spi_read_write_byte(0x05);
  63. byte = spi_read_write_byte(0Xff);
  64. gpio_bit_write(GPIOF, GPIO_PIN_6, SET);
  65. }while( ( byte & 0x01 ) == 1 );
  66. }
  67. /**********************************************************
  68. * 函 数 名 称:W25Q64_write_enable
  69. * 函 数 功 能:发送写使能
  70. * 传 入 参 数:无
  71. * 函 数 返 回:无
  72. * 作 者:LC
  73. * 备 注:无
  74. **********************************************************/
  75. void W25Q64_write_enable(void)
  76. {
  77. W25Q_CS_SELECT();
  78. spi_read_write_byte(0x06);
  79. W25Q_CS_UNSELECT();
  80. }
  81. /**********************************************************
  82. * 函 数 名 称:W25Q64_erase_sector
  83. * 函 数 功 能:擦除一个扇区
  84. * 传 入 参 数:addr=擦除的扇区号
  85. * 函 数 返 回:无
  86. * 作 者:LC
  87. * 备 注:addr=擦除的扇区号,范围=0~15
  88. **********************************************************/
  89. void W25Q64_erase_sector(uint32_t addr)
  90. {
  91. addr *= 4096;
  92. W25Q64_write_enable(); //写使能
  93. W25Q64_wait_busy(); //判断忙
  94. gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);
  95. spi_read_write_byte(0x20);
  96. spi_read_write_byte((uint8_t)((addr)>>16));
  97. spi_read_write_byte((uint8_t)((addr)>>8));
  98. spi_read_write_byte((uint8_t)addr);
  99. gpio_bit_write(GPIOF, GPIO_PIN_6, SET);
  100. //等待擦除完成
  101. W25Q64_wait_busy();
  102. }
  103. /**********************************************************
  104. * 函 数 名 称:W25Q64_write
  105. * 函 数 功 能:写数据到W25Q64进行保存
  106. * 传 入 参 数:buffer=写入的数据内容 addr=写入地址 numbyte=写入数据的长度
  107. * 函 数 返 回:无
  108. * 作 者:LC
  109. * 备 注:无
  110. **********************************************************/
  111. void W25Q64_write(uint8_t* buffer, uint32_t addr, uint16_t numbyte)
  112. { //0x02e21
  113. unsigned int i = 0;
  114. W25Q64_erase_sector(addr/4096);//擦除扇区数据
  115. W25Q64_write_enable();//写使能
  116. W25Q64_wait_busy(); //忙检测
  117. //写入数据
  118. W25Q_CS_SELECT();
  119. spi_read_write_byte(0x02);
  120. spi_read_write_byte((uint8_t)((addr)>>16));
  121. spi_read_write_byte((uint8_t)((addr)>>8));
  122. spi_read_write_byte((uint8_t)addr);
  123. for(i=0;i<numbyte;i++)
  124. {
  125. spi_read_write_byte(buffer[i]);
  126. }
  127. W25Q_CS_UNSELECT();
  128. W25Q64_wait_busy(); //忙检测
  129. }
  130. /**********************************************************
  131. * 函 数 名 称:W25Q64_read
  132. * 函 数 功 能:读取W25Q64的数据
  133. * 传 入 参 数:buffer=读出数据的保存地址 read_addr=读取地址 read_length=读去长度
  134. * 函 数 返 回:无
  135. * 作 者:LC
  136. * 备 注:无
  137. **********************************************************/
  138. void W25Q64_read(uint8_t* buffer,uint32_t read_addr,uint16_t read_length)
  139. {
  140. uint16_t i;
  141. W25Q_CS_SELECT();
  142. spi_read_write_byte(0x03);
  143. spi_read_write_byte((uint8_t)((read_addr)>>16));
  144. spi_read_write_byte((uint8_t)((read_addr)>>8));
  145. spi_read_write_byte((uint8_t)read_addr);
  146. for(i=0;i<read_length;i++)
  147. {
  148. buffer[i]= spi_read_write_byte(0XFF);
  149. }
  150. W25Q_CS_UNSELECT();
  151. }

测试逻辑如下:

  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "Usart0.h"
  6. #include "SPI.h"
  7. #include "bsp_w25q64.h"
  8. #include "string.h"
  9. uint8_t cnt = 0;
  10. void Usart0_on_recv(uint8_t* data, uint32_t len) {
  11. printf("recv: %X\r\n", data[0]);
  12. if(data[0] == 0x00) {
  13. printf("ID = %X\r\n",W25Q64_readID());
  14. } else if(data[0] == 0x01) {
  15. char buff[128];
  16. sprintf(buff, "hello: %d", cnt++);
  17. printf("write: %s (%d)\r\n", buff, strlen(buff));
  18. W25Q64_write((uint8_t*)buff, 0, strlen(buff));
  19. } else if(data[0] == 0x02) {
  20. uint8_t buff[128];
  21. W25Q64_read(buff, 0, 16);
  22. printf("read: %s (%d)\r\n", (char*)buff, strlen((char*)buff));
  23. }
  24. }
  25. int main(void)
  26. {
  27. systick_config();
  28. Usart0_init();
  29. SPI_init();
  30. W25Q64_init();
  31. while(1)
  32. {
  33. }
  34. }

练习题

  1. 实现GD32平台w25q128的驱动移植
  2. 实现STM32平台w25q128的驱动移植