[G32A] [G32A] 极海G32A1465测评】—— 软件SPI读写W25Q64

[复制链接]
 楼主| bingbing12138 发表于 2025-1-9 21:31 | 显示全部楼层 |阅读模式
本帖最后由 bingbing12138 于 2025-1-9 22:12 编辑

#技术资源# 非常感谢极海官方给我这样一个测评的机会,本次主要使用G32A1465实现软件SPI读写W25Q64功能,
首先我们需要了解一下什么是SPI:
一、SPI通信:
1.SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线
2.四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)
3.同步,全双工
4.支持总线挂载多设备(一主多从)
二、SPI硬件电路:
1.所有SPI设备的SCK、MOSI、MISO分别连在一起
2.主机另外引出多条SS控制线,分别接到各从机的SS引脚
3.输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入 图片1.png





三、SPI数据传输示意图: Snipaste_2025-01-09_21-39-31.png


四、SPI时序基本单元:
起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平
1111111.png
交换一个字节(模式0)
CPOL=0:空闲状态时,SCK为低电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据
222222222.png

交换一个字节(模式1)
CPOL=0:空闲状态时,SCK为低电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据
3333333333333333333.png
交换一个字节(模式2)
CPOL=1:空闲状态时,SCK为高电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据
44444444444444.png
交换一个字节(模式3)
CPOL=1:空闲状态时,SCK为高电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据
5555555555555555.png
五、W25Q64简介:
W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景
存储介质:Nor Flash(闪存)
时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)
存储容量(24位地址):
        W25Q40:          4Mbit / 512KByte
        W25Q80:          8Mbit / 1MByte
        W25Q16:          16Mbit / 2MByte
        W25Q32:          32Mbit / 4MByte
        W25Q64:          64Mbit / 8MByte
        W25Q128:  128Mbit / 16MByte
        W25Q256:  256Mbit / 32MByte

8787877857.png
六、Flash操作注意事项:
写入操作时:
  • 写入操作前,必须先进行写使能
  • 每个数据位只能由1改写为0,不能由0改写为1
  • 写入数据前必须先擦除,擦除后,所有数据位变为1
  • 擦除必须按最小擦除单元进行
  • 连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
  • 写入操作结束后,芯片进入忙状态,不响应新的读写操作

读取操作时:
   直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

七、代码及效果展示
主循环代码:
  1. #include "user_config.h"
  2. #include "g32a1xxx_pins.h"
  3. #include "board.h"
  4. #include "w25q64.h"
  5. #include "bsp_spi.h"

  6. uint8_t write_array[5] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE};
  7. uint8_t read_array[5] = {0};
  8. uint8_t MID;
  9. uint16_t DID;
  10. int main(void)
  11. {
  12.     /* Initialize clock */
  13.     CLOCK_SYS_Init(&g_clockConfig);
  14.    
  15.     W25Q64_Init();
  16.    
  17.     W25Q64_ReadID(&MID, &DID);
  18.    
  19.     W25Q64_SectorErase(0x002000);//擦除指定扇区
  20.     W25Q64_PageProgram(0x002000, write_array, 5);//在指定扇区写
  21.     W25Q64_ReadData(0x002000, read_array, 5);//从指定扇区读
  22.     while (1)
  23.     {
  24.         ;
  25.     }
  26. }
