tinyriscv/sdk/bsp/lib/flash_n25q.c

243 lines
6.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <stdint.h>
#include "../../bsp/include/spi.h"
#include "../../bsp/include/rvic.h"
#include "../../bsp/include/utils.h"
#include "../../bsp/include/flash_n25q.h"
/* N25Q064特点:
* 1.总共64Mb大小即8MB
* 2.总共128个扇区每个扇区大小为64KB
* 3.总共2048个子扇区每个子扇区大小为4KB
* 4.总共37768页每页大小为256B
* 5.擦除的最小单位是子扇区,编程(写)的最小单位是页,读的最小单位是字节
*/
static uint8_t current_spi_mode;
static uint32_t spi_base_addr;
void flash_n25q_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_n25q_set_spi_mode(uint8_t mode)
{
current_spi_mode = mode;
}
void flash_n25q_set_spi_controller(uint32_t controller)
{
spi_base_addr = controller;
}
// 写使能
// 擦除或者编程或者写寄存器之前必须先发送写使能命令
void flash_n25q_write_enable(uint8_t en)
{
uint8_t cmd;
if (en)
cmd = CMD_WRITE_ENABLE;
else
cmd = CMD_WRITE_DISABLE;
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);
}
// 读寄存器
uint8_t flash_n25q_read_reg(uint8_t cmd)
{
uint8_t data;
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);
return data;
}
// 写寄存器
void flash_n25q_write_reg(uint8_t cmd, uint8_t data)
{
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);
}
// 读状态寄存器
uint8_t flash_n25q_read_status_reg()
{
uint8_t data;
data = flash_n25q_read_reg(CMD_READ_STATUS_REG);
return data;
}
// 是否正在擦除或者编程
uint8_t flash_n25q_is_busy()
{
if (flash_n25q_read_status_reg() & 0x1)
return 1;
else
return 0;
}
// 读数据
// addr: 0, 1, 2, ...
void flash_n25q_read(uint8_t data[], uint32_t len, uint32_t addr)
{
uint8_t cmd, i;
uint8_t tran_addr[3];
if (current_spi_mode == SPI_MODE_STANDARD)
cmd = CMD_READ;
else
cmd = CMD_FAST_READ;
tran_addr[0] = (addr >> 16) & 0xff;
tran_addr[1] = (addr >> 8) & 0xff;
tran_addr[2] = (addr >> 0) & 0xff;
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);
if (current_spi_mode != SPI_MODE_STANDARD) {
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_n25q_write_enable(1);
tran_addr[0] = (addr >> 16) & 0xff;
tran_addr[1] = (addr >> 8) & 0xff;
tran_addr[2] = (addr >> 0) & 0xff;
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_n25q_is_busy());
flash_n25q_write_enable(0);
}
// 子扇区擦除
// subsector第几个子扇区: 0 ~ N
void flash_n25q_subsector_erase(uint32_t subsector)
{
sector_erase(CMD_SUBSECTOR_ERASE, N25Q_SUBSECTOR_TO_ADDR(subsector));
}
// 扇区擦除
// sector第几个扇区: 0 ~ N
void flash_n25q_sector_erase(uint32_t sector)
{
sector_erase(CMD_SECTOR_ERASE, N25Q_SECTOR_TO_ADDR(sector));
}
// 页编程
// page第几页: 0 ~ N
void flash_n25q_page_program(uint8_t data[], uint32_t len, uint32_t page)
{
uint8_t tran_addr[3];
uint8_t cmd;
uint32_t addr;
flash_n25q_write_enable(1);
addr = N25Q_PAGE_TO_ADDR(page);
tran_addr[0] = (addr >> 16) & 0xff;
tran_addr[1] = (addr >> 8) & 0xff;
tran_addr[2] = (addr >> 0) & 0xff;
cmd = CMD_PAGE_PROGRAM;
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_write_bytes(spi_base_addr, data, len);
spi_set_ss_level(spi_base_addr, 1);
while (flash_n25q_is_busy());
flash_n25q_write_enable(0);
}
// 使能QUAD SPI模式
void flash_n25q_enable_quad_mode(uint8_t en)
{
uint8_t data;
flash_n25q_write_enable(1);
data = flash_n25q_read_reg(CMD_READ_ENHANCED_VOL_CONF_REG);
if (en) {
data &= ~(1 << 7);
data |= 1 << 6;
} else {
data |= 0x3 << 6;
}
flash_n25q_write_reg(CMD_WRITE_ENHANCED_VOL_CONF_REG, data);
flash_n25q_write_enable(0);
}
// 设置n25q dummy cycles
void flash_n25q_set_dummy_clock_cycles(uint8_t num)
{
uint8_t data;
flash_n25q_write_enable(1);
data = flash_n25q_read_reg(CMD_READ_VOL_CONF_REG);
data &= ~(0xf << 4);
data |= num << 4;
flash_n25q_write_reg(CMD_WRITE_VOL_CONF_REG, data);
flash_n25q_write_enable(0);
}
// 读flash ID
n25q_id_t flash_n25q_read_id(uint8_t cmd)
{
n25q_id_t id;
uint8_t data[3];
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, 3);
spi_set_ss_level(spi_base_addr, 1);
id.manf_id = data[0];
id.mem_type = data[1];
id.mem_cap = data[2];
return id;
}