stm32 NVIC_EnableIRQ() 裸机等效?
Posted
技术标签:
【中文标题】stm32 NVIC_EnableIRQ() 裸机等效?【英文标题】:stm32 NVIC_EnableIRQ() bare metal equivalent? 【发布时间】: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
【问题讨论】:
Cortex内核相关的细节一般在《编程手册》中提到,而不是在《参考手册》中。 在此处找到 NVIC 寄存器的描述:booksite.elsevier.com/9780124080829/downloads/APP-06.pdf 【参考方案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 代码需要正确初始化定时器并触发中断,我没有检查。
有例子,你需要继续看。
最小值是
-
表格中的向量
设置中断设置启用寄存器中的位
启用中断以离开外设
触发中断
不一定按这个顺序。
【讨论】:
啊哈!好的,这给了我足够的线索让它发挥作用。我在数据表中看不到 NVIC_ISER0,所以我有点困惑。尽管如此,地址似乎是 0xE000E100 - 这是我通过谷歌搜索得到的。我只需要使用我尝试设置的中断号来设置该位。我会更新我原来的问题。非常感谢。以上是关于stm32 NVIC_EnableIRQ() 裸机等效?的主要内容,如果未能解决你的问题,请参考以下文章