sonicfirr 发表于 2022-3-12 09:26

六、USART1发送和接收的DMA实现

<p>在下的GD32L233C-START专题测评:</p>

<p>一、开箱评测&nbsp;<a href="https://bbs.eeworld.com.cn/thread-1192788-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1192788-1-1.html</a></p>

<p>二、GD32L233C-START环境搭建<a href="https://bbs.eeworld.com.cn/thread-1193053-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1193053-1-1.html</a></p>

<p>三、点灯案例与扩展<a href="https://bbs.eeworld.com.cn/thread-1193183-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1193183-1-1.html</a></p>

<p>四、Demo工程的分析<a href="https://bbs.eeworld.com.cn/thread-1193279-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1193279-1-1.html</a></p>

<p>五、USART0的printf实现和DMA实现<a href="https://bbs.eeworld.com.cn/thread-1193517-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1193517-1-1.html</a></p>

<hr />
<p>开学后,本人变得忙碌了,到了周末再发一篇,继续研究串口DMA,这回拿USART1来实现,并为后面连接ESP8266&nbsp;AT模块做好准备。</p>

<p>1、案例思路</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;本例以GD32L233C-START之USART1口的发送、接收功能实现,外加DMA方式作为目标,在工程中添加了Bsp目录,并添加自己的串口驱动文件&ldquo;aita_gd32l233_usart.c&rdquo;和&ldquo;aita_gd32l233_usart.h&rdquo;(当然,这是源于个人习惯,写在main.c里也一样)。</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;头文件中,增加宏定义,用于条件编译,控制DMA发送和DMA接收的开启。</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;源文件中,因为是DMA方式,定义了发送和接收缓存区,接收缓冲区索引量,发送中标志量。另外,为了在多个函数中可以配置DMA,定义了全局的&ldquo;dma_parameter_struct(DMA参数配置)&rdquo;结构体变量。</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;USART1初始化与DMA初始化分别写成两个函数,由宏控制USART1初始化函数对DMA初始化函数的调用。发送功能函数也有宏来控制,分为开启DMA方式和不开启方式。DMA发送中判定标志量,以防止调用冲突。</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;gd32l23x_it.c中,增加USART1中断函数和DMA通道2(用于发送)及通道3(用于接收)中断函数。USART1_IRQHandler()中,启用DMA时进行&ldquo;空闲中断&rdquo;检测以保证有接收即反应(否则是DMA缓存区收满才有反应),不启用时进行&ldquo;接收中断&rdquo;检测以实现收到字节即存入缓冲区。</p>

<p>2、代码实现</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;&ldquo;..\GD32L233C-START\GD32L233C_START_Demo_Suites\Projects\AITA_GD32L233C\Bsp\inc\aita_gd32l233_usart.h&rdquo;(本人建立的路径)代码:</p>

<pre>
<code>#ifndef _AITA_USART_H
#define _AITA_USART_H

//控制DMA是否启用
#define AITA_DMA_U1Tx_EN          1//开启UART1口的DMA发送
#define AITA_DMA_U1Rx_EN          1//开启UART1口的DMA接收
//DMA USART1 发送和接收寄存器地址
#define USART1_RDATA_ADDRESS      (&amp;USART_RDATA(USART1))
#define USART1_TDATA_ADDRESS      (&amp;USART_TDATA(USART1))
//USART1接收和发送缓存长度
#define USART1_BUF_LENGTH         128

#endif</code></pre>

<p>&nbsp;</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&ldquo;..\GD32L233C-START\GD32L233C_START_Demo_Suites\Projects\AITA_GD32L233C\Bsp\inc\aita_gd32l233_usart.c&rdquo;(本人建立的路径)代码:</p>

<pre>
<code>#include "aita_sys.h"

//DMA USART1 用到的内存缓存
uint8_t u1txbuffer;
uint8_t u1rxbuffer;
//接收缓存索引
uint16_t u1rxindex = 0;
//发送中标志(用于DMA发送)
uint8_t u1txdoing = 0;
//全局DMA参数配置结构体变量
dma_parameter_struct dma_init_struct;

