Estoy tratando de usar UART con DMA,
antes del DMA probé el método de sondeo con while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);
función y pude comunicarme con éxito,
Cuando uso el
while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);
función, los datos no se envían correctamente, solo el primer byte correcto, otros bytes son incorrectos. ¿Por qué sucedió esto? Como puedó resolver esté problema ?
Esta es mi secuencia inicial:
MX_DMA_Init();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
Las siguientes líneas intentan enviar datos:
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);
}
Estas son las funciones de inicialización:
..
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 */
}
Esta es la función de interrupción en 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 */
}
EDITAR:
Probé este método pero no funcionó para mí.
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;
Estos dos deben ajustarse a la alineación de bytes. Está configurado en media palabra, por lo que hace DMA en 2 bytes, pero los registros UART solo tienen 1 byte, por lo que la mitad de los bytes caen en un agujero negro:
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.
Solo se transmiten los bytes en negrita:
0x02 , 0x0A, 0x00 , 0x01, 0x00 , 0x00, 0x00 , 0x01, 0x0D
También puede habilitar el UART FIFO.
Acerca de este código:
while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);
Si el núcleo DMA está ocupado, HAL_UART_Transmit_DMA devolverá HAL_BUSY. Entonces el while(); esperará. Cuando el núcleo DMA esté disponible, iniciará la transferencia y devolverá HAL_OK. Tenga en cuenta que while() sale después de que se inicia la transferencia, pero no espera a que finalice la transferencia. La transferencia ocurrirá en segundo plano, que es el punto de DMA. Pero si toca algunos registros UART o usa HAL_UART_Transmit mientras se ejecuta el DMA, entonces podría interferir y eso explicaría sus problemas. Para saber si la transferencia ha terminado, utilice la interrupción. Y si usa una variable global, configúrela como volátil.
No puede volver a transmitir hasta que el mensaje anterior no se haya enviado por completo.
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 Bursic
matemático
Marko Bursic
Marko Bursic
matemático
matemático