嵌入式编程无中断

时间:2017-08-31 04:28:51

标签: arm embedded interrupt bare-metal arm7

我永远不会相信我可以在2017年担任这个职位,但是我有一个目标系统(LPC2138)绝对拒绝处理中断,尽管我有很多尝试。出于各种原因,我确实需要使用它,所以只需要继续使用它。该应用程序是“中断友好的”,具有多个异步I / O流(SPI,UART)和定时器信号。对我有利的一点是处理器与我的实时要求相比非常快,所以我有足够的备用grunt可用。

我坚持的方法是在一个大的轮询循环中完成整个过程,包括3个FIFO来处理I / O.快速浏览它似乎可行,有没有人根据经验有任何评论?

中断问题不是一件容易的事,100%平台兼容的hello world片段直接离开Web无法正常工作,当他们运行系统崩溃时处于混乱状态。如果确实发生了一个明确的,专家解决方案,某人知道,指针将非常感激。

2 个答案:

答案 0 :(得分:4)

如果不了解您的应用程序和目标平台,我无法给您一个明确的答案!

但是,您根据经验征求意见。这里是: - )

  1. 您提到了实时要求。没有中断的工作实际上可以帮助你!当我们执行具有硬实时要求的项目时,我们没有使用中断。让我们说我们正在处理传入的数据流,并且只有20个我们处理一个单词,否则我们会错过下一个单词,正好在处理一个单词的过程中我们被中断了。砰!失去了下一个。所以我们做了很多民意调查。在不同的应用程序中,设计决策可能不同,使用中断来处理时间关键的工作,代价是非实时代码,或者某些代码。我当时在店里工作的理念非常反中断。

  2. 轮询可能会浪费"一些资源(不是真的浪费,因为你必须这样做:-))。但是你提到你的处理器足够快。如果您能满足您的速度要求,请享受民意调查。在很多方面,它比中断更容易处理。您的程序可以更好地控制何时会发生什么。

  3. FIFO很好。它们可以减少您的实时要求。

  4. 我根本不知道你的设计,但如果你有一个逻辑芯片和一个灵活的工程师(好的,这是一个矛盾的:-))你可以路由一些通过hw输入等等,并使用hw逻辑来处理一些简单的需求,并为您提供一个界面,使编写程序更容易(例如,某种FIFO可以根据您的特定需求进行优化,或者注册投票,一次性给你几条信息,等等。)

  5. 所以,去吧!您将学习一种甚至具有一些优势的全新方法。

答案 1 :(得分:3)

没有线索你被困在哪里我们需要更多的信息,但也许一个小骨架会证实你至少做了这些事情。你之前做过arm7还是第一次这样做,或者你是精通arm7 / arm世界但却无法让中断工作?

的start.s

.globl _start
_start:

.globl _start
_start:
    ldr pc,reset_handler
    ldr pc,undefined_handler
    ldr pc,swi_handler
    ldr pc,prefetch_handler
    ldr pc,data_handler
    ldr pc,unused_handler
    ldr pc,irq_handler
    ldr pc,fiq_handler
reset_handler:      .word reset
undefined_handler:  .word hang
swi_handler:        .word hang
prefetch_handler:   .word hang
data_handler:       .word hang
unused_handler:     .word hang
irq_handler:        .word irq
fiq_handler:        .word hang

reset:
    ;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD2
    msr cpsr_c,r0
    ldr sp,=0x40002000

    ;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD1
    msr cpsr_c,r0
    ldr sp,=0x40003000

    ;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD3
    msr cpsr_c,r0
    ldr sp,=0x40004000

    bl notmain
hang: b hang

.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.globl dummy
dummy:
    bx lr

.globl enable_irq
enable_irq:
    mrs r0,cpsr
    bic r0,r0,#0x80
    msr cpsr_c,r0
    bx lr

irq:
    push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
    bl c_irq_handler
    pop  {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
    subs pc,lr,#4

是的,这是太多的寄存器......只需要编译器覆盖其他寄存器的易失性寄存器。

notmain:

void c_irq_handler ( void )
{
}
void notmain ( void )
{
    unsigned int ra;
    for(ra=0;ra<100;ra++) dummy(ra);
}

flash.ld

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x40000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > rom
    .bss : { *(.bss*) } > ram
}
猜猜我不需要.bss

构建

arm-none-eabi-as start.s -o start.o
arm-none-eabi-gcc -O2 -c notmain.c -o notmain.o
arm-none-eabi-ld -T flash.ld start.o notmain.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf -O binary so.bin

