Ich versuche, UART mit DMA zu verwenden,
Vor dem DMA habe ich die Polling-Methode mit while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);
Funktion ausprobiert und konnte erfolgreich kommunizieren.
Wenn ich die verwende
while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);
Funktion, Daten werden nicht korrekt gesendet, nur das erste Byte ist korrekt, andere Bytes sind falsch. Warum ist das passiert? Wie kann ich dieses Problem lösen?
Dies ist meine Initialisierungssequenz:
MX_DMA_Init();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
Die folgenden Zeilen versuchen, Daten zu senden:
while(1U)
{
// Communication Test Block //
while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);
// while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);
}
Dies sind die Initialisierungsfunktionen:
..
else if(huart->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspInit 0 */
/* USER CODE END USART2_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**USART2 GPIO Configuration
PD5 ------> USART2_TX
PD6 ------> USART2_RX
*/
GPIO_InitStruct.Pin = USART2_RS485_TX_Pin|USART2_RS485_RX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* USART2 DMA Init */
/* USART2_RX Init */
hdma_usart2_rx.Instance = DMA1_Stream5;
hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(huart,hdmarx,hdma_usart2_rx);
/* USART2_TX Init */
hdma_usart2_tx.Instance = DMA1_Stream6;
hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4;
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_usart2_tx.Init.Mode = DMA_NORMAL;
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(huart,hdmatx,hdma_usart2_tx);
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspInit 1 */
/* USER CODE END USART2_MspInit 1 */
}
Dies ist die Interrupt-Funktion in stm32f4xx_it.c
void DMA1_Stream5_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream5_IRQn 0 */
/* USER CODE END DMA1_Stream5_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart2_rx);
/* USER CODE BEGIN DMA1_Stream5_IRQn 1 */
/* USER CODE END DMA1_Stream5_IRQn 1 */
}
/**
* @brief This function handles DMA1 stream6 global interrupt.
*/
void DMA1_Stream6_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Stream6_IRQn 0 */
/* USER CODE END DMA1_Stream6_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart2_tx);
/* USER CODE BEGIN DMA1_Stream6_IRQn 1 */
/* USER CODE END DMA1_Stream6_IRQn 1 */
}
/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
// SerialPrint("Hi");
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
BEARBEITEN:
Ich habe diese Methode ausprobiert, aber sie hat bei mir nicht funktioniert.
while(1U)
{
// Communication Test Block //
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17);
while(!dmaTransmitCompletedFlag);
dmaTransmitCompletedFlag = 0;
}
..
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
dmaTransmitCompletedFlag = 1;
}
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
Diese beiden sollten auf Byte-Alignment gesetzt werden. Es ist auf Halbwort eingestellt, also führt es DMA auf 2 Bytes aus, aber die UART-Register haben nur 1 Byte, sodass die Hälfte der Bytes in ein schwarzes Loch fällt:
uint8_t rs485TxBuffer[17]={0x02,0x0A,0x00,0x01,0x00,0x00,0x00,0x01,0x0D,..}
but I see the data like 02 00 00 00 0D 00 00 84 0A 02 04 on the line.
Es werden nur die fettgedruckten Bytes übertragen:
0x02 , 0x0A, 0x00 , 0x01, 0x00 , 0x00, 0x00 , 0x01, 0x0D
Sie können auch den UART FIFO aktivieren.
Über diesen Code:
while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);
Wenn der DMA-Kern beschäftigt ist, gibt HAL_UART_Transmit_DMA HAL_BUSY zurück. Also das while(); Ich werde warten. Wenn der DMA-Kern verfügbar ist, initiiert er die Übertragung und gibt HAL_OK zurück. Beachten Sie, dass while() beendet wird, nachdem die Übertragung initiiert wurde, aber nicht auf das Ende der Übertragung wartet. Die Übertragung erfolgt im Hintergrund, was der Sinn von DMA ist. Aber wenn Sie einige UART-Register berühren oder HAL_UART_Transmit verwenden, während der DMA läuft, könnte es damit herumspielen, und das würde Ihre Probleme erklären. Verwenden Sie den Interrupt, um zu erfahren, ob die Übertragung abgeschlossen ist. Und wenn Sie eine globale Variable verwenden, setzen Sie sie auf flüchtig.
Sie können nicht erneut senden, bis die vorherige Nachricht nicht vollständig gesendet wurde.
dmaTransmitCompletedFlag = 1;
while(1U)
{
// Communication Test Block //
if dmaTransmitCompletedFlag
{
dmaTransmitCompletedFlag = 0;
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17);
}
}
..
void UART_DMATransmitCplt(&huart2)
{
dmaTransmitCompletedFlag = 1;
}
Marko Buršič
mathco
Marko Buršič
Marko Buršič
mathco
mathco