在STM32F103C8T6中使用HAL库函数的UART模块时注意事项

本文介绍了在使用STM32F103C8T6和HAL库进行UART通信时需要注意的问题。包括HAL_UART_MspInit()中禁止使用中断发送和接收函数,HAL_UART_RxCpltCallback()回调中避免发送乱码,HAL_UART_Transmit()参数限制,快速收发可能导致的卡死问题,以及中断处理函数中避免阻塞式收发等细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发板:普中A7板子
核心板:STM32F103C8T6
HAL库版本:STM32Cube_FW_F1_V1.8.0

注意事项:
1、在HAL_UART_MspInit()函数中不能使用HAL_UART_Receive_IT()或HAL_UART_Transmit_IT()函数,否则不会进入中断处理函数中,也就是说设置中断无效;
2、在HAL_UART_RxCpltCallback()回调函数中不能使用HAL_UART_Transmit_IT()进行发送数据,否则发送出去的就是乱码’?'这个符号;
3、在HAL_UART_Transmit(&UART_Struct,recv_buf,50,1000)这个函数中参数50会导致输出一部分字符后卡死;只能设置为HAL_UART_Transmit(&UART_Struct,recv_buf,1,1000)参数为1;
4、如果在HAL_UART_RxCpltCallback()函数中使用HAL_UART_Transmit()来发送数据,串口调试助手发送数据到单片机点快一点的话 就会卡死,而使用直接写寄存器发就不会卡死;在printf重定位中的fputc()函数中使用HAL_UART_Transmit()来发送数据,并在main()主函数中的while(1)死循环里面只用printf()函数打印数据就不会出现卡死的情况;
5、不能在USART1_IRQHandler()中断处理函数中使用HAL_UART_Receive()和HAL_UART_Transmit()函数,因为这两个是阻塞式的容易造成程序卡死

/****************************************************************************************
 * 函数功能:main主函数
 * 参    数:
 * 返 回 值:
 * 注意事项:
 ***************************************************************************************/
int main(void)
{
   
   
	unsigned char i, j = 0;
	HAL_Init();
	SystemClock_Config();
	
	UART_Init();
	Led_Init();
	
	printf("This is test function\r\n");

	while (1)
	{
   
   
		printf("Hello World %d\r\n", j++);
//		for(i=0; i<8; i++)
//		{
   
   
//			LED_ON(i);
//			HAL_Delay(500);
//			LED_OFF(i);	
//		}		
	}
}

/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow : 
  *            System Clock source            = PLL (HSI)
  *            SYSCLK(Hz)                     = 72000000
  *            HCLK(Hz)                       = 72000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 2
  *            APB2 Prescaler                 = 1
  *            PLLMUL                         = 9
  *            Flash Latency(WS)              = 2
  * @param  None
  * @retval None
  */