始终检查你的so.list

他们有一种有趣的方法来确定你是否有闪存或者是否应该转储到他们的引导程序中

  

有效用户代码的标准:保留的ARM中断向量   location(0x0000 0014)应包含2的补码   剩余中断向量的校验和。这会导致校验和   所有向量一起为0。

我还没有这样做,可以手动完成,或者以编程方式执行程序。

将其更改为此

ldr pc,reset_handler
ldr pc,undefined_handler
ldr pc,swi_handler
ldr pc,prefetch_handler
ldr pc,data_handler
.word 0xb8a06f58  @ldr pc,unused_handler
ldr pc,irq_handler
ldr pc,fiq_handler

这应该可以正常工作。

如果这对小学来说完全无用,那么让我知道会删除这个答案没问题。

notmain.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void enable_irq ( void );

#define T0IR 0xE0004000
#define T0TCR 0xE0004004
#define T0PC  0xE0004010
#define T0MCR 0xE0004014
#define T0TC  0xE0004008
#define T0MCR0 0xE0004018

void c_irq_handler ( void )
{
    PUT32(T0IR,1);
}
void notmain ( void )
{
    PUT32(T0TCR,2);
    PUT32(T0TCR,0);
    PUT32(T0TC,0);
    PUT32(T0MCR0,0x100000);
    PUT32(T0MCR,0x1); //3);
    PUT32(T0TCR,1);
    while(1)
    {
        if(GET32(T0IR&1)) break;
    }
    PUT32(T0IR,1);

    PUT32(T0TCR,2);
    PUT32(T0TCR,0);
    PUT32(T0TC,0);
    PUT32(T0MCR0,0x100000);
    PUT32(T0MCR,0x1); //3);
    PUT32(T0IR,1);
    enable_irq();
    PUT32(T0TCR,1);
    while(1) continue;

}

刚从手册中解决这个问题并没有检查是否有时钟启用计时器等。我个人首先获得gpio,用一个大的计数器循环闪烁一个led

for(ra=0;ra<0x20000;ra++) dummy(ra);

然后在轮询模式下使用一个计时器(只是让它开始自由运行)根据时间闪烁,可以计算出时钟速度,进入uart,得到uart,up,有一个简单的程序用于打印数据

void hexstring ( 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;
        uart_send(rc);
        if(rb==0) break;
    }
    uart_send(0x0D);
    uart_send(0x0A);
}
八角更容易,但男人很难在八进制中思考...

那么最后中断的东西,拿上面的代码,我会轮询和打印定时器中的各种寄存器,看看该中断寄存器是否先在轮询模式下触发(第二,第三,第四......没有启用对处理器的中断(暂时不要这样做)。

一旦我可以在外围层面看到它,有些人不这样做但是假设这个有。然后在这种情况下有一个VIC,我假设这个寄存器

VICRawIntr 0xFFFFF008
如果定时器中断被置位并被触发,也应该断言

。确认它(出现第4位)确认在外设中清除中断时它会消失。

VICIntSelect重置为零,这是irq,这就是我们现在不需要触摸它。

我假设在VICIntEnable中设置了第4位

然后再次打印出轮询,看看发生了什么。

现在我希望在VICIRQStatus中看到中断显示(仍然完全轮询还没有启用处理器的irq)并在外设清零时消失,或者如果外设中断被清除则弄清楚如何清除它不要这么做。

现在是时候启用irq到处理器了,我个人会把一个字节塞进uart看它弹出来。或轻弹导致或某事。

理论上我们只需清除外围设备并返回安全返回应用程序。

无论处理器是mcu还是全尺寸等,我都遵循相同的程序。中断可能是噩梦,你编写的代码越多而不测试失败的可能性越大。有时每个测试需要一行代码。 YMMV。

如果这是完全无用的抱歉,将删除。我想我有一个/一些2148但不是一个2138,并且我不打算只编写一个来编写/测试工作代码。因为这款ARMV7TDMI已经出现并且正在流行到现在更加痛苦的armv8s,所以一直在使用arm。 pi-zero等非常有趣,因为他们像这样的老式学校7 ...

我认为计时器将更容易从中得到真正的中断。另一个可能是一个gpio引脚虽然我认为这是更多的工作,但会在引脚之间绑一个跳线,如果这个芯片有它使用输出gpio来改变输入的状态,那么一个输出另一个带中断边沿检测的输入一个干净的方式,并通过整个轮询过程观察中断依次命中每一层,并确认你可以每次在每一层删除中断。然后敲击核心的边缘,然后最终让它进入核心。