3554|1

35

帖子

1

TA的资源

一粒金砂(中级)

楼主
 

【GD32L233C-START评测】5、UART+DMA接收不定长数据 [复制链接]

本帖最后由 emmnn 于 2022-3-28 17:44 编辑

前言

在嵌入式开发领域,USART外设可以说是被应用最广的片上外设。从最基础的作为调试输入输出口使用外,USART也常被用于各种功能模块的交互接口。例如市面上常见的串口屏,各种WiFi、蓝牙等无线模组等。

 

在使用USART作为通讯接口应用过程中,我们经常会使用USART口接收外部数据帧解析并做进一步的接收处理。在这个过程中,如果USART数据帧不是特别长,并且交互次数不是很频繁的情况下,我们可以通过串口接收中断一个字节一个字节地做接收处理(放到代码缓存中),在接收到一帧数据后,给个标志位并主程序中做解析处理。但是在实际的应用中,我们遇到的,更多的是需要接收不定长数据,并且频繁交互的情况。今天,我们要介绍的,就是如何使用USART+DMA去接收一帧不定长的数据。


USART+DMA接收不定长数据

  • DMA是什么?

DMA,直接存储器访问控制器。同USART一样,这也是一种片上硬件外设。它的主要功能是能在CPU不参与的前提下进行数据的传输。关于DMA的介绍可以直接阅读用户手册第十章《直接存储器访问控制器》,这里也不再进行赘述。

对于采用DMA方式实现USART数据接收的配置,在手册上也能找到相关的配置流程图


  • USART+DMA的接收处理流程

除了以上配置外,我们还需要思考两个问题:

  • 如何得知一帧数据是否传输完毕?
  • 当一帧传输完成后,如何得知接收的字节数?

这里我们介绍下USART中的IDLE线检测中断和DMA中的通道计数寄存器。

IDLE线检测中断

也叫空闲中断。在开始接收串口数据后,当检测总线上出现空闲的时候,USART状态寄存器上的IDLEF标志位会被置位,同时会产生中断。利用此中断,我们可以获知一帧不定长的数据的传输是否完成。

DMA通道计数寄存器

关于此寄存器,手册上说明如下

在进行DMA的相关配置时,我们可以预先往这个寄存器写一个足够大的数,而在接收完每一帧数据包后,我们可以在IDLE中断中再去读这个寄存器的值,前后读这个寄存器值相减,就可以得到接收的这一帧数据包的字节数。

 

综上所述,USART+DMA的接收处理流程如下:

 


代码实现

首先是宏定义和一些全局变量的声明

#define WIFI_COM_PER                    (USART1)
#define WIFI_COM_BAUD                   (115200UL)
#define WIFI_USART_GPIO_PORT            (GPIOA)
#define WIFI_USART_TX_PIN               (GPIO_PIN_2)
#define WIFI_USART_RX_PIN               (GPIO_PIN_3)

#define WIFI_COM_CLK                    (RCU_USART1)
#define WIFI_COM_GPIO_CLK               (RCU_GPIOA)
#define WIFI_COM_AF                     (GPIO_AF_7)

#define WIFI_RXDMACH                    (DMA_CH1)
#define WIFI_TXDMACH                    (DMA_CH2)

#define USART1_RDATA_ADDRESS            (&USART_RDATA(USART1))
#define USART1_TDATA_ADDRESS            (&USART_TDATA(USART1))
#define ARRAYNUM(arr_nanme)             (uint32_t)(sizeof(arr_nanme) / sizeof(*(arr_nanme)))

#define USART_FIFO_LEN                  (256)

uint8_t g_ucRecvBuf_FIFO1[USART_FIFO_LEN] = {0};
uint16_t g_ucRecvCnt_FIFO1 = 0;

USART初始化

/**
 * @brief Uart Init
 * 
 */