void SystemClock_Config(void)
{
   
   
	RCC_ClkInitTypeDef clkinitstruct = {
   
   0};
	RCC_OscInitTypeDef oscinitstruct = {
   
   
<think>我们正在讨论的是GD32F527,而不是STM32。因此,我们需要参考GD32的标准外设(或HAL)的API。然而,GD32STM32HAL并不完全相同。在GD32中,我们通常使用标准外设(类似STM32的标准外设)或新的GD32 HAL(如果提供了的话)。 根据用户的问题,他想要一个替代`HAL_UART_DMAStop(huart)`的方法,该函数在STM32 HAL中用于停止UART的DMA传输。在GD32中,我们通常使用标准外设库函数来控制DMA。 在GD32的标准外设中,停止DMA通道传输的函数是`dma_channel_disable`。因此,我们可以通过分别禁用接收和发送的DMA通道来达到与`HAL_UART_DMAStop`相同的效果。 但是,请注意,`HAL_UART_DMAStop`在STM32 HAL中会同停止发送和接收的DMA。如果我们只想停止接收而不影响发送,那么我们应该只停止接收DMA通道。 在GD32中,我们可以这样做: 1. 首先,我们需要知道接收和发送DMA分别使用的通道(比如DMA0的通道5用于接收,通道4用于发送)。 2. 然后,在空闲中断中,我们只停止接收DMA通道。 具体步骤: 假设我们使用USART0,接收使用DMA0通道5,发送使用DMA0通道4。 在空闲中断处理函数中: ```c void USART0_IRQHandler(void) { if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE) != RESET) { // 清除空闲中断标志 uint32_t temp = USART_STAT0(USART0); // 读取状态寄存器 temp = USART_DATA(USART0); // 读取数据寄存器 // 停止接收DMA通道(这里是DMA0通道5) dma_channel_disable(DMA0, DMA_CH5); // 计算接收到的数据长度 uint32_t recv_len = BUFFER_SIZE - dma_transfer_number_get(DMA0, DMA_CH5); // 处理接收到的数据... // 重新配置DMA接收(如果需要继续接收) // 先设置传输数量 dma_transfer_number_config(DMA0, DMA_CH5, BUFFER_SIZE); // 重新使能DMA通道 dma_channel_enable(DMA0, DMA_CH5); // 或者,如果不需要立即重新接收,可以稍后再配置 } } ``` 但是,用户提到在STM32中,使用`HAL_UART_DMAStop`会同停止发送和接收,这可能会影响发送。所以,如果我们只想停止接收而不影响发送,我们只需要停止接收DMA通道即可。 因此,在GD32中,替代`HAL_UART_DMAStop(huart)`(仅停止接收)的方法是单独停止接收DMA通道。 如果用户希望完全模仿`HAL_UART_DMAStop`的行为(即同停止发送和接收),那么可以分别停止两个通道: ```c // 停止接收DMA dma_channel_disable(DMA0, DMA_CH5); // 停止发送DMA dma_channel_disable(DMA0, DMA_CH4); ``` 但是,根据引用[2]中的描述,用户可能只想停止接收而不影响发送。因此,我们只需要停止接收DMA通道。 另外,在GD32中,重新启动DMA接收,需要重新配置传输数量并重新使能DMA通道。注意,在停止DMA通道后,该通道的配置寄存器可能仍然保留,但传输数量需要重新设置。 总结: 在GD32F527中,替代`HAL_UART_DMAStop(huart)`(仅停止接收DMA)的方法是使用标准外设的`dma_channel_disable`函数来禁用接收DMA通道。同,为了避免影响发送,不要停止发送DMA通道。 示例代码(仅停止接收DMA): ```c // 假设接收DMA为DMA0, 通道5 dma_channel_disable(DMA0, DMA_CH5); ``` 注意:在停止DMA接收后,如果需要再次启动接收,需要重新设置传输数量(因为DMA传输计数器在停止后不会自动重置)并重新使能通道。 重新启动接收DMA的示例: ```c dma_transfer_number_config(DMA0, DMA_CH5, BUFFER_SIZE); // 设置传输数量 dma_channel_enable(DMA0, DMA_CH5); // 使能通道 ``` 另外,在重新启动DMA接收之前,确保DMA的配置(如内存地址、外设地址等)没有改变,如果改变了需要重新配置。 最后,关于空闲中断的处理,请参考之前的清除空闲中断标志的方法。 希望这能帮助用户解决问题。</think>在 GD32F527 中替代 `HAL_UART_DMAStop()` 的方法需要直接操作 DMA 控制寄存器,以下是完整解决方案: ### GD32F527 替代 HAL_UART_DMAStop 的方法 ```c // 仅停止接收DMA而不影响发送DMA void gd32_uart_dma_rx_stop(usart_dev* usartx) { // 1. 获取DMA通道配置(以USART0_RX DMA0_CH5为例) dma_channel_enum rx_ch = DMA_CH5; // 根据实际配置修改 // 2. 停止接收DMA DMA_CHCTL(DMA0, rx_ch) &= ~DMA_CHXCTL_CHEN; // 清除通道使能位 // 3. 清除DMA传输完成标志 DMA_INTC(DMA0) |= DMA_FLAG_ADD(DMA_INTF_FTFIF, rx_ch); // 4. 禁用USART的DMA接收请求 USART_CTL2(usartx) &= ~USART_CTL2_DENR; } ``` ### 使用示例(在空闲中断中) ```c void USART0_IRQHandler(void) { if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) { // 1. 清除空闲中断标志(GD32特殊序列) uint32_t temp = USART_STAT0(USART0); temp = USART_DATA(USART0); // 2. 停止接收DMA(不影响发送) gd32_uart_dma_rx_stop(USART0); // 3. 计算接收数据长度 uint16_t recv_len = BUFFER_SIZE - dma_transfer_number_get(DMA0, DMA_CH5); // 4. 处理数据... process_rx_data(rx_buffer, recv_len); // 5. 重启DMA接收 dma_transfer_number_config(DMA0, DMA_CH5, BUFFER_SIZE); dma_channel_enable(DMA0, DMA_CH5); // 重新使能通道 USART_CTL2(USART0) |= USART_CTL2_DENR; // 重新使能DMA请求 } } ``` ### 关键操作解析 1. **DMA通道独立控制**: ```c DMA_CHCTL(DMA0, rx_ch) &= ~DMA_CHXCTL_CHEN; // 仅禁用接收通道 ``` 通过直接操作通道控制寄存器,避免影响发送通道 2. **标志位清除**: ```c DMA_INTC(DMA0) |= DMA_FLAG_ADD(DMA_INTF_FTFIF, rx_ch); ``` 清除该通道的传输完成标志,防止残留中断 3. **USART DMA请求控制**: ```c USART_CTL2(usartx) &= ~USART_CTL2_DENR; // 禁用接收请求 ``` 防止USART继续触发DMA请求[^2] 4. **安全重启流程**: - 必须重新配置传输数量 - 通道使能和DMA请求使能需分开设置 - 避免在DMA活动修改配置 > 实际使用需根据硬件连接修改: > 1. `DMA0` → 实际使用的DMA控制器 > 2. `DMA_CH5` → 实际分配的DMA通道 > 3. `USART_CTL2_DENR` → 接收使能位(部分型号为`DMAR`) ### 与STM32 HAL的主要差异 | 操作 | STM32 HAL | GD32 直接操作 | |--------------|-------------------------|----------------------| | 停止DMA | `HAL_UART_DMAStop()` | 手动清除`CHEN`位 | | 标志清除 | 自动处理 | 需手动清除`INTF` | | 通道控制 | 统一控制收发 | 需分开控制收发通道 | | 重启配置 | 需重新初始化外设[^1] | 只需重置通道参数 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值