【GD32L233C-START评测】5、UART+DMA接收不定长数据
[复制链接]
本帖最后由 emmnn 于 2022-3-28 17:44 编辑
前言
在嵌入式开发领域,USART外设可以说是被应用最广的片上外设。从最基础的作为调试输入输出口使用外,USART也常被用于各种功能模块的交互接口。例如市面上常见的串口屏,各种WiFi、蓝牙等无线模组等。
在使用USART作为通讯接口应用过程中,我们经常会使用USART口接收外部数据帧解析并做进一步的接收处理。在这个过程中,如果USART数据帧不是特别长,并且交互次数不是很频繁的情况下,我们可以通过串口接收中断一个字节一个字节地做接收处理(放到代码缓存中),在接收到一帧数据后,给个标志位并主程序中做解析处理。但是在实际的应用中,我们遇到的,更多的是需要接收不定长数据,并且频繁交互的情况。今天,我们要介绍的,就是如何使用USART+DMA去接收一帧不定长的数据。
USART+DMA接收不定长数据
DMA,直接存储器访问控制器。同USART一样,这也是一种片上硬件外设。它的主要功能是能在CPU不参与的前提下进行数据的传输。关于DMA的介绍可以直接阅读用户手册第十章《直接存储器访问控制器》,这里也不再进行赘述。
对于采用DMA方式实现USART数据接收的配置,在手册上也能找到相关的配置流程图
除了以上配置外,我们还需要思考两个问题:
- 如何得知一帧数据是否传输完毕?
- 当一帧传输完成后,如何得知接收的字节数?
这里我们介绍下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接收不定长数据,本文就简单介绍到这里。工程源码见附件。若是上述有任何地方叙述错误,也欢迎大家留言指出,谢谢!
|