目次

外部割込み処理

 ADuC7026には、外部IRQピンが4本あります。
 物理ピンとの対応は、以下となっています。

 ADuC7026の外部割込みは、Hレベル検出の
 レベルタイプで、エッジトリガーではない
 のです。

 エッジトリガーの外部割込みを使うためには
 PLA(Programmable Logic Array)を使います。

 シフトレジスタを利用して、rising edgeを
 捕らえる回路を考えてみました。



 PLAには、次のようなマルチプレクサとレジスタを
 接続したブロックの内部にあります。



 各PLAを含んだブロックを、ELEMENTと呼び
 各レジスタにパラメータを設定することで
 マルチプレクサ、PLAの機能を指定します。

 割込みを発生させるのは、PLAIRQ0、PLAIRQ1で
 ELEMENT0、ELEMENT1、ELEMENT15のいずれかの
 出力になっています。

 例として、外部割込み信号はP1.4から入力し
 ELEMENT0からPLAIRQ0を生成させることします。



 シフトレジスタを動かすためのクロックが必要
 なので、timer1のオーバーフローを使います。

 PLAに関係するレジスタのクロックは、次のどれか
 を選択できます。

 場面、場面で使えるクロックが異なるので、システム
 に合わせて選べばよいようになっています。

 今回は、timer1を使えたので、timer1のオーバーフロー
 を使うことにしました。

 仕様が確定したので、PLAに関わるレジスタへの
 設定パラメータを決めていきます。

 P1.4をPLA入力に利用するので、GP1CON中の
 機能選択で、PLADINを指定します。
    GP1CON = 0x00030000;

 PLAIRQ0の割込みを使い、ELEMENT0の出力を指定する
 ので、PLAIRQレジスタへのパラメータを設定します。
    PLAIRQ = 0x10 ;

 クロックは、timer1のオーバーフローを選んで
 対応するので、分周比とモードを設定します。
    T1LD  = 4352 ; /* (41.78MHz / 16) / 600Hz */
    T1CON = 0xc4  ; /* enable , cyclic , 1/16 */ 

 NTSC信号のVSYNCを、P1.4に入れるため、60Hzの10倍の
 クロックを指定しました。

 ここまでで、ELEMENTの外の設定を終えたので
 内部のPLAに関係するレジスタの値を決めます。

 ELEMENT0
  ELEMENT4、ELEMENT5の出力を利用するので
  マルチプレクサとPLAの設定は、下の図の
  ようになります。



  マルチプレクサの設定パラメータは、次の
  リストとします。

  上から順番にならべると 10101001001 となります。
  これを16進で表現し、対応レジスタに設定します。
   PLAELM0 = 0x549 ;

 ELEMENT5
  ELEMENT4の出力を記憶して、出力するので
  マルチプレクサとPLAの設定は、下の図の
  ようになります。



  マルチプレクサの設定パラメータは、次の
  リストとします。

  上から順番にならべると 10001011000 となります。
  これを16進で表現し、対応レジスタに設定します。
   PLAELM5 = 0x458 ;

 ELEMENT4
  P1.4からの入力を記憶して、出力するので
  マルチプレクサとPLAの設定は、下の図の
  ようになります。


  マルチプレクサの設定パラメータは、次の
  リストとします。

  上から順番にならべると 000110100 となります。
  これを16進で表現し、対応レジスタに設定します。
   PLAELM4 = 0x34 ;

 レジスタに設定する16進コードを、ビット列の組み合わせで
 確定することに、神経を集中したくないので、AWKスクリプト
 で簡単に生成するようにしました。

 AWKスクリプトは、以下です。

