这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 【Mini-F5265-OB】实现DMA+空闲中断实现不定长数据收发

共1条 1/1 1 跳转至

【Mini-F5265-OB】实现DMA+空闲中断实现不定长数据收发

助工
2025-01-03 08:10:01     打赏

【Mini-F5265-OB】4、串口不定长收发-电子产品世界论坛

在上一篇中,我分享了如何使用串口接收中断+空闲中断来实现不定长数据的收发。虽然做到了高效的数据收发,但是会频繁进入中断,那么这一篇我将采用数据搬动工DMA来实现高效的串口不定长接收。

【开发环境】

灵动微【Mini-F5265-OB】开发板。

【灵动微DMA简介】

Mini-F5265-OB所使用的主控MM32F5265,内置 2 个 8 路通用 DMA 可以管理存储器到存储器、设备到存储器和存储器到设备 的数据传输;DMA 控制器支持环形缓冲区的管理,避免了控制器传输到达缓冲区结尾时所 产生的中断。 每个通道都有专门的硬件 DMA 请求逻辑,同时可以由软件触发每个通道;传输的长度、 传输的源地址和目标地址都可以通过软件单独设置。

【DMA通道选择】

通过用户手,我这里使用uart3的DMA通道为DMA1的通道2、通道3。

image.pngimage.png

【程序设计】

1、配置串口,开启DMA接收,并开启中断发送与接收。

2、配置串口,开启空闲中断,当进入空闲中断时,视为接收完一次完整的数据。通知数据可以进行处理。本篇这里直接使用串口DMA回传给串口。

【代码】

1、串口初始化:

/***********************************************************************************************************************
  * @brief  Initialize console for printf
  * @note   none
  * @param  Baudrate : UART3 communication baudrate
  * @retval none
  *********************************************************************************************************************/
void PLATFORM_InitConsole(uint32_t Baudrate)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    UART_InitTypeDef UART_InitStruct;
		NVIC_InitTypeDef NVIC_InitStruct;
	
    RCC_APB1PeriphClockCmd(DEBUG_UART_RCC, ENABLE);

    UART_StructInit(&UART_InitStruct);
    UART_InitStruct.BaudRate      = Baudrate;
    UART_InitStruct.WordLength    = UART_WordLength_8b;
    UART_InitStruct.StopBits      = UART_StopBits_1;
    UART_InitStruct.Parity        = UART_Parity_No;
    UART_InitStruct.HWFlowControl = UART_HWFlowControl_None;
    UART_InitStruct.Mode          = UART_Mode_Tx | UART_Mode_Rx;;
    UART_Init(DEBUG_UART, &UART_InitStruct);

    UART_DMACmd(UART3, UART_DMAReq_EN, ENABLE);          //开启DMA
	
    UART_ITConfig(DEBUG_UART, UART_IT_RXIDLE, ENABLE);   //开启空闲中断
	
    RCC_AHBPeriphClockCmd(DEBUG_UART_TX_GPIO_RCC, ENABLE);

		
    GPIO_PinAFConfig(DEBUG_UART_TX_PORT, DEBUG_UART_TX_PIN_SOURC, DEBUG_UART_GPIO_AF);
    GPIO_PinAFConfig(DEBUG_UART_RX_PORT, DEBUG_UART_RX_PIN_SOURC, DEBUG_UART_GPIO_AF);
	
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = DEBUG_UART_TX_PIN;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(DEBUG_UART_TX_PORT, &GPIO_InitStruct);
		
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin  = DEBUG_UART_RX_PIN;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(DEBUG_UART_RX_PORT, &GPIO_InitStruct);
			
    NVIC_InitStruct.NVIC_IRQChannel = DEBUG_UART_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);



    UART_ClearITPendingBit(UART3, 0x7FF);
    UART_Cmd(DEBUG_UART, ENABLE);		
		
}

在初始中,特别值得注意,就是需要开启DMA数据接收与发送。

2、串口DMA发送代码:

/***********************************************************************************************************************
  * @brief
  * @note   none
  * @param  none
  * @retval none
  *********************************************************************************************************************/
void UART_TxData_DMA_Interrupt(uint8_t *Buffer, uint8_t Length)
{
    DMA_InitTypeDef  DMA_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel2);

    DMA_StructInit(&DMA_InitStruct);
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(UART3->TDR);
    DMA_InitStruct.DMA_MemoryBaseAddr     = (uint32_t)Buffer;
    DMA_InitStruct.DMA_DIR                = DMA_DIR_PeripheralDST;
    DMA_InitStruct.DMA_BufferSize         = Length;
    DMA_InitStruct.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStruct.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode               = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority           = DMA_Priority_VeryHigh;
    DMA_InitStruct.DMA_M2M                = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_Reload        = DMA_Auto_Reload_Disable;
    DMA_Init(DMA1_Channel2, &DMA_InitStruct);

    DMA_ClearFlag(DMA1_FLAG_TC2);
    DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);

    NVIC_InitStruct.NVIC_IRQChannel = DMA1_CH2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    UART_TX_DMA_InterruptFlag = 0;

    DMA_Cmd(DMA1_Channel2, ENABLE);
}

