2017-08-31 2 views
2

Ich hätte nie geglaubt, dass ich 2017 in dieser Position sein könnte, aber ich habe ein Zielsystem (LPC2138), das sich absolut nicht mit Interrupts abmüht, trotz vielem auf meiner Seite. Aus verschiedenen Gründen muss ich damit arbeiten, es ist also nur eine Frage des Weiterkommens. Die Anwendung ist 'Interrupt-freundlich', mit mehreren asynchronen I/O-Streams (SPI, UART) und Timer-Signalen. Die eine Sache zu meinen Gunsten ist, dass der Prozessor im Vergleich zu meinen Echtzeitanforderungen sehr schnell ist, so dass ich reichlich Ersatz-Grunt zur Verfügung habe.Programmierung eingebettet ohne Interrupts

Der Ansatz, mit dem ich festhalte, ist, das Ganze in einer großen abgefragten Schleife zu machen, die 3 FIFOs enthalten wird, um I/O zu handhaben. Auf den ersten Blick scheint es machbar zu sein, hat jemand irgendwelche Erfahrungen?

Das Interrupt-Problem ist nicht trivial, 100% Plattform-kompatibel hallo Weltschnipsel direkt aus dem Internet nicht funktionieren, wenn sie das System ausgeführt wird, stürzt in einem verschlüsselten Zustand ab. Wenn es irgendwo einen klaren, sachverständigen Fixpunkt gibt, von dem jemand weiß, würde ein Zeiger sehr geschätzt werden.

+0

bieten ein minimales Programm, das nicht funktioniert ... –

+1

nichts falsch mit Polling so lange wie Sie Ihr Timing treffen können, viel einfacher zu schreiben und zu debuggen, nur unterbrechen, wenn Sie unbedingt müssen. –

+0

Ich/wir haben sehr viel Erfahrung mit Arm und anderen Muskelgruppen. Ich schätze, Ihre Beispiele sind relativ groß und um Ihre Füße nass zu machen, brauchen Sie das nicht ... –

Antwort

4

Ohne Ihre Anwendung und Ihre Zielplattform gut zu kennen, kann ich Ihnen keine definitive Antwort geben!

Aber Sie bat um Kommentare basierend auf Erfahrung. Hier gehts :-)

  1. Sie erwähnen Echtzeitanforderungen. Arbeiten ohne Interrupts kann dabei helfen! Wenn wir Projekte mit harten Echtzeitanforderungen erstellten, verwendeten wir nicht Interrupts. Nehmen wir an, wir würden den eingehenden Datenstrom handhaben und hätten genau 20 Sekunden Zeit, um ein Wort zu verarbeiten, oder wir würden das nächste verpassen, und mitten in der Bearbeitung eines Wortes wurden wir unterbrochen. Knall! Verlor den nächsten. Also haben wir viel Polling gemacht. In einer anderen Anwendung könnten die Entwurfsentscheidungen unterschiedlich sein, indem Unterbrechungen verwendet werden, um zeitkritische Arbeit auf Kosten von Nicht-Echtzeit-Code oder dergleichen zu verarbeiten. Die Philosophie in dem Laden, in dem ich damals arbeitete, war sehr anti-interrupt.

  2. Polling kann einige Ressourcen "verschwenden" (nicht wirklich verschwenden, da Sie es tun müssen :-)). Aber Sie erwähnen, dass Ihr Prozessor schnell genug ist. Wenn Sie Ihre Geschwindigkeitsanforderungen erfüllen können, genießen Sie die Abstimmung. Es ist einfacher zu handhaben als Unterbrechungen, in vielerlei Hinsicht. Ihr Programm hat mehr Kontrolle darüber, was wann passiert.

  3. FIFOs sind gut. Sie mildern Ihre Echtzeitanforderungen.

  4. Ich kenne Ihr hw Design überhaupt nicht, aber wenn Sie einen Logikchip dort und einen flexiblen hw Ingenieur haben (OK, das ist ein Oxymoron :-)) können Sie einige Ihrer Eingänge und so weiter durchgehen Das hw und hw Logik verwenden, um einige Ihrer einfachen Anforderungen zu behandeln, und präsentieren Ihnen eine Schnittstelle, um das Schreiben des Programms einfacher zu machen (könnte eine Art von FIFO für Ihre spezifischen Bedürfnisse optimiert, zum Beispiel, oder ein Register zu abfragen, das gibt Ihnen mehrere Informationen in einem Stück, etc.)

Also los! Sie werden einen ganz neuen Ansatz erlernen, der sogar einige Vorteile hat.

3

haben Sie keine Ahnung, wo Sie stecken bleiben wir brauchen mehr Informationen, aber vielleicht ein kleines Skelett wird bestätigen, dass Sie zumindest diese wenigen Dinge tun. Hast du schon einmal einen Arm7 gemacht oder ist das das erste Mal, oder bist du in der Arm7/Arm-Welt versiert, aber kannst du die Interrupts einfach nicht zur Arbeit bringen?

starten.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 

ja das sind zu viele register ... nur brauchen die flüchtigen die compiler die anderen abdeckt.

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 
} 

denke ich nicht brauchen .bss

build

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 

immer Ihre so.list untersuchen

sie haben eine interessante Art und Weise zu bestimmen, ob Sie einen Blitz dort haben, oder wenn sie in ihren Bootloader

