目次
前
次
外部割込み処理
ADuC7026には、外部IRQピンが4本あります。
物理ピンとの対応は、以下となっています。
- P0.4 IRQ0
- P0.5 IRQ1
- P1.4 PLAIRQ0
- P1.5 PLAIRQ1
ADuC7026の外部割込みは、Hレベル検出の
レベルタイプで、エッジトリガーではない
のです。
エッジトリガーの外部割込みを使うためには
PLA(Programmable Logic Array)を使います。
シフトレジスタを利用して、rising edgeを
捕らえる回路を考えてみました。
PLAには、次のようなマルチプレクサとレジスタを
接続したブロックの内部にあります。
各PLAを含んだブロックを、ELEMENTと呼び
各レジスタにパラメータを設定することで
マルチプレクサ、PLAの機能を指定します。
割込みを発生させるのは、PLAIRQ0、PLAIRQ1で
ELEMENT0、ELEMENT1、ELEMENT15のいずれかの
出力になっています。
例として、外部割込み信号はP1.4から入力し
ELEMENT0からPLAIRQ0を生成させることします。
シフトレジスタを動かすためのクロックが必要
なので、timer1のオーバーフローを使います。
PLAに関係するレジスタのクロックは、次のどれか
を選択できます。
- P0.7
- P0.5
- P0.0
- HCLK
- 外部水晶の32.768kHz
- timer1のオーバーフロー
場面、場面で使えるクロックが異なるので、システム
に合わせて選べばよいようになっています。
今回は、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の設定は、下の図の
ようになります。
マルチプレクサの設定パラメータは、次の
リストとします。
- MUX0 10
- MUX1 10
- MUX2 1
- MUX3 0
- PLA 0100
- MUX4 1
上から順番にならべると 10101001001 となります。
これを16進で表現し、対応レジスタに設定します。
PLAELM0 = 0x549 ;
ELEMENT5
ELEMENT4の出力を記憶して、出力するので
マルチプレクサとPLAの設定は、下の図の
ようになります。
マルチプレクサの設定パラメータは、次の
リストとします。
- MUX0 10
- MUX1 00(don't care)
- MUX2 1
- MUX3 0(don't care)
- PLA 1100
- MUX4 0
上から順番にならべると 10001011000 となります。
これを16進で表現し、対応レジスタに設定します。
PLAELM5 = 0x458 ;
ELEMENT4
P1.4からの入力を記憶して、出力するので
マルチプレクサとPLAの設定は、下の図の
ようになります。
マルチプレクサの設定パラメータは、次の
リストとします。
- MUX0 00(don't care)
- MUX1 00(don't care)
- MUX2 0(don't care)
- MUX3 1
- PLA 1010
- MUX4 0
上から順番にならべると 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と変化する
ことを判断して、トリガーフラグを設定します。
目次
前
次