{
  result = 0 ;
  # MUX4
  result += $6 ;
  # LUT
  result += $5 * 2 ;
  # MUX3
  result += $4 * 32 ;
  # MUX2
  result += $3 * 64 ;
  # MUX1
  result += $2 * 128 ;
  # MUX0
  result += $1 * 512 ;
  # show
  printf("%s => 0x%08x \n",$0,result);
}

 テキストファイルに、ELEMENTごとのマルチプレクサ
 PLAの機能を1行に書いて使います。

  2 2 1 0 4 1
  2 0 1 0 12 0
  0 0 0 1 10 0

 次のようにAWKスクリプトを使い、各ELEMENTの設定
 パラメータを生成します。



 PLAに関連する初期化を一つの関数に
 まとめます。

void  init_pla(void)
{
  /* initialize element */
  PLAELM0 = 0x549 ; /* NOT B and A */
  PLAELM4 = 0x34  ; /* select GP1.4 , B */ 
  PLAELM5 = 0x458 ; /* select E4 , A */
  /* select clock */
  PLACLK = 0x05 ; /* Timer1 overflow */
  /* interrupt enable */
  PLAIRQ = 0x10 ; /* element 0 H */
}

 割込みを使うので、割込みハンドラを記述します。

 ARMでは、一つの割込みハンドラで、全割込みを捌く
 ので、使いたい割込みに対応する処理は、ハンドラ
 内部で交通整理して対応します。

 timer0、timer1、外部割込みを使うとして、割込み
 ハンドラを作成しました。

 対応する割込みが来たら、外付けしたLEDを点滅させる
 仕様としました。LEDの点灯、消灯だけでも、割込みが
 入ったことを目視できるので、便利です。

 割込みハンドラのコードは、以下。

void IRQ_Handler(void) __irq
{
  /* judge timer0 interruption (100us) */
  if ( (IRQSTA & RTOS_TIMER_BIT) == RTOS_TIMER_BIT ) {
    /* clear timer0 interrupt flag */
    T0CLRI = 0xff ;
    /* increment */
    timcnt++ ;
    /* judge */
    if ( (timcnt & 0x3ff) == 0 ) {
      GP4DAT ^= (1 << 23);
    }
  }
  /* judge timer1 interruption (600Hz) */
  if ( (IRQSTA & GP_TIMER_BIT) == GP_TIMER_BIT ) {
    /* clear timer0 interrupt flag */
    T1CLRI = 0xff ;
    /* increment */
    vscnt++ ;
    /* 600Hz handling */
    if ( vscnt == 600 ) {
      vscnt = 0 ;
      /* get vsync signal */
      GP3DAT ^= (1 << 23) ;
    }
  }
  /* judge PLA0 interruption (exteranl interruption) */
  if ( (IRQSTA & PLA_IRQ0_BIT) == PLA_IRQ0_BIT ) {
    vtrg = OFF ;
    /* enable */
    if ( strg == ON ) {
      /* set flag */
      vtrg = ON ;
    }
    /* get vsync signal Debug */
    GP3DAT ^= (1 << 22) ;
  }
}

 timer1の割込みが来ているかを目視できるように
 したのは、PLAに使われているレジスタのクロック
 として、timer1のオーバーフローが使われている
 から。

 timer1のオーバーフローが発生していれば、PLA内
 のレジスタにクロック供給はあり、シフトレジスタ
 の動作が確認できます。

 timer0の割込みで、ADuC7026の基板上のLEDを点滅
 します。他の割込みが動かないときに、少なくとも
 ファームウエアが動いていることを確認するため。

 このPLAによる外部割込みをテストしたコードは以下。

#include <ADuC7026.h>

#define OFF 0
#define ON  OFF+1

/* data definitions */
typedef unsigned char  UBYTE ;
typedef   signed char  SBYTE ;
typedef unsigned short UWORD ;
typedef   signed short SWORD ;
typedef unsigned long  ULONG ;
typedef   signed long  SLONG ;

void  IRQ_Handler(void) __irq;
void  init_usr(void);

#define MASKFF 0xFF
#define MASK0F 0x0F
#define MASK80 0x80
#define MASK40 0x40
#define MASK20 0x20
#define MASK10 0x10
#define MASK08 0x08
#define MASK04 0x04
#define MASK02 0x02
#define MASK01 0x01