void aita_InitU1(void)
{
    /* enable COM GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART1);

    /* connect port to USART TX */
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_2);
    /* connect port to USART RX */
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_3);

    /* configure USART TX as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);

    /* configure USART RX as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_3);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_3);

    /* USART configure */
    usart_deinit(USART1);
    usart_word_length_set(USART1, USART_WL_8BIT);
    usart_stop_bit_set(USART1, USART_STB_1BIT);
    usart_parity_config(USART1, USART_PM_NONE);
    usart_baudrate_set(USART1, 115200U);
    usart_receive_config(USART1, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);

    nvic_irq_enable(USART1_IRQn, 0);
    //启用Usart1 Rx DMA则开启空闲中断,否则开启接收中断
#if !AITA_DMA_U1Rx_EN
    usart_interrupt_enable(USART1, USART_INT_RBNE);
#else
    usart_interrupt_enable(USART1, USART_INT_IDLE);
#endif
    usart_enable(USART1);
                aita_InitDMAForU1();
}

void aita_InitDMAForU1(void) {
#if AITA_DMA_U1Tx_EN || AITA_DMA_U1Rx_EN
                /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA);
#endif
   
#if AITA_DMA_U1Tx_EN
    /*configure DMA interrupt*/
    nvic_irq_enable(DMA_Channel2_IRQn, 0); //DMA CH2 for Uart1 Tx

    /* initialize DMA channel 2 */
    dma_deinit(DMA_CH2);
    dma_struct_para_init(&amp;dma_init_struct);
    dma_init_struct.request      = DMA_REQUEST_USART1_TX;
    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr= (uint32_t)u1txbuffer;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number       = ARRAYNUM(u1txbuffer);
    dma_init_struct.periph_addr= (uint32_t)USART1_TDATA_ADDRESS;
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority   = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH2, &amp;dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(DMA_CH2);
    dma_memory_to_memory_disable(DMA_CH2);
    /* disable the DMAMUX_MUXCH2 synchronization mode */
    dmamux_synchronization_disable(DMAMUX_MUXCH2);
    /* USART DMA enable for transmission and reception */
    usart_dma_transmit_config(USART1, USART_DENT_ENABLE);
    /* enable DMA channel 2 transfer complete interrupt */
    dma_interrupt_enable(DMA_CH2, DMA_INT_FTF);
    /* disable DMA channel 2,每次发送时临时开启DMA */
    dma_channel_disable(DMA_CH2);
#endif

#if AITA_DMA_U1Rx_EN
    /*configure DMA interrupt*/
    nvic_irq_enable(DMA_Channel3_IRQn, 0); //DMA CH3 for Uart1 Rx

    /* initialize DMA channel 3 */
    dma_deinit(DMA_CH3);
    dma_struct_para_init(&amp;dma_init_struct);
    dma_init_struct.request      = DMA_REQUEST_USART1_RX;
    dma_init_struct.direction    = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr= (uint32_t)u1rxbuffer;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number       = ARRAYNUM(u1rxbuffer);
    dma_init_struct.periph_addr= (uint32_t)USART1_RDATA_ADDRESS;
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority   = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH3, &amp;dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(DMA_CH3);
    dma_memory_to_memory_disable(DMA_CH3);
    /* disable the DMAMUX_MUXCH3 synchronization mode */
    dmamux_synchronization_disable(DMAMUX_MUXCH3);
    /* USART DMA enable for reception */
    usart_dma_receive_config(USART1, USART_DENR_ENABLE);
    /* enable DMA channel 3 transfer complete interrupt */
    dma_interrupt_enable(DMA_CH3, DMA_INT_FTF);
    /* enable DMA channel 3, 接收是被动的所以DMA要一直开启 */
    dma_channel_enable(DMA_CH3);
#endif
}

void aita_U1SendStr(uint8_t *arr, uint16_t len) {
#if AITA_DMA_U1Tx_EN //DMA发送模式,加载缓冲区,开启通道
while(u1txdoing);
memcpy(u1txbuffer, arr, len);
aita_U1DMASend(len);
#else                //非DMA模式,程序查询,操作USART1_DR
uint16_t i=0;
while(i &lt; len) {
    usart_data_transmit(USART1, arr);
    while(RESET == usart_flag_get(USART1, USART_FLAG_TBE));
}
#endif
}

void aita_ClearU1RxBuffer(void) {
memset(u1rxbuffer, 0, USART1_BUF_LENGTH);
u1rxindex = 0;
}