软件SPI.c
  1. #include "bsp_spi.h"

  2. //片选
  3. void MySPI_W_SS(uint8_t BitValue)
  4. {
  5.     PINS_WritePin(CS_GPIO_BASE, CS_PIN, BitValue);
  6. }
  7. //时钟
  8. void MySPI_W_SCK(uint8_t BitValue)
  9. {
  10.     PINS_WritePin(CLK_GPIO_BASE, CLK_PIN, BitValue);
  11. }
  12. //主机输出
  13. void MySPI_W_MOSI(uint8_t BitValue)
  14. {
  15.     PINS_WritePin(MOSI_GPIO_BASE, MOSI_PIN, BitValue);
  16. }
  17. //从机输入
  18. uint8_t MySPI_R_MISO(void)
  19. {
  20.     return (uint8_t)((PINS_ReadPinsInput(MISO_GPIO_BASE) >> MISO_PIN) & 0x01U);
  21. //    return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
  22. }

  23. void BSP_SPI_Init(void)
  24. {
  25.     /* Enable Clock to Port D */
  26.     CLOCK_SYS_ConfigModuleClock(PMA_CLK, NULL);
  27.     CLOCK_SYS_ConfigModuleClock(PMB_CLK, NULL);
  28.     /* Set pin mode */
  29.     PINS_SetMuxModeSel(CS_PORT_BASE, CS_PIN, PM_MUX_AS_GPIO);
  30.     PINS_SetMuxModeSel(CLK_PORT_BASE, CLK_PIN, PM_MUX_AS_GPIO);
  31.     PINS_SetMuxModeSel(MOSI_PORT_BASE, MOSI_PIN, PM_MUX_AS_GPIO);
  32.     /* Set pin interrupt */
  33.     PINS_SetPinIntSel(CS_PORT_BASE, CS_PIN, PM_DMA_INT_DISABLED);
  34.     PINS_SetPinIntSel(CLK_PORT_BASE, CLK_PIN, PM_DMA_INT_DISABLED);
  35.     PINS_SetPinIntSel(MOSI_PORT_BASE, MOSI_PIN, PM_DMA_INT_DISABLED);
  36.     /* GPIO Initialization */
  37.     PINS_SetPins(CS_GPIO_BASE, 1U << CS_PIN);
  38.     PINS_SetPins(CLK_GPIO_BASE, 1U << CLK_PIN);
  39.     PINS_SetPins(MOSI_GPIO_BASE, 1U << MOSI_PIN);
  40.     /* Set pin as output */
  41.     PINS_SetPinDir(CS_GPIO_BASE, CS_PIN, 1U);
  42.     PINS_SetPinDir(CLK_GPIO_BASE, CLK_PIN, 1U);
  43.     PINS_SetPinDir(MOSI_GPIO_BASE, MOSI_PIN, 1U);
  44.    
  45.     /* Set pin mode */
  46.     PINS_SetMuxModeSel(MISO_PORT_BASE, MISO_PIN, PM_MUX_AS_GPIO);
  47.     /* Set pin interrupt */
  48.     PINS_SetPinIntSel(MISO_PORT_BASE, MISO_PIN, PM_DMA_INT_DISABLED);
  49.     /* set pin input to enabled state */
  50.     PINS_SetPmInputDisable(MISO_GPIO_BASE, 0U<<MISO_PIN);

  51.     /* set pin Pull-up resistor */
  52.     PINS_SetPullSel(MISO_PORT_BASE, MISO_PIN,PM_INTERNAL_PULL_UP_ENABLED);
  53.    
  54.     MySPI_W_SS(1);//高电平默认不选中从机
  55.     MySPI_W_SCK(0);//模式0 空闲状态为低电平
  56. }

  57. void BSP_SPI_Start(void)
  58. {
  59.     MySPI_W_SS(0);
  60. }

  61. void BSP_SPI_Stop(void)
  62. {
  63.     MySPI_W_SS(1);
  64. }

  65. uint8_t BSP_SPI_SwapByte(uint8_t ByteSend)
  66. {
  67.     uint8_t i,ByteReceive = 0x00;
  68.    
  69.     for (i = 0; i < 8; i++)
  70.     {
  71.         //使用掩码方式
  72.         MySPI_W_MOSI(ByteSend & (0x80 >> i));//数据移出
  73.         MySPI_W_SCK(1);//时钟变为高电平
  74.         if (MySPI_R_MISO() == 1)
  75.         {
  76.             ByteReceive |= (0x80 >> i);//数据移入
  77.         }
  78.         MySPI_W_SCK(0);//时钟变为低电平
  79.     }
  80.    
  81.     return ByteReceive;
  82. }
软件SPI.h
  1. #ifndef __BSP_SPI_H
  2. #define __BSP_SPI_H

  3. #include "g32a1xxx_pins.h"
  4. #include "user_config.h"

  5. //CS
  6. #define CS_PIN              (0U)
  7. #define CS_PORT_BASE        PMB
  8. #define CS_GPIO_BASE        GPIOB

  9. //CLK
  10. #define CLK_PIN             (1U)
  11. #define CLK_PORT_BASE       PMB
  12. #define CLK_GPIO_BASE       GPIOB

  13. //MOSI
  14. #define MOSI_PIN            (0U)
  15. #define MOSI_PORT_BASE      PMA
  16. #define MOSI_GPIO_BASE      GPIOA

  17. //MISO
  18. #define MISO_PIN            (1U)
  19. #define MISO_PORT_BASE      PMA
  20. #define MISO_GPIO_BASE      GPIOA


  21. void BSP_SPI_Init(void);
  22. void BSP_SPI_Start(void);
  23. void BSP_SPI_Stop(void);
  24. uint8_t BSP_SPI_SwapByte(uint8_t ByteSend);

  25. #endif