#define MASKF0 0xF0

ULONG timcnt ;

void  rs_putchar(UBYTE x);
void  crlf(void);
void  rs_puts(UBYTE *x);
void  init_pla(void);

/* global variables */
volatile UWORD vscnt ;
volatile UBYTE vtrg ;

int main(void)
{
  /* initialize user */
  init_usr();	
  /* show message */
  rs_puts("Hello"); crlf();
  /* endless loop */
  while(ON)
  {
    /* external interrupt */
    if ( vtrg == ON ) {
      /* clear flag */
      vtrg = OFF ;
      /* inverse */
      GP3DAT ^= (1 << 21);
    }
  }
  /* dummy return */
  return (0);
}

void IRQ_Handler(void) __irq
{
  /* judge timer0 interruption (100us) */
  if ( (IRQSTA & RTOS_TIMER_BIT) == RTOS_TIMER_BIT ) {
    /* clear timer0 interrupt flag */
    T0CLRI = 0xff ;
    /* increment */
    timcnt++ ;
    /* judge */
    if ( (timcnt & 0x3ff) == 0 ) {
      GP4DAT ^= (1 << 23);
    }
  }
  /* judge timer1 interruption (100Hz) */
  if ( (IRQSTA & GP_TIMER_BIT) == GP_TIMER_BIT ) {
    /* clear timer0 interrupt flag */
    T1CLRI = 0xff ;
    /* increment */
    vscnt++ ;
    /* 600Hz handling */
    if ( vscnt == 600 ) {
      vscnt = 0 ;
      /* get vsync signal */
      GP3DAT ^= (1 << 23);
    }
  }
  /* judge PLA0 interruption (exteranl interruption) */
  if ( (IRQSTA & PLA_IRQ0_BIT) == PLA_IRQ0_BIT ) {
    vtrg = OFF ;
    /* enable */
    if ( strg == ON ) {
      /* set flag */
      vtrg = ON ;
    }
    /* get vsync signal Debug */
    GP3DAT ^= (1 << 22);
  }
}

void init_usr(void)
{
  /* select clock 41.78MHz 
       initialized in start up routine
  */
  PLLKEY1 = 0xaa ;
  PLLCON  = 0x01 ;
  PLLKEY2 = 0x55 ;
  /* power control
       initialized in start up routine 
  */
  /* clear flag */
  vtrg = OFF ;
  /* clear counter */
  timcnt = 0 ;
  vscnt  = 0 ;
  /* P1 */
  {
    /* select PLA input */
    GP1CON = 0x00030011 ;
    /* */
    GP1DAT = 0x6E000000 ;
  }
  /* P3 */
  {
    GP3DAT = 0xffff0000 ;
  }
  /* P4 */
  {
    GP4DAT = 0xff000000 ;
  }
  /* initialize timer 0 (100us) */
  {
    T0LD  = 4178 ; /* (41.78MHz / 1) / 10kHz */
    T0CON = 0xc0 ; /* enable , cyclic , 1/1 */ 
  }
  /* initialize timer 1 (600Hz) */
  {
    T1LD  = 4352 ; /* (41.78MHz / 16) / 600Hz */
    T1CON = 0xc4  ; /* enable , cyclic , 1/16 */ 
  }
  /* initialize PLA */
  init_pla();
  /* enable timer 1 , timer 0 and PLA interrupt */
  IRQEN = RTOS_TIMER_BIT | GP_TIMER_BIT | PLA_IRQ0_BIT ;
}

/* UART putchar */
void  rs_putchar(UBYTE x)
{
  /* ? transmmit buffer empty */
  while( (COMSTA0 & 0x40) == 0 ) ;
  /* set value */
  COMTX = x ;
}

/* carriage return and line feed */
void  crlf(void)
{
  rs_putchar('\r');
  rs_putchar('\n');
}

