我想在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();
}
}
答案 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.