打印
[G32A]

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

[复制链接]
314|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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.输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入





三、SPI数据传输示意图:


四、SPI时序基本单元:
起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平

交换一个字节(模式0)
CPOL=0:空闲状态时,SCK为低电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据


交换一个字节(模式1)
CPOL=0:空闲状态时,SCK为低电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据

交换一个字节(模式2)
CPOL=1:空闲状态时,SCK为高电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

交换一个字节(模式3)
CPOL=1:空闲状态时,SCK为高电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据

五、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


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

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

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

uint8_t write_array[5] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE};
uint8_t read_array[5] = {0};
uint8_t MID;
uint16_t DID;
int main(void)
{
    /* Initialize clock */
    CLOCK_SYS_Init(&g_clockConfig);
   
    W25Q64_Init();
   
    W25Q64_ReadID(&MID, &DID);
   
    W25Q64_SectorErase(0x002000);//擦除指定扇区
    W25Q64_PageProgram(0x002000, write_array, 5);//在指定扇区写
    W25Q64_ReadData(0x002000, read_array, 5);//从指定扇区读
    while (1)
    {
        ;
    }
}
软件SPI.c
#include "bsp_spi.h"

//片选
void MySPI_W_SS(uint8_t BitValue)
{
    PINS_WritePin(CS_GPIO_BASE, CS_PIN, BitValue);
}
//时钟
void MySPI_W_SCK(uint8_t BitValue)
{
    PINS_WritePin(CLK_GPIO_BASE, CLK_PIN, BitValue);
}
//主机输出
void MySPI_W_MOSI(uint8_t BitValue)
{
    PINS_WritePin(MOSI_GPIO_BASE, MOSI_PIN, BitValue);
}
//从机输入
uint8_t MySPI_R_MISO(void)
{
    return (uint8_t)((PINS_ReadPinsInput(MISO_GPIO_BASE) >> MISO_PIN) & 0x01U);
//    return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

void BSP_SPI_Init(void)
{
    /* Enable Clock to Port D */
    CLOCK_SYS_ConfigModuleClock(PMA_CLK, NULL);
    CLOCK_SYS_ConfigModuleClock(PMB_CLK, NULL);
    /* Set pin mode */
    PINS_SetMuxModeSel(CS_PORT_BASE, CS_PIN, PM_MUX_AS_GPIO);
    PINS_SetMuxModeSel(CLK_PORT_BASE, CLK_PIN, PM_MUX_AS_GPIO);
    PINS_SetMuxModeSel(MOSI_PORT_BASE, MOSI_PIN, PM_MUX_AS_GPIO);
    /* Set pin interrupt */
    PINS_SetPinIntSel(CS_PORT_BASE, CS_PIN, PM_DMA_INT_DISABLED);
    PINS_SetPinIntSel(CLK_PORT_BASE, CLK_PIN, PM_DMA_INT_DISABLED);
    PINS_SetPinIntSel(MOSI_PORT_BASE, MOSI_PIN, PM_DMA_INT_DISABLED);
    /* GPIO Initialization */
    PINS_SetPins(CS_GPIO_BASE, 1U << CS_PIN);
    PINS_SetPins(CLK_GPIO_BASE, 1U << CLK_PIN);
    PINS_SetPins(MOSI_GPIO_BASE, 1U << MOSI_PIN);
    /* Set pin as output */
    PINS_SetPinDir(CS_GPIO_BASE, CS_PIN, 1U);
    PINS_SetPinDir(CLK_GPIO_BASE, CLK_PIN, 1U);
    PINS_SetPinDir(MOSI_GPIO_BASE, MOSI_PIN, 1U);
   
    /* Set pin mode */
    PINS_SetMuxModeSel(MISO_PORT_BASE, MISO_PIN, PM_MUX_AS_GPIO);
    /* Set pin interrupt */
    PINS_SetPinIntSel(MISO_PORT_BASE, MISO_PIN, PM_DMA_INT_DISABLED);
    /* set pin input to enabled state */
    PINS_SetPmInputDisable(MISO_GPIO_BASE, 0U<<MISO_PIN);

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

void BSP_SPI_Start(void)
{
    MySPI_W_SS(0);
}

void BSP_SPI_Stop(void)
{
    MySPI_W_SS(1);
}

uint8_t BSP_SPI_SwapByte(uint8_t ByteSend)
{
    uint8_t i,ByteReceive = 0x00;
   
    for (i = 0; i < 8; i++)
    {
        //使用掩码方式
        MySPI_W_MOSI(ByteSend & (0x80 >> i));//数据移出
        MySPI_W_SCK(1);//时钟变为高电平
        if (MySPI_R_MISO() == 1)
        {
            ByteReceive |= (0x80 >> i);//数据移入
        }
        MySPI_W_SCK(0);//时钟变为低电平
    }
   
    return ByteReceive;
}
软件SPI.h
#ifndef __BSP_SPI_H
#define __BSP_SPI_H

#include "g32a1xxx_pins.h"
#include "user_config.h"

//CS
#define CS_PIN              (0U)
#define CS_PORT_BASE        PMB
#define CS_GPIO_BASE        GPIOB

//CLK
#define CLK_PIN             (1U)
#define CLK_PORT_BASE       PMB
#define CLK_GPIO_BASE       GPIOB

//MOSI
#define MOSI_PIN            (0U)
#define MOSI_PORT_BASE      PMA
#define MOSI_GPIO_BASE      GPIOA

//MISO
#define MISO_PIN            (1U)
#define MISO_PORT_BASE      PMA
#define MISO_GPIO_BASE      GPIOA


void BSP_SPI_Init(void);
void BSP_SPI_Start(void);
void BSP_SPI_Stop(void);
uint8_t BSP_SPI_SwapByte(uint8_t ByteSend);

#endif
W25Q64.c
#include "w25q64.h"
#include "bsp_spi.h"

void W25Q64_Init(void)
{
    BSP_SPI_Init();
}

void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
    BSP_SPI_Start();
    BSP_SPI_SwapByte(W25Q64_JEDEC_ID);
    *MID = BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE);
    *DID = BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE);
    *DID <<= 8;
    *DID |= BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE);
    BSP_SPI_Stop();
}

//写使能
void W25Q64_WriteEnable(void)
{
    BSP_SPI_Start();//片选
    BSP_SPI_SwapByte(W25Q64_WRITE_ENABLE);
    BSP_SPI_Stop();
}
//等待状态寄存器1 为非忙状态
void W25Q64_WaitBusy(void)
{
    uint32_t TimeOut;
    BSP_SPI_Start();//片选
    BSP_SPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
//    while((BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01);
    TimeOut = 100000;
    while(BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01)
    {
        TimeOut--;
        if (TimeOut == 0)
        {
            break;
        }
    }
    BSP_SPI_Stop();
}

//开始写之前 先打开写使能,之后执行数据写入
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArry, uint16_t Count)
{
    uint16_t i;
    W25Q64_WriteEnable();//写使能
   
    BSP_SPI_Start();//片选
    BSP_SPI_SwapByte(W25Q64_PAGE_PROGRAM);
    BSP_SPI_SwapByte(Address >> 16);
    BSP_SPI_SwapByte(Address >> 8);
    BSP_SPI_SwapByte(Address);
    for (i = 0; i < Count; i++)
    {
        BSP_SPI_SwapByte(DataArry[i]);
    }
    BSP_SPI_Stop();
   
    W25Q64_WaitBusy();//等待忙完
}
//擦除指定扇区  开启写使能 之后开始擦除扇区 最后等待状态寄存器为非忙状态
void W25Q64_SectorErase(uint32_t Address)
{
    W25Q64_WriteEnable();//写使能
   
    BSP_SPI_Start();//片选
    BSP_SPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
    BSP_SPI_SwapByte(Address >> 16);
    BSP_SPI_SwapByte(Address >> 8);
    BSP_SPI_SwapByte(Address);
    BSP_SPI_Stop();
   
    W25Q64_WaitBusy();//等待忙完
}
//从指定地址读取数据
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArry, uint16_t Count)
{
    uint16_t i;
    BSP_SPI_Start();//片选
    BSP_SPI_SwapByte(W25Q64_READ_DATA);
    BSP_SPI_SwapByte(Address >> 16);
    BSP_SPI_SwapByte(Address >> 8);
    BSP_SPI_SwapByte(Address);
    for (i = 0; i < Count; i++)
    {
        DataArry[i] = BSP_SPI_SwapByte(W25Q64_DUMMY_BYTE);
    }
    BSP_SPI_Stop();
}

W25Q64.h
#ifndef __W25Q64_H
#define __W25Q64_H

#include "g32a1xxx_pins.h"

#define W25Q64_WRITE_ENABLE                         0x06
#define W25Q64_WRITE_DISABLE                        0x04
#define W25Q64_READ_STATUS_REGISTER_1               0x05
#define W25Q64_READ_STATUS_REGISTER_2               0x35
#define W25Q64_WRITE_STATUS_REGISTER                0x01
#define W25Q64_PAGE_PROGRAM                         0x02
#define W25Q64_QUAD_PAGE_PROGRAM                    0x32
#define W25Q64_BLOCK_ERASE_64KB                     0xD8
#define W25Q64_BLOCK_ERASE_32KB                     0x52
#define W25Q64_SECTOR_ERASE_4KB                     0x20
#define W25Q64_CHIP_ERASE                           0xC7
#define W25Q64_ERASE_SUSPEND                        0x75
#define W25Q64_ERASE_RESUME                         0x7A
#define W25Q64_POWER_DOWN                           0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE                0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET           0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID     0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID               0x90
#define W25Q64_READ_UNIQUE_ID                       0x4B
#define W25Q64_JEDEC_ID                             0x9F
#define W25Q64_READ_DATA                            0x03
#define W25Q64_FAST_READ                            0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT                0x3B
#define W25Q64_FAST_READ_DUAL_IO                    0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT                0x6B
#define W25Q64_FAST_READ_QUAD_IO                    0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO              0xE3

#define W25Q64_DUMMY_BYTE                           0xFF

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

#endif

实物展示:

效果展示:


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

2

帖子

0

粉丝