/* UART puts */
void  rs_puts(UBYTE *x)
{
  while ( *x != '\0' ) {
    rs_putchar( *x ) ;
    x++ ;
  }
}

/* initialize PLA */
void  init_pla(void)
{
  /* initialize element */
  PLAELM0 = 0x549 ; /* NOT B and A */
  PLAELM4 = 0x34  ; /* select GP1.4 , B */ 
  PLAELM5 = 0x458 ; /* select E4 , A */
  /* select clock */
  PLACLK = 0x05 ; /* Timer1 overflow */
  /* interrupt enable */
  PLAIRQ = 0x10 ; /* element 0 H */
}

 VSYNCは周波数60Hzで変化するので、外部割込みとして
 PLAに信号が入ると、外部に接続したLEDを点滅します。

 LEDは、次のように接続しています。



 上のファームウエア用スタートアップルーチンは
 以下です。(Keilの開発環境が生成してくる内容に
 PLL関係設定を変更しただけになっています。)

// Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

        Mode_USR  EQU      0x10
        Mode_FIQ  EQU      0x11
        Mode_IRQ  EQU      0x12
        Mode_SVC  EQU      0x13
        Mode_ABT  EQU      0x17
        Mode_UND  EQU      0x1B
        Mode_SYS  EQU      0x1F

        I_Bit     EQU      0x80    /* when I bit is set, IRQ is disabled */
        F_Bit     EQU      0x40    /* when F bit is set, FIQ is disabled */

/*
//  Stack Configuration (Stack Sizes in Bytes)
//    Undefined Mode      <0x0-0xFFFFFFFF:4>
//    Supervisor Mode     <0x0-0xFFFFFFFF:4>
//    Abort Mode          <0x0-0xFFFFFFFF:4>
//    Fast Interrupt Mode <0x0-0xFFFFFFFF:4>
//    Interrupt Mode      <0x0-0xFFFFFFFF:4>
//    User/System Mode    <0x0-0xFFFFFFFF:4>
// 
*/
        UND_Stack_Size  EQU     0x00000004
        SVC_Stack_Size  EQU     0x00000004
        ABT_Stack_Size  EQU     0x00000004
        FIQ_Stack_Size  EQU     0x00000004
        IRQ_Stack_Size  EQU     0x00000080
        USR_Stack_Size  EQU     0x00000400

AREA   STACK, DATA, READWRITE, ALIGN=2
        DS   (USR_Stack_Size+3)&~3  ; Stack for User/System Mode 
        DS   (SVC_Stack_Size+3)&~3  ; Stack for Supervisor Mode
        DS   (IRQ_Stack_Size+3)&~3  ; Stack for Interrupt Mode
        DS   (FIQ_Stack_Size+3)&~3  ; Stack for Fast Interrupt Mode 
        DS   (ABT_Stack_Size+3)&~3  ; Stack for Abort Mode
        DS   (UND_Stack_Size+3)&~3  ; Stack for Undefined Mode
Top_Stack:

// MMR definitions
        MMR_BASE        EQU     0xFFFF0000      ; MMR Base Address
        REMAP_OFFSET    EQU         0x0220
        PREMAP_OFFSET   EQU         0x0224
        POWKEY1_OFFSET  EQU         0x0404
        POWCON_OFFSET   EQU         0x0408
        POWKEY2_OFFSET  EQU         0x040C

// PLL Setup

        PLL_SETUP       EQU     1
        PLLCFG_Val      EQU     0x00000000

// Pin Setup
        GPIO_SETUP      EQU     1
        GPIOBASE        EQU     0xFFFFF400

//     Port 0
        GP0CON_Val      EQU     0x00000000

//     Port 1
        GP1CON_Val      EQU     0x00000000

//     Port 2
        GP2CON_Val      EQU     0x00000000

//     Port 3
        GP3CON_Val      EQU     0x00000000

