我已经彻底搜索了我正在使用的STM32F4 MCU的数据表和用户手册(包括用于STM32F4xx MCU的PM0214)以及有关通用MCU的在线信息,以便了解如何在没有库的情况下编程中断......但无济于事。 NVIC是如此紧密地绑定到编程中断的硬件并指定一个ISR地址和一个功能的首字母缩写,如今没有某种类型的库是不切实际的吗?在每篇文章和文档中,我都看到了类似的内容:
NVIC_EnableIRQ(IRQn_Type IRQn)
NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
但如果有人想从头开始编程ISR用于学习目的呢?
涉及的步骤是什么?支持文档在哪里?建议/值得我这么做吗?
答案 0 :(得分:4)
一个完整的例子,没有头文件没有库。 NUCLEO-F411RE板。许多STMF4几乎相同,皮质-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
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
可以将皮质-m0s改变为皮质-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
一旦我看到挂起的寄存器改变并用正确的中断文档确认,那么你可以在相应的set enable寄存器中设置相同的位,最后让中断命中cpu。
构建并复制notmain.bin到虚拟核心驱动器,当中断触发并且wfi唤醒时,它将每秒打印UV。当然,你通常不想在中断服务程序中打印uart,但在这种情况下,我们知道它只是一秒钟,没有其他任何东西干扰外设,因此在这种特定情况下它是安全的。 / p> Linux上的
/ dev / ttyACM0或Windows上的等价物是uart输出来自NUCLEO调试板的地方。你可以轻松改变 这使闪烁的LED。注意我全天候删除了保护, 弄乱时钟可以很快打砖块。 STM32系列有一个内部自举程序和带状引脚,所以你可以使用它来解决问题,但是在深入了解时钟初始化代码之前,要非常小心,一次只能采取一个慢速步骤,理想情况下使用RC时钟进行启动所以你可以看到发生了什么,就像上面观察中断会发生什么一样。
你最初不得不搞乱NVIC的优先事项。每次读取时都会设置使能寄存器和清除使能寄存器,告诉您想要启用。有设置待处理和清除挂起,或者会告诉你读取时挂起的内容。通常在任何系统上都有中断,你理想地想要知道如何在源处清除待处理的中断,然后按照你的方式向处理器工作,当你在源处清除它时,一些芯片设计,它会一直清除,有些像这个锁定它,所以你必须在两个地方清除它。
每种类型有16个NVIC注册器,因此512个可能的单独中断,就像我说他们疯狂的皮质m几乎是微不足道的,你没有一个中断线然后你必须趟过去看谁造成它和交易当你清除第一个排队时,其他人进来。您可以让一个外围设备有多个中断,但它是一个外设,而不是系统中的所有外设。他们还设计了cortex-m异常逻辑,你可以直接在向量表中放置一个(n eabi兼容的编译器)C函数,你不需要将该状态保存在堆栈上并清理你不要使用中断指令的特殊返回。 cortex-m逻辑为你做了所有这些,所以要理解你对这个芯片/系列有点不满,但是你可以在这里弄湿你的脚,然后研究可能更复杂的MCU设计。按照相同的步骤,尽可能在可能的情况下一步一步地进行轮询,在实际中断CPU之前,根据需要采取尽可能多的步骤来理解外围设备,然后根据cpu设计,根据需要进行操作,确定待处理的内容以及如何清除它并在返回之前检查其他中断等等......
答案 1 :(得分:3)
我快速阅读了PM0214,它比我记得的要短。有关NVIC的详细信息,请参阅第4.3节。它从4.3.2节开始讨论寄存器本身。
所有CMSIS库都与这些寄存器进行交互。如果您非常认真地跟进,您可以在几个小时内为您的特定应用构建自己的NVIC库(希望如此!)。
更简单的方法是简单地浏览STM32Fx CMSIS库的源代码。我这样做是为了实现比CMSIS中包含的更快的GPIO切换方法。
因此我觉得可以值得你这么做。我发现许多STM32Fx库非常笨重,我不喜欢它们的语法。
多年来我没有这样做,所以我可能完全离开了。如果这对您有用,请告诉我!