Embedded 在STM32上编程NVIC(无库)

Embedded 在STM32上编程NVIC(无库),embedded,interrupt,stm32,isr,Embedded,Interrupt,Stm32,Isr,我已经彻底搜索了我正在使用的STM32F4 MCU(包括STM32F4xx MCU的PM0214)的数据表和用户手册,甚至在线搜索了有关通用MCU的信息,以了解如何在没有库的情况下编程中断。。。但是没有用。NVIC与硬件的联系是否如此紧密,以至于在没有某种库的情况下,编程一个中断并指定一个ISR地址和函数的首字母缩略词是不现实的?在每一篇文章和文档中,我都会看到如下内容: NVIC_EnableIRQ(IRQn_Type IRQn) NVIC_SetPriority(IRQn_Type IRQn

我已经彻底搜索了我正在使用的STM32F4 MCU(包括STM32F4xx MCU的PM0214)的数据表和用户手册,甚至在线搜索了有关通用MCU的信息,以了解如何在没有库的情况下编程中断。。。但是没有用。NVIC与硬件的联系是否如此紧密,以至于在没有某种库的情况下,编程一个中断并指定一个ISR地址和函数的首字母缩略词是不现实的?在每一篇文章和文档中,我都会看到如下内容:

NVIC_EnableIRQ(IRQn_Type IRQn)
NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
但是,如果有人为了学习而从头开始编写ISR,该怎么办


涉及哪些步骤?证明文件在哪里?建议/值得我这么做吗?

我快速通读了PM0214,它比我记忆中的要短。有关NVIC的详细信息,请参阅第4.3节。从第4.3.2节开始讨论寄存器本身

CMSIS库所做的就是与这些寄存器交互。如果您非常仔细地遵循,您可以很好地在几个小时内(希望如此!)为您的特定应用程序构建自己的NVIC库

一种更快的方法是浏览STM32Fx CMSIS库的源代码。我这样做是为了实现比CMSIS中包含的方法更快的GPIO切换方法

因此,我认为这是值得你花时间的。我发现许多STM32Fx库非常笨拙,我不喜欢它们的语法


我已经很多年没有这样做了,所以我可能已经完全离开了。让我知道这是否适合你

一个完整的示例,没有头文件也没有库。核仁-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
诺曼

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);
}
建造

可以将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
一旦我可以看到挂起寄存器发生变化,并通过文档确认该中断是正确的,那么您可以在相应的set enable寄存器中设置相同的位,以最终让中断命中cpu

构建notmain.bin并将其复制到虚拟NucleoDrive,当中断触发和wfi唤醒时,它将每秒打印一次UV。当然,您通常不希望在中断服务例程中打印uart中的内容,但在这种情况下,我们知道这是每秒一次,没有其他东西会干扰外围设备,因此在这种特定情况下是安全的

/linux上的dev/ttyACM0或windows上的任何等价物是uart输出来自核子调试板的地方。你可以很容易地改变 这会使LED闪烁。注:我移除了24小时保护初始化, 弄乱时钟会很快造成芯片损坏。STM32系列有一个内部引导加载程序和带引脚,因此您可以使用它来解除锁定,但在深入研究时钟初始化代码之前,请非常小心,一次只执行一个缓慢的步骤,最好使用RC时钟启动uart,这样您就可以看到正在发生的事情,就像上面看到的中断发生了什么一样

你不必一开始就搞乱NVIC的优先级。有set enable寄存器和clear enable寄存器,每次读取时都会告诉您所需的已启用。有set pending和clear pending,它们都会在读取时告诉您什么是挂起的。对于任何系统上的中断,理想情况下,您希望知道如何在源位置清除挂起的中断,然后朝处理器方向努力,一些芯片设计当您在源位置清除中断时,它会一直清除,一些类似于此的中断会锁存它,因此您必须在两个位置清除它


每种类型都有16个NVIC寄存器,因此512个可能的独立中断,就像我说的,它们使cortex-m变得几乎微不足道,你没有一条中断线,然后你必须费力地通过,看看是谁造成的,并在清除第一条中断线的同时处理其他进来的中断。您可以让一个外设具有多个中断,但它是一个外设,而不是系统中的所有中断。他们还设计了cortex-m异常逻辑,使您可以直接将(eabi兼容编译器)C函数放入向量表中,您不需要通过保存堆栈上的状态和清理来包装代码,也不需要使用中断指令的特殊返回。cortex-m逻辑为您完成了所有这些,因此请理解,您对该芯片/系列有点宠坏了,但这没关系,您可以在这里湿脚,然后进行可能更复杂的MCU设计。遵循相同的步骤,尽管尽可能一步一步地轮询,但在实际中断CPU之前,尽可能多地执行您需要了解的外围设备步骤,然后根据CPU设计根据需要执行这些步骤,了解如何确定挂起的内容,以及如何清除挂起的内容,并在返回之前检查是否存在其他中断等等。

您提到的函数都是作为源代码提供的-您只需查看它们就可以了。它们非常简单。不过,我不想麻烦-它们是标准的CMSIS功能,可供所有用户使用
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
 80000fc:   08000137
 8000100:   08000137
 8000104:   08000137
 8000108:   08000169
 800010c:   08000137
 8000110:   08000137
 8000114:   08000137