void USART1Init(void)
{
    /* enable COM GPIO clock */
    rcu_periph_clock_enable(WIFI_COM_GPIO_CLK);

    /* enable USART clock */
    rcu_periph_clock_enable(WIFI_COM_CLK);

    /* usart deinit */
    usart_deinit(WIFI_COM_PER);

    /* USART interrupt configuration */
    nvic_irq_enable(USART1_IRQn, 0);

    /* enable USART1 receive interrupt */
    usart_interrupt_enable(WIFI_COM_PER, USART_INT_IDLE);

    /* connect port to USARTx_Tx */
    gpio_af_set(WIFI_USART_GPIO_PORT, WIFI_COM_AF, WIFI_USART_TX_PIN);

    /* connect port to USARTx_Rx */
    gpio_af_set(WIFI_USART_GPIO_PORT, WIFI_COM_AF, WIFI_USART_RX_PIN);

    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(WIFI_USART_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, WIFI_USART_TX_PIN);
    gpio_output_options_set(WIFI_USART_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, WIFI_USART_TX_PIN);

    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(WIFI_USART_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, WIFI_USART_RX_PIN);
    gpio_output_options_set(WIFI_USART_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, WIFI_USART_RX_PIN);

    /* USART configure */
    usart_baudrate_set(WIFI_COM_PER, WIFI_COM_BAUD);
    usart_receive_config(WIFI_COM_PER, USART_RECEIVE_ENABLE);
    usart_transmit_config(WIFI_COM_PER, USART_TRANSMIT_ENABLE);
    usart_enable(WIFI_COM_PER);

    /* DMA init */
    DMAInit();
}

DMA初始化

/**
 * @brief DMA INIT
 * 
 */
static void DMAInit(void)
{
    dma_parameter_struct dma_init_struct;
    /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA);

    /* initialize DMA channel 1 */
    dma_deinit(WIFI_RXDMACH);
    dma_struct_para_init(&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)g_ucRecvBuf_FIFO1;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number       = ARRAYNUM(g_ucRecvBuf_FIFO1);
    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(WIFI_RXDMACH, &dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(WIFI_RXDMACH);
    dma_memory_to_memory_disable(WIFI_RXDMACH);
    /* disable the DMAMUX_MUXCH1 synchronization mode */
    dmamux_synchronization_disable(DMAMUX_MUXCH1);
    /* USART DMA enable for reception */
    usart_dma_receive_config(WIFI_COM_PER, USART_DENR_ENABLE);
    /* enable DMA channel 1 */
    dma_channel_enable(WIFI_RXDMACH);
}

中断处理

/*!
    \brief      this function handles USART1 exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void USART1_IRQHandler(void)
{
    uint8_t i;
    if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) 
    {
        usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE);

        /* disable DMA channel */
        dma_channel_disable(WIFI_RXDMACH);
        
        /* read one byte from the receive data register */
        g_ucRecvCnt_FIFO1 = ARRAYNUM(g_ucRecvBuf_FIFO1) - dma_transfer_number_get(WIFI_RXDMACH);

        PRO_LOG(LOG_DEBUG, "g_ucRecvCnt_FIFO1: %d. \r\n", g_ucRecvCnt_FIFO1);

        for(i = 0; i < g_ucRecvCnt_FIFO1; i++)
        {
            PRO_LOG(LOG_DEBUG, "g_ucRecvBuf_FIFO1[%d]: 0x%x. \r\n", i, g_ucRecvBuf_FIFO1);
        }
        /* set DMA memory base address */
        dma_memory_address_config(WIFI_RXDMACH, (uint32_t)g_ucRecvBuf_FIFO1);

        /* set the number of remaining data to be transferred by the DMA */
        dma_transfer_number_config(WIFI_RXDMACH, ARRAYNUM(g_ucRecvBuf_FIFO1));

        /* enable DMA channel */
        dma_channel_enable(WIFI_RXDMACH);
    }
}

 板上测试结果如下

 


总结

关于如何使用USART+DMA接收不定长数据,本文就简单介绍到这里。工程源码见附件。若是上述有任何地方叙述错误,也欢迎大家留言指出,谢谢!

 

gd32l233_template.7z

626.64 KB, 下载次数: 34

此帖出自GD32 MCU论坛

最新回复

不定长确实不好处理,以前看过一篇帖子,各种临界因素要考虑。   详情 回复 发表于 2022-3-29 17:34
点赞 关注
 

回复
举报

7452

帖子

2

TA的资源

五彩晶圆(高级)

沙发
 

不定长确实不好处理,以前看过一篇帖子,各种临界因素要考虑。

此帖出自GD32 MCU论坛
 
个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表