#if AITA_DMA_U1Tx_EN
void aita_U1DMASend(uint16_t size) {
u1txdoing = 1;
dma_transfer_number_config(DMA_CH2, size);
dma_channel_enable(DMA_CH2);
}
#endif
</code></pre>

<p>&nbsp;</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;也是建立的系统头文件aita_sys.h中,新增(主要是变量声明)代码:</p>

<pre>
<code>#include "aita_gd32l233_usart.h"

extern uint8_t rx_buffer;
extern uint32_t nbr_data_to_read;
extern volatile uint32_t rx_counter;
//DMA USART1 用到的内存缓存
extern uint8_t u1txbuffer;
extern uint8_t u1rxbuffer;
extern uint16_t u1rxindex;
extern uint8_t u1txdoing;
extern dma_parameter_struct dma_init_struct;</code></pre>

<p>&nbsp;</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;gd32l23x_it.c中建立的中断函数代码:</p>

<pre>
<code>void USART1_IRQHandler(void)
{
#if !AITA_DMA_U1Rx_EN
    //未启用U1接收DMA,则采用接收中断
    if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) {
      //如果缓冲区填满,则清空
      if(u1rxindex &gt; USART1_BUF_LENGTH) {
          u1rxindex = 0;
          memset(u1rxbuffer, 0, USART1_BUF_LENGTH);
      }
      /* read one byte from the receive data register */
      u1rxbuffer = (uint8_t)usart_data_receive(USART1);
    }
#else
if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) {
    usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE);
    dma_channel_disable(DMA_CH3); //串口空闲后,关闭接收DMA防止干扰
    u1rxindex = USART1_BUF_LENGTH
          - dma_transfer_number_get(DMA_CH3);//获取接收到的字节数   

    dma_transfer_number_config(DMA_CH3, USART1_BUF_LENGTH);
    dma_channel_enable(DMA_CH3);//再开启接收DMA
}
#endif
}

#if AITA_DMA_U1Tx_EN
void DMA_Channel2_IRQHandler(void)
{
    if(RESET != dma_interrupt_flag_get(DMA_CH2, DMA_INT_FLAG_FTF)){
      dma_interrupt_flag_clear(DMA_CH2, DMA_INT_FLAG_G);
      dma_channel_disable(DMA_CH2);
      u1txdoing = 0;
    }
}
#endif

#if AITA_DMA_U1Rx_EN
//Usart1 Rx DMA ISR(应当永不触发)
void DMA_Channel3_IRQHandler(void)
{
    if(RESET != dma_interrupt_flag_get(DMA_CH3, DMA_INT_FLAG_FTF)){
      dma_interrupt_flag_clear(DMA_CH3, DMA_INT_FLAG_G);
      dma_channel_disable(DMA_CH3);
      dma_transfer_number_config(DMA_CH3, USART1_BUF_LENGTH);
      dma_channel_enable(DMA_CH3);
    }
}
#endif</code></pre>

<p>&nbsp;</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;最后是main.c中&nbsp;编写的测试函数(这个很简单,仅是收发测试):</p>

<pre>
<code>void aita_TestU1(void) {
while(1) {
    aita_U1SendStr("hello from usart1 in dma\r\n", sizeof("hello from usart1 in dma\r\n"));
                if(u1rxindex) {
      aita_U1SendStr("Rsv: ", sizeof("Rsv: "));
      aita_U1SendStr(u1rxbuffer, u1rxindex);
      aita_U1SendStr("\r\n", sizeof("\r\n"));
      aita_ClearU1RxBuffer();
    }
    delay_1ms(1000);               
}
}</code></pre>

<p>&nbsp;</p>

罂粟花开何时醉 发表于 2023-5-17 02:22

<p>你好 ,能上传测试结果吗 ?<br />
我参考了你的测试没反应呢。</p>

sonicfirr 发表于 2023-5-18 16:52

罂粟花开何时醉 发表于 2023-5-17 02:22
你好 ,能上传测试结果吗 ?
我参考了你的测试没反应呢。

<p>很久前做的了,现在不太方便复现了。不要意思。</p>
页: [1]
查看完整版本: 六、USART1发送和接收的DMA实现