GD32E230 SPI 使用 DMA 模式实例代码(收发同步)

[复制链接]
786|0
lxs0026 发表于 2025-8-28 12:04 | 显示全部楼层 |阅读模式
等待 DMA 的 “传输完成标志(FTF)” 时,需同时判断 TX 和 RX 通道,避免单方面完成导致数据不完整。


#include "gd32e23x.h"

// 沿用上方引脚定义,新增DMA配置
#define DMA_CHANNEL_TX       DMA_CH2 // SPI0_TX对应DMA1_CH2(参考GD32E23x手册)
#define DMA_CHANNEL_RX       DMA_CH3 // SPI0_RX对应DMA1_CH3
#define DMA_CLK              RCU_DMA1

// DMA 初始化(SPI0_TX和SPI0_RX)
void dma_spi0_init(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t data_len) {
    // 1. 使能DMA时钟
    rcu_periph_clock_enable(DMA_CLK);

    // 2. 配置DMA_TX(SPI0_DATA寄存器地址 ->  tx_buf,内存增量)
    dma_parameter_struct dma_tx_init;
    dma_deinit(DMA1, DMA_CHANNEL_TX);
    dma_struct_para_init(&dma_tx_init);
    dma_tx_init.periph_addr  = (uint32_t)&SPI_DATA(SPI0); // 外设地址:SPI数据寄存器
    dma_tx_init.memory_addr  = (uint32_t)tx_buf; // 内存地址:发送缓冲区
    dma_tx_init.direction    = DMA_MEMORY_TO_PERIPH; // 内存到外设
    dma_tx_init.periph_inc   = DMA_PERIPH_INCREASE_DISABLE; // 外设地址不增量
    dma_tx_init.memory_inc   = DMA_MEMORY_INCREASE_ENABLE; // 内存地址增量
    dma_tx_init.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; // 外设8位
    dma_tx_init.memory_width = DMA_MEMORY_WIDTH_8BIT; // 内存8位
    dma_tx_init.priority     = DMA_PRIORITY_MEDIUM; // 中等优先级
    dma_tx_init.number       = data_len; // 传输数据长度
    dma_tx_init.circular_mode= DMA_CIRCULAR_MODE_DISABLE; // 非循环模式
    dma_init(DMA1, DMA_CHANNEL_TX, &dma_tx_init);

    // 3. 配置DMA_RX(SPI0_DATA寄存器地址 ->  rx_buf,内存增量)
    dma_parameter_struct dma_rx_init;
    dma_deinit(DMA1, DMA_CHANNEL_RX);
    dma_struct_para_init(&dma_rx_init);
    dma_rx_init.periph_addr  = (uint32_t)&SPI_DATA(SPI0);
    dma_rx_init.memory_addr  = (uint32_t)rx_buf;
    dma_rx_init.direction    = DMA_PERIPH_TO_MEMORY; // 外设到内存
    dma_rx_init.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_rx_init.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_rx_init.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_rx_init.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_rx_init.priority     = DMA_PRIORITY_MEDIUM;
    dma_rx_init.number       = data_len;
    dma_rx_init.circular_mode= DMA_CIRCULAR_MODE_DISABLE;
    dma_init(DMA1, DMA_CHANNEL_RX, &dma_rx_init);

    // 4. 使能SPI DMA请求(TX和RX)
    spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
    spi_dma_enable(SPI0, SPI_DMA_RECEIVE);
}

// SPI DMA 收发启动函数(阻塞等待完成)
void spi0_dma_send_recv(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t data_len) {
    // 1. 初始化DMA(每次传输前重新配置长度,避免残留)
    dma_spi0_init(tx_buf, rx_buf, data_len);

    // 2. 拉低NSS,使能DMA通道
    gpio_bit_reset(SPI_NSS_GPIO_PORT, SPI_NSS_GPIO_PIN);
    dma_channel_enable(DMA1, DMA_CHANNEL_TX);
    dma_channel_enable(DMA1, DMA_CHANNEL_RX);

    // 3. 等待DMA收发完成(TX和RX都完成才退出)
    while(dma_flag_get(DMA1, DMA_CHANNEL_TX, DMA_FLAG_FTF) == RESET);
    while(dma_flag_get(DMA1, DMA_CHANNEL_RX, DMA_FLAG_FTF) == RESET);

    // 4. 关闭DMA,拉高NSS,清除标志
    dma_channel_disable(DMA1, DMA_CHANNEL_TX);
    dma_channel_disable(DMA1, DMA_CHANNEL_RX);
    dma_flag_clear(DMA1, DMA_CHANNEL_TX, DMA_FLAG_FTF);
    dma_flag_clear(DMA1, DMA_CHANNEL_RX, DMA_FLAG_FTF);
    gpio_bit_set(SPI_NSS_GPIO_PORT, SPI_NSS_GPIO_PIN);
}

// 主函数调用示例
int main(void) {
    uint8_t tx_data[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
    uint8_t rx_data[5] = {0};

    spi0_init(); // 先初始化SPI

    // 1. 不使用DMA:单字节收发
    uint8_t recv_byte = spi0_send_recv_byte(0x50);

    // 2. 不使用DMA:多字节发送
    spi0_send_multi_byte(tx_data, 5);

    // 3. 使用DMA:同步收发
    spi0_dma_send_recv(tx_data, rx_data, 5);

    while(1) {
        // 循环处理
    }
}

您需要登录后才可以回帖 登录 | 注册

本版积分规则

103

主题

1290

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部