串口与中断的关系和使用--详细解释

串口(Serial Port)与中断(Interrupt) 的关系

1. 数据传输的异步性: 串口通信是一种异步通信方式,发送端和接收端的时钟不同步。这就意味着当有数据到达时,处理器不一定会立即处理它们,需要在适当的时候对数据进行处理。

2. 轮询 vs 中断: 为了检测串口是否有数据到达,你可以使用两种主要的方法:轮询和中断。在轮询方式中,处理器会定期地检查串口状态以确定是否有新的数据到达。但这种方式可能会浪费处理器资源,因为处理器可能会在没有数据到达时也不停地检查串口状态。而中断则允许处理器在数据到达时立即被通知,从而可以及时处理数据而不需要不断地轮询。

3. 中断处理: 当串口接收到新数据时,它会产生一个中断请求。处理器在执行当前任务的同时,会检测到这个中断请求,然后跳转到预先设定好的中断服务函数(ISR),这个函数会处理串口接收到的数据。这样就实现了在数据到达时立即处理数据的目的,而不需要不断地轮询串口状态。

4. 中断优先级: 在一些情况下,系统中可能会有多个中断源,因此需要根据其重要性设置中断的优先级。对于串口通信来说,如果串口数据接收的实时性很重要,你可能会将串口中断的优先级设置得比其他中断更高,以确保及时处理接收到的数据。

因此,串口通信中使用中断可以提高系统的响应速度和效率,特别是在需要处理实时数据的情况下。

也会是说,使用串口要有中断服务函数

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
    // 串口接收到了数据,可以进行相应的处理
}

比如,下面这个函数

void USART1_IRQHandler(void)
{

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
	{
		USART_ClearFlag(USART1, USART_FLAG_RXNE);
	}

}

这段代码是针对STM32微控制器中USART1串口的中断处理函数。

- `USART1_IRQHandler(void)`: 这是一个中断处理函数,当USART1(串口1)接收或发送数据时触发中断,处理函数便会被调用。

- `if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)`: 这行代码检查串口1是否接收到了新的数据(使用串口,一定会有这行代码)

`USART_GetITStatus()`函数用于检查特定的USART中断标志位,此处使用的是`USART_IT_RXNE`,表示接收寄存器非空中断(Receive Data register Not Empty)。如果接收寄存器非空(即接收到了新的数据),则条件成立,执行相应的操作。

- `USART_ClearFlag(USART1, USART_FLAG_RXNE)`: 这行代码用于清除接收中断标志位,以便能够再次检测到新的接收数据。(这行代码也会有)

清除标志位后,可以进行接收数据的处理。

这个函数的作用是处理USART1串口接收中断。在实际应用中,你可以在这个函数中添加相应的处理代码,比如读取接收到的数据并进行处理,或者发送数据等操作。(当串口收到信息,就会去执行其中的代码)

USART串口在嵌入式系统中通常用于与外部设备进行通信,如与电脑、传感器、其他微控制器等进行数据交换。通过中断机制,可以在数据到达时及时进行处理,而不需要轮询地检查串口状态。

 

 再比如,下面代码,在中断服务函数中,实现--如果串口收到结束标志':'时,就把数据存储到某位置

void USART1_IRQHandler(void)
{
	
	
   //若是非空,则返回值为1,与RESET(0)判断,不相等则判断为真
   if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
   {	

		/* DR读取接受到的数据*/
		buffer[rx_count++] = USART_ReceiveData(USART1);	 //先赋值再加
		
	   if(buffer[rx_count-1] == ':')  //判断是否接收到结束标志
	   {
		    for(rx_i=0; rx_i<rx_count-1; rx_i++)//为什么要多弄个循环和数据存储数组?因为把接收到的数据存储,而不要显示结束标志在新的数据存储数组
		   {
				rx_buffer[rx_i] = buffer[rx_i]; //将数据存储在rx_buffer数组中
		   }
	   
		   rx_flag 	= 1; 	//rx_flag = 1表示接收字符串完成
		   rx_count = 0;
		   
		   memset(buffer, 0, sizeof(buffer));//清空存储数据的数组buffer[]
	   }
 
		//判断为真后,为下次中断做准备,则需要对中断的标志清零
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);	   
   }	

}

上面的函数清除接收标志使用了 

USART_ClearITPendingBit(USART1,USART_IT_RXNE);

与上面第一个函数的清除标志不同,根据下面的函数说明可知,这个函数也能作为清除标志。 

/**
  * @brief  Clears the USARTx's interrupt pending bits.
  * @param  USARTx: where x can be 1, 2, 3, 4, 5, 6, 7 or 8 to select the USART or 
  *         UART peripheral.
  * @param  USART_IT: specifies the interrupt pending bit to clear.
  *          This parameter can be one of the following values:
  *            @arg USART_IT_CTS:  CTS change interrupt (not available for UART4 and UART5)
  *            @arg USART_IT_LBD:  LIN Break detection interrupt
  *            @arg USART_IT_TC:   Transmission complete interrupt. 
  *            @arg USART_IT_RXNE: Receive Data register not empty interrupt.
  *
  * @note   PE (Parity error), FE (Framing error), NE (Noise error), ORE (OverRun 
  *          error) and IDLE (Idle line detected) pending bits are cleared by 
  *          software sequence: a read operation to USART_SR register 
  *          (USART_GetITStatus()) followed by a read operation to USART_DR register 
  *          (USART_ReceiveData()).
  * @note   RXNE pending bit can be also cleared by a read to the USART_DR register 
  *          (USART_ReceiveData()).

  * @note   TC pending bit can be also cleared by software sequence: a read 
  *          operation to USART_SR register (USART_GetITStatus()) followed by a write 
  *          operation to USART_DR register (USART_SendData()).
  * @note   TXE pending bit is cleared only by a write to the USART_DR register 
  *          (USART_SendData()).
  *  
  * @retval None
  */
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)
{
  uint16_t bitpos = 0x00, itmask = 0x00;
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_CLEAR_IT(USART_IT)); 

  /* The CTS interrupt is not available for UART4 and UART5 */
  if (USART_IT == USART_IT_CTS)
  {
    assert_param(IS_USART_1236_PERIPH(USARTx));
  } 
    
  bitpos = USART_IT >> 0x08;
  itmask = ((uint16_t)0x01 << (uint16_t)bitpos);
  USARTx->SR = (uint16_t)~itmask;
}

