为什么 HAL_UART_Transmit_DMA() 不适用于 Nucleo F103RB 上的串口?
Why doesn't HAL_UART_Transmit_DMA() work for serial ports on a Nucleo F103RB?
我有以下代码,其中大部分是由 STM32CubeMX 生成的。 (为了便于阅读,我删除了大量生成的评论。)
volatile int txDoneFlag = 0;
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
txDoneFlag = 1;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_USART1_UART_Init();
MX_DMA_Init();
MX_USART3_UART_Init();
while (1)
{
LD2_GPIO_Port->BSRR = (uint32_t)LD2_Pin;
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"1: on \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_UART_Transmit_DMA(&huart2, (uint8_t*)"2: on \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_UART_Transmit_DMA(&huart3, (uint8_t*)"3: on \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_Delay(100);
LD2_GPIO_Port->BSRR = (uint32_t)LD2_Pin << 16U;
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"1: off \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_UART_Transmit_DMA(&huart2, (uint8_t*)"2: off \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_UART_Transmit_DMA(&huart3, (uint8_t*)"3: off \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_Delay(100);
}
}
DMA 是在 STM32CubeMX 生成器中设置的,所以它应该是正确的。
当我 运行 这段代码时,它在第一个 while(!txDoneFlag);
陷入无限循环,这意味着 HAL_UART_TxCpltCallback()
永远不会被调用。
这让我觉得我需要做一些进一步的事情来启用 DMA。
如何让 HAL_UART_Transmit_DMA()
工作?
我已经尝试重新排序生成的 MX...
调用,以便 MX_DMA_Init()
在 ...UART_Init()
之前被调用。
--
更新:请求代码。所有三个 MX_USARTn_UART_Init()
函数都具有相同的主体(除了 uart 编号。
/**
* @brief USART3 Initialization Function
* @param None
* @retval None
*/
static void MX_USART3_UART_Init(void)
{
/* USER CODE BEGIN USART3_Init 0 */
/* USER CODE END USART3_Init 0 */
/* USER CODE BEGIN USART3_Init 1 */
/* USER CODE END USART3_Init 1 */
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART3_Init 2 */
/* USER CODE END USART3_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
/* DMA1_Channel3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
/* DMA1_Channel4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
/* DMA1_Channel5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
/* DMA1_Channel6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
/* DMA1_Channel7_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
}
确保在您的 STM32Cube ioc 文件中检查了您正在使用的 UART 外设的全局中断的所有三个,一些 IRQ 根据芯片进行组合。
它们位于您的 stm32F1xx_it.c 文件中。如果需要,设置断点并确保这些中断正在触发。在 ISR 内部,您可以看到正在调用哪个回调,如果有的话(如果缺少某些配置)。您的 MX_DMA_Init() 和 UART_INIT() 中的配置是什么样的?你能分享那些吗?否则,您可以确保自己连接好一切。虽然,我可能会提醒您不要将此技术用于 DMA,但 DMA 的全部意义在于能够在 CPU 上执行其他指令,同时 DMA 接触内存并处理内存操作。
在所有三个 UART 外设发送消息和 DMA 的情况下,您应该能够在回调中使用一些标志并使用 if 语句而不是用 while 循环阻塞。
HAL 中的回调是弱类型的,因此您需要确保符号具有正确定义的路径,即 ..extern 或清晰的包含路径,以便将正确的内存地址分配给您的回调主文件在这里。这样,当从 ISR 发出回调时,主文件中的回调将是它去的回调。
我有以下代码,其中大部分是由 STM32CubeMX 生成的。 (为了便于阅读,我删除了大量生成的评论。)
volatile int txDoneFlag = 0;
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
txDoneFlag = 1;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_USART1_UART_Init();
MX_DMA_Init();
MX_USART3_UART_Init();
while (1)
{
LD2_GPIO_Port->BSRR = (uint32_t)LD2_Pin;
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"1: on \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_UART_Transmit_DMA(&huart2, (uint8_t*)"2: on \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_UART_Transmit_DMA(&huart3, (uint8_t*)"3: on \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_Delay(100);
LD2_GPIO_Port->BSRR = (uint32_t)LD2_Pin << 16U;
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"1: off \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_UART_Transmit_DMA(&huart2, (uint8_t*)"2: off \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_UART_Transmit_DMA(&huart3, (uint8_t*)"3: off \n", 16);
while(!txDoneFlag);
txDoneFlag = 0;
HAL_Delay(100);
}
}
DMA 是在 STM32CubeMX 生成器中设置的,所以它应该是正确的。
当我 运行 这段代码时,它在第一个 while(!txDoneFlag);
陷入无限循环,这意味着 HAL_UART_TxCpltCallback()
永远不会被调用。
这让我觉得我需要做一些进一步的事情来启用 DMA。
如何让 HAL_UART_Transmit_DMA()
工作?
我已经尝试重新排序生成的 MX...
调用,以便 MX_DMA_Init()
在 ...UART_Init()
之前被调用。
--
更新:请求代码。所有三个 MX_USARTn_UART_Init()
函数都具有相同的主体(除了 uart 编号。
/**
* @brief USART3 Initialization Function
* @param None
* @retval None
*/
static void MX_USART3_UART_Init(void)
{
/* USER CODE BEGIN USART3_Init 0 */
/* USER CODE END USART3_Init 0 */
/* USER CODE BEGIN USART3_Init 1 */
/* USER CODE END USART3_Init 1 */
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART3_Init 2 */
/* USER CODE END USART3_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
/* DMA1_Channel3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
/* DMA1_Channel4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
/* DMA1_Channel5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
/* DMA1_Channel6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
/* DMA1_Channel7_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
}
确保在您的 STM32Cube ioc 文件中检查了您正在使用的 UART 外设的全局中断的所有三个,一些 IRQ 根据芯片进行组合。
它们位于您的 stm32F1xx_it.c 文件中。如果需要,设置断点并确保这些中断正在触发。在 ISR 内部,您可以看到正在调用哪个回调,如果有的话(如果缺少某些配置)。您的 MX_DMA_Init() 和 UART_INIT() 中的配置是什么样的?你能分享那些吗?否则,您可以确保自己连接好一切。虽然,我可能会提醒您不要将此技术用于 DMA,但 DMA 的全部意义在于能够在 CPU 上执行其他指令,同时 DMA 接触内存并处理内存操作。
在所有三个 UART 外设发送消息和 DMA 的情况下,您应该能够在回调中使用一些标志并使用 if 语句而不是用 while 循环阻塞。
HAL 中的回调是弱类型的,因此您需要确保符号具有正确定义的路径,即 ..extern 或清晰的包含路径,以便将正确的内存地址分配给您的回调主文件在这里。这样,当从 ISR 发出回调时,主文件中的回调将是它去的回调。