W25Q64.c
  1. #include "w25q64.h"
  2. #include "bsp_spi.h"

  3. void W25Q64_Init(void)
  4. {
  5.     BSP_SPI_Init();
  6. }

  7. void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
  8. {
  9.     BSP_SPI_Start();
  10.     BSP_SPI_SwapByte(W25Q64_JEDEC_ID);
  11.     *MID = BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE);
  12.     *DID = BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE);
  13.     *DID <<= 8;
  14.     *DID |= BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE);
  15.     BSP_SPI_Stop();
  16. }

  17. //写使能
  18. void W25Q64_WriteEnable(void)
  19. {
  20.     BSP_SPI_Start();//片选
  21.     BSP_SPI_SwapByte(W25Q64_WRITE_ENABLE);
  22.     BSP_SPI_Stop();
  23. }
  24. //等待状态寄存器1 为非忙状态
  25. void W25Q64_WaitBusy(void)
  26. {
  27.     uint32_t TimeOut;
  28.     BSP_SPI_Start();//片选
  29.     BSP_SPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
  30. //    while((BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01);
  31.     TimeOut = 100000;
  32.     while(BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01)
  33.     {
  34.         TimeOut--;
  35.         if (TimeOut == 0)
  36.         {
  37.             break;
  38.         }
  39.     }
  40.     BSP_SPI_Stop();
  41. }

  42. //开始写之前 先打开写使能,之后执行数据写入
  43. void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArry, uint16_t Count)
  44. {
  45.     uint16_t i;
  46.     W25Q64_WriteEnable();//写使能
  47.    
  48.     BSP_SPI_Start();//片选
  49.     BSP_SPI_SwapByte(W25Q64_PAGE_PROGRAM);
  50.     BSP_SPI_SwapByte(Address >> 16);
  51.     BSP_SPI_SwapByte(Address >> 8);
  52.     BSP_SPI_SwapByte(Address);
  53.     for (i = 0; i < Count; i++)
  54.     {
  55.         BSP_SPI_SwapByte(DataArry[i]);
  56.     }
  57.     BSP_SPI_Stop();
  58.    
  59.     W25Q64_WaitBusy();//等待忙完
  60. }
  61. //擦除指定扇区  开启写使能 之后开始擦除扇区 最后等待状态寄存器为非忙状态
  62. void W25Q64_SectorErase(uint32_t Address)
  63. {
  64.     W25Q64_WriteEnable();//写使能
  65.    
  66.     BSP_SPI_Start();//片选
  67.     BSP_SPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
  68.     BSP_SPI_SwapByte(Address >> 16);
  69.     BSP_SPI_SwapByte(Address >> 8);
  70.     BSP_SPI_SwapByte(Address);
  71.     BSP_SPI_Stop();
  72.    
  73.     W25Q64_WaitBusy();//等待忙完
  74. }
  75. //从指定地址读取数据
  76. void W25Q64_ReadData(uint32_t Address, uint8_t *DataArry, uint16_t Count)
  77. {
  78.     uint16_t i;
  79.     BSP_SPI_Start();//片选
  80.     BSP_SPI_SwapByte(W25Q64_READ_DATA);
  81.     BSP_SPI_SwapByte(Address >> 16);
  82.     BSP_SPI_SwapByte(Address >> 8);
  83.     BSP_SPI_SwapByte(Address);
  84.     for (i = 0; i < Count; i++)
  85.     {
  86.         DataArry[i] = BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE);
  87.     }
  88.     BSP_SPI_Stop();
  89. }

W25Q64.h
  1. #ifndef __W25Q64_H
  2. #define __W25Q64_H

  3. #include "g32a1xxx_pins.h"

  4. #define W25Q64_WRITE_ENABLE                         0x06
  5. #define W25Q64_WRITE_DISABLE                        0x04
  6. #define W25Q64_READ_STATUS_REGISTER_1               0x05
  7. #define W25Q64_READ_STATUS_REGISTER_2               0x35
  8. #define W25Q64_WRITE_STATUS_REGISTER                0x01
  9. #define W25Q64_PAGE_PROGRAM                         0x02
  10. #define W25Q64_QUAD_PAGE_PROGRAM                    0x32
  11. #define W25Q64_BLOCK_ERASE_64KB                     0xD8
  12. #define W25Q64_BLOCK_ERASE_32KB                     0x52
  13. #define W25Q64_SECTOR_ERASE_4KB                     0x20
  14. #define W25Q64_CHIP_ERASE                           0xC7
  15. #define W25Q64_ERASE_SUSPEND                        0x75
  16. #define W25Q64_ERASE_RESUME                         0x7A
  17. #define W25Q64_POWER_DOWN                           0xB9
  18. #define W25Q64_HIGH_PERFORMANCE_MODE                0xA3
  19. #define W25Q64_CONTINUOUS_READ_MODE_RESET           0xFF
  20. #define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID     0xAB
  21. #define W25Q64_MANUFACTURER_DEVICE_ID               0x90
  22. #define W25Q64_READ_UNIQUE_ID                       0x4B
  23. #define W25Q64_JEDEC_ID                             0x9F
  24. #define W25Q64_READ_DATA                            0x03
  25. #define W25Q64_FAST_READ                            0x0B
  26. #define W25Q64_FAST_READ_DUAL_OUTPUT                0x3B
  27. #define W25Q64_FAST_READ_DUAL_IO                    0xBB
  28. #define W25Q64_FAST_READ_QUAD_OUTPUT                0x6B
  29. #define W25Q64_FAST_READ_QUAD_IO                    0xEB
  30. #define W25Q64_OCTAL_WORD_READ_QUAD_IO              0xE3

  31. #define W25Q64_DUMMY_BYTE                           0xFF

  32. void W25Q64_Init(void);//初始化函数
  33. void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);//读取设备ID号
  34. void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArry, uint16_t Count);//在指定扇区写
  35. void W25Q64_SectorErase(uint32_t Address);//擦除指定扇区
  36. void W25Q64_ReadData(uint32_t Address, uint8_t *DataArry, uint16_t Count);//从指定扇区读

  37. #endif

实物展示:
极海.png
效果展示:
程序.png

您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

2

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部