这个函数中,我们将发送的缓冲区与长度传入,最后开启DMA传输。

3、DMA数据接收。

/***********************************************************************************************************************
  * @brief
  * @note   none
  * @param  none
  * @retval none
  *********************************************************************************************************************/
void UART_RxData_DMA_Interrupt(uint8_t *Buffer, uint8_t Length)
{
    DMA_InitTypeDef  DMA_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel3);

    DMA_StructInit(&DMA_InitStruct);
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(UART3->RDR);
    DMA_InitStruct.DMA_MemoryBaseAddr     = (uint32_t)Buffer;
    DMA_InitStruct.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStruct.DMA_BufferSize         = Length;
    DMA_InitStruct.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStruct.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode               = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority           = DMA_Priority_VeryHigh;
    DMA_InitStruct.DMA_M2M                = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_Reload        = DMA_Auto_Reload_Disable;
    DMA_Init(DMA1_Channel3, &DMA_InitStruct);

    DMA_ClearFlag(DMA1_FLAG_TC3);
    DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);

    NVIC_InitStruct.NVIC_IRQChannel = DMA1_CH3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    UART_RX_DMA_InterruptFlag = 0;

    DMA_Cmd(DMA1_Channel3, ENABLE);
}

这里我们传入接收到的缓冲区地址,使用外设到内存的方式。开启DMA空闲接收。

4、空闲中断回调函数。

/***********************************************************************************************************************
  * @brief  This function handles UART1 Handler
  * @note   none
  * @param  none
  * @retval none
  *********************************************************************************************************************/
void UART3_IRQHandler(void)
{

    if (SET == UART_GetITStatus(UART3, UART_IT_RXIDLE))
    {
        UART_ClearITPendingBit(UART3, UART_IT_RXIDLE);
        UART_RxLength = DMA_RX_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel3);;
    }
}

在空头接收中断中,我这里首先查询到接收缓冲区的剩余空间,计算出已接收到的字符数。通过UART_RxLength来传递已经收到的数。

5、空闲发送与接收代码:

/***********************************************************************************************************************
  * @brief  This function handles UART1 Handler
  * @note   none
  * @param  none
  * @retval none
  *********************************************************************************************************************/
void UART3_IRQHandler(void)
{

    if (SET == UART_GetITStatus(UART3, UART_IT_RXIDLE))
    {
        UART_ClearITPendingBit(UART3, UART_IT_RXIDLE);
        UART_RxLength = DMA_RX_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel3);;
    }
}


/***********************************************************************************************************************
  * @brief  This function handles DMA1_CH4 Handler
  * @note   none
  * @param  none
  * @retval none
  *********************************************************************************************************************/
void DMA1_CH2_IRQHandler(void)
{
    if (SET == DMA_GetITStatus(DMA1_IT_TC2))
    {
        DMA_Cmd(DMA1_Channel2, DISABLE);

        UART_TX_DMA_InterruptFlag = 1;

        DMA_ClearITPendingBit(DMA1_IT_TC2);
    }
}

在发送DMA中断,如果进入回调函数,说明发送已结束,清除中断,并更新发送完毕的参数。

在接收DMA中断回调中,我这里会设计比较长的接收buff,使他不会进入回调函数。

【测试代码】

/***********************************************************************************************************************
  * @brief  This function is main entrance
  * @note   main
  * @param  none
  * @retval none
  *********************************************************************************************************************/
int main(void)
{
    PLATFORM_Init();
		UART_RxLength = 0;
   // GPIO_LED_Toggle_Sample();

		UART_RxData_DMA_Interrupt(UART_RxBuffer,DMA_RX_MAX_LEN);
    while (1)
    {
			if (UART_RxLength)
			{
				
				UART_TxData_DMA_Interrupt((uint8_t *)UART_RxBuffer, UART_RxLength);
				while (0 == UART_TX_DMA_InterruptFlag)
            {
            }
				UART_RxLength = 0;
        UART_RxData_DMA_Interrupt((uint8_t *)UART_RxBuffer,DMA_RX_MAX_LEN);
			}
    }
}

在主程序中,我们判断接收的长度是否为零,如果不为零,则使用DMA回传给串口。

在发送结速之后,我们又重新开启DMA接收。

【实验现象】





关键词: Mini-F5265-OB     空闲     中断     DMA    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]