【问题标题】:stm32 NVIC_EnableIRQ() bare metal equivalent?stm32 NVIC_EnableIRQ() 裸机等效?
【发布时间】:2021-01-07 11:13:48
【问题描述】:

我正在使用蓝色药丸,并试图找出中断。我有一个中断处理程序:

void __attribute__ ((interrupt ("TIM4_IRQHandler"))) myhandler()
{
    puts("hi");
    TIM4->EGR |= TIM_EGR_UG; // send an update even to reset timer and apply settings
    TIM4->SR &= ~0x01; // clear UIF
    TIM4->DIER |= 0x01; // UIE
}

我设置了计时器:

    RCC_APB1ENR |= RCC_APB1ENR_TIM4EN;
    TIM4->PSC=7999;
    TIM4->ARR=1000;
    TIM4->EGR |= TIM_EGR_UG; // send an update even to reset timer and apply settings
    TIM4->EGR |= (TIM_EGR_TG | TIM_EGR_UG);
    TIM4->DIER |= 0x01; // UIE enable interrupt
    TIM4->CR1 |= TIM_CR1_CEN;
   

我的计时器似乎没有启动。我认为我实际上并没有启用它。我有吗??

我在很多示例代码命令中看到:

NVIC_EnableIRQ(USART1_IRQn);

NVIC_EnableIRQ() 中实际发生了什么?

我在 Google 上四处搜索,但找不到与我类似的实际裸机代码。

我似乎错过了一个关键步骤。

2020 年 9 月 23 日更新感谢回答此问题的人。诀窍是在 NVIC_ISER 寄存器中设置中断号位。正如我在下面指出的,STM32F101xx 参考手册中似乎没有提到这一点,所以我可能永远无法自己解决这个问题;并不是说我在阅读数据表方面有任何真正的技能。

无论如何,天哪,我设法让中断工作!你可以在这里看到代码:https://github.com/blippy/rpi/tree/master/stm32/bare/04-timer-interrupt

【问题讨论】:

标签: stm32 interrupt bare-metal


【解决方案1】:

即使您使用裸机,您可能仍希望使用 CMSIS 头文件,这些头文件提供了非常基本的 ARM Cortex 元素的声明和内联版本,例如 NVIC_EnableIRQ

你可以在https://github.com/ARM-software/CMSIS_5/blob/develop/CMSIS/Core/Include/core_cm3.h#L1508找到NVIC_EnableIRQ

定义为:


#define NVIC_EnableIRQ __NVIC_EnableIRQ

__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    __COMPILER_BARRIER();
    NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
    __COMPILER_BARRIER();
  }
}

如果你愿意,你可以忽略__COMPILER_BARRIER()。以前的版本没有使用它。

该定义适用于 Cortex M-3 芯片。其他 Cortex 版本则不同。

