即使没有数据发送到UART,也始终会调用Isr

时间:2020-09-16 21:29:02

标签: embedded stm32 microcontroller

我想在USART1上启用isr进行空闲检测,但是在控制寄存器中设置了适当的标志之后,我在调试器中看到一直在调用isr过程。我的意思是,我在该过程上设置了一个断点,因此我可以看到它一直被调用,而不仅仅是被调用,甚至被调用为零。老实说,我希望在我向UART发送一些数据之前不会调用isr过程。

下面是我的代码。 我使用STM32F103和libopencm3。

extern "C" {
  #include <libopencm3/stm32/rcc.h>
  #include <libopencm3/stm32/gpio.h>
  #include <libopencm3/cm3/systick.h>
  #include <libopencm3/stm32/timer.h>
  #include <libopencm3/cm3/nvic.h>
  #include <libopencm3/stm32/usart.h>
  #include <libopencm3/stm32/dma.h>
}

static void rcc_setup()
{
  //Peripherals clock
  rcc_periph_clock_enable(RCC_GPIOA);
  rcc_periph_clock_enable(RCC_GPIOB); 
}

int main()
{
  rcc_setup();
  
  rcc_clock_setup_in_hse_8mhz_out_72mhz();

  systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
  systick_set_reload(8999);

  systick_interrupt_enable();
  systick_counter_enable();

  rcc_periph_clock_enable(RCC_USART1);
  rcc_periph_clock_enable(RCC_DMA1);

  // -> GPIO
  gpio_set_mode(GPIO_BANK_USART1_TX, GPIO_MODE_OUTPUT_50_MHZ, 
    GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX);

  gpio_set_mode(GPIO_BANK_USART1_RX, GPIO_MODE_INPUT, 
    GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_USART1_RX);

  // -> Configuration
  usart_set_mode(USART1, USART_MODE_TX_RX);
  usart_set_baudrate(USART1, 9600);
  usart_set_parity(USART1, USART_PARITY_NONE);
  usart_set_databits(USART1, 8);
  usart_set_stopbits(USART1, 1);
  usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);

  USART_CR1(USART1) |= USART_CR1_IDLEIE; //without this, the isr is not called

  nvic_enable_irq(NVIC_USART1_IRQ);

  // -> Enable 
  usart_enable(USART1);


  while(true)
  {
    [[maybe_unused]] int x = 10;
  }
}

void usart1_isr()
{
  [[maybe_unused]]auto x = (USART_CR1(USART1) & USART_CR1_IDLEIE);
  [[maybe_unused]]auto y = (USART_SR(USART1) & USART_FLAG_IDLE);

    if (((USART_CR1(USART1) & USART_CR1_IDLEIE) != 0) &&
        ((USART_SR(USART1) & USART_FLAG_IDLE) != 0))
  {
    [[maybe_unused]] auto x = USART1_SR;
    [[maybe_unused]] auto y = USART1_DR; 

    // process_rx();

  }
}

2 个答案:

答案 0 :(得分:1)

如果某些情况下某些UART或SPI外设需要通过读取清除标志,则应这样做:

void usart1_isr (void)
{
   // read registers in well-defined, clear order to ensure flags are cleared
   volatile uint32_t usart_sr = USART1_SR; // MUST be volatile 
   volatile uint32_t usart_dr = USART1_DR;

   ...
   if(usart_sr == USART_FLAG_IDLE) // check the local variable, not the register
   ....   
}

尤其是 您不应在嵌入式系统中使用会损坏大脑的C ++ 11 auto关键字! 。由于(USART_CR1(USART1) & USART_CR1_IDLEIE)是类型int的左值,因此该方法正在从局部变量中删除volatile限定符。进而可能导致编译器无法生成与实际寄存器读取相对应的指令。

一个简单的示例,演示了C ++ 11 auto的许多危险之一:

volatile int a=1;
auto b = a & 1; // b is now int, not volatile int

int* pa = &a; // not OK since a is volatile
int* pb = &b; // OK since b is not volatile

我强烈建议尽快将此程序移植到C。或至少是C ++ 03。


编辑

好吧,现在我对该部分进行RTFM,显然,在大多数情况下,您也可以通过向其写入0来清除SR。哪些标志引起中断取决于您如何设置CR寄存器。您设置USART_CR1(USART1) |= USART_CR1_IDLEIE;来生成线路空闲时的中断,然后通过从ISR内部读取SR清除空闲位。

因此,您已经告诉程序在线路空闲时生成无尽的中断,这就是它的作用。抛弃IDLEIE位,您应该只获得TX或RX的中断,理想情况下还应覆盖OR和FE。

答案 1 :(得分:0)

STM32参考手册说明,只要USART_ISR寄存器中的IDLE = 1,就会产生IDLE中断。当检测到空闲线时,USART_ISR中的IDLE位由硬件置1。通过软件将其清除,将1写入USART_ICR寄存器中的IDLECF。我猜测您需要在ISR中清除此错误,以防止立即重新声明该中断。尝试在usart1_isr()上添加以下内容。

USART1_ICR = USART_ICR_IDLECF;  // Clear Idle flag in the USART_ISR register.