2

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

我坚持的方法是在一个大的轮询循环中完成整个事情,这将包括 3 个 FIFO 来处理 I/O。乍一看似乎可行,有没有人根据经验发表任何评论?

中断问题并非微不足道,100% 平台兼容的 hello world 代码片段直接从网络上运行失败,当它们在混乱状态下运行时系统崩溃。如果碰巧在某人知道的某个地方有明确的专家修复,将不胜感激。

4

2 回答 2

4

在不了解您的应用程序和目标平台的情况下,我无法给您明确的答案!

但是,您要求根据经验发表评论。开始 :-)

  1. 您提到了实时要求。没有中断的工作实际上可以帮助解决这个问题!当我们做具有严格实时要求的项目时,我们没有使用中断。假设我们正在处理传入的数据流,并且正好有 20 个我们来处理一个单词,否则我们会错过下一个,并且在处理一个单词的过程中我们被打断了。砰! 丢了下一个。所以我们做了很多投票。在不同的应用程序中,设计决策可能会有所不同,使用中断来处理时间关键的工作而牺牲非实时代码等。我当时工作的那家店的理念是非常抗干扰。

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

  3. FIFO 很好。它们软化了您的实时要求。

  4. 我根本不知道你的硬件设计,但如果你有一个逻辑芯片和一个灵活的硬件工程师(好吧,那是一个矛盾的说法:-))你可以通过硬件路由你的一些输入等等并使用硬件处理您的一些简单需求的逻辑,并为您提供一个使编写程序更容易的界面(例如,可以是针对您的特定需求进行优化的某种 FIFO,或者一个轮询寄存器,它可以在一个中为您提供多条信息去,等等)

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

于 2017-08-31T05:37:34.213 回答
3

不知道你被困在哪里我们需要更多信息,但也许一个小骨架会确认你至少在做这几件事。你以前做过 arm7 还是第一次,或者你精通 arm7/arm 世界但不能让中断工作?

开始.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

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

不是主要的:

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

闪存.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 中断向量位置 (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

这应该可以正常工作。

如果这对初级完全没用,那么让我知道将删除这个答案没问题。

不是main.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,启动,有一个简单的例程来打印数据

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 以查看它弹出。或轻弹led或其他东西。

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

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

再次如果这完全没用对不起,将删除。我想我有一个/一些 2148,但没有 2138,我不会为了编写/测试工作代码而订购一个。自从这个 ARMV7TDMI 出来之后就一直在使用 arm,并且在现在的 armv8 中变得流行起来,这要痛苦得多。pi-zero 之类的东西很有趣,因为它们是像 arm7 这样的老派......

我认为计时器会更容易获得真正的中断。另一个可能是 gpio 引脚,尽管我认为这需要更多工作,但会在引脚之间连接一个跳线,如果该芯片使用输出 gpio 更改输入的状态,则将一个作为输出,另一个作为带有中断边沿检测的输入一种干净的方式,并通过观察中断依次击中每一层的整个轮询过程,并确认您可以在每一层每次移除中断。然后敲击核心边缘,最后让它进入核心。

于 2017-08-31T06:00:43.507 回答