STM32L071使用cubeMX库开发---利用串口rxne中断 接收数据问题static void MX_USART1_UART_Init(void) { huart1.Instance =

本文详细解析了STM32使用Cubemx生成的UART初始化代码中出现的中断接收异常问题,通过修改中断服务函数,直接访问RDR寄存器获取数据,解决了接收数据异常和RXNE中断无法正常工作的问题。

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

 Cubemx生成的初始化函数如下:

static void MX_USART1_UART_Init(void)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }

}

管脚及中断初始化在void HAL_UART_MspInit(UART_HandleTypeDef* huart)函数中,需要自己添加函数开启RXNE中断。

if(huart->Instance==USART1)
{
    /* Peripheral clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF4_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		
    /* Jx: enable rxne */
    __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
    
    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
}

问题出在中断服务函数中,在判断了标志位后,使用了HAL_UART_Receive_IT()函数接收一个字节的数据,出现我在上位机发送一个字节,但是stm32收到7,8个字节并且全是00,不仅如此如果再次发送数据,发现再也不会,置位rxne中断。

针对这个情况,不再使用HAL_UART_Receive_IT()函数接收数据 ,我改为以下方式:

void USART1_IRQHandler(void)
{
    uint8_t buf=0;
	
    if(RESET != __HAL_UART_GET_IT(&huart1, UART_IT_RXNE) )
    {
	buf = huart1.Instance->RDR;
	printf("%02x ",buf);
    }

}

直接去访问  rdr寄存器 取回数据 。其实  应该加上一些错误处理,比如判断 溢出错误(ORE),噪声探测 (NE),校验错误(PE)。。。这个 可以 参考 cubemx生成的void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)函数 。
 

