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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user