Я пытаюсь использовать UART с DMA,
перед прямым доступом к памяти я пробовал метод опроса с while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);
функцией, и я мог успешно общаться,
Когда я использую
в то время как (HAL_UART_Transmit_DMA (& huart2, (uint8_t *) rs485TxBuffer, 17)! = HAL_OK);
функция, данные не отправляются правильно, только первый байт правильный, другие байты неверны. Почему это произошло? Как я могу решить эту проблему ?
Это моя последовательность инициализации:
MX_DMA_Init();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
Следующие строки пытаются отправить данные:
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);
}
Это инициализирующие функции:
..
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 */
}
Это функция прерывания в 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 */
}
РЕДАКТИРОВАТЬ:
Я пробовал этот метод, но не работал для меня.
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;
Эти два должны быть установлены для выравнивания байтов. Он настроен на половину слова, поэтому он выполняет DMA для 2 байтов, но регистры UART имеют только 1 байт, поэтому половина байтов попадает в черную дыру:
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.
Передаются только байты, выделенные жирным шрифтом:
0x02 , 0x0A, 0x00 , 0x01, 0x00 , 0x00, 0x00 , 0x01, 0x0D
Также вы можете включить UART FIFO.
Об этом коде:
while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);
Если ядро DMA занято, HAL_UART_Transmit_DMA вернет HAL_BUSY. Итак, пока(); будет ждать. Когда ядро DMA будет доступно, оно инициирует передачу и вернет HAL_OK. Обратите внимание, что while() завершает работу после инициации передачи, но не ожидает завершения передачи. Передача будет происходить в фоновом режиме, что и является точкой прямого доступа к памяти. Но если вы прикоснетесь к некоторым регистрам UART или используете HAL_UART_Transmit во время работы DMA, это может привести к путанице, и это объяснит ваши проблемы. Чтобы узнать, завершена ли передача, используйте прерывание. И если вы используете глобальную переменную, установите для нее значение volatile.
Вы не можете повторить передачу, пока предыдущее сообщение не будет полностью отправлено.
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;
}
Марко Буршич
матко
Марко Буршич
Марко Буршич
матко
матко