在上两篇的按键中断(exti.c),也有用到中断服务函数


//6、 编写中断服务函数。 这个函数在startup_stm32f40_41.s中查阅
//不需要自己调用,中断响应后,CPU自动去执行的函数
void EXTI4_IRQHandler(void)
{

	
	if(EXTI_GetITStatus(EXTI_Line4) == SET)  //判断中断线0是否置1
	{
		GPIO_ToggleBits(GPIOA, GPIO_Pin_6);//置1,灯亮
	}
	//7、清除中断标志位
	EXTI_ClearITPendingBit(EXTI_Line4);

}

当按键触发了中断之后,就会去执行这个函数里的代码,以实现功能。 

使用串口,是否可以没有中断服务函数?

在许多嵌入式系统中,串口通信通常需要中断服务函数来处理接收和发送数据。然而,并不是所有的串口通信都必须使用中断服务函数,而这在某些情况下取决于具体的硬件和软件设计。

1. **Polling(轮询)方式**:除了使用中断服务函数之外,另一种处理串口通信的方式是轮询。在轮询方式中,程序会周期性地查询串口接收缓冲区是否有新的数据到达,并发送数据时也会检查发送缓冲区是否准备好。尽管轮询方式会消耗一定的处理器资源,但对于某些简单的应用或者资源受限的系统而言,这是一种简单且有效的处理方式。

2. **硬件辅助**:有些串口控制器(如一些型号的UART或USART)内部具有缓冲区和状态寄存器,在数据到达或发送完成时会产生相应的标志位。这些标志位可以由主程序中的轮询或定时器中断进行检查和处理,而不需要专门的串口中断服务函数。

对于使用ESP8266连接串口的情况,ESP8266模块通常使用UART与微控制器或其他设备进行串口通信。ESP8266的UART可以通过轮询方式或者检查标志位的方式进行数据的接收和发送,而不一定需要使用中断服务函数。这主要取决于你的应用需求、硬件平台以及软件设计。

 

我遇到的问题 

使用esp8266连接串口,并没有使用中断服务函数,这也可以。但因为没有连接好esp8266,导致其他外部设备要用到的函数代码也没有执行。

主程序`main()`中包含了一个无限循环`while(1)`,在循环内部有一系列的操作。在这种情况下,只要程序开始执行`main()`函数,它就会进入这个无限循环,直到出现某种终止条件才会退出。

在你描述的情况下,如果ESP8266没有连接好(可能是硬件连接问题或者初始化失败等原因),导致了在 `ESP8266_Init()` 函数中的初始化不成功,而且 `ESP8266_SendCmd()` 函数在检查连接状态时也一直返回失败,那么程序就会一直停留在 `while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT")) DelayXms(500);` 这个循环中。因为 `ESP8266_SendCmd()` 函数返回失败,导致 `while` 循环条件一直为真,程序就会一直在这个循环中等待,而不会继续执行后面的代码。

解决这个问题的方法是检查 ESP8266 模块的连接情况和初始化是否正确,并确保 `ESP8266_Init()` 和 `ESP8266_SendCmd()` 函数正常工作。另外,建议在程序中添加一些错误处理的逻辑,例如设置最大重试次数或者超时计数器,以防止程序陷入无限循环。

相关推荐

  1. 串口中断关系使用--详细解释

    2024-03-22 11:24:02       40 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-22 11:24:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-22 11:24:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-22 11:24:02       82 阅读
  4. Python语言-面向对象

    2024-03-22 11:24:02       91 阅读

热门阅读

  1. Android中广播的基本介绍

    2024-03-22 11:24:02       40 阅读
  2. STM32利用ADC和DMA外设读取4路电压值Oled显示

    2024-03-22 11:24:02       42 阅读
  3. js- find() 方法

    2024-03-22 11:24:02       36 阅读
  4. Mybatis之like、likeRight、likeLeft的使用

    2024-03-22 11:24:02       39 阅读
  5. 记录 Selenium 常用功能和API

    2024-03-22 11:24:02       37 阅读
  6. vue-pdf 预览pdf (数据流)

    2024-03-22 11:24:02       37 阅读
  7. 使用python和perl语言实现xlsx转化为csv

    2024-03-22 11:24:02       47 阅读
  8. Ubuntu20.04配置

    2024-03-22 11:24:02       42 阅读
  9. Elastic-Job 分布式任务调度

    2024-03-22 11:24:02       45 阅读
  10. Redis中的事务机制

    2024-03-22 11:24:02       41 阅读
  11. 脏牛提权漏洞

    2024-03-22 11:24:02       45 阅读