Kriterium für gültigen Code-Dump sollte: Der reservierte ARM Interruptvektor Lage (0x0000 0014) enthalten sollte die 2er-Komplement der Check-Summe der restlichen Interrupt-Vektoren. Dies bewirkt, dass die Prüfsumme von allen Vektoren zusammen 0.

ich sein knapp über das noch tun, tun können, die von Hand oder haben ein Programm, kommen nach und tun es programmatisch.

Änderung es zu diesem

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 

und das sollte gut funktionieren.

Wenn dies für elementare völlig nutzlos ist, dann lassen Sie mich wissen, wird diese Antwort löschen kein Problem.

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; 

} 

dies nur knallte von dem manuellen nur knapp sein Ziel überprüfen, ob es eine Uhr für den Timer aktivieren war usw. Persönlich habe ich den gpio bis ersten, blinkt eine mit einem großen Zähler geführt Schleife

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

dann einen Timer in einem Abfragemodus verwenden, bis zu blinken basierend auf der Zeit von dieser kann herausfinden, die Taktgeschwindigkeit, die in die uart führt, erhält die uart, (es nur freilaufende Einstieg) , haben eine triviale Routine zum Drucken von Daten

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

Oktal ist es noch einfacher, aber der Mensch ist es schwer, in Oktal zu denken ...

schließlich die Interrupt-Zeug, Code wie oben nehmen und ich würde die verschiedenen Register in den Timer abzufragen und drucken, würde sehen, ob dieses Interrupt-Register in einem Abfragemodus zuerst feuert (zweite, dritte, vierte ... ohne Interrupts zum Prozessor zu aktivieren (tun Sie das für eine Weile nicht).

Sobald ich es auf der peripheren Ebene sehen kann, die einige nicht so funktionieren, aber davon ausgehen, dass dies tut.Dann in diesem Fall gibt es ein VIC, und ich nehme an, das Register

VICRawIntr 0xFFFFF008 

auch geltend gemacht werden soll, wenn das Timer-Interrupt aktiviert und gebrannt hat. Bestätigen Sie, dass es ist (Bit 4 erscheint) Bestätigen Sie, dass es verschwindet, wenn der Interrupt im Peripheriegerät gelöscht wird.

VICIntSelect wird auf Null zurückgesetzt, was irq ist, das ist es, was wir nicht brauchen, um es für jetzt zu berühren.

Ich gehe davon aus Bit 4 in VICIntEnable gesetzt

Sie dann die Abfrage, was erneut zu drucken, um zu sehen, was los ist.

Jetzt würde ich erwarten, dass der Interrupt in VICIRQStatus angezeigt wird (noch vollständig Abfragen die IRQ zum Prozessor noch nicht aktivieren) und weggehen, wenn das Peripheriegerät löscht, oder herausfinden, wie es zu löschen, wenn der periphere Interrupt gelöscht wird macht es nicht so weit.

JETZT ist es Zeit, die IRQ an den Prozessor zu aktivieren, und ich persönlich würde ein Byte in die uart schieben, um es herausspringen zu sehen. oder eine LED einschalten oder so.

In der Theorie löschen wir einfach die Peripherie und kehren zurück, um sicher zur Anwendung zurückzukehren.

Ich folge der gleichen Prozedur, egal, welcher Prozessor es MCU oder voller Größe ist, etc. Interrupts können Albträume sein und je mehr Code Sie schreiben, ohne zu testen, desto wahrscheinlicher zu scheitern. manchmal ist eine Zeile Code pro Test erforderlich. YMMV.

wieder, wenn dies völlig nutzlos ist, sorry, wird gelöscht. Ich glaube, ich habe eine/einige 2148, aber keine 2138, und ich werde keine bestellen, nur um Arbeitscode zu schreiben/zu testen. Seit dieser ARMV7TDMI war er arm und wurde in den gegenwärtigen Armvs populär, die viel schmerzhafter sind. die pi-zero und solche sind ziemlich lustig, wie sie alte Schule sind wie dieser arm7 ...

Ich denke, der Timer wäre der leichtere, um einen echten Interrupt von zu bekommen. ein anderer könnte ein gpio Pin sein, obwohl ich denke, das ist mehr Arbeit, würde aber einen Jumper zwischen den Pins binden, einen Ausgang zum anderen einen Eingang mit Interrupt Flankendetektion machen, wenn dieser Chip den Ausgang gpio verwendet, um den Zustand des Eingangs zu ändern eine saubere Art und Weise, und gehen Sie durch den gesamten Abrufprozess der Beobachtung des Interrupts treffen jede Schicht der Reihe nach und bestätigen Sie, dass Sie den Interrupt jedes Mal auf jeder Schicht entfernen können. dann knalle an den Rand des Kerns, und dann lass es schließlich in den Kern.

+0

die ldr, PC in der Ausnahmetabelle sind oft Overkill kann manchmal einfach eine Verzweigung auf das Etikett. sollte hier nicht positionsabhängig sein, also muss man diese Tabelle nicht wirklich verschieben und es funktionieren lassen, so dass Zweige auch in Ordnung sind ... aber dann muss der 0x14 Wert neu berechnet werden, um die Prüfsumme zu übergeben, damit der Code überhaupt läuft. . –

+1

Und um dies zu verstärken, verwenden Sie nur Interrupts, wenn Sie unbedingt, letzte Möglichkeit, IMO müssen. –