キーボード制御LED——S 3 C 44 B 0 XのIRQプログラミング



「簡単なS 3 C 44 B 0 X Bootloader」で説明したBootloaderには異常な処理はありません.これは明らかに実用的ではありません.次に、キーボード制御LEDの例と組み合わせて、このBootloaderでIRQの処理を実現します.
まず基本的な情報を明確にします.原理図によると、私のボードには、1 x 4キーボードの4つのボタンがそれぞれS 3 C 44 B 0 XのEXTINT 4~7の4つのピンに接続されており、これらの外部割り込み信号によってボタンの押下を処理することができます.PGIOの詳細は後述しませんが、参照コードは問題ないはずです.LEDの詳細については、「最初のランニングランププログラム」を参照してください.
『簡単なS 3 C 44 B 0 X Bootloader』のheadを思い出す.sでは,カーネル異常ベクトルテーブルを予約し,処理はいずれもデッドサイクルに入った.もう二度とそんなことはできません実際の内容を記入する必要がありますすなわち、ISR(Interrupt Service Routine、割り込みサービスルーチン)である.また、ARM 7アーキテクチャの特性のため、ISRに陥る前にIRQモード(irq mode)およびそのモードでのスタックを初期化する必要がある(ISRは、一般的にはC関数を呼び出すなどのスタック操作を多く有するため、このステップは少なくない).改善されたコードは以下の通りである.
head.s
.equ    KERNEL_STACK,   0x0c002000      @       (    )
.equ    KERNEL_LIMIT,   0x0c001000      @       
.equ    IRQ_STACK,      0x0c002200      @ IRQ      

.text

vectors:
@        0x0c000000(RAM)
        b       undef_handler           @       0
        b       swi_handler             @       1
        b       pabort_handler          @       2
        b       dabort_handler          @       3
        b       irq_handler             @       4
        b       fiq_handler             @       5

.space  0x100 - 6 * 4

@        0x0c000100
start:
        @          
        ldr     sp, = KERNEL_STACK      @    svc     
        ldr     sl, = KERNEL_LIMIT      @   svc     

        @    IRQ  
        mrs     r0, cpsr                @     (    )
        msr     cpsr_all, #0x000000d2   @   IRQ FIQ,ARM     ,  irq  
        ldr     sp, = IRQ_STACK         @   IRQ     
        msr     cpsr_all, r0            @     (      )

        @   C    
        bl      entry                   @    C     
        mov     pc, #0                  @    

undef_handler:
swi_handler:
pabort_handler:
dabort_handler:
fiq_handler:
        b       .                       @        

irq_handler:
        stmfd   sp!, {r0-r12, lr}       @        C  ,             
        bl      on_irq                  @   ISR C  
        ldmfd   sp!, {r0-r12, lr}       @         
        subs    pc, lr, #4              @  IRQ    

これでISR全体をon_に置くことができますIRqというC関数に対応していて便利ですね.
アプリの部分を見るとmain.c里:
main.c
#define PORT(addr)      (*(volatile int *)(addr))

#define PCONC           PORT(0x01d20010)
#define PDATC           PORT(0x01d20014)
#define PCONG           PORT(0x01d20040)
#define PDATG           PORT(0x01d20044)

#define EXTINT          PORT(0x01d20050)
#define EXTINTPND       PORT(0x01d20054)

#define INTCON          PORT(0x01e00000)
#define INTPND          PORT(0x01e00004)
#define INTMOD          PORT(0x01e00008)
#define INTMSK          PORT(0x01e0000c)
#define I_ISPR          PORT(0x01e00020)
#define I_ISPC          PORT(0x01e00024)

static void init(void)
{
        PCONC = 0xaaaaaa56;     /* PC1~3=output */
        PCONG = 0xff00;         /* PG4~7=EINT4~7 */
        PDATC = 0x0000;         /*     LED */
#ifdef LOW_LEVEL
        EXTINT = 0x00000000;    /* EINT4~7=        */
#else
        EXTINT = 0x22220000;    /* EINT4~7=       */
#endif
        INTCON = 0x5;           /*        ,IRQ   */
        INTMOD = 0x000000;      /* EINT4~7=IRQ mode */
        INTMSK = 0x03dfffff;    /* EINT4~7=IRQ   */
}

static void led(int num, int light)
{
        if (light)
                PDATC |= 1 << num;
        else
                PDATC &= ~(1 << num);
}

static void key(void)           /*        */
{
        static int stat[3];     /*     LED        */
        int i;

        for (i = 0; i < 3; i++) {

                if (!(EXTINTPND & (1 << i)))
                        continue;       /*          ,    */

                if (!stat[i]) { /*     LED      */
                        led(i + 1, 1);  /*    LED */
                        stat[i] = 1;
#ifdef LOW_LEVEL
                        /*                     */
                        EXTINT &= ~(0x6 << (16 + (i << 2)));
                        EXTINT |= 0x1 << (16 + (i << 2));
#endif
                } else {        /*     LED      */
                        led(i + 1, 0);  /*    LED */
                        stat[i] = 0;
#ifdef LOW_LEVEL
                        /*                     */
                        EXTINT &= ~(0x7 << (16 + (i << 2)));
#endif
                }

                EXTINTPND |= 1 << i;    /*               */
        }
}

void entry(void)
{
        init();
        while (1);              /*          ISR    ,         */
}

void on_irq(void)               /* ISR   */
{
        if (!(I_ISPR & 0x200000))
                return;         /*     EXTINT4~7     */

        if (!(INTPND & 0x200000))
                return;         /*     EXTINT4~7            */

        key();                  /*      */
        I_ISPC |= 0x200000;     /*   EXTINT4~7        */
}

最後に、boot.sのresetサブルーチンの先頭に、svcモードのCPSR制御ビットのIビット(IRQ割り込み制御ビット)を開くことを忘れないでください.
    
          reset:
             @ initialize s3c44b0x
             mov     r0, #0x00000053                 @ enable IRQ, disable FIQ, ARM state, svc mode
             msr     cpsr_all, r0                    @ set CPSR
             ...
特に言うことはありませんが、注釈は全部書いてあります.注意gccコンパイルオプションはLOW_を定義していますLEVELはレベルトリガ方式を採用し、そうでなければ下降エッジトリガを採用する.キー0~2はLED 1~3に対応します.レベルトリガモードでは、押すとランプが点灯し、放すとランプが消灯する.エッジトリガモードで、ランプを押して点灯し、もう一度押すと消灯します.
この雛形があれば,種々のIRQをプログラミング処理することができる.