【Mini-F5265-OB】4、串口不定长收发-电子产品世界论坛
在上一篇中,我分享了如何使用串口接收中断+空闲中断来实现不定长数据的收发。虽然做到了高效的数据收发,但是会频繁进入中断,那么这一篇我将采用数据搬动工DMA来实现高效的串口不定长接收。
【开发环境】
灵动微【Mini-F5265-OB】开发板。
【灵动微DMA简介】
Mini-F5265-OB所使用的主控MM32F5265,内置 2 个 8 路通用 DMA 可以管理存储器到存储器、设备到存储器和存储器到设备 的数据传输;DMA 控制器支持环形缓冲区的管理,避免了控制器传输到达缓冲区结尾时所 产生的中断。 每个通道都有专门的硬件 DMA 请求逻辑,同时可以由软件触发每个通道;传输的长度、 传输的源地址和目标地址都可以通过软件单独设置。
【DMA通道选择】
通过用户手,我这里使用uart3的DMA通道为DMA1的通道2、通道3。


【程序设计】
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接收。
【实验现象】
我要赚赏金
