GD32 SPI 查询方式和DMA方式在全双模式下效率区别

最近在使用SPI的时候,遇到了一些数据传输效率问题,在此记录自己学习过程。SPI的基础知识这里就不在讲述了,直接分析SPI查询方式和DMA方式的效率问题。这里使用的芯片是GD32F303CC。

SPI以查询方式进行全双工通信

1.查询手册,SPI0挂载在APB2总线下,最高频率120MHZ。

2.接下来对SPI0进行配置

以下只是简化的配置内容,可以看出SPI0的CLK速率是3.75MHZ,还有一个注意的点MISO配置的是输入模式,而不是输出。

// SPI0 主机模式 使用软件SPI片选信号
/* SPI0 GPIO config:NSS/PA3, SCK/PA5, MISO/PA6, MOSI/PA7 */
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6); // MOSI 浮空输入
/* PA3 as NSS */
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3);

/* SPI0 parameter config */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
spi_init_struct.nss = SPI_NSS_SOFT;     //软件片选
spi_init_struct.prescale = SPI_PSC_32;  //120M/32 = 3.75M
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init(SPI0, &spi_init_struct);

3.使用API函数进行数据的发送和接收,最后通过逻辑分析仪抓取时序图

发现SPI传输数据的过程中,SCK的时钟频率为3.703M符合我们设置的CLK频率,但是我们发现两个SPI的字节之间有一段SCK是空闲的,从逻辑分析仪上看时间高达1.95us,比SPI传输一个自字节的时间还长,在极大的影响了SPI的传输速率。其实这个空闲时间正是MCU在搬运SPI的数据。

到底是不是这么回事呢,我们提升SPI的SCK速率,来继续观察。

 这里我们把SPI的CLK速率提升到15Mhz,发现空闲状态的时间已经是传输数据的时间的好几倍了,且空闲状态的时间为1.87us(逻辑分析仪采样率不足导致),与1.95us接近。如果传输大量数据的话,使用查询的方式效率还是太低。

SPI以DMA进行全双工通信

1.查询SPI0的DMA相关信息 

DMA 功能在传输过程中将应用程序从数据读写过程中释放出来, 从而提高了系统效率。根据查询手册我们知道,SPI0的RX和TX分别挂载在DMA0的通道1和通道2下面。

2.接下来对SPI0进行配置

其他SPI的配置都和前面一致,只是新增了SPI DMA相关的配置。这里简短的贴上一部分代码:

void spi_dma_config(void)
{
    dma_parameter_struct dma_init_struct;

    /* SPI0 transmit dma config:DMA0-DMA_CH2  */
    dma_deinit(DMA0, DMA_CH2);
    dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0);
    dma_init_struct.memory_addr = (uint32_t)spi0_send_array;
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_LOW;
    dma_init_struct.number = arraysize;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init(DMA0, DMA_CH2, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH2);
    dma_memory_to_memory_disable(DMA0, DMA_CH2);

    /* SPI0 receive dma config:DMA0-DMA_CH1  */
    dma_deinit(DMA0, DMA_CH1);
    dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0);
    dma_init_struct.memory_addr = (uint32_t)spi0_receive_array;
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.priority = DMA_PRIORITY_HIGH;
    dma_init(DMA0, DMA_CH1, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH1);
    dma_memory_to_memory_disable(DMA0, DMA_CH1);

    /* SPI1 transmit dma config:DMA0,DMA_CH4  */
    dma_deinit(DMA0, DMA_CH4);
    dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI1);
    dma_init_struct.memory_addr = (uint32_t)spi1_send_array;
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
    dma_init(DMA0, DMA_CH4, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH4);
    dma_memory_to_memory_disable(DMA0, DMA_CH4);

    /* SPI1 receive dma config:DMA0,DMA_CH3  */
    dma_deinit(DMA0, DMA_CH3);
    dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI1);
    dma_init_struct.memory_addr = (uint32_t)spi1_receive_array;
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA0, DMA_CH3, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH3);
    dma_memory_to_memory_disable(DMA0, DMA_CH3);
}
3.使用API函数进行数据的发送和接收,最后通过逻辑分析仪抓取时序图

从逻辑分析仪上可以看出,SPI使用DMA发送不会再出现两个字节之间CLK空余时间,极大的提升了SPI的传输速率。

防伪记录:文章原创于yl浪迹天涯。

总结

对于一般数据几个字节的传输,还是建议使用查询式(阻塞式)SPI,因为传输更加容易,DMA方式适合于高频次大容量数据传输的场景。比如在使用SPI 读取W25Q128芯片ID或者一般的操作指令的时候,就没有必要使用DMA方式了,但是在一次读取几个页的数据的时候,建议还是使用DMA方式。

附带源码

参考源码来源于GD32官网的GD32F30x_Firmware_Library_Vx.x.x库里面的Examples,具体代码直接去GD32官网下载即可。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值