Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,550 @@
|
||||
#include "exflash_spi4_gd25qxx.h"
|
||||
#include "gd32h7xx_dma.h"
|
||||
#include <stddef.h>
|
||||
|
||||
/* NOR Flash 标准指令 */
|
||||
#define EXFLASH_CMD_WRITE (0x02u) /* Page Program */
|
||||
#define EXFLASH_CMD_WREN (0x06u) /* Write Enable */
|
||||
#define EXFLASH_CMD_READ (0x03u) /* Read Data */
|
||||
#define EXFLASH_CMD_RDSR (0x05u) /* Read Status Register */
|
||||
#define EXFLASH_CMD_RDID (0x9Fu) /* Read JEDEC ID */
|
||||
#define EXFLASH_CMD_SE (0x20u) /* 4KB Sector Erase */
|
||||
#define EXFLASH_CMD_BE (0xC7u) /* Bulk Erase */
|
||||
#define EXFLASH_CMD_EN4B (0xB7u) /* Enter 4-byte address mode */
|
||||
|
||||
/* 状态寄存器位定义 */
|
||||
#define EXFLASH_SR_WIP_MASK (0x01u) /* WIP=1 表示写/擦进行中 */
|
||||
|
||||
/* 读操作发送的占位字节 */
|
||||
#define EXFLASH_DUMMY_BYTE (0xA5u)
|
||||
#define EXFLASH_DMA_PERIPH (DMA0)
|
||||
#define EXFLASH_DMA_RX_CH (DMA_CH1)
|
||||
#define EXFLASH_DMA_TX_CH (DMA_CH2)
|
||||
#define EXFLASH_DMA_WAIT_MAX_LOOP (0x00FFFFFFu)
|
||||
|
||||
/* Cortex-M7 D-Cache line size = 32 bytes */
|
||||
#define EXFLASH_DCACHE_LINE_SIZE (32u)
|
||||
|
||||
/* DMA 中断置位标志:1=该通道传输完成 */
|
||||
static volatile uint8_t s_spi4_dma_rx_done = 0u;
|
||||
static volatile uint8_t s_spi4_dma_tx_done = 0u;
|
||||
|
||||
/* 读阶段 TX 使用的固定哑字节;DMA 配置为 memory_inc disable */
|
||||
static uint8_t s_spi4_dma_dummy_tx = EXFLASH_DUMMY_BYTE;
|
||||
/* 写阶段 RX 丢弃字节;DMA 配置为 memory_inc disable */
|
||||
static uint8_t s_spi4_dma_dummy_rx = 0u;
|
||||
|
||||
static uint32_t cache_addr_down_align(uint32_t addr, uint32_t align)
|
||||
{
|
||||
return addr & ~(align - 1u);
|
||||
}
|
||||
|
||||
static uint32_t cache_len_up_align(uint32_t addr, uint32_t len, uint32_t align)
|
||||
{
|
||||
uint32_t end = addr + len;
|
||||
uint32_t end_aligned = (end + (align - 1u)) & ~(align - 1u);
|
||||
return end_aligned - cache_addr_down_align(addr, align);
|
||||
}
|
||||
|
||||
static void dcache_clean_by_addr(uint32_t addr, uint32_t len)
|
||||
{
|
||||
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
|
||||
uint32_t a = cache_addr_down_align(addr, EXFLASH_DCACHE_LINE_SIZE);
|
||||
uint32_t l = cache_len_up_align(addr, len, EXFLASH_DCACHE_LINE_SIZE);
|
||||
SCB_CleanDCache_by_Addr((uint32_t *)a, (int32_t)l);
|
||||
#else
|
||||
(void)addr;
|
||||
(void)len;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void dcache_invalidate_by_addr(uint32_t addr, uint32_t len)
|
||||
{
|
||||
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
|
||||
uint32_t a = cache_addr_down_align(addr, EXFLASH_DCACHE_LINE_SIZE);
|
||||
uint32_t l = cache_len_up_align(addr, len, EXFLASH_DCACHE_LINE_SIZE);
|
||||
SCB_InvalidateDCache_by_Addr((uint32_t *)a, (int32_t)l);
|
||||
#else
|
||||
(void)addr;
|
||||
(void)len;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送 32bit 地址(A31~A0)。
|
||||
* @param addr 线性地址。
|
||||
*/
|
||||
static void spi_flash_send_addr(uint32_t addr)
|
||||
{
|
||||
spi_flash_send_byte((uint8_t)((addr >> 24) & 0xFFu));
|
||||
spi_flash_send_byte((uint8_t)((addr >> 16) & 0xFFu));
|
||||
spi_flash_send_byte((uint8_t)((addr >> 8) & 0xFFu));
|
||||
spi_flash_send_byte((uint8_t)(addr & 0xFFu));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 进入 4-byte 地址模式(支持 32MB 地址空间访问)。
|
||||
*/
|
||||
static void spi_flash_enter_4byte_addr_mode(void)
|
||||
{
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_EN4B);
|
||||
SPI_FLASH_CS_HIGH();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 等待 DMA 收发双通道完成。
|
||||
* @return 1 成功,0 超时。
|
||||
*/
|
||||
static uint8_t spi4_dma_wait_done(void)
|
||||
{
|
||||
uint32_t timeout = EXFLASH_DMA_WAIT_MAX_LOOP;
|
||||
|
||||
while (timeout-- > 0u) {
|
||||
if ((s_spi4_dma_rx_done == 1u) && (s_spi4_dma_tx_done == 1u)) {
|
||||
return 1u;
|
||||
}
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动 SPI4 DMA 全双工传输(同时配置 RX/TX)。
|
||||
* @param tx_buf TX 源地址。
|
||||
* @param tx_inc TX 地址是否自增。
|
||||
* @param rx_buf RX 目标地址。
|
||||
* @param rx_inc RX 地址是否自增。
|
||||
* @param length 传输字节数。
|
||||
* @return 1 启动并完成成功,0 超时/失败。
|
||||
*/
|
||||
static uint8_t spi4_dma_transfer(uint8_t *tx_buf,
|
||||
uint32_t tx_inc,
|
||||
uint8_t *rx_buf,
|
||||
uint32_t rx_inc,
|
||||
uint16_t length)
|
||||
{
|
||||
dma_single_data_parameter_struct dma_init_struct;
|
||||
|
||||
if ((tx_buf == NULL) || (rx_buf == NULL) || (length == 0u)) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
s_spi4_dma_rx_done = 0u;
|
||||
s_spi4_dma_tx_done = 0u;
|
||||
|
||||
dma_deinit(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH);
|
||||
dma_deinit(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH);
|
||||
|
||||
dma_single_data_para_struct_init(&dma_init_struct);
|
||||
|
||||
/* DMA 读 TX 前清 cache;DMA 写 RX 后需要 invalidate cache */
|
||||
if (tx_inc == DMA_MEMORY_INCREASE_ENABLE) {
|
||||
dcache_clean_by_addr((uint32_t)tx_buf, (uint32_t)length);
|
||||
}
|
||||
if (rx_inc == DMA_MEMORY_INCREASE_ENABLE) {
|
||||
dcache_invalidate_by_addr((uint32_t)rx_buf, (uint32_t)length);
|
||||
}
|
||||
|
||||
/* RX: SPI4_RDATA -> memory */
|
||||
dma_init_struct.request = DMA_REQUEST_SPI4_RX;
|
||||
dma_init_struct.periph_addr = (uint32_t)&SPI_RDATA(SPI4);
|
||||
dma_init_struct.periph_inc = DMA_MEMORY_INCREASE_DISABLE;
|
||||
dma_init_struct.memory0_addr = (uint32_t)rx_buf;
|
||||
dma_init_struct.memory_inc = rx_inc;
|
||||
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
|
||||
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
|
||||
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
|
||||
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
|
||||
dma_init_struct.number = length;
|
||||
dma_single_data_mode_init(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, &dma_init_struct);
|
||||
dma_interrupt_enable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, DMA_INT_FTF);
|
||||
|
||||
/* TX: memory -> SPI4_TDATA */
|
||||
dma_init_struct.request = DMA_REQUEST_SPI4_TX;
|
||||
dma_init_struct.periph_addr = (uint32_t)&SPI_TDATA(SPI4);
|
||||
dma_init_struct.periph_inc = DMA_MEMORY_INCREASE_DISABLE;
|
||||
dma_init_struct.memory0_addr = (uint32_t)tx_buf;
|
||||
dma_init_struct.memory_inc = tx_inc;
|
||||
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
|
||||
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
|
||||
dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
|
||||
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
|
||||
dma_init_struct.number = length;
|
||||
dma_single_data_mode_init(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, &dma_init_struct);
|
||||
dma_interrupt_enable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, DMA_INT_FTF);
|
||||
|
||||
spi_dma_enable(SPI4, SPI_DMA_RECEIVE);
|
||||
spi_dma_enable(SPI4, SPI_DMA_TRANSMIT);
|
||||
|
||||
/* 显式启动一次主机传输,保证时钟输出 */
|
||||
spi_master_transfer_start(SPI4, SPI_TRANS_START);
|
||||
|
||||
dma_channel_enable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH);
|
||||
dma_channel_enable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH);
|
||||
|
||||
if (spi4_dma_wait_done() == 0u) {
|
||||
spi_dma_disable(SPI4, SPI_DMA_RECEIVE);
|
||||
spi_dma_disable(SPI4, SPI_DMA_TRANSMIT);
|
||||
dma_channel_disable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH);
|
||||
dma_channel_disable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH);
|
||||
return 0u;
|
||||
}
|
||||
|
||||
spi_dma_disable(SPI4, SPI_DMA_RECEIVE);
|
||||
spi_dma_disable(SPI4, SPI_DMA_TRANSMIT);
|
||||
dma_channel_disable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH);
|
||||
dma_channel_disable(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH);
|
||||
|
||||
/* DMA 写完 RX 后,CPU 读取前再次 invalidate,避免 cache 仍是旧数据 */
|
||||
if (rx_inc == DMA_MEMORY_INCREASE_ENABLE) {
|
||||
dcache_invalidate_by_addr((uint32_t)rx_buf, (uint32_t)length);
|
||||
}
|
||||
return 1u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化 SPI4 与外部 Flash 片选 GPIO。
|
||||
* @details
|
||||
* - CS: PJ9(软件控制)
|
||||
* - SCK: PK0,MOSI: PJ10,MISO: PJ11
|
||||
* - SPI Mode0,8bit,MSB first
|
||||
*/
|
||||
void spi_flash_init(void)
|
||||
{
|
||||
spi_parameter_struct spi_init_struct;
|
||||
|
||||
/* 1) 时钟使能 */
|
||||
rcu_periph_clock_enable(RCU_GPIOJ);
|
||||
rcu_periph_clock_enable(RCU_GPIOK);
|
||||
rcu_periph_clock_enable(RCU_SPI4);
|
||||
rcu_periph_clock_enable(RCU_DMA0);
|
||||
rcu_periph_clock_enable(RCU_DMAMUX);
|
||||
rcu_spi_clock_config(IDX_SPI4, RCU_SPISRC_APB2);
|
||||
|
||||
/* DMA 中断配置放在 DMA 时钟就绪后,避免早期访问 DMA 寄存器引发异常 */
|
||||
nvic_irq_enable(DMA0_Channel1_IRQn, 4U, 0U);
|
||||
nvic_irq_enable(DMA0_Channel2_IRQn, 4U, 0U);
|
||||
|
||||
/* 2) 片选脚初始化:默认拉高(未选中) */
|
||||
gpio_mode_set(GPIOJ, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_9);
|
||||
gpio_output_options_set(GPIOJ, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_9);
|
||||
SPI_FLASH_CS_HIGH();
|
||||
|
||||
/* 3) SPI4 复用脚初始化 */
|
||||
gpio_af_set(GPIOK, GPIO_AF_5, GPIO_PIN_0); /* SCK */
|
||||
gpio_af_set(GPIOJ, GPIO_AF_5, GPIO_PIN_10 | GPIO_PIN_11); /* MOSI/MISO */
|
||||
|
||||
gpio_mode_set(GPIOK, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
|
||||
gpio_output_options_set(GPIOK, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_0);
|
||||
|
||||
gpio_mode_set(GPIOJ, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
|
||||
gpio_output_options_set(GPIOJ, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_11);
|
||||
|
||||
gpio_mode_set(GPIOJ, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
|
||||
gpio_output_options_set(GPIOJ, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, GPIO_PIN_10);
|
||||
|
||||
/* 4) SPI4 参数初始化 */
|
||||
spi_i2s_deinit(SPI4);
|
||||
spi_struct_para_init(&spi_init_struct);
|
||||
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; /* 全双工 */
|
||||
spi_init_struct.device_mode = SPI_MASTER; /* 主机 */
|
||||
spi_init_struct.data_size = SPI_DATASIZE_8BIT; /* 8bit 帧 */
|
||||
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; /* Mode 0 */
|
||||
spi_init_struct.nss = SPI_NSS_SOFT; /* 软件 NSS */
|
||||
spi_init_struct.prescale = SPI_PSC_32; /* 分频,保证稳定 */
|
||||
spi_init_struct.endian = SPI_ENDIAN_MSB; /* 高位先发 */
|
||||
spi_init(SPI4, &spi_init_struct);
|
||||
|
||||
/* 5) 使能 SPI 外设 */
|
||||
spi_byte_access_enable(SPI4);
|
||||
spi_nss_output_enable(SPI4);
|
||||
spi_enable(SPI4);
|
||||
|
||||
/* 清理可能残留的 DMA 中断完成标志,防止首次传输误触发 */
|
||||
dma_interrupt_flag_clear(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, DMA_INT_FLAG_FTF);
|
||||
dma_interrupt_flag_clear(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, DMA_INT_FLAG_FTF);
|
||||
|
||||
/* 启用 4-byte 地址模式,支持访问 0x00000000~0x01FFFFFF(32MB) */
|
||||
spi_flash_enter_4byte_addr_mode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 擦除指定地址所在扇区(4KB)。
|
||||
* @param sector_addr 32bit 扇区地址(建议 4KB 对齐)。
|
||||
*/
|
||||
void spi_flash_sector_erase(uint32_t sector_addr)
|
||||
{
|
||||
spi_flash_write_enable();
|
||||
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_SE);
|
||||
spi_flash_send_addr(sector_addr);
|
||||
SPI_FLASH_CS_HIGH();
|
||||
|
||||
spi_flash_wait_for_write_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 全片擦除。
|
||||
* @warning 全片擦除耗时很长,仅在必要时调用。
|
||||
*/
|
||||
void spi_flash_bulk_erase(void)
|
||||
{
|
||||
spi_flash_write_enable();
|
||||
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_BE);
|
||||
SPI_FLASH_CS_HIGH();
|
||||
|
||||
spi_flash_wait_for_write_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 页内编程(不跨页)。
|
||||
* @param pbuffer 写入源缓冲区。
|
||||
* @param write_addr 写起始地址。
|
||||
* @param num_byte_to_write 页内写入字节数(建议 <=256 且不跨页)。
|
||||
*/
|
||||
void spi_flash_page_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
|
||||
{
|
||||
if ((pbuffer == NULL) || (num_byte_to_write == 0u)) {
|
||||
return;
|
||||
}
|
||||
|
||||
spi_flash_write_enable();
|
||||
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_WRITE);
|
||||
spi_flash_send_addr(write_addr);
|
||||
(void)spi4_dma_transfer(pbuffer,
|
||||
DMA_MEMORY_INCREASE_ENABLE,
|
||||
&s_spi4_dma_dummy_rx,
|
||||
DMA_MEMORY_INCREASE_DISABLE,
|
||||
num_byte_to_write);
|
||||
|
||||
SPI_FLASH_CS_HIGH();
|
||||
spi_flash_wait_for_write_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 连续写(自动按页拆分)。
|
||||
* @param pbuffer 写入源缓冲区。
|
||||
* @param write_addr 写起始地址。
|
||||
* @param num_byte_to_write 写入总字节数。
|
||||
* @details
|
||||
* 原始实现将分页计数变量定义为 uint8_t,在数据量 >255 页时会溢出;
|
||||
* 此处统一使用 uint32_t,避免分页计算错误。
|
||||
*/
|
||||
void spi_flash_buffer_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
|
||||
{
|
||||
uint32_t addr_offset;
|
||||
uint32_t first_page_space;
|
||||
uint32_t remain;
|
||||
uint32_t page_bytes;
|
||||
|
||||
if ((pbuffer == NULL) || (num_byte_to_write == 0u)) {
|
||||
return;
|
||||
}
|
||||
|
||||
addr_offset = write_addr % SPI_FLASH_PAGE_SIZE;
|
||||
first_page_space = SPI_FLASH_PAGE_SIZE - addr_offset;
|
||||
remain = num_byte_to_write;
|
||||
|
||||
/* 若首地址非页对齐,先补齐当前页 */
|
||||
if ((addr_offset != 0u) && (remain != 0u)) {
|
||||
page_bytes = (remain < first_page_space) ? remain : first_page_space;
|
||||
spi_flash_page_write(pbuffer, write_addr, (uint16_t)page_bytes);
|
||||
write_addr += page_bytes;
|
||||
pbuffer += page_bytes;
|
||||
remain -= page_bytes;
|
||||
}
|
||||
|
||||
/* 整页写 */
|
||||
while (remain >= SPI_FLASH_PAGE_SIZE) {
|
||||
spi_flash_page_write(pbuffer, write_addr, SPI_FLASH_PAGE_SIZE);
|
||||
write_addr += SPI_FLASH_PAGE_SIZE;
|
||||
pbuffer += SPI_FLASH_PAGE_SIZE;
|
||||
remain -= SPI_FLASH_PAGE_SIZE;
|
||||
}
|
||||
|
||||
/* 尾包写 */
|
||||
if (remain > 0u) {
|
||||
spi_flash_page_write(pbuffer, write_addr, (uint16_t)remain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 连续读取数据。
|
||||
* @param pbuffer 读出目标缓冲区。
|
||||
* @param read_addr 读取起始地址。
|
||||
* @param num_byte_to_read 读取字节数。
|
||||
*/
|
||||
void spi_flash_buffer_read(uint8_t *pbuffer, uint32_t read_addr, uint16_t num_byte_to_read)
|
||||
{
|
||||
if ((pbuffer == NULL) || (num_byte_to_read == 0u)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_READ);
|
||||
spi_flash_send_addr(read_addr);
|
||||
(void)spi4_dma_transfer(&s_spi4_dma_dummy_tx,
|
||||
DMA_MEMORY_INCREASE_DISABLE,
|
||||
pbuffer,
|
||||
DMA_MEMORY_INCREASE_ENABLE,
|
||||
num_byte_to_read);
|
||||
|
||||
SPI_FLASH_CS_HIGH();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取 JEDEC ID(三字节)。
|
||||
* @return 24bit ID:Manufacturer[23:16] | MemoryType[15:8] | Capacity[7:0]。
|
||||
*/
|
||||
uint32_t spi_flash_read_id(void)
|
||||
{
|
||||
uint32_t id0;
|
||||
uint32_t id1;
|
||||
uint32_t id2;
|
||||
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_RDID);
|
||||
id0 = spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
|
||||
id1 = spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
|
||||
id2 = spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
|
||||
SPI_FLASH_CS_HIGH();
|
||||
|
||||
return (id0 << 16) | (id1 << 8) | id2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发起连续读序列(保持片选有效)。
|
||||
* @param read_addr 读取起始地址。
|
||||
*/
|
||||
void spi_flash_start_read_sequence(uint32_t read_addr)
|
||||
{
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_READ);
|
||||
spi_flash_send_addr(read_addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在连续读序列中读取 1 字节。
|
||||
* @return 读到的数据字节。
|
||||
*/
|
||||
uint8_t spi_flash_read_byte(void)
|
||||
{
|
||||
return spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI4 全双工收发一个字节。
|
||||
* @param byte 发送字节。
|
||||
* @return 同步接收字节。
|
||||
*/
|
||||
uint8_t spi_flash_send_byte(uint8_t byte)
|
||||
{
|
||||
uint32_t timeout = 0x00FFFFFFu;
|
||||
|
||||
/* GD32H7 SPI 主机在部分场景需要显式启动传输,否则时钟不输出,RP 永远不置位 */
|
||||
spi_master_transfer_start(SPI4, SPI_TRANS_START);
|
||||
|
||||
while ((RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_TP)) && (timeout-- > 0u)) {
|
||||
/* 等待发送缓冲区可写 */
|
||||
}
|
||||
if (timeout == 0u) {
|
||||
return 0xFFu;
|
||||
}
|
||||
|
||||
spi_i2s_data_transmit(SPI4, byte);
|
||||
|
||||
timeout = 0x00FFFFFFu;
|
||||
while ((RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_RP)) && (timeout-- > 0u)) {
|
||||
/* 等待接收缓冲区有数据 */
|
||||
}
|
||||
if (timeout == 0u) {
|
||||
return 0xFFu;
|
||||
}
|
||||
|
||||
return (uint8_t)spi_i2s_data_receive(SPI4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SPI4 全双工收发半字。
|
||||
* @param half_word 发送半字。
|
||||
* @return 同步接收半字。
|
||||
*/
|
||||
uint16_t spi_flash_send_halfword(uint16_t half_word)
|
||||
{
|
||||
uint32_t timeout = 0x00FFFFFFu;
|
||||
|
||||
spi_master_transfer_start(SPI4, SPI_TRANS_START);
|
||||
|
||||
while ((RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_TP)) && (timeout-- > 0u)) {
|
||||
/* 等待发送缓冲区可写 */
|
||||
}
|
||||
if (timeout == 0u) {
|
||||
return 0xFFFFu;
|
||||
}
|
||||
|
||||
spi_i2s_data_transmit(SPI4, half_word);
|
||||
|
||||
timeout = 0x00FFFFFFu;
|
||||
while ((RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_RP)) && (timeout-- > 0u)) {
|
||||
/* 等待接收缓冲区有数据 */
|
||||
}
|
||||
if (timeout == 0u) {
|
||||
return 0xFFFFu;
|
||||
}
|
||||
|
||||
return (uint16_t)spi_i2s_data_receive(SPI4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送 Write Enable 指令(WREN)。
|
||||
*/
|
||||
void spi_flash_write_enable(void)
|
||||
{
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_WREN);
|
||||
SPI_FLASH_CS_HIGH();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 等待写/擦操作结束(轮询 SR.WIP)。
|
||||
*/
|
||||
void spi_flash_wait_for_write_end(void)
|
||||
{
|
||||
uint8_t flash_status;
|
||||
|
||||
SPI_FLASH_CS_LOW();
|
||||
spi_flash_send_byte(EXFLASH_CMD_RDSR);
|
||||
|
||||
do {
|
||||
flash_status = spi_flash_send_byte(EXFLASH_DUMMY_BYTE);
|
||||
} while ((flash_status & EXFLASH_SR_WIP_MASK) != 0u);
|
||||
|
||||
SPI_FLASH_CS_HIGH();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DMA0 Channel1 IRQ:SPI4 RX 完成中断。
|
||||
*/
|
||||
void DMA0_Channel1_IRQHandler(void)
|
||||
{
|
||||
if (SET == dma_interrupt_flag_get(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, DMA_INT_FLAG_FTF)) {
|
||||
dma_interrupt_flag_clear(EXFLASH_DMA_PERIPH, EXFLASH_DMA_RX_CH, DMA_INT_FLAG_FTF);
|
||||
s_spi4_dma_rx_done = 1u;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DMA0 Channel2 IRQ:SPI4 TX 完成中断。
|
||||
*/
|
||||
void DMA0_Channel2_IRQHandler(void)
|
||||
{
|
||||
if (SET == dma_interrupt_flag_get(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, DMA_INT_FLAG_FTF)) {
|
||||
dma_interrupt_flag_clear(EXFLASH_DMA_PERIPH, EXFLASH_DMA_TX_CH, DMA_INT_FLAG_FTF);
|
||||
s_spi4_dma_tx_done = 1u;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef EXFLASH_SPI4_GD25QXX_H
|
||||
#define EXFLASH_SPI4_GD25QXX_H
|
||||
|
||||
#include "gd32h7xx.h"
|
||||
|
||||
/* GD25QXX 页大小:每页 256 字节 */
|
||||
#define SPI_FLASH_PAGE_SIZE (0x100u)
|
||||
|
||||
/* 片选脚由 PJ9 软件控制 */
|
||||
#define SPI_FLASH_CS_LOW() gpio_bit_reset(GPIOJ, GPIO_PIN_9)
|
||||
#define SPI_FLASH_CS_HIGH() gpio_bit_set(GPIOJ, GPIO_PIN_9)
|
||||
|
||||
/* 初始化 SPI4 与片选 GPIO */
|
||||
void spi_flash_init(void);
|
||||
/* 擦除 4KB 扇区 */
|
||||
void spi_flash_sector_erase(uint32_t sector_addr);
|
||||
/* 全片擦除(耗时较长) */
|
||||
void spi_flash_bulk_erase(void);
|
||||
/* 页内写(不处理跨页) */
|
||||
void spi_flash_page_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write);
|
||||
/* 连续写(自动处理跨页) */
|
||||
void spi_flash_buffer_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write);
|
||||
/* 连续读 */
|
||||
void spi_flash_buffer_read(uint8_t *pbuffer, uint32_t read_addr, uint16_t num_byte_to_read);
|
||||
/* 读取 JEDEC ID */
|
||||
uint32_t spi_flash_read_id(void);
|
||||
/* 发起连续读命令序列(随后可重复 spi_flash_read_byte) */
|
||||
void spi_flash_start_read_sequence(uint32_t read_addr);
|
||||
/* 连续读阶段读取 1 字节 */
|
||||
uint8_t spi_flash_read_byte(void);
|
||||
/* SPI 收发 1 字节 */
|
||||
uint8_t spi_flash_send_byte(uint8_t byte);
|
||||
/* SPI 收发 1 半字 */
|
||||
uint16_t spi_flash_send_halfword(uint16_t half_word);
|
||||
/* 置写使能(WREN) */
|
||||
void spi_flash_write_enable(void);
|
||||
/* 轮询 WIP 位,等待写/擦完成 */
|
||||
void spi_flash_wait_for_write_end(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
# externalflash 模块说明(SPI4 外部 Flash)
|
||||
|
||||
> 本文档描述 `CCU621_M/BSP/externalflash` 模块的职责、接口、初始化位置以及 DMA + D-Cache 场景下的使用注意事项。
|
||||
|
||||
[返回主说明](./../../README.md)
|
||||
|
||||
## 1. 模块组成
|
||||
|
||||
- **底层驱动(SPI4 + GD25QXX)**
|
||||
- `exflash_spi4_gd25qxx.c`
|
||||
- `exflash_spi4_gd25qxx.h`
|
||||
- **统一对外接口(业务层推荐使用)**
|
||||
- `flash_external_data.c`
|
||||
- `flash_external_data.h`
|
||||
|
||||
## 2. 初始化入口
|
||||
|
||||
- 系统硬件初始化入口:`BSP/sys_drv_init.c`
|
||||
- `v_sys_hardware_init()` 中调用 `v_flash_dataflash_init()`,完成 SPI4/GPIO/DMA 初始化。
|
||||
|
||||
> 约束:业务层/测试层不要重复 init,避免初始化时序与资源重复配置。
|
||||
|
||||
## 3. 推荐对外接口(统一接口)
|
||||
|
||||
业务层请优先使用以下接口(位于 `flash_external_data.c/.h`):
|
||||
|
||||
- `U8_T v_flash_dataflash_init(void)`
|
||||
- `U8_T s32_flash_dataflash_write(U32_T addr, U8_T *buf, U32_T len)`
|
||||
- `U8_T s32_flash_dataflash_read(U32_T addr, U8_T *buf, U32_T len)`
|
||||
- `U8_T s32_flash_dataflash_erase_sector(U32_T addr)`
|
||||
- 兼容旧命名:`unsigned int SPI_flsh_ReadID(void)`
|
||||
|
||||
### 3.1 地址规划(摘自 `flash_external_data.h`)
|
||||
|
||||
- 全空间(4-byte 地址模式):`0x00000000 ~ 0x01FFFFFF`(32MB)
|
||||
- OTA 区:`0x00000000 ~ 0x001FFFFF`
|
||||
- 参数区:`0x210000 ~ ...`
|
||||
- 业务记录区:`0x220000 ~ ...`
|
||||
- FatFs 映射区:`0xA00000 ~ 0x01FFFFFF`
|
||||
|
||||
> 说明:以上为“线性地址”规划,调用统一接口时直接传该地址即可。
|
||||
|
||||
## 4. DMA + D-Cache 使用注意事项(非常重要)
|
||||
|
||||
当前 SPI4 外部 Flash 读写采用 **DMA** 方式提升性能;在 Cortex-M7 **开启 D-Cache** 的情况下,DMA 与 CPU 会出现数据一致性问题:
|
||||
|
||||
- DMA 写入内存后,CPU 可能仍读取到旧 cache 数据;
|
||||
- DMA 从内存读取 TX 数据时,可能读不到 CPU 刚写入但未 clean 的 cache 内容。
|
||||
|
||||
### 4.1 当前工程的处理策略
|
||||
|
||||
- 底层 SPI4 DMA 传输在驱动内部做了 D-Cache clean/invalidate(按 32B cache line 对齐)。
|
||||
- 统一接口 `s32_flash_dataflash_read/write` 额外做了一层保护:
|
||||
- 如果业务传入的 `buf` **不是 32B 对齐**,内部会自动使用 **对齐 bounce buffer** 完成 DMA 传输,再 `memcpy` 到业务缓冲。
|
||||
|
||||
### 4.2 业务层建议
|
||||
|
||||
- 业务层可直接使用 `s32_flash_dataflash_read/write`,无需自行对齐或自行做 cache 维护。
|
||||
- 若业务存在高频读写,可考虑在模块内复用一块“32B 对齐的工作缓冲”,减少 malloc/free 次数(后续可优化)。
|
||||
|
||||
## 5. 测试入口
|
||||
|
||||
- `app/app_init/app_test.c`
|
||||
- 周期执行 flash 擦写读回校验
|
||||
- 输出擦除/读写耗时与速度统计
|
||||
- 新增 `u8_app_test_flash_32m_stride_rw()`:以 1MB 步长扫描 32MB 地址空间做擦写读回校验
|
||||
|
||||
## 6. 常见问题排查
|
||||
|
||||
- **读回校验失败**
|
||||
- 首先确认是否走统一接口(避免绕过 bounce buffer)
|
||||
- 其次检查 D-Cache 是否开启、MPU 区域属性是否覆盖了 DMA buffer
|
||||
- **卡死在 SPI_FLAG_RP 等待**
|
||||
- 通常表示 SPI 主机传输未启动或时钟未输出,需要检查 `spi_master_transfer_start` 是否调用、片选是否正确、外设时钟是否使能
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
#include "flash_external_data.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "app_init/app_init.h"
|
||||
#include "exflash_spi4_gd25qxx.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "portable.h"
|
||||
|
||||
#define FLASH_IO_ALIGN (32u)
|
||||
#define FLASH_IO_GUARD_SIZE (32u)
|
||||
|
||||
static uint8_t *align_up_u8(uint8_t *p, uint32_t align)
|
||||
{
|
||||
uintptr_t v = (uintptr_t)p;
|
||||
v = (v + (align - 1U)) & ~(uintptr_t)(align - 1U);
|
||||
return (uint8_t *)v;
|
||||
}
|
||||
|
||||
static uint8_t need_bounce_buf(const void *p)
|
||||
{
|
||||
return (((uintptr_t)p) & (FLASH_IO_ALIGN - 1U)) ? 1U : 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 外部 Flash 初始化(统一接口)。
|
||||
* @return 0 成功。
|
||||
*/
|
||||
U8_T v_flash_dataflash_init(void)
|
||||
{
|
||||
spi_flash_init();
|
||||
printf("[EXFLASH] JEDEC ID: 0x%06X\r\n", (unsigned int)spi_flash_read_id());
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 外部 Flash 写接口(统一接口)。
|
||||
* @param addr 24bit 线性地址。
|
||||
* @param buf 写入数据缓冲区。
|
||||
* @param len 写入长度(字节)。
|
||||
* @return 0 成功,1 参数错误,2 长度超驱动接口上限。
|
||||
*/
|
||||
U8_T s32_flash_dataflash_write(U32_T addr, U8_T *buf, U32_T len)
|
||||
{
|
||||
uint8_t *raw;
|
||||
uint8_t *aligned;
|
||||
uint32_t alloc_size;
|
||||
|
||||
if ((buf == NULL) || (len == 0U)) {
|
||||
return 1U;
|
||||
}
|
||||
|
||||
/* 底层驱动 write 接口长度参数为 uint16_t。 */
|
||||
if (len > 0xFFFFu) {
|
||||
return 2U;
|
||||
}
|
||||
|
||||
/* 若用户缓冲未按 32B 对齐,使用 bounce buffer,避免 DMA/DCache 维护影响堆头或触发一致性问题 */
|
||||
if (need_bounce_buf(buf) == 0U) {
|
||||
spi_flash_buffer_write(buf, addr, (U16_T)len);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
alloc_size = len + FLASH_IO_ALIGN + FLASH_IO_GUARD_SIZE;
|
||||
raw = (uint8_t *)pvPortMalloc(alloc_size);
|
||||
if (raw == NULL) {
|
||||
return 3U;
|
||||
}
|
||||
aligned = align_up_u8(raw, FLASH_IO_ALIGN);
|
||||
memcpy(aligned, buf, len);
|
||||
spi_flash_buffer_write(aligned, addr, (U16_T)len);
|
||||
vPortFree(raw);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 外部 Flash 读接口(统一接口)。
|
||||
* @param addr 24bit 线性地址。
|
||||
* @param buf 读出数据缓冲区。
|
||||
* @param len 读取长度(字节)。
|
||||
* @return 0 成功,1 参数错误,2 长度超驱动接口上限。
|
||||
*/
|
||||
U8_T s32_flash_dataflash_read(U32_T addr, U8_T *buf, U32_T len)
|
||||
{
|
||||
uint8_t *raw;
|
||||
uint8_t *aligned;
|
||||
uint32_t alloc_size;
|
||||
|
||||
if ((buf == NULL) || (len == 0U)) {
|
||||
return 1U;
|
||||
}
|
||||
|
||||
if (len > 0xFFFFu) {
|
||||
return 2U;
|
||||
}
|
||||
|
||||
if (need_bounce_buf(buf) == 0U) {
|
||||
spi_flash_buffer_read(buf, addr, (U16_T)len);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
alloc_size = len + FLASH_IO_ALIGN + FLASH_IO_GUARD_SIZE;
|
||||
raw = (uint8_t *)pvPortMalloc(alloc_size);
|
||||
if (raw == NULL) {
|
||||
return 3U;
|
||||
}
|
||||
aligned = align_up_u8(raw, FLASH_IO_ALIGN);
|
||||
spi_flash_buffer_read(aligned, addr, (U16_T)len);
|
||||
memcpy(buf, aligned, len);
|
||||
vPortFree(raw);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 擦除指定地址所在扇区。
|
||||
* @param addr 扇区起始地址(建议 4KB 对齐)。
|
||||
* @return 0 成功。
|
||||
*/
|
||||
U8_T s32_flash_dataflash_erase_sector(U32_T addr)
|
||||
{
|
||||
spi_flash_sector_erase(addr);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取外部 Flash JEDEC ID(兼容旧命名)。
|
||||
*/
|
||||
unsigned int SPI_flsh_ReadID(void)
|
||||
{
|
||||
return (unsigned int)spi_flash_read_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 擦除 FatFs 映射区全部扇区。
|
||||
*/
|
||||
void v_flash_dataflash_fatfs_esaes_sectot(void)
|
||||
{
|
||||
U32_T i;
|
||||
U32_T sector_addr;
|
||||
|
||||
for (i = 0U; i < FATFS_TOTAL_SECTORS; i++) {
|
||||
sector_addr = FATFS_PHYSICAL_START_ADDR + (i * SPI_SECTOR_SIZE);
|
||||
printf("SPI flash erase sector: 0x%08X\r\n", (unsigned int)sector_addr);
|
||||
spi_flash_sector_erase(sector_addr);
|
||||
mSleep(1U);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#ifndef FLASH_EXTERNAL_DATA_H
|
||||
#define FLASH_EXTERNAL_DATA_H
|
||||
|
||||
#include "publicdata/type.h"
|
||||
#include "meter_calculate/meter_calculate_impl.h"
|
||||
|
||||
/* ============================================================================
|
||||
* 外部 Flash 容量与地址说明
|
||||
*
|
||||
* 当前上电识别 JEDEC ID: 0xC84019
|
||||
* - Manufacturer: 0xC8(GigaDevice)
|
||||
* - Capacity code: 0x19(典型为 256Mbit = 32MB)
|
||||
*
|
||||
* 当前底层驱动已启用 4-byte 地址模式(4 字节地址):
|
||||
* - 可直接访问线性地址范围:0x00000000 ~ 0x01FFFFFF(32MB)
|
||||
* - 总容量:0x02000000 bytes
|
||||
* ============================================================================ */
|
||||
|
||||
#define EXFLASH_ADDR_MIN (0x00000000u)
|
||||
#define EXFLASH_ADDR_MAX (0x01FFFFFFu)
|
||||
#define EXFLASH_TOTAL_SIZE (0x02000000u)
|
||||
|
||||
/* SPI NOR Flash 物理扇区大小(字节) */
|
||||
#define SPI_SECTOR_SIZE (4096u)
|
||||
|
||||
#ifndef RECORD_ZERO_SAVE
|
||||
#define RECORD_ZERO_SAVE (0x7FFFu)
|
||||
#endif
|
||||
#ifndef RECORD_HEADER
|
||||
#define RECORD_HEADER (0x55u)
|
||||
#endif
|
||||
|
||||
/* OTA 区(升级保留区) */
|
||||
#define UPGRADE_START_ADDR (0x00000000u)
|
||||
#define UPGRADE_END_ADDR (0x001FFFFFu)
|
||||
#define UPGRADE_TOTAL_SIZE (UPGRADE_END_ADDR - UPGRADE_START_ADDR + 1u)
|
||||
|
||||
/* 配置区 */
|
||||
#define DATAFLASH_VAR_CFG_DATA1_ADDR (0x210000u)
|
||||
#define DATAFLASH_VAR_CFG_DATA2_ADDR (0x211000u)
|
||||
#define DATAFLASH_VAR_CFG_DATA3_ADDR (0x212000u)
|
||||
#define DATAFLASH_FIX_CFG_DATA1_ADDR (0x213000u)
|
||||
#define DATAFLASH_FIX_CFG_DATA2_ADDR (0x214000u)
|
||||
#define DATAFLASH_FIX_CFG_DATA3_ADDR (0x215000u)
|
||||
#define DATAFLASH_OCPP_CFG_DATA_ADDR (0x216000u)
|
||||
|
||||
/* 故障、卡号、历史记录区 */
|
||||
#define DATAFLASH_FAULT_ADDR (0x220000u)
|
||||
#define DATAFLASH_FAULT_SECTOR_CNT (2u)
|
||||
#define DATAFLASH_FAULT_END_ADDR (DATAFLASH_FAULT_ADDR + (DATAFLASH_FAULT_SECTOR_CNT * SPI_SECTOR_SIZE) - 1u)
|
||||
|
||||
#define DATAFLASH_OCPP_MV_OFFLINE_ADDR (0x2C0000u)
|
||||
#define DATAFLASH_OCPP_MV_OFFLINE_HALF_SIZE (0x8000u)
|
||||
#define DATAFLASH_OCPP_MV_OFFLINE_SECTOR_CNT (16u)
|
||||
#define DATAFLASH_OCPP_MV_OFFLINE_TOTAL_SIZE (DATAFLASH_OCPP_MV_OFFLINE_HALF_SIZE * 2u)
|
||||
|
||||
#define DATAFLASH_CARD_ADDR (0x222000u)
|
||||
#define DATAFLASH_CARD_SECTOR_CNT (7u)
|
||||
#define DATAFLASH_CARD_END_ADDR (DATAFLASH_CARD_ADDR + (DATAFLASH_CARD_SECTOR_CNT * SPI_SECTOR_SIZE) - 1u)
|
||||
|
||||
#define DATAFLASH_HIS_RECORD_ADDR (0x230000u)
|
||||
#define DATAFLASH_HIS_RECORD_END_ADDR (0x2BFFFFu)
|
||||
#define DATAFLASH_HIS_RECORD_SIZE (DATAFLASH_HIS_RECORD_END_ADDR - DATAFLASH_HIS_RECORD_ADDR + 1u)
|
||||
#define DATAFLASH_HIS_RECORD_SECTOR_CNT (DATAFLASH_HIS_RECORD_SIZE / SPI_SECTOR_SIZE)
|
||||
|
||||
/* Charge-order storage layout (compatible with migrated flash_file_mgr module) */
|
||||
#ifndef CHG_ORDER_RECORD_SIZE
|
||||
#define CHG_ORDER_RECORD_SIZE (sizeof(S_LOG_DATA))
|
||||
#endif
|
||||
#ifndef CHG_ORDER_FULL_RECORD_SIZE
|
||||
#define CHG_ORDER_FULL_RECORD_SIZE (1u + CHG_ORDER_RECORD_SIZE + 1u)
|
||||
#endif
|
||||
#ifndef CHG_ORDER_PER_SECTOR_CNT
|
||||
#define CHG_ORDER_PER_SECTOR_CNT (SPI_SECTOR_SIZE / CHG_ORDER_FULL_RECORD_SIZE)
|
||||
#endif
|
||||
#ifndef CHG_ORDER_MAX_CNT
|
||||
#define CHG_ORDER_MAX_CNT (200u)
|
||||
#endif
|
||||
|
||||
#ifndef TEMP_CHG_RECORD_SIZE
|
||||
#define TEMP_CHG_RECORD_SIZE (sizeof(S_LOG_DATA))
|
||||
#endif
|
||||
|
||||
/* FatFs 映射区 */
|
||||
#define FATFS_PHYSICAL_START_ADDR (0xA00000u)
|
||||
#define FATFS_PHYSICAL_STOP_ADDR (EXFLASH_ADDR_MAX)
|
||||
#define FATFS_PARTITION_SIZE (FATFS_PHYSICAL_STOP_ADDR - FATFS_PHYSICAL_START_ADDR + 1u)
|
||||
#define FATFS_TOTAL_SECTORS (FATFS_PARTITION_SIZE / SPI_SECTOR_SIZE)
|
||||
|
||||
/* 统一对外接口:0=成功,非0=失败 */
|
||||
U8_T v_flash_dataflash_init(void);
|
||||
U8_T s32_flash_dataflash_write(U32_T addr, U8_T *buf, U32_T len);
|
||||
U8_T s32_flash_dataflash_read(U32_T addr, U8_T *buf, U32_T len);
|
||||
U8_T s32_flash_dataflash_erase_sector(U32_T addr);
|
||||
|
||||
/* 兼容旧接口命名 */
|
||||
unsigned int SPI_flsh_ReadID(void);
|
||||
void v_flash_dataflash_fatfs_esaes_sectot(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @file flash_upgrade.c
|
||||
* @brief 外置 Flash 固件升级区读写(与 CCU601E_D / CCU621_IAP 布局一致)
|
||||
*/
|
||||
|
||||
#include "flash_upgrade.h"
|
||||
#include "app_init/app_init.h"
|
||||
#include "wdt_task/wdt_task.h"
|
||||
#include "exflash_spi4_gd25qxx.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
extern U8_T s32_flash_dataflash_write(U32_T addr, U8_T *buf, U32_T len);
|
||||
extern U8_T s32_flash_dataflash_read(U32_T addr, U8_T *buf, U32_T len);
|
||||
extern U8_T s32_flash_dataflash_erase_sector(U32_T addr);
|
||||
|
||||
/* 0: 按包擦扇区;1: upgrade_erase_program_area 已整体擦除 */
|
||||
static int g_upgrade_program_pre_erased = 0;
|
||||
|
||||
static uint8_t s_upgrade_header_buf[sizeof(upgrade_header_t)]
|
||||
__attribute__((aligned(32)));
|
||||
|
||||
__attribute__((weak)) void upgrade_erase_yield(void)
|
||||
{
|
||||
}
|
||||
|
||||
int upgrade_init(void)
|
||||
{
|
||||
upgrade_header_t header;
|
||||
|
||||
if (upgrade_read_header(&header) == 0) {
|
||||
if (header.upgrade_flag == UPGRADE_FLAG_NEED_UP) {
|
||||
return 1;
|
||||
}
|
||||
if (header.upgrade_flag == UPGRADE_FLAG_DONE) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
g_upgrade_program_pre_erased = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upgrade_write_header(const upgrade_header_t *header)
|
||||
{
|
||||
if (header == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
spi_flash_wait_for_write_end();
|
||||
v_wdt_dog_toggle();
|
||||
s32_flash_dataflash_erase_sector(HEADER_SECTOR_ADDR);
|
||||
v_wdt_dog_toggle();
|
||||
|
||||
memcpy(s_upgrade_header_buf, header, sizeof(upgrade_header_t));
|
||||
if (s32_flash_dataflash_write(HEADER_SECTOR_ADDR, s_upgrade_header_buf,
|
||||
sizeof(upgrade_header_t)) != 0U) {
|
||||
printf("[FW_UPGRADE] header write failed\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
spi_flash_wait_for_write_end();
|
||||
v_wdt_dog_toggle();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upgrade_read_header(upgrade_header_t *header)
|
||||
{
|
||||
if (header == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s32_flash_dataflash_read(HEADER_SECTOR_ADDR, (U8_T *)header, sizeof(upgrade_header_t)) != 0U) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upgrade_success_update_header(upgrade_header_t *header)
|
||||
{
|
||||
return upgrade_write_header(header);
|
||||
}
|
||||
|
||||
int upgrade_write_packet(uint32_t packet_index, const uint8_t *data, uint32_t data_len)
|
||||
{
|
||||
uint32_t packet_addr;
|
||||
uint32_t sector_start;
|
||||
|
||||
if ((data == NULL) || (data_len == 0U) || (data_len > PACKET_SIZE)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
packet_addr = PROGRAM_START_ADDR + (packet_index * PACKET_SIZE);
|
||||
|
||||
if (packet_addr + data_len > UPGRADE_END_ADDR) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!g_upgrade_program_pre_erased) {
|
||||
sector_start = (packet_addr / SECTOR_SIZE) * SECTOR_SIZE;
|
||||
if ((packet_index * PACKET_SIZE) % SECTOR_SIZE == 0U) {
|
||||
s32_flash_dataflash_erase_sector(sector_start);
|
||||
v_wdt_dog_toggle();
|
||||
mSleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (s32_flash_dataflash_write(packet_addr, (U8_T *)data, data_len) != 0U) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upgrade_read_packet(uint32_t packet_index, uint8_t *data, uint32_t *data_len)
|
||||
{
|
||||
uint32_t packet_addr;
|
||||
uint32_t max_read_size;
|
||||
|
||||
if ((data == NULL) || (data_len == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
packet_addr = PROGRAM_START_ADDR + (packet_index * PACKET_SIZE);
|
||||
|
||||
if (packet_addr > UPGRADE_END_ADDR) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s32_flash_dataflash_read(packet_addr, data, PACKET_SIZE) != 0U) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
max_read_size = UPGRADE_END_ADDR - packet_addr + 1U;
|
||||
if (max_read_size < PACKET_SIZE) {
|
||||
*data_len = max_read_size;
|
||||
} else {
|
||||
*data_len = PACKET_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upgrade_erase_program_area(void)
|
||||
{
|
||||
return upgrade_erase_program_area_for_size(PROGRAM_MAX_SIZE);
|
||||
}
|
||||
|
||||
int upgrade_erase_program_area_for_size(uint32_t firmware_bytes)
|
||||
{
|
||||
uint32_t sector_addr;
|
||||
uint32_t last_byte;
|
||||
uint32_t last_sector;
|
||||
|
||||
if (firmware_bytes == 0U) {
|
||||
return -1;
|
||||
}
|
||||
if (firmware_bytes > PROGRAM_MAX_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
last_byte = PROGRAM_START_ADDR + firmware_bytes - 1U;
|
||||
if (last_byte > UPGRADE_END_ADDR) {
|
||||
return -1;
|
||||
}
|
||||
last_sector = (last_byte / SECTOR_SIZE) * SECTOR_SIZE;
|
||||
|
||||
printf("[FW_UPGRADE] erase start size=%lu sectors=0x%08lX..0x%08lX\r\n",
|
||||
(unsigned long)firmware_bytes,
|
||||
(unsigned long)PROGRAM_START_ADDR,
|
||||
(unsigned long)last_sector);
|
||||
|
||||
for (sector_addr = PROGRAM_START_ADDR; sector_addr <= last_sector; sector_addr += SECTOR_SIZE) {
|
||||
s32_flash_dataflash_erase_sector(sector_addr);
|
||||
v_wdt_dog_toggle();
|
||||
mSleep(1);
|
||||
upgrade_erase_yield();
|
||||
if (((sector_addr - PROGRAM_START_ADDR) / SECTOR_SIZE) % 10U == 9U) {
|
||||
printf("[FW_UPGRADE] erase progress at 0x%08lX\r\n",
|
||||
(unsigned long)sector_addr);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[FW_UPGRADE] erase done\r\n");
|
||||
|
||||
g_upgrade_program_pre_erased = 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @file flash_upgrade.h
|
||||
* @brief 固件升级兼容头文件(整合自 CCU601E_D BSP/spi_Flash/flash_upgrade.h)
|
||||
*
|
||||
* CCU621_M 平台:升级相关常量、类型与接口与 CCU601E_D 保持一致。
|
||||
*/
|
||||
|
||||
#ifndef __FLASH_UPGRADE_H__
|
||||
#define __FLASH_UPGRADE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "main.h"
|
||||
#include "externalflash/flash_external_data.h"
|
||||
|
||||
/* 扇区大小 (4KB) */
|
||||
#define SECTOR_SIZE 4096
|
||||
/* 数据包大小 */
|
||||
#define PACKET_SIZE (1024)
|
||||
|
||||
/* 头部信息扇区 (第一个扇区) */
|
||||
#define HEADER_SECTOR_ADDR UPGRADE_START_ADDR
|
||||
#define HEADER_SECTOR_SIZE SECTOR_SIZE
|
||||
|
||||
/* 程序数据起始地址 (第二个扇区开始) */
|
||||
#define PROGRAM_START_ADDR (UPGRADE_START_ADDR + HEADER_SECTOR_SIZE)
|
||||
#define PROGRAM_MAX_SIZE (UPGRADE_TOTAL_SIZE - HEADER_SECTOR_SIZE)
|
||||
|
||||
/* 升级标志定义 */
|
||||
#define UPGRADE_FLAG_IDLE 0x00
|
||||
#define UPGRADE_FLAG_NEED_UP 0x55AA
|
||||
#define UPGRADE_FLAG_DONE 0xAA55
|
||||
#define UPGRADE_FLAG_ERROR 0xEEFF
|
||||
|
||||
/* 升级头部信息结构 */
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint16_t upgrade_flag;
|
||||
uint32_t file_size;
|
||||
uint32_t packet_count;
|
||||
uint32_t crc32;
|
||||
uint32_t timestamp;
|
||||
uint8_t version[16];
|
||||
uint8_t reserved[32];
|
||||
} upgrade_header_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
/* 函数声明 */
|
||||
int upgrade_init(void);
|
||||
int upgrade_read_header(upgrade_header_t *header);
|
||||
int upgrade_write_packet(uint32_t packet_index, const uint8_t *data, uint32_t data_len);
|
||||
int upgrade_read_packet(uint32_t packet_index, uint8_t *data, uint32_t *data_len);
|
||||
int upgrade_success_update_header(upgrade_header_t *header);
|
||||
int upgrade_erase_program_area(void);
|
||||
int upgrade_erase_program_area_for_size(uint32_t firmware_bytes);
|
||||
|
||||
/** 擦除循环中让出 CPU,读 BLE/TCP 入站(默认 weak 空实现,tcp_server 覆盖) */
|
||||
void upgrade_erase_yield(void);
|
||||
|
||||
#endif /* __FLASH_UPGRADE_H__ */
|
||||
Reference in New Issue
Block a user