<think>在STM32开发中,中断配置和外设初始化的顺序确实很重要。对于USART2的初始化,正确的顺序应该是: 1. 初始化外设(配置USART2的波特率、数据位等) 2. 配置中断优先级(设置抢占优先级和子优先级) 3. 使能中断(打开中断通道) 原因如下: - 如果在初始化外设之前就开启了中断,那么在外设初始化完成之前,如果发生了中断事件(比如接收到数据),就会进入中断服务函数,而此时USART2可能还没有配置好,导致错误。 - 先配置中断优先级再使能中断是为了避免在配置优先级的过程中发生中断,导致不可预测的行为。 因此,推荐的顺序是: 1. MX_USART2_UART_Init(); // 初始化USART2 2. HAL_NVIC_SetPriority(USART2_IRQn, 1, 0U); // 设置中断优先级 3. HAL_NVIC_EnableIRQ(USART2_IRQn); // 使能中断 但是,我们也要注意,在MX_USART2_UART_Init()函数内部,通常会调用HAL_UART_MspInit()函数,这个函数是用户实现或CubeMX生成的,用于配置USART2的GPIO、时钟等。在这个函数中,一般不会开启中断,所以即使先调用MX_USART2_UART_Init(),只要没有调用HAL_UART_Receive_IT()等启动接收的函数,中断是不会被触发的。 然而,为了安全起见,我们仍然按照上述顺序执行。 下面是一个典型的代码示例: ```c int main(void) { HAL_Init(); SystemClock_Config(); // 初始化USART2 MX_USART2_UART_Init(); // 配置USART2中断优先级 HAL_NVIC_SetPriority(USART2_IRQn, 1, 0); // 使能USART2中断 HAL_NVIC_EnableIRQ(USART2_IRQn); // 启动接收中断(在初始化完成后启动) HAL_UART_Receive_IT(&huart2, &rx_data, 1); while (1) { // 主循环 } } ``` 在MX_USART2_UART_Init()函数中,通常只是配置了USART的参数(波特率、数据位、停止位等),并没有开启中断。开启接收中断的操作是在后续调用HAL_UART_Receive_IT()函数时进行的。所以,即使先使能了NVIC中断(HAL_NVIC_EnableIRQ),只要没有调用HAL_UART_Receive_IT,那么USART2就不会产生接收中断。 但是,如果MX_USART2_UART_Init()函数内部调用了HAL_UART_Receive_IT(),那么顺序就很重要了。不过,CubeMX生成的初始化函数通常不会自动启动中断接收,所以需要用户显式调用。 因此,严格来说,顺序要求是: 1. 初始化外设(MX_USART2_UART_Init) 2. 设置中断优先级(HAL_NVIC_SetPriority) 3. 使能NVIC中断(HAL_NVIC_EnableIRQ) 4. 启动外设中断(如HAL_UART_Receive_IT) 其中,第4步可以在第2、3步之后任意位置,只要在需要接收数据之前即可。 但是,如果先使能了NVIC中断(第3步)然后才设置优先级(第2步),那么在设置优先级之前,如果发生了中断,那么中断使用默认优先级(可能为0,最高优先级),这可能会导致优先级混乱。因此,设置优先级应该在使能中断之前。 总结: 1. 先初始化外设(配置寄存器,但不开启中断) 2. 设置中断优先级(避免在使能中断后设置优先级时发生中断) 3. 使能NVIC中断(允许CPU响应中断) 4. 启动外设中断(如使能USART接收中断等) 所以,不能先使能中断再初始化外设,这样可能导致外设未初始化完成就进入中断,引发错误。同样,设置优先级应该在使能中断之前。 但是,如果由于某些原因必须先使能中断,那么必须确保在使能中断之前,外设已经初始化完成,并且中断优先级已经设置。但是这样的顺序是不推荐的。 因此,在STM32F0系列中,我们遵循: 1. MX_USART2_UART_Init() 2. HAL_NVIC_SetPriority() 3. HAL_NVIC_EnableIRQ() 这个顺序。 另外,在USART中断服务函数中,我们需要调用HAL_UART_IRQHandler()来处理中断: ```c void USART2_IRQHandler(void) { HAL_UART_IRQHandler(&huart2); } ``` 而HAL_UART_IRQHandler会调用相应的回调函数,例如当接收到数据时会调用HAL_UART_RxCpltCallback()。 所以,为了安全性和可维护性,请严格按照推荐的顺序初始化。</think>### STM32F0 串口中断初始化顺序详解 在 STM32F0 系列中,**中断配置和外设初始化顺序至关重要**,不正确的顺序可能导致中断丢失、数据错误甚至系统死锁。对于 USART2 的初始化,推荐的严格顺序是: ```c // 正确顺序 MX_USART2_UART_Init(); // 1. 初始化串口硬件 HAL_NVIC_SetPriority(USART2_IRQn, 1, 0); // 2. 设置中断优先级 HAL_NVIC_EnableIRQ(USART2_IRQn); // 3. 使能中断 ``` #### 为什么顺序很重要? 1. **硬件初始化优先**: - `MX_USART2_UART_Init()` 配置 USART2 的: - 波特率(BRR 寄存器) - 数据位/停止位(CR1/CR2 寄存器) - 硬件流控制(CR3 寄存器) - GPIO 复用功能 - 如果先使能中断,**串口配置完成前可能触发错误中断** 2. **优先级设置在前**: - 如果先使能中断再设置优先级: ```c // 危险顺序 - 可能导致中断丢失 HAL_NVIC_EnableIRQ(USART2_IRQn); // 中断已使能但优先级未设置 HAL_NVIC_SetPriority(USART2_IRQn, 1, 0); // 此时若发生中断使用默认优先级 ``` - Cortex-M0 默认优先级为 0(最高优先级),可能导致优先级反转 3. **CubeMX 生成的代码顺序**: STM32CubeMX 总是生成以下顺序: ```c static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; // ... 其他配置 ... HAL_UART_Init(&huart2); // 内部调用 HAL_UART_MspInit } void HAL_UART_MspInit(UART_HandleTypeDef* huart) { if(huart->Instance == USART2) { // 1. 使能时钟 __HAL_RCC_USART2_CLK_ENABLE(); // 2. 配置GPIO GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 3. 设置中断优先级 HAL_NVIC_SetPriority(USART2_IRQn, 0, 1); // 4. 使能中断 HAL_NVIC_EnableIRQ(USART2_IRQn); } } ``` #### 如果先开中断再初始化会发生什么? ```c // 危险的反序操作 HAL_NVIC_EnableIRQ(USART2_IRQn); // 先使能中断 MX_USART2_UART_Init(); // 后初始化硬件 HAL_NVIC_SetPriority(USART2_IRQn, 1, 0); // 最后设置优先级 ``` 可能导致的后果: 1. **虚假中断触发**: - 初始化过程中的寄存器写入可能触发 TXE(发送寄存器空)或 ORE(过载错误)中断 - 示例错误处理: ```c void USART2_IRQHandler(void) { // 如果此时USART2尚未初始化完成 uint32_t isr = USART2->ISR; if(isr & USART_ISR_ORE) { // 过载错误 // 但USART2->CR1等寄存器可能未配置 USART2->ICR |= USART_ICR_ORECF; // 可能写入无效地址 } } ``` 2. **优先级冲突**: - 中断使能后到优先级设置前的窗口期: - 若发生中断使用默认优先级0(最高) - 可能抢占关键系统中断(如SysTick) 3. **数据竞争**: - 如果初始化前已有数据到达(如其他设备发送): ```text 时间轴: t0: 使能中断 t1: 数据到达 -> 触发RXNE中断 t2: 中断服务程序尝试读取未初始化的USART2->RDR -> 总线错误 t3: 初始化代码执行 ``` #### 最佳实践和完整示例 **安全初始化序列:** ```c // 步骤1: 外设初始化 MX_USART2_UART_Init(); // 步骤2: 设置优先级 HAL_NVIC_SetPriority(USART2_IRQn, 1, 0); // 抢占1,子优先级0 // 步骤3: 使能中断 HAL_NVIC_EnableIRQ(USART2_IRQn); // 步骤4: 启动接收(关键!) uint8_t rx_buffer[32]; HAL_UART_Receive_IT(&huart2, rx_buffer, sizeof(rx_buffer)); ``` **中断服务程序:** ```c // stm32f0xx_it.c void USART2_IRQHandler(void) { HAL_UART_IRQHandler(&huart2); } // 回调函数(在HAL中实现) void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART2) { // 处理接收到的数据 process_rx_data(huart->pRxBuffPtr, huart->RxXferSize); // 重新启动接收 HAL_UART_Receive_IT(huart, huart->pRxBuffPtr, huart->RxXferSize); } } ``` #### 特殊场景处理 1. **DMA传输**: ```c // 在初始化后添加 HAL_UART_Receive_DMA(&huart2, dma_buffer, DMA_BUFFER_SIZE); ``` 2. **低功耗模式**: ```c // 进入低功耗前 HAL_NVIC_DisableIRQ(USART2_IRQn); HAL_UART_DeInit(&huart2); // 唤醒后 MX_USART2_UART_Init(); HAL_NVIC_EnableIRQ(USART2_IRQn); HAL_UART_Receive_IT(&huart2, ...); ``` 3. **错误处理增强**: ```c void USART2_IRQHandler(void) { // 检查是否真的需要处理 if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE) || __HAL_UART_GET_FLAG(&huart2, UART_FLAG_ORE)) { HAL_UART_IRQHandler(&huart2); } } ``` #### 调试技巧 如果怀疑顺序问题1. 检查 `USART2->CR1` 寄存器: ```c // 中断使能后CR1应包含这些位 #define USART_CR1_RE (1 << 2) // 接收使能 #define USART_CR1_TE (1 << 3) // 发送使能 #define USART_CR1_RXNEIE (1 << 5) // RX中断使能 ``` 2. 使用调试器监测中断触发: ```c // 在中断入口处设断点 void USART2_IRQHandler(void) { __asm("nop"); // 设置断点于此 // ... } ``` 3. 检查 NVIC 寄存器: ```c // 确认中断已使能 volatile uint32_t iser = NVIC->ISER[0]; if(!(iser & (1 << (USART2_IRQn & 0x1F)))) { // 中断未使能 } ``` ### 总结 在 STM32F0 系列中,**必须遵循以下顺序**: 1. 外设硬件初始化 (`MX_USART2_UART_Init()`) 2. 设置中断优先级 (`HAL_NVIC_SetPriority()`) 3. 使能中断 (`HAL_NVIC_EnableIRQ()`) 4. 启动传输 (`HAL_UART_Receive_IT()`) **绝对不能先开中断再初始化**,否则可能导致: - 虚假中断触发 - 优先级冲突 - 硬件寄存器访问冲突 - 数据丢失或系统锁定 遵循 CubeMX 生成的代码顺序是最安全可靠的做法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值