Embedded Isr一直被调用,即使没有数据发送到UART

Embedded Isr一直被调用,即使没有数据发送到UART,embedded,stm32,microcontroller,Embedded,Stm32,Microcontroller,我想在USART1上为空闲检测启用isr,但在控制寄存器中设置了适当的标志后,我在调试器中看到一直在调用isr过程。我的意思是,我已经在这个过程上设置了一个断点,所以我可以看到它一直被调用,而不仅仅是一次,甚至零次。老实说,在我将一些数据发送到UART之前,我希望不会调用isr过程 下面是我的代码。 我使用STM32F103和libopencm3 extern "C" { #include <libopencm3/stm32/rcc.h> #include

我想在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();

  }
}
extern“C”{
#包括
#包括
#包括
#包括
#包括
#包括
#包括
}
静态无效rcc_设置()
{
//外围设备时钟
rcc外围时钟启用(rcc GPIOA);
rcc外围时钟启用(rcc GPIOB);
}
int main()
{
rcc_设置();
rcc_时钟_设置_输入_hse_8mhz_输出_72mhz();
系统时钟源(STK、CSR、CLKSOURCE、AHB、DIV8);
systick_设置_重新加载(8999);
systick_中断_启用();
systick_计数器_启用();
rcc外围时钟启用(rcc USART1);
rcc外围时钟启用(rcc DMA1);
//->GPIO
gpio_设置_模式(gpio_银行_USART1_发送,gpio_模式_输出_50_MHZ,
GPIO_CNF_输出_ALTFN_推拉,GPIO_USART1_TX);
gpio_设置_模式(gpio_银行_用户1_接收,gpio_模式_输入,
GPIO_CNF_输出_ALTFN_OPENDRAIN,GPIO_USART1_RX);
//->配置
usart_设置_模式(USART1、usart_模式_发送_接收);
usart\U set\U波特率(USART19600);
usart\U集合\U奇偶校验(USART1,usart\U奇偶校验\U无);
usart_集合_数据位(USART1,8);
usart\U set\U停止位(USART1,1);
usart\U设置\U流量控制(USART1,usart\U流量控制\U无);
USART_CR1(USART1)|=USART_CR1_IDLEIE;//如果没有这个,就不会调用isr
nvic_启用_irq(nvic_USART1_irq);
//->启用
usart_启用(USART1);
while(true)
{
[maybe_unused]]intx=10;
}
}
无效usart1_isr()
{
[maybe_unused]]auto x=(USART_CR1(USART-1)和USART_CR1_IDLEIE);
[[maybe_unused]]autoy=(USART_SR(USART1)和USART_FLAG_IDLE);
如果((USART\U CR1(USART1)和USART\U CR1\U IDLEIE)!=0)&&
((USART_SR(USART1)和USART_FLAG_IDLE)!=0))
{
[maybe_unused]]auto x=USART1_SR;
[maybe_unused]]auto y=USART1\u DR;
//过程_rx();
}
}

STM32参考手册说明,只要USART\U ISR寄存器中的IDLE=1,就会生成IDLE中断。检测到空闲线路时,硬件会设置USART_ISR中的空闲位。通过软件清除,将1写入USART_ICR寄存器中的IDLECF。我猜您需要在ISR中清除此项,以防止中断被立即重新断言。尝试将类似于此行的内容添加到
usart1\u isr()


如果要通过读取清除标志,如某些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关键字。这是从局部变量中丢弃volatile限定符,因为
(USART\u CR1(USART1)&USART\u CR1\u IDLEIE)
int
类型的左值。这反过来会导致编译器无法生成与实际寄存器读取相对应的指令

演示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(USART 1)|=USART_CR1_IDLEIE
在线路空闲时生成中断,然后通过从ISR内部读取SR清除空闲位


因此,您已经告诉程序,当线路空闲时,会产生无休止的中断,这就是它所做的。抛开
IDLEIE
位,您应该只得到TX或RX的中断,理想情况下还包括or和FE。

几乎所有类型的ISR都要求您从ISR中清除中断源标志。我猜
可能未使用的
会阻止编译器发出警告。但它是否会阻止编译器优化从USART1\u DR读取的数据?如果你通过ISR,USART1\U DR真的被阅读了吗?关闭优化有帮助吗?制作
x,y
volatile有帮助吗?我删除了可能未使用的,我制作了x,y volatile,我添加了一些代码,根据DR值执行一些条件移动。没有任何帮助。@bielu000问题在于你的魔法宏和变量。我在所有的生产代码中都使用了空闲中断,它工作得很好。建议使用STM提供的CMSIS头和宏,而不是第三方坏头和库。它总是按预期工作。不,它既不是通过宏也不是通过外设地址。目前,我的usart1 isr与HAL实现中的完全相同。我还比较了我读取的地址,它们与HAL中的地址相同。是的,但遵循文档:它由软件序列清除(先读取USART\U SR寄存器,然后读取USART\U DR寄存器)。正如你所看到的,我在我的代码中这样做了,但是由于某些原因,这个标志没有被清除。啊,我在看一本STM32L4的参考手册,上面写着写给USART1_ICR。现在我看到了STM32F10x的参考手册,上面写着读自USART_SR和USART_DR。我更改了代码,它没有改变任何东西。我还尝试在我的isr中使用LL_USART_ClearFlag_IDLE函数——我从STM Hal实现中复制了函数体,但它仍然不起作用。我还将SR和DR寄存器的地址与HAL中定义的地址进行了比较,它们是相同的。汇编代码看起来不错,有来自SR和DR寄存器的读取。我不会通过USART发送任何东西。MCU刚刚连接到FT232RL。所以我的头脑是空白的。我已经没有主意了。@bielu000在ISR内设置一个断点并检查SR。设置了什么标志?SR中设置了以下标志:IDLE、TC、TXE。我知道了,但我不同意
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