//     Port 4
        GP4CON_Val      EQU     0x00000000

// External Memory Interface
        XM_SETUP        EQU     0
        XMBASE          EQU     0xFFFFF000

//     Enable Memory Region 0
        XM0CON_Val      EQU     0x00000000
        XM0PAR_Val      EQU     0x000070FF

//     Enable Memory Region 1
        XM1CON_Val      EQU     0x00000000
        XM1PAR_Val      EQU     0x000070FF

//     Enable Memory Region 2
        XM2CON_Val      EQU     0x00000000
        XM2PAR_Val      EQU     0x000070FF

//     Enable Memory Region 3
        XM3CON_Val      EQU     0x00000000
        XM3PAR_Val      EQU     0x000070FF

//            Memory Muxed Mode
        XMCFG_Val       EQU     0x00000001
//   

//

$IF (RAM_INTVEC)
// Exception Vector Area in RAM
AREA   VECTORS, DATA, AT 0x00010000
                DS      64
$ENDIF

// Startup Code must be linked at address which it expects to run.

AREA   STARTUPCODE, CODE, AT 0x00080000
       PUBLIC  __startup

       EXTERN  CODE32 (?C?INIT)

__startup       PROC    CODE32

// Pre-defined interrupt handlers that may be directly 
// overwritten by C interrupt functions
EXTERN CODE32 (Undef_Handler?A)
EXTERN CODE32 (SWI_Handler?A)
EXTERN CODE32 (PAbt_Handler?A)
EXTERN CODE32 (DAbt_Handler?A)
EXTERN CODE32 (IRQ_Handler?A)
EXTERN CODE32 (FIQ_Handler?A)

// Exception Vectors
// Mapped to Address 0.
// Absolute addressing mode must be used.

Vectors:        LDR     PC,Reset_Addr
                LDR     PC,Undef_Addr
                LDR     PC,SWI_Addr
                LDR     PC,PAbt_Addr
                LDR     PC,DAbt_Addr
                NOP                            /* Reserved Vector */
                LDR     PC,IRQ_Addr
                LDR     PC,FIQ_Addr

Reset_Addr:     DD      Reset_Handler
Undef_Addr:     DD      Undef_Handler?A
SWI_Addr:       DD      SWI_Handler?A
PAbt_Addr:      DD      PAbt_Handler?A
DAbt_Addr:      DD      DAbt_Handler?A
                DD      0                      /* Reserved Address */
IRQ_Addr:       DD      IRQ_Handler?A
FIQ_Addr:       DD      FIQ_Handler?A

// Reset Handler

Reset_Handler: 

