STM32L073+USART_DMA范例讲解 由于以前学习、开发STM32程序时,都是利用STM32的标准库来开发程序的。得到STM32L073学习板后,就去STM32官网查找STM32L0系列的库文件;找了半天发现STML0系列没有标准库而只要HAL库来,所以今天就利用HAL库来写篇基于STML073利用USART1+DMA和USART4+DMA串口通信(实现MODBUS协议和串口控制LED). 1、 平台: 程序库版本:STM32L0系列1.4.0版本HAL库; 软件平台:KEIL 5.14 调试工具:JLINK V9 2、 新建工程如下图所示: 3、移植代码 参考stm32cubel0\STM32Cube_FW_L0_V1.4.0\Projects\STM32L073RZ-Nucleo下的UART串口通信范例移植代码如下所示: externDMA_HandleTypeDef lusHdma_Uart1_tx; externDMA_HandleTypeDef lusHdma_Uart1_rx; externDMA_HandleTypeDef lusHdma_Uart4_tx; externDMA_HandleTypeDef lusHdma_Uart4_rx; voidHAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_InitStruct; if(huart->Instance == USART1) { /*##-2- Configure peripheralGPIO ##########################################*/ /* UART TX and RX GPIO pinconfiguration */ GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate =GPIO_AF4_USART1; HAL_GPIO_Init(GPIOA,&GPIO_InitStruct); /* UART RX GPIO pinconfiguration */ GPIO_InitStruct.Pin =GPIO_PIN_10; GPIO_InitStruct.Alternate =GPIO_AF4_USART1; HAL_GPIO_Init(GPIOA,&GPIO_InitStruct); /*##-3- Configure the DMA##################################################*/ /* Configure the DMA handlerfor Transmission process */ lusHdma_Uart1_tx.Instance = DMA1_Channel4; lusHdma_Uart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; lusHdma_Uart1_tx.Init.PeriphInc = DMA_PINC_DISABLE; lusHdma_Uart1_tx.Init.MemInc = DMA_MINC_ENABLE; lusHdma_Uart1_tx.Init.PeriphDataAlignment= DMA_PDATAALIGN_BYTE; lusHdma_Uart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; lusHdma_Uart1_tx.Init.Mode = DMA_NORMAL; lusHdma_Uart1_tx.Init.Priority = DMA_PRIORITY_LOW; lusHdma_Uart1_tx.Init.Request = DMA_REQUEST_3; HAL_DMA_Init(&lusHdma_Uart1_tx); /* Associate the initializedDMA handle to the UART handle */ __HAL_LINKDMA(huart, hdmatx,lusHdma_Uart1_tx); /* Configure the DMA handlerfor reception process */ lusHdma_Uart1_rx.Instance = DMA1_Channel5; lusHdma_Uart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; lusHdma_Uart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; lusHdma_Uart1_rx.Init.MemInc = DMA_MINC_ENABLE; lusHdma_Uart1_rx.Init.PeriphDataAlignment= DMA_PDATAALIGN_BYTE; lusHdma_Uart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; lusHdma_Uart1_rx.Init.Mode = DMA_NORMAL; lusHdma_Uart1_rx.Init.Priority = DMA_PRIORITY_HIGH; lusHdma_Uart1_rx.Init.Request = DMA_REQUEST_3; HAL_DMA_Init(&lusHdma_Uart1_rx); /* Associate the initializedDMA handle to the the UART handle */ __HAL_LINKDMA(huart, hdmarx,lusHdma_Uart1_rx); } else if(huart->Instance == USART4) { /*##-2- Configure peripheralGPIO ##########################################*/ /* UART TX and RX GPIO pinconfiguration */ GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull =GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate =GPIO_AF6_USART4; HAL_GPIO_Init(GPIOC,&GPIO_InitStruct); /*##-3- Configure the DMA##################################################*/ /* Configure the DMA handlerfor Transmission process */ lusHdma_Uart4_tx.Instance = DMA1_Channel7; lusHdma_Uart4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; lusHdma_Uart4_tx.Init.PeriphInc = DMA_PINC_DISABLE; lusHdma_Uart4_tx.Init.MemInc = DMA_MINC_ENABLE; lusHdma_Uart4_tx.Init.PeriphDataAlignment= DMA_PDATAALIGN_BYTE; lusHdma_Uart4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; lusHdma_Uart4_tx.Init.Mode = DMA_NORMAL; lusHdma_Uart4_tx.Init.Priority = DMA_PRIORITY_LOW; lusHdma_Uart4_tx.Init.Request = DMA_REQUEST_12; HAL_DMA_Init(&lusHdma_Uart4_tx); /* Associate the initializedDMA handle to the UART handle */ __HAL_LINKDMA(huart, hdmatx, lusHdma_Uart4_tx); /* Configure the DMA handlerfor reception process */ lusHdma_Uart4_rx.Instance = DMA1_Channel6; lusHdma_Uart4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; lusHdma_Uart4_rx.Init.PeriphInc = DMA_PINC_DISABLE; lusHdma_Uart4_rx.Init.MemInc = DMA_MINC_ENABLE; lusHdma_Uart4_rx.Init.PeriphDataAlignment= DMA_PDATAALIGN_BYTE; lusHdma_Uart4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; lusHdma_Uart4_rx.Init.Mode = DMA_NORMAL; lusHdma_Uart4_rx.Init.Priority = DMA_PRIORITY_HIGH; lusHdma_Uart4_rx.Init.Request = DMA_REQUEST_12; HAL_DMA_Init(&lusHdma_Uart4_rx); /* Associate the initializedDMA handle to the the UART handle */ __HAL_LINKDMA(huart, hdmarx,lusHdma_Uart4_rx); } } voidHAL_UART_MspDeInit(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { /*##-1- Reset peripherals##################################################*/ __HAL_RCC_USART1_FORCE_RESET(); __HAL_RCC_USART1_RELEASE_RESET(); /*##-2- Disable peripheralsand GPIO Clocks #################################*/ /* Configure USARTx Tx asalternate function */ HAL_GPIO_DeInit(GPIOA,GPIO_PIN_9); /* Configure USARTx Rx asalternate function */ HAL_GPIO_DeInit(GPIOA,GPIO_PIN_10); /*##-3- Disable the DMA#####################################################*/ /* De-Initialize the DMAchannel associated to reception process */ if(huart->hdmarx != 0) { HAL_DMA_DeInit(huart->hdmarx); } /* De-Initialize the DMAchannel associated to transmission process */ if(huart->hdmatx != 0) { HAL_DMA_DeInit(huart->hdmatx); } } else if(huart->Instance == USART4) { /*##-1- Reset peripherals##################################################*/ __HAL_RCC_USART4_FORCE_RESET(); __HAL_RCC_USART4_RELEASE_RESET(); /*##-2- Disable peripheralsand GPIO Clocks #################################*/ /* Configure USARTx Tx asalternate function */ HAL_GPIO_DeInit(GPIOC, GPIO_PIN_10); /* Configure USARTx Rx asalternate function */ HAL_GPIO_DeInit(GPIOC,GPIO_PIN_11); /*##-3- Disable the DMA#####################################################*/ /* De-Initialize the DMAchannel associated to reception process */ if(huart->hdmarx != 0) { HAL_DMA_DeInit(huart->hdmarx); } /* De-Initialize the DMAchannel associated to transmission process */ if(huart->hdmatx != 0) { HAL_DMA_DeInit(huart->hdmatx); } } } 其中 A、USART1引脚为GPIOA的GPIO_PIN_9和GPIO_PIN_10,DMA为DMA1_Channle4和DMA1_Channel5通道; B、USART2引脚为GPIOC的GPIO_PIN_10和GPIO_PIN_11,DMA为DMA1_Channel6和DMA1_Channel7通道; 4、 串口配置初始化 A、 使能GPIOA和GPIOC时钟; B、 使能DMA1、USART1和USART2时钟; C、 设置DMA1、USART1和USART2中断; D、 配置USART1和USART2串口; 5、 配置DMA+USART1和DMA+USART2串口接收 HAL_UART_Receive_DMA(&lusUart1Handle,(uint8_t *)aRxBuffer, MAX_BUF_SIZE); HAL_UART_Receive_DMA(&lusUart4Handle,(uint8_t *)bRxBuffer, MAX_BUF_SIZE); 6、 测试此函数功能时,发现DMA必须接收到MAX_BUF_SIZE数据后,才能触发调用HAL_UART_RxCpltCallback函数;为了实现发送任意长度的串口数据都能触发调用HAL_UART_RxCpltCallback函数,需要修改stm32l0xx_hal_usart.c库文件, 7、修改内容如下所示: A、添加USART的IDLE空闲中断 在stm32l0xx_hal_usart.c文件内查找到HAL_UART_Receive_DMA函数,添加开启串口UART_IT_IDLE空闲中断,如下所示 HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart,uint8_t *pData, uint16_t Size) { uint32_t *tmp; if((huart->State ==HAL_UART_STATE_READY) || (huart->State == HAL_UART_STATE_BUSY_TX)) { if((pData == NULL ) ||(Size == 0)) { return HAL_ERROR; } /* Process Locked */ __HAL_LOCK(huart); huart->pRxBuffPtr =pData; huart->RxXferSize =Size; huart->ErrorCode =HAL_UART_ERROR_NONE; /* Check if a transmitprocess is ongoing or not */ if(huart->State ==HAL_UART_STATE_BUSY_TX) { huart->State =HAL_UART_STATE_BUSY_TX_RX; } else { huart->State =HAL_UART_STATE_BUSY_RX; } /* Set the UART DMAtransfert complete callback */ huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; /* Set the UART DMA Halftransfer complete callback */ huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt; /* Set the DMA errorcallback */ huart->hdmarx->XferErrorCallback = UART_DMAError; /* Enable the DMA Stream*/ tmp =(uint32_t*)&pData; HAL_DMA_Start_IT(huart->hdmarx,(uint32_t)&huart->Instance->RDR, *(uint32_t*)tmp, Size); /* Enable the DMA transferfor the receiver request by setting the DMAR bit in the UART CR3register */ huart->Instance->CR3 |= USART_CR3_DMAR; /* Process Unlocked */ __HAL_UNLOCK(huart); //modify by kl.yao 2016/03/04 /* Enable the UART IDLEInterrupt */ __HAL_UART_ENABLE_IT(huart,UART_IT_IDLE); return HAL_OK; } else { return HAL_BUSY; } } B、添加串口IDLE空闲中断处理 在stm32l0xx_hal_usart.c文件内查找到HAL_UART_IRQHandler函数,添加串口IDLE空闲中断处理,如下所示: void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) { /* UART parity errorinterrupt occurred ------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_PE) != RESET) && (__HAL_UART_GET_IT_SOURCE(huart, UART_IT_PE)!= RESET)) { __HAL_UART_CLEAR_IT(huart,UART_CLEAR_PEF); huart->ErrorCode |=HAL_UART_ERROR_PE; /* Set the UART stateready to be able to start again the process */ huart->State =HAL_UART_STATE_READY; } /* UART frame errorinterrupt occured --------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_FE) != RESET) && (__HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR)!= RESET)) { __HAL_UART_CLEAR_IT(huart,UART_CLEAR_FEF); huart->ErrorCode |=HAL_UART_ERROR_FE; /* Set the UART stateready to be able to start again the process */ huart->State =HAL_UART_STATE_READY; } /* UART noise errorinterrupt occured --------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_NE) != RESET) && (__HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR)!= RESET)) { __HAL_UART_CLEAR_IT(huart,UART_CLEAR_NEF); huart->ErrorCode |=HAL_UART_ERROR_NE; /* Set the UART stateready to be able to start again the process */ huart->State =HAL_UART_STATE_READY; } /* UART Over-Run interruptoccured -----------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_ORE) != RESET) && (__HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR)!= RESET)) { __HAL_UART_CLEAR_IT(huart,UART_CLEAR_OREF); huart->ErrorCode |=HAL_UART_ERROR_ORE; /* Set the UART stateready to be able to start again the process */ huart->State =HAL_UART_STATE_READY; } /* Call UART Error Callback function if need be --------------------------*/ if(huart->ErrorCode !=HAL_UART_ERROR_NONE) { HAL_UART_ErrorCallback(huart); } /* UART Wake Up interruptoccured ------------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_WUF) != RESET) && (__HAL_UART_GET_IT_SOURCE(huart, UART_IT_WUF)!= RESET)) { __HAL_UART_CLEAR_IT(huart,UART_CLEAR_WUF); /* Set the UART stateready to be able to start again the process */ huart->State =HAL_UART_STATE_READY; HAL_UARTEx_WakeupCallback(huart); } /* UART in mode Receiver---------------------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_RXNE) != RESET) && (__HAL_UART_GET_IT_SOURCE(huart,UART_IT_RXNE) != RESET)) { UART_Receive_IT(huart); } /* UART in mode Transmitter------------------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_TXE) != RESET) &&(__HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE)!= RESET)) { UART_Transmit_IT(huart); } /* UART in mode Transmitter-- TC ------------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_TC) != RESET) &&(__HAL_UART_GET_IT_SOURCE(huart, UART_IT_TC) !=RESET)) { UART_EndTransmit_IT(huart); } /* UART in mode IDLE-------------------------------------------------------*/ if((__HAL_UART_GET_IT(huart,UART_IT_IDLE) != RESET) &&(__HAL_UART_GET_IT_SOURCE(huart,UART_IT_IDLE) != RESET)) { /* Disable the UART TransmitComplete Interrupt */ __HAL_UART_CLEAR_IT(huart,UART_CLEAR_IDLEF); UART_EndReceive_IT(huart); } } 8、 串口通信处理任务描述 voidapp_usart_task(void) { /*USART1*/ if(Inverter1_Slave_QueueStack.QueueIsEmpty(&Inverter1_Slave_QueueStack.Queue)== 0) { Inverter1_Slave_QueueStack.QueueOut(&Inverter1_Slave_QueueStack.Queue,lubRx1Buf, &luwRx1Len); //从缓存区读取数据 //接收判断与处理 Inverter_recv_data_analysis(lubRx1Buf,&luwRx1Len); //数据发送 usart_tran_data_inverter(&lusUart1Handle,lubRx1Buf, luwRx1Len); } /*USART4*/ if(Inverter2_Slave_QueueStack.QueueIsEmpty(&Inverter2_Slave_QueueStack.Queue)== 0) { Inverter2_Slave_QueueStack.QueueOut(&Inverter2_Slave_QueueStack.Queue,lubRx4Buf, &luwRx4Len); //从缓存区读取数据 //解析数据包 sAnalyProtocal(lubRx4Buf); memset(lubRx4Buf, 0x00,sizeof(lubRx4Buf)); } } voidHAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) { if(UartHandle->Instance == USART1) { usart_recv_data_inverter(UartHandle,aRxBuffer, UartHandle->RxXferSize -UartHandle->hdmarx->Instance->CNDTR); UartHandle->hdmarx->Lock= HAL_UNLOCKED; HAL_UART_Receive_DMA(UartHandle,(uint8_t *)aRxBuffer, MAX_BUF_SIZE); } else if(UartHandle->Instance ==USART4) { usart_recv_data_inverter(UartHandle,bRxBuffer, UartHandle->RxXferSize -UartHandle->hdmarx->Instance->CNDTR); UartHandle->hdmarx->Lock= HAL_UNLOCKED; HAL_UART_Receive_DMA(UartHandle,(uint8_t *)bRxBuffer, MAX_BUF_SIZE); } } 描述: A、 串口进入IDLE空闲中断后,触发调用HAL_UART_RxCpltCallback函数,然后根据串口号进行数据保存(数据保存到FIFO); B、 通信任务函数时刻检测判断FIFO内是否有数据,然后读取FIFO内数据进行处理; C、 USART1+DMA用来进行MODBUS通信; D、 USART2+DMA用来通信控制LED亮灭; 9、最后上整个工程文件(包含全部源代码)
|