spo*_*mus 2 embedded interrupt stm32 isr
我已经彻底搜索了我正在使用的 STM32F4 MCU 的数据表和用户手册(包括用于 STM32F4xx MCU 的 PM0214)以及关于一般 MCU 的在线信息,以了解如何在没有库的情况下进行编程中断......但无济于事。NVIC 是否与硬件如此紧密地联系在一起,以至于在没有某种库的情况下对中断进行编程并为函数指定 ISR 地址和首字母缩略词在当今是不切实际的?在每篇文章和文档中,我都会看到类似的内容:
NVIC_EnableIRQ(IRQn_Type IRQn)
NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
Run Code Online (Sandbox Code Playgroud)
但是如果有人出于学习目的想从头开始编写 ISR 呢?
涉及哪些步骤?支持文件在哪里?是否建议/值得我这样做?
一个完整的例子,没有头文件没有库。NUCLEO-F411RE 板。许多 STMF4 几乎相同,cortex-m4 将相同。在任何系统 MCU 或其他方式上,您都应该尽可能缓慢地中断 CPU,一次一层/一步。这样就容易多了。
flash.s
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang @ NMI
.word hang @ HardFault
.word hang @ MemManage
.word hang @ BusFault
.word hang @ UsageFault
.word hang @ 7
.word hang @ 8
.word hang @ 9
.word hang @ 10
.word hang @ SVCall
.word hang @ DebugMonitor
.word hang @ Reserved
.word hang @ PendSV
.word hang @ SysTick
.word hang @ External interrupt 0
.word hang @ External interrupt 1
.word hang @ External interrupt 2
.word hang @ External interrupt 3
.word hang @ External interrupt 4
.word hang @ External interrupt 5
.word hang @ External interrupt 6
.word hang @ External interrupt 7
.word hang @ External interrupt 8
.word hang @ External interrupt 9
.word hang @ External interrupt 10
.word hang @ External interrupt 11
.word hang @ External interrupt 12
.word hang @ External interrupt 13
.word hang @ External interrupt 14
.word hang @ External interrupt 15
.word hang @ External interrupt 16
.word hang @ External interrupt 17
.word hang @ External interrupt 18
.word hang @ External interrupt 19
.word hang @ External interrupt 20
.word hang @ External interrupt 21
.word hang @ External interrupt 22
.word hang @ External interrupt 23
.word hang @ External interrupt 24
.word hang @ External interrupt 25
.word hang @ External interrupt 26
.word hang @ External interrupt 27
.word hang @ External interrupt 28
.word hang @ External interrupt 29
.word hang @ External interrupt 30
.word hang @ External interrupt 31
.word hang @ External interrupt 32
.word hang @ External interrupt 33
.word hang @ External interrupt 34
.word hang @ External interrupt 35
.word hang @ External interrupt 36
.word hang @ External interrupt 37
.word hang @ External interrupt 38
.word hang @ External interrupt 39
.word hang @ External interrupt 40
.word hang @ External interrupt 41
.word hang @ External interrupt 42
.word hang @ External interrupt 43
.word hang @ External interrupt 44
.word hang @ External interrupt 45
.word hang @ External interrupt 46
.word hang @ External interrupt 47
.word hang @ External interrupt 48
.word hang @ External interrupt 49
.word tim5_handler @ External interrupt 50
.word hang @ External interrupt 51
.word hang @ External interrupt 52
.word hang @ External interrupt 53
.word hang @ External interrupt 54
.word hang @ External interrupt 55
.word hang @ External interrupt 56
.word hang @ External interrupt 57
.word hang @ External interrupt 58
.word hang @ External interrupt 59
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl DOWFI
DOWFI:
wfi
bx lr
Run Code Online (Sandbox Code Playgroud)
不是main.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void DOWFI ( void );
#define RCCBASE 0x40023800
#define RCC_CR (RCCBASE+0x00)
#define RCC_CFGR (RCCBASE+0x08)
#define RCC_APB1RSTR (RCCBASE+0x20)
#define RCC_AHB1ENR (RCCBASE+0x30)
#define RCC_APB1ENR (RCCBASE+0x40)
#define RCC_BDCR (RCCBASE+0x70)
#define GPIOABASE 0x40020000
#define GPIOA_MODER (GPIOABASE+0x00)
#define GPIOA_OTYPER (GPIOABASE+0x04)
#define GPIOA_OSPEEDR (GPIOABASE+0x08)
#define GPIOA_PUPDR (GPIOABASE+0x0C)
#define GPIOA_BSRR (GPIOABASE+0x18)
#define GPIOA_AFRL (GPIOABASE+0x20)
#define USART2BASE 0x40004400
#define USART2_SR (USART2BASE+0x00)
#define USART2_DR (USART2BASE+0x04)
#define USART2_BRR (USART2BASE+0x08)
#define USART2_CR1 (USART2BASE+0x0C)
#define TIM5BASE 0x40000C00
#define TIM5_CR1 (TIM5BASE+0x00)
#define TIM5_DIER (TIM5BASE+0x0C)
#define TIM5_SR (TIM5BASE+0x10)
#define TIM5_CNT (TIM5BASE+0x24)
#define TIM5_PSC (TIM5BASE+0x24)
#define TIM5_ARR (TIM5BASE+0x2C)
#define NVIC_ISER1 0xE000E104
#define NVIC_ICPR1 0xE000E284
//PA2 is USART2_TX alternate function 1
//PA3 is USART2_RX alternate function 1
static int clock_init ( void )
{
unsigned int ra;
//switch to external clock.
ra=GET32(RCC_CR);
ra|=1<<16;
PUT32(RCC_CR,ra);
while(1) if(GET32(RCC_CR)&(1<<17)) break;
ra=GET32(RCC_CFGR);
ra&=~3;
ra|=1;
PUT32(RCC_CFGR,ra);
while(1) if(((GET32(RCC_CFGR)>>2)&3)==1) break;
return(0);
}
static int uart2_init ( void )
{
unsigned int ra;
ra=GET32(RCC_AHB1ENR);
ra|=1<<0; //enable port A
PUT32(RCC_AHB1ENR,ra);
ra=GET32(RCC_APB1ENR);
ra|=1<<17; //enable USART2
PUT32(RCC_APB1ENR,ra);
ra=GET32(GPIOA_MODER);
ra&=~(3<<4); //PA2
ra&=~(3<<6); //PA3
ra|=2<<4; //PA2
ra|=2<<6; //PA3
PUT32(GPIOA_MODER,ra);
ra=GET32(GPIOA_OTYPER);
ra&=~(1<<2); //PA2
ra&=~(1<<3); //PA3
PUT32(GPIOA_OTYPER,ra);
ra=GET32(GPIOA_OSPEEDR);
ra|=3<<4; //PA2
ra|=3<<6; //PA3
PUT32(GPIOA_OSPEEDR,ra);
ra=GET32(GPIOA_PUPDR);
ra&=~(3<<4); //PA2
ra&=~(3<<6); //PA3
PUT32(GPIOA_PUPDR,ra);
ra=GET32(GPIOA_AFRL);
ra&=~(0xF<<8); //PA2
ra&=~(0xF<<12); //PA3
ra|=0x7<<8; //PA2
ra|=0x7<<12; //PA3
PUT32(GPIOA_AFRL,ra);
ra=GET32(RCC_APB1RSTR);
ra|=1<<17; //reset USART2
PUT32(RCC_APB1RSTR,ra);
ra&=~(1<<17);
PUT32(RCC_APB1RSTR,ra);
//PUT32(USART2_CR1,(1<<13));
//8000000/(16*115200) = 4.34 4+5/16
PUT32(USART2_BRR,0x45);
PUT32(USART2_CR1,(1<<3)|(1<<2)|(1<<13));
return(0);
}
static void uart2_send ( unsigned int x )
{
while(1) if(GET32(USART2_SR)&(1<<7)) break;
PUT32(USART2_DR,x);
}
static void hexstrings ( unsigned int d )
{
//unsigned int ra;
unsigned int rb;
unsigned int rc;
rb=32;
while(1)
{
rb-=4;
rc=(d>>rb)&0xF;
if(rc>9) rc+=0x37; else rc+=0x30;
uart2_send(rc);
if(rb==0) break;
}
uart2_send(0x20);
}
static void hexstring ( unsigned int d )
{
hexstrings(d);
uart2_send(0x0D);
uart2_send(0x0A);
}
void tim5_handler ( void )
{
uart2_send(0x55);
PUT32(TIM5_SR,0);
PUT32(NVIC_ICPR1,0x00040000);
}
int notmain ( void )
{
unsigned int ra;
unsigned int rb;
clock_init();
uart2_init();
hexstring(0x12345678);
ra=GET32(RCC_APB1ENR);
ra|=1<<3; //enable TIM5
PUT32(RCC_APB1ENR,ra);
if(0)
{
PUT32(TIM5_CR1,0x0000);
PUT32(TIM5_DIER,0x0000);
PUT32(TIM5_PSC,0x0000);
PUT32(TIM5_ARR,16000000-1);
PUT32(TIM5_CNT,16000000-1);
PUT32(TIM5_CR1,0x0001);
PUT32(TIM5_SR,0);
ra=GET32(TIM5_SR);
hexstring(ra);
while(1)
{
rb=GET32(TIM5_SR);
if(rb!=ra)
{
ra=rb;
hexstring(ra);
PUT32(TIM5_SR,0);
}
}
}
if(0)
{
PUT32(TIM5_CR1,0x0000);
PUT32(TIM5_DIER,0x0001);
PUT32(TIM5_PSC,0x0000);
PUT32(TIM5_ARR,16000000-1);
PUT32(TIM5_CNT,16000000-1);
PUT32(TIM5_CR1,0x0001);
PUT32(TIM5_SR,0);
while(1)
{
ra=GET32(NVIC_ICPR1);
if(ra)
{
hexstring(ra);
PUT32(TIM5_SR,0);
PUT32(NVIC_ICPR1,ra);
}
}
}
if(0)
{
PUT32(TIM5_CR1,0x0000);
PUT32(TIM5_DIER,0x0001);
PUT32(TIM5_PSC,0x0000);
PUT32(TIM5_ARR,16000000-1);
PUT32(TIM5_CNT,16000000-1);
PUT32(TIM5_CR1,0x0001);
PUT32(TIM5_SR,0);
while(1)
{
ra=GET32(NVIC_ICPR1);
if(ra)
{
hexstring(ra);
PUT32(TIM5_SR,0);
PUT32(NVIC_ICPR1,ra);
}
}
}
if(1)
{
PUT32(TIM5_CR1,0x0000);
PUT32(TIM5_DIER,0x0001);
PUT32(TIM5_PSC,0x0000);
PUT32(TIM5_ARR,16000000-1);
PUT32(TIM5_CNT,16000000-1);
PUT32(TIM5_CR1,0x0001);
PUT32(TIM5_SR,0);
PUT32(NVIC_ICPR1,0x00040000);
PUT32(NVIC_ISER1,0x00040000);
while(1)
{
DOWFI();
uart2_send(0x56);
}
}
return(0);
}
Run Code Online (Sandbox Code Playgroud)
闪存文件
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
Run Code Online (Sandbox Code Playgroud)
建造
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -o notmain.elf -T flash.ld flash.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
Run Code Online (Sandbox Code Playgroud)
可以将 cortex-m0s 更改为 cortex-m4s。
cortex-m4 架构参考手册显示了 NVIC 寄存器的地址,一旦您了解外设如何设置其中断状态,您就可以启用和轮询各种 NVIC 中断挂起寄存器,直到看到一组。然后找出它是什么中断号,并查看 ST 文档,它应该匹配,在这种情况下,第二个寄存器中的第 18 位(如果从所有寄存器的开始到结束计数,则为第 50 位)是计时器 5,看在 ST 文档中断 50 是计时器 5,以便匹配。st 文档还告诉我们它是地址 0x108,它恰好与我手工计算它们相匹配。
80000fc: 08000137
8000100: 08000137
8000104: 08000137
8000108: 08000169
800010c: 08000137
8000110: 08000137
8000114: 08000137
Run Code Online (Sandbox Code Playgroud)
一旦我可以看到待处理的寄存器发生变化并通过文档确认是正确的中断,那么您可以在相应的设置启用寄存器中设置相同的位,最终让中断击中 CPU。
构建并将 notmain.bin 复制到虚拟核驱动器,它会在中断触发和 wfi 唤醒时每秒打印一次 UV。自然地,您通常不想在中断服务例程中将内容打印出 uart,但在这种情况下,我们知道它是每秒一次,没有其他任何事情会干扰外设,因此在这种特定情况下是安全的。
linux 上的 /dev/ttyACM0 或 windows 上的任何等效项是 uart 输出来自 NUCLEO 调试板的地方。您可以轻松地将其更改为闪烁 LED。注意我取消了时钟初始化的保护,弄乱时钟会很快使芯片变砖。STM32 系列有一个内部引导加载程序和带引脚,因此您可以使用它不受限制,但在深入研究时钟初始化代码之前,要非常小心,一次慢一步,理想情况下使用 RC 时钟启动 uart所以你可以看到发生了什么,就像上面观察中断发生了什么一样。
您最初不必在 NVIC 上搞乱优先级。有设置使能寄存器和清除使能寄存器,每次读取时都会告诉您要启用。有设置挂起和清除挂起,读取时会告诉您什么是挂起的。对于任何系统上的一般中断,您理想地想知道如何在源头清除挂起的中断,然后朝着处理器前进,某些芯片设计当您在源头清除它时,它会一直清除,有些像这个锁住它,所以你必须在两个地方清除它。
每种类型有 16 个 NVIC regstier,所以有 512 个可能的独立中断,就像我说的那样,它们让 cortex-m 变得微不足道,你没有一条中断线,然后你必须涉足看看是谁造成的,并处理其他人的到来在您清除队列中的第一个时。您可以让一个外设有多个中断,但它是一个外设,而不是系统中的所有中断。他们还以一种方式设计了 cortex-m 异常逻辑,您可以将一个(与 eabi 兼容的编译器)C 函数直接放入向量表中,您不需要通过将状态保存在堆栈上并清理来包装该代码,然后您不要使用中断指令的特殊返回。cortex-m 逻辑为您完成了所有这些,所以请理解您对这个芯片/系列有点宠坏了,但是没关系,你可以在这里弄湿你的脚,然后进行可能更复杂的 MCU 设计。遵循相同的步骤,尽管尽可能一次轮询您的方式,在实际中断 CPU 之前采取尽可能多的步骤来了解外设,然后甚至根据 CPU 设计的需要,研究如何确定待处理的内容以及如何清除它并在返回之前检查其他中断等...