// Setup PLL
IF (PLL_SETUP != 0)
                LDR     R0, =MMR_BASE
                MOV     R1, #0x01
                STR     R1, [R0,#POWKEY1_OFFSET]
                MOV     R1, #PLLCFG_Val
                STR     R1, [R0,#POWCON_OFFSET]
                MOV     R1, #0xF4
                STR     R1, [R0,#POWKEY2_OFFSET]
ENDIF   ; PLL_SETUP

// Setup Pins
IF (GPIO_SETUP != 0)
                ADR     R10, GPIO_CFG          /* Pointer to GPIO CFG */
                LDMIA   R10, {R0-R5}           /* Load GPIO Configuration */
                STMIA   R0, {R1-R5}            /* Store GPxCON */
                B       GPIO_END

GPIO_CFG:       DD      GPIOBASE
                DD      GP0CON_Val
                DD      GP1CON_Val
                DD      GP2CON_Val
                DD      GP3CON_Val
                DD      GP4CON_Val
GPIO_END:

ENDIF   ; GPIO_SETUP

// Setup External Memory Interface
IF (XM_SETUP != 0)
                ADR     R10, XM_CFG            /* Pointer to XM CFG */
                LDMIA   R10, {R0-R9}           /* Load XM Configuration */
                STR     R1, [R0],#0x10         /* Store XMCFG */
                STMIA   R0, {R2-R9}            /* Store XMxCON & XMxPAR */
                B       XM_END

XM_CFG:         DD      XMBASE
                DD      XMCFG_Val
                DD      XM0CON_Val
                DD      XM1CON_Val
                DD      XM2CON_Val
                DD      XM3CON_Val
                DD      XM0PAR_Val
                DD      XM1PAR_Val
                DD      XM2PAR_Val
                DD      XM3PAR_Val
XM_END:

ENDIF   ; XM_SETUP

// Copy Exception Vectors to Internal RAM and Remap Memory
//  (when Interrupt Vectors are in RAM)
$IF (RAM_INTVEC)
                ADR     R8, Vectors         ; Source
                LDR     R9, =0x00010000     ; Destination
                LDMIA   R8!, {R0-R7}        ; Load Vectors 
                STMIA   R9!, {R0-R7}        ; Store Vectors 
                LDMIA   R8!, {R0-R7}        ; Load Handler Addresses 
                STMIA   R9!, {R0-R7}        ; Store Handler Addresses 
                LDR     R0, =MMR_BASE
                MOV     R1, #1
                STR     R1, [R0,#PREMAP_OFFSET]
                STR     R1, [R0,#REMAP_OFFSET]
$ENDIF

// Setup Stack for each mode
                LDR     R0, =Top_Stack

// Enter Undefined Instruction Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_UND|I_Bit|F_Bit
                MOV     SP, R0
                SUB     R0, R0, #UND_Stack_Size

// Enter Abort Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_ABT|I_Bit|F_Bit
                MOV     SP, R0
                SUB     R0, R0, #ABT_Stack_Size

// Enter FIQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_FIQ|I_Bit|F_Bit
                MOV     SP, R0
                SUB     R0, R0, #FIQ_Stack_Size

// Enter IRQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_IRQ|I_Bit|F_Bit
                MOV     SP, R0
                SUB     R0, R0, #IRQ_Stack_Size

// Enter Supervisor Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_SVC|I_Bit|F_Bit
                MOV     SP, R0
                SUB     R0, R0, #SVC_Stack_Size

// Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR
                MOV     SP, R0

// Enter the C code
                LDR     R0,=?C?INIT
                TST     R0,#1       ; Bit-0 set: main is Thumb
                LDREQ   LR,=exit?A  ; ARM Mode
                LDRNE   LR,=exit?T  ; Thumb Mode
                BX      R0
                ENDP

PUBLIC exit?A
exit?A          PROC    CODE32
                B       exit?A
                ENDP

PUBLIC exit?T
exit?T          PROC    CODE16
                B       exit?T
                ENDP

                END

 スイッチのような動作が遅いデバイスで、外部割込みをかける
 場合、タイマー割込みとシフトレジスタで実現する方がPLAを
 使うよりも簡単です。



 タイマー割込み処理の中で、次のようにシフトレジスタ
 の値を更新し、同時にトリガーフラグを設定します。

  /* judge timer3 interruption (1ms) */
  if ( (IRQSTA & WATCHDOG_TIMER_BIT) == WATCHDOG_TIMER_BIT ) {
    /* clear timer3 interrupt flag */
    T3CLRI = 0xff ;
    /* increment */
    scnt++ ;
    if ( scnt == SCNT_MAX ) {
      scnt = 0 ;
      str_sft <<= 1 ;
      str_sft &= MASK07 ;
      if ( GP0DAT & MASK20 ) { str_sft |= ON ; }
      str_trg = OFF ;
      if ( str_sft == 3 ) { eflag = ON ; }
    }
  }

 8ビットのレジスタ中の下位3ビットを利用し
 スイッチの状態をタイマー割込みで更新します。

 上の例では、スイッチの論理値が0→1と変化する
 ことを判断して、トリガーフラグを設定します。


目次

inserted by FC2 system