我想在我的USART1上启用isr进行空闲检测,但在控制寄存器中设置了适当的标志后,我在调试器中看到isr过程总是被调用。我的意思是,我在这个过程上设置了一个断点,所以我可以看到它总是被调用,而不是只调用一次,甚至是零次。老实说,我预计isr过程不会被调用,直到我向UART发送了一些数据...
下面是我的代码。我使用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();
}
}发布于 2020-09-18 15:37:41
如果要通过读取清除标志,就像某些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 关键字!。因为(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,显然,在大多数情况下,你也可以通过向SR写入0来清除SR。引起中断的标志取决于您如何设置CR寄存器。将USART_CR1(USART1) |= USART_CR1_IDLEIE;设置为在线路空闲时生成中断,然后通过从内部读取SR来清除空闲位。
您已经告诉程序在线路空闲时生成无休止的中断,这就是它所做的。丢弃IDLEIE位,您应该只获得TX或RX的中断,理想情况下还应覆盖or和FE。
发布于 2020-09-17 06:02:17
《STM32参考手册》解释说,只要在USART_ISR寄存器中执行IDLE=1,就会产生IDLE中断。当检测到空闲线路时,USART_ISR中的空闲位由硬件设置。它由软件清0,将1写入USART_ICR寄存器中的IDLECF值。我猜您需要在ISR中清除它,以防止中断立即被重新断言。尝试向usart1_isr()添加类似下面这一行的内容。
USART1_ICR = USART_ICR_IDLECF; // Clear Idle flag in the USART_ISR register.https://stackoverflow.com/questions/63928188
复制相似问题