【讨论】:

    【解决方案2】:

    库仍然被认为是裸机。没有操作系统,但无论如何,你有在这个级别学习的愿望很好。必须有人为其他人编写库。

    我打算在这里做一个完整的例子,(它真的需要很少的代码来做到这一点),但会从我的代码中获取这个使用 timer1 的板。

    您显然需要 ARM 文档(cortex-m3 的技术参考手册和 armv7-m 的体系结构参考手册)以及此部分的数据表和参考手册(不需要任何一家公司的程序员手册)。

    您几乎没有提供与使零件正常工作相关的任何信息。您永远不应该直接进入中断,它们是高级主题,您应该在最终启用中断进入内核之前尽可能多地轮询。

    我更喜欢让 uart 工作,然后在翻转、计数等时使用它来观察计时器寄存器。然后查看/确认状态寄存器已触发,学习/确认如何清除它(有时它只是一个清除正在阅读)。

    然后将其启用到 NVIC 并通过轮询查看 NVIC 看到它,并且您可以清除它。

    您没有显示您的向量表,这是让您的中断处理程序正常工作的关键。更不用说核心启动了。

    08000000 <_start>:
     8000000:   20005000
     8000004:   080000b9
     8000008:   080000bf
     800000c:   080000bf
    ...
     80000a0:   080000bf
     80000a4:   080000d1
     80000a8:   080000bf
    ...
    080000b8 <reset>:
     80000b8:   f000 f818   bl  80000ec <notmain>
     80000bc:   e7ff        b.n 80000be <hang>
    ...
    080000be <hang>:
     80000be:   e7fe        b.n 80000be <hang>
    ...
    080000d0 <tim1_handler>:
    

    第一个字加载堆栈指针,其余的是向量,处理程序的地址或与一个或一个(我会让你查一下)。

    在这种情况下,参考手册显示中断 25 是地址 0x000000A4 处的 TIM1_UP。哪个镜像到 0x080000A4,这就是处理程序在我的二进制文件中的位置,如果你的不是那么两件事,一个你可以使用 VTOR 找到对齐的空间,有时是 sram 或你为此构建的其他闪存空间并指向那里,但您的向量表处理程序必须具有正确的指针,否则您的中断处理程序将无法运行。

    volatile unsigned int counter;
    void tim1_handler ( void )
    {
        counter++;
        PUT32(TIM1_SR,0);
    }
    

    volatile 不一定是在中断处理程序和前台任务之间共享变量的正确方法,它恰好对我使用这个编译器/代码,你可以做研究甚至更好,检查编译器输出(反汇编二进制文件)以确认这不是问题。

    ra=GET32(RCC_APB2ENR);
    ra|=1<<11;  //TIM1
    PUT32(RCC_APB2ENR,ra);
    
    ...
    
    counter=0;
    PUT32(TIM1_CR1,0x00001);
    PUT32(TIM1_DIER,0x00001);
    PUT32(NVIC_ISER0,0x02000000);
    for(rc=0;rc<10;)
    {
        if(counter>=1221)
        {
            counter=0;
            toggle_led();
            rc++;
        }
    }
    PUT32(TIM1_CR1,0x00000);
    PUT32(TIM1_DIER,0x00000);
    

    tim1 的最小初始化和运行时。

    请注意,NVIC_ISER0 的第 25 位设置为启用中断 25。

    在尝试此代码之前,我轮询了计时器状态寄存器以查看它是如何工作的,与文档进行比较,根据文档清除中断。然后通过 NVIC_ICPR0,1,2 寄存器确认它是中断 25。此外,外围设备和 NVIC 之间没有其他门(某些供应商的某些芯片可能有)。

    然后通过 NVIC_ISER0 将其释放到内核。

    如果您不采取这些婴儿步骤,也许您已经采取了,这只会使任务变得更糟并且花费更长的时间(是的,有时您很幸运)。

    TIM4 在向量表中看起来是中断 30,偏移量/地址 0x000000B8。 NVIC_ISER0 (0xE000E100) 涵盖了前 32 个中断,因此该寄存器中有 30 个。如果您反汇编您使用库生成的代码,那么我们可以看到发生了什么,或者在库源代码中查找它(就像有人已经为您所做的那样)。

    当然,你的定时器 4 代码需要正确初始化定时器并触发中断,我没有检查。

    有例子,你需要继续看。

    最小值是

    1. 表格中的向量
    2. 设置中断设置启用寄存器中的位
    3. 启用中断以离开外设
    4. 触发中断

    不一定按这个顺序。

    【讨论】:

    • 啊哈!好的,这给了我足够的线索让它发挥作用。我在数据表中看不到 NVIC_ISER0,所以我有点困惑。尽管如此,地址似乎是 0xE000E100 - 这是我通过谷歌搜索得到的。我只需要使用我尝试设置的中断号来设置该位。我会更新我原来的问题。非常感谢。
    猜你喜欢
    • 2021-12-04
    • 1970-01-01
    • 2012-01-12
    • 2012-12-03
    • 2021-09-16
    • 1970-01-01
    • 2011-10-15
    • 2014-04-29
    • 1970-01-01
    相关资源
    最近更新 更多