我已经彻底搜索了我正在使用的STM32F4 MCU的数据表和用户手册(包括用于STM32F4xx MCU的PM0214),甚至是有关常规MCU的在线信息,以加深对无库编程中断的理解。但无济于事。 NVIC是否与硬件紧密地联系在一起,以至于在没有某种库的情况下,编程中断并指定ISR地址和函数的首字母缩写如今不切实际?在每个帖子和文档中,我都会看到类似以下内容的内容:
NVIC_EnableIRQ(IRQn_Type IRQn)
NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
但是,如果有人想为学习目的而从头开始编写ISR,该怎么办?
涉及哪些步骤?支持文件在哪里?是否建议/值得我这样做?
最佳答案
一个完整的例子,没有头文件也没有库。 NUCLEO-F411RE板。许多STMF4几乎相同,而cortex-m4将相同。在任何系统MCU上,您都应尽可能缓慢地中断CPU,一次一层/一步。这样容易得多。
闪存
.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
notmain.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);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
建立
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
可以将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
一旦我看到待处理的寄存器发生变化并通过正确的中断文档进行确认,便可以在相应的设置使能寄存器中设置相同的位,以最终使中断进入CPU。
编译并复制notmain.bin到虚拟核驱动器,并在中断触发和wfi唤醒时每秒打印UV。自然,您通常不希望在中断服务程序中将uart打印出来,但是在这种情况下,我们知道这是每秒一次,因此没有其他事情会干扰外围设备,因此在这种情况下是安全的。
linux上的/ dev / ttyACM0或Windows上的等效文件是uart输出来自NUCLEO调试板的位置。您可以轻松更改
这使LED灯闪烁。请注意,我已全天候删除了保护,
弄乱时钟可以很快使芯片变砖。 STM32系列具有一个内部引导加载程序和带状引脚,因此您可以使用它自如,但是在深入了解时钟初始化代码之前,请务必非常小心并一次缓慢执行一次,理想情况下是使用RC时钟启动uart因此您可以看到发生了什么,就像上面的内容一样,观察中断发生了什么。
最初您不必弄乱NVIC上的优先级。读取时会告诉您启用了设置的使能寄存器和清除使能寄存器。有已设置的待处理和清除的待处理,读取时两者都会告诉您什么是待处理。通常,在任何系统上使用中断时,理想情况下,您都想知道如何清除源中的待处理中断,然后朝处理器的方向工作;某些芯片设计在您从源中清除时,会一直清除,例如这个锁住了它,所以您必须在两个地方都将其清除。
每种类型都有16个NVIC寄存器,因此有512个可能的独立中断,就像我说过的,它们使cortex-m疯狂得几乎是微不足道的,您没有一条中断线,然后您必须走过去看看是谁造成了它并与其他人打交道当您清除第一个队列时。您可以让一个外设拥有一个以上的中断,但是这不是系统中所有外围设备的唯一外设。他们还设计了cortex-m异常逻辑,您可以将一个(兼容nabi的编译器)C函数直接放入向量表中,而无需将代码保存到堆栈中并进行清理即可包装代码,不要使用中断指令的特殊返回。 cortex-m逻辑会为您完成所有这些工作,因此请了解您对此芯片/系列有点满意,但这没关系,您可以先弄湿这里,然后再进行可能更复杂的MCU设计。遵循相同的步骤,尽管在可能的情况下一次轮询一次,但在实际中断CPU之前需要采取尽可能多的步骤来了解外围设备,然后根据CPU的设计,甚至在需要的地方进行操作确定什么挂起以及如何清除该挂起并在返回之前检查其他中断,等等。
关于embedded - 在STM32上编程NVIC(无库),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43059223/