sdk: add support for gd25xx nor flash
Signed-off-by: liangkangnan <liangkangnan@163.com>verilator
parent
26a221a132
commit
ccadad262f
|
@ -29,6 +29,7 @@ C_SRCS += $(BSP_DIR)/lib/i2c.c
|
|||
C_SRCS += $(BSP_DIR)/lib/spi.c
|
||||
C_SRCS += $(BSP_DIR)/lib/pinmux.c
|
||||
C_SRCS += $(BSP_DIR)/lib/flash_n25q.c
|
||||
C_SRCS += $(BSP_DIR)/lib/flash_gd25q.c
|
||||
C_SRCS += $(BSP_DIR)/lib/flash_ctrl.c
|
||||
|
||||
LINKER_SCRIPT := $(BSP_DIR)/link_rom.lds
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef _FLASH_GD25Q_H_
|
||||
#define _FLASH_GD25Q_H_
|
||||
|
||||
#define GD25Q_PAGE_SIZE (256)
|
||||
|
||||
#define GD25Q_PAGE_TO_ADDR(page) ((page) << 8)
|
||||
#define GD25Q_SECTOR_TO_ADDR(sector) ((sector) << 12)
|
||||
|
||||
#define CMD_WRITE_STATUS_REG1 (0x01)
|
||||
#define CMD_WRITE_STATUS_REG2 (0x31)
|
||||
#define CMD_WRITE_STATUS_REG3 (0x11)
|
||||
#define CMD_READ_STATUS_REG1 (0x05)
|
||||
#define CMD_READ_STATUS_REG2 (0x35)
|
||||
#define CMD_READ_STATUS_REG3 (0x15)
|
||||
#define CMD_PAGE_PROGRAM (0x02)
|
||||
#define CMD_QUAD_PAGE_PROGRAM (0x32)
|
||||
#define CMD_READ (0x03)
|
||||
#define CMD_QUAD_IO_FAST_READ (0xeb)
|
||||
#define CMD_WRITE_ENABLE (0x06)
|
||||
#define CMD_WRITE_DISABLE (0x04)
|
||||
#define CMD_SECTOR_ERASE (0x20)
|
||||
#define CMD_BLOCK32K_ERASE (0x52)
|
||||
#define CMD_BLOCK64K_ERASE (0xd8)
|
||||
#define CMD_READ_ID (0x90)
|
||||
#define CMD_QUAD_IO_READ_ID (0x94)
|
||||
|
||||
#define DUMMY_CNT (0x4)
|
||||
|
||||
typedef struct {
|
||||
uint8_t manf_id;
|
||||
uint8_t dev_id;
|
||||
} gd25q_id_t;
|
||||
|
||||
void flash_gd25q_init(uint32_t controller, uint16_t clk_div);
|
||||
void flash_gd25q_set_spi_mode(uint8_t mode);
|
||||
void flash_gd25q_set_spi_controller(uint32_t controller);
|
||||
gd25q_id_t flash_gd25q_read_id();
|
||||
void flash_gd25q_write_enable(uint8_t en);
|
||||
uint8_t flash_gd25q_read_reg(uint8_t cmd);
|
||||
void flash_gd25q_write_reg(uint8_t cmd, uint8_t data);
|
||||
uint8_t flash_gd25q_is_busy();
|
||||
void flash_gd25q_read(uint8_t data[], uint32_t len, uint32_t addr);
|
||||
void flash_gd25q_sector_erase(uint32_t sector);
|
||||
void flash_gd25q_page_program(uint8_t data[], uint32_t len, uint32_t page);
|
||||
void flash_gd25q_enable_quad_mode(uint8_t en);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,244 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "../../bsp/include/spi.h"
|
||||
#include "../../bsp/include/rvic.h"
|
||||
#include "../../bsp/include/utils.h"
|
||||
#include "../../bsp/include/flash_gd25q.h"
|
||||
|
||||
/* GD25Q127特点:
|
||||
* 1.总共128Mb大小,即16MB
|
||||
* 2.总共64K页,每页大小256字节
|
||||
* 3.总共4K扇区,每个扇区大小为4K字节,16页
|
||||
* 4.总共256个block,每个block大小为64K字节,16个扇区
|
||||
* 5.擦除的最小单位是扇区,编程(写)的最小单位是页,读的最小单位是字节
|
||||
*/
|
||||
|
||||
static uint8_t current_spi_mode;
|
||||
static uint32_t spi_base_addr;
|
||||
|
||||
void flash_gd25q_init(uint32_t controller, uint16_t clk_div)
|
||||
{
|
||||
spi_base_addr = controller;
|
||||
|
||||
spi_set_clk_div(spi_base_addr, clk_div);
|
||||
spi_set_role_mode(spi_base_addr, SPI_ROLE_MODE_MASTER);
|
||||
spi_set_spi_mode(spi_base_addr, SPI_MODE_STANDARD);
|
||||
spi_set_cp_mode(spi_base_addr, SPI_CPOL_0_CPHA_0);
|
||||
spi_set_msb_first(spi_base_addr);
|
||||
spi_master_set_ss_delay(spi_base_addr, 1);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
spi_set_ss_ctrl_by_sw(spi_base_addr, 1);
|
||||
spi_set_enable(spi_base_addr, 1);
|
||||
}
|
||||
|
||||
void flash_gd25q_set_spi_mode(uint8_t mode)
|
||||
{
|
||||
current_spi_mode = mode;
|
||||
}
|
||||
|
||||
void flash_gd25q_set_spi_controller(uint32_t controller)
|
||||
{
|
||||
spi_base_addr = controller;
|
||||
}
|
||||
|
||||
// 写使能
|
||||
// 擦除或者编程或者写寄存器之前必须先发送写使能命令
|
||||
void flash_gd25q_write_enable(uint8_t en)
|
||||
{
|
||||
uint8_t cmd;
|
||||
|
||||
if (en)
|
||||
cmd = CMD_WRITE_ENABLE;
|
||||
else
|
||||
cmd = CMD_WRITE_DISABLE;
|
||||
|
||||
spi_set_spi_mode(spi_base_addr, SPI_MODE_STANDARD);
|
||||
|
||||
spi_set_ss_level(spi_base_addr, 0);
|
||||
spi_master_write_bytes(spi_base_addr, &cmd, 1);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
|
||||
spi_set_spi_mode(spi_base_addr, current_spi_mode);
|
||||
}
|
||||
|
||||
// 读寄存器
|
||||
uint8_t flash_gd25q_read_reg(uint8_t cmd)
|
||||
{
|
||||
uint8_t data;
|
||||
|
||||
spi_set_spi_mode(spi_base_addr, SPI_MODE_STANDARD);
|
||||
|
||||
spi_set_ss_level(spi_base_addr, 0);
|
||||
spi_master_write_bytes(spi_base_addr, &cmd, 1);
|
||||
spi_master_read_bytes(spi_base_addr, &data, 1);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
|
||||
spi_set_spi_mode(spi_base_addr, current_spi_mode);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// 写寄存器
|
||||
void flash_gd25q_write_reg(uint8_t cmd, uint8_t data)
|
||||
{
|
||||
spi_set_spi_mode(spi_base_addr, SPI_MODE_STANDARD);
|
||||
|
||||
spi_set_ss_level(spi_base_addr, 0);
|
||||
spi_master_write_bytes(spi_base_addr, &cmd, 1);
|
||||
spi_master_write_bytes(spi_base_addr, &data, 1);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
|
||||
spi_set_spi_mode(spi_base_addr, current_spi_mode);
|
||||
}
|
||||
|
||||
// 是否正在擦除或者编程
|
||||
uint8_t flash_gd25q_is_busy()
|
||||
{
|
||||
if (flash_gd25q_read_reg(CMD_READ_STATUS_REG1) & 0x1)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 读数据
|
||||
// addr: 0, 1, 2, ...
|
||||
void flash_gd25q_read(uint8_t data[], uint32_t len, uint32_t addr)
|
||||
{
|
||||
uint8_t cmd, i;
|
||||
uint8_t tran_addr[4];
|
||||
|
||||
tran_addr[0] = (addr >> 16) & 0xff;
|
||||
tran_addr[1] = (addr >> 8) & 0xff;
|
||||
tran_addr[2] = (addr >> 0) & 0xff;
|
||||
tran_addr[3] = 0x00;
|
||||
|
||||
if (current_spi_mode == SPI_MODE_STANDARD) {
|
||||
cmd = CMD_READ;
|
||||
spi_set_ss_level(spi_base_addr, 0);
|
||||
spi_master_write_bytes(spi_base_addr, &cmd, 1);
|
||||
spi_master_write_bytes(spi_base_addr, tran_addr, 3);
|
||||
spi_master_read_bytes(spi_base_addr, data, len);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
} else {
|
||||
cmd = CMD_QUAD_IO_FAST_READ;
|
||||
// 标准模式发送CMD
|
||||
spi_set_spi_mode(spi_base_addr, SPI_MODE_STANDARD);
|
||||
spi_set_ss_level(spi_base_addr, 0);
|
||||
spi_master_write_bytes(spi_base_addr, &cmd, 1);
|
||||
spi_set_spi_mode(spi_base_addr, SPI_MODE_QUAD);
|
||||
// QSPI模式发送ADDR
|
||||
spi_master_write_bytes(spi_base_addr, tran_addr, 4);
|
||||
for (i = 0; i < (DUMMY_CNT >> 1); i++)
|
||||
spi_master_read_bytes(spi_base_addr, data, 1);
|
||||
spi_reset_rxfifo(spi_base_addr);
|
||||
spi_master_read_bytes(spi_base_addr, data, len);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void sector_erase(uint8_t cmd, uint32_t addr)
|
||||
{
|
||||
uint8_t tran_addr[3];
|
||||
|
||||
flash_gd25q_write_enable(1);
|
||||
|
||||
tran_addr[0] = (addr >> 16) & 0xff;
|
||||
tran_addr[1] = (addr >> 8) & 0xff;
|
||||
tran_addr[2] = (addr >> 0) & 0xff;
|
||||
|
||||
spi_set_spi_mode(spi_base_addr, SPI_MODE_STANDARD);
|
||||
|
||||
spi_set_ss_level(spi_base_addr, 0);
|
||||
spi_master_write_bytes(spi_base_addr, &cmd, 1);
|
||||
spi_master_write_bytes(spi_base_addr, tran_addr, 3);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
|
||||
while (flash_gd25q_is_busy());
|
||||
|
||||
flash_gd25q_write_enable(0);
|
||||
|
||||
spi_set_spi_mode(spi_base_addr, current_spi_mode);
|
||||
}
|
||||
|
||||
// 扇区擦除
|
||||
// sector,第几个扇区: 0 ~ N
|
||||
void flash_gd25q_sector_erase(uint32_t sector)
|
||||
{
|
||||
sector_erase(CMD_SECTOR_ERASE, GD25Q_SECTOR_TO_ADDR(sector));
|
||||
}
|
||||
|
||||
// 页编程
|
||||
// page,第几页: 0 ~ N
|
||||
void flash_gd25q_page_program(uint8_t data[], uint32_t len, uint32_t page)
|
||||
{
|
||||
uint8_t tran_addr[3];
|
||||
uint8_t cmd;
|
||||
uint32_t addr;
|
||||
|
||||
flash_gd25q_write_enable(1);
|
||||
|
||||
addr = GD25Q_PAGE_TO_ADDR(page);
|
||||
tran_addr[0] = (addr >> 16) & 0xff;
|
||||
tran_addr[1] = (addr >> 8) & 0xff;
|
||||
tran_addr[2] = (addr >> 0) & 0xff;
|
||||
|
||||
if (current_spi_mode == SPI_MODE_STANDARD)
|
||||
cmd = CMD_PAGE_PROGRAM;
|
||||
else
|
||||
cmd = CMD_QUAD_PAGE_PROGRAM;
|
||||
|
||||
spi_set_ss_level(spi_base_addr, 0);
|
||||
spi_set_spi_mode(spi_base_addr, SPI_MODE_STANDARD);
|
||||
spi_master_write_bytes(spi_base_addr, &cmd, 1);
|
||||
spi_master_write_bytes(spi_base_addr, tran_addr, 3);
|
||||
spi_set_spi_mode(spi_base_addr, current_spi_mode);
|
||||
spi_master_write_bytes(spi_base_addr, data, len);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
|
||||
while (flash_gd25q_is_busy());
|
||||
|
||||
flash_gd25q_write_enable(0);
|
||||
}
|
||||
|
||||
// 使能QUAD SPI模式
|
||||
void flash_gd25q_enable_quad_mode(uint8_t en)
|
||||
{
|
||||
uint8_t data;
|
||||
|
||||
flash_gd25q_write_enable(1);
|
||||
|
||||
data = flash_gd25q_read_reg(CMD_READ_STATUS_REG2);
|
||||
if (en) {
|
||||
data |= 1 << 1;
|
||||
} else {
|
||||
data &= ~(1 << 1);
|
||||
}
|
||||
flash_gd25q_write_reg(CMD_WRITE_STATUS_REG2, data);
|
||||
|
||||
flash_gd25q_write_enable(0);
|
||||
}
|
||||
|
||||
// 读flash ID
|
||||
gd25q_id_t flash_gd25q_read_id()
|
||||
{
|
||||
gd25q_id_t id;
|
||||
uint8_t cmd;
|
||||
uint8_t tran_addr[3];
|
||||
uint8_t data[2];
|
||||
|
||||
tran_addr[0] = 0x00;
|
||||
tran_addr[1] = 0x00;
|
||||
tran_addr[2] = 0x00;
|
||||
|
||||
cmd = CMD_READ_ID;
|
||||
spi_set_ss_level(spi_base_addr, 0);
|
||||
spi_master_write_bytes(spi_base_addr, &cmd, 1);
|
||||
spi_master_write_bytes(spi_base_addr, tran_addr, 3);
|
||||
spi_master_read_bytes(spi_base_addr, data, 2);
|
||||
spi_set_ss_level(spi_base_addr, 1);
|
||||
|
||||
id.manf_id = data[0];
|
||||
id.dev_id = data[1];
|
||||
|
||||
return id;
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "../../bsp/include/uart.h"
|
||||
#include "../../bsp/include/spi.h"
|
||||
#include "../../bsp/include/xprintf.h"
|
||||
#include "../../bsp/include/utils.h"
|
||||
#include "../../bsp/include/rvic.h"
|
||||
#include "../../bsp/include/pinmux.h"
|
||||
#include "../../bsp/include/sim_ctrl.h"
|
||||
#include "../../bsp/include/flash_gd25q.h"
|
||||
|
||||
|
||||
#define BUFFER_SIZE (64)
|
||||
|
||||
uint8_t program_data[BUFFER_SIZE];
|
||||
uint8_t read_data[BUFFER_SIZE];
|
||||
|
||||
// 标准三线SPI测试
|
||||
static void standard_spi_test()
|
||||
{
|
||||
uint16_t i;
|
||||
gd25q_id_t id;
|
||||
|
||||
xprintf("Standard SPI test started...\n");
|
||||
|
||||
flash_gd25q_set_spi_mode(SPI_MODE_STANDARD);
|
||||
// 读flash ID
|
||||
id = flash_gd25q_read_id();
|
||||
xprintf("manf id = 0x%2x\n", id.manf_id);
|
||||
xprintf("dev id = 0x%2x\n", id.dev_id);
|
||||
|
||||
// 初始化要编程的数据
|
||||
for (i = 0; i < BUFFER_SIZE; i++)
|
||||
program_data[i] = i + 1;
|
||||
|
||||
// 擦除第0个扇区
|
||||
flash_gd25q_sector_erase(0);
|
||||
xprintf("program data: \n");
|
||||
// 打印要编程的数据
|
||||
for (i = 0; i < BUFFER_SIZE; i++)
|
||||
xprintf("0x%x\n", program_data[i]);
|
||||
// 编程第1页
|
||||
flash_gd25q_page_program(program_data, BUFFER_SIZE, 1);
|
||||
// 读第1页
|
||||
flash_gd25q_read(read_data, BUFFER_SIZE, GD25Q_PAGE_TO_ADDR(1));
|
||||
xprintf("read data: \n");
|
||||
// 打印读出来的数据
|
||||
for (i = 0; i < BUFFER_SIZE; i++)
|
||||
xprintf("0x%x\n", read_data[i]);
|
||||
|
||||
for (i = 0; i < BUFFER_SIZE; i++) {
|
||||
if (program_data[i] != read_data[i]) {
|
||||
xprintf("test failed!!!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
xprintf("Standard SPI test succ...\n");
|
||||
}
|
||||
|
||||
// QSPI测试
|
||||
static void quad_spi_test()
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
xprintf("\nQuad SPI test started...\n");
|
||||
|
||||
// 使能QSPI模式
|
||||
flash_gd25q_enable_quad_mode(1);
|
||||
flash_gd25q_set_spi_mode(SPI_MODE_QUAD);
|
||||
|
||||
// 初始化要编程的数据
|
||||
for (i = 0; i < BUFFER_SIZE; i++)
|
||||
program_data[i] = i + 2;
|
||||
|
||||
// 擦除第1个扇区
|
||||
flash_gd25q_sector_erase(1);
|
||||
xprintf("program data: \n");
|
||||
// 打印要编程的数据
|
||||
for (i = 0; i < BUFFER_SIZE; i++)
|
||||
xprintf("0x%x\n", program_data[i]);
|
||||
// 编程第16页
|
||||
flash_gd25q_page_program(program_data, BUFFER_SIZE, 16);
|
||||
// 读第16页
|
||||
flash_gd25q_read(read_data, BUFFER_SIZE, GD25Q_PAGE_TO_ADDR(16));
|
||||
xprintf("read data: \n");
|
||||
// 打印读出来的数据
|
||||
for (i = 0; i < BUFFER_SIZE; i++)
|
||||
xprintf("0x%x\n", read_data[i]);
|
||||
|
||||
// 失能QSPI模式
|
||||
flash_gd25q_enable_quad_mode(0);
|
||||
spi_set_spi_mode(SPI0, SPI_MODE_STANDARD);
|
||||
flash_gd25q_set_spi_mode(SPI_MODE_STANDARD);
|
||||
|
||||
for (i = 0; i < BUFFER_SIZE; i++) {
|
||||
if (program_data[i] != read_data[i]) {
|
||||
xprintf("test failed!!!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
xprintf("Quad SPI test succ...\n");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// UART引脚配置
|
||||
pinmux_set_io0_func(IO0_UART0_TX);
|
||||
pinmux_set_io3_func(IO3_UART0_RX);
|
||||
// SPI引脚配置
|
||||
pinmux_set_io10_func(IO10_SPI_CLK);
|
||||
pinmux_set_io11_func(IO11_SPI_SS);
|
||||
pinmux_set_io12_func(IO12_SPI_DQ0);
|
||||
pinmux_set_io13_func(IO13_SPI_DQ1);
|
||||
pinmux_set_io14_func(IO14_SPI_DQ2);
|
||||
pinmux_set_io15_func(IO15_SPI_DQ3);
|
||||
|
||||
uart_init(UART0, uart0_putc);
|
||||
// 115200bps
|
||||
uart_set_baud_div(UART0, 0x68);
|
||||
flash_gd25q_init(SPI0, 5);
|
||||
|
||||
standard_spi_test();
|
||||
quad_spi_test();
|
||||
|
||||
while (1);
|
||||
}
|
Loading…
Reference in New Issue