Chapter 3. レッスン
3.3 レッスン 2: LED 点滅
PICkit™ 3 スタータキット ユーザガイド
DS41628B_JP - p.48 2015 Microchip Technology Inc.
アセンブリでは非常によく使う命令です。通常、データをWREGに代入した後、演 算を実行するか別のレジスタへ代入します。
3.3.5.1.2 movwf
movlwに似た命令で、WREGのデータを別のレジスタへ代入します。
例3-7:
3.3.5.1.3 decfsz
レジスタの値を1 つデクリメントします。デクリメント後のレジスタの値が「0」の 場合、次の命令をスキップします。これは遅延ループの作成に使います。
3.3.5.1.4 bra/goto
これら 2 つの命令は、コードの別のセクションへのジャンプに使います。BRA は現 在のプログラム カウンタの位置からの相対分岐です。エンハンスト ミッドレンジ コ アの場合、カウンタはプログラムメモリの-256≤n≤255のアドレスレンジにアクセス できます。PIC18のBRAは、プログラムメモリの-1024≤n≤1023のアドレスレンジ にアクセスできます。値と符号に注意が必要です。エンハンスト ミッドレンジ コア では、相対分岐を使うとページ境界を越えてジャンプできるため便利です。
gotoは無条件ジャンプで、エンハンスト ミッドレンジの場合は現在のページ内の任 意のアドレスにアクセスできます。PIC18では、goto命令で全てのプログラムメモ リにアクセスできます。ただしプログラムメモリを2ワード必要とする事に注意が必
要です。つまりPIC18では1つのgoto命令がBRA命令の2倍の空間を必要とします。
PIC18では、分岐先アドレスが現在のアドレスから±1024以内であればGOTOではな
く相対分岐を推奨します。エンハンスト ミッドレンジでは、ページ境界を越えたジャ ンプを繰り返す場合のみ相対分岐に利点があります。
3.3.5.2 PIC18
3.3.5.2.1 btg
指定したレジスタのビット値を反転します。
3.3.6 アセンブリ
3.3.6.1 エンハンストミッドレンジ
例3-8:
表3-6: 新しく学ぶPIC18の命令
命令 英語名 目的
btg Bit Toggle f LEDを点滅する
movwf OPTION_REG 命令実行前:
OPTION_REG = 0xFF W = 0x4F
命令実行後:
OPTION_REG = 0x4F W = 0x4F
movlw b'00111000' ;set cpu clock speed movwf OSCCON
ここではPIC MCUの動作周波数を500 kHzに設定しています。ワーキング レジスタ
(WREG) を使ってレジスタにバイト値を代入します。このレジスタへの書き込みを
省略した場合も、PIC16F1829は既定値の500 kHzで動作します。しかし既定値は デバイスによって異なるため、コードの冒頭で必ずこのレジスタへの書き込みが必 要です。これで、PIC MCUは1命令を8 µsで実行するようになります(式3-1参照)。
式3-1: 実行時間
LEDを点滅させるには、まずLEDを点灯した後に一定時間待ってから同じ時間だけ LEDを消灯するようにプログラムを記述する必要があります。これには内蔵RAMを 使います。
例3-9:
CBLOCKはユーザメモリを割り当てます。CBLOCKの後の数値は、どのアドレスからメ モリを割り当てるかを指定します。0x70は、エンハンスト ミッドレンジ コアでは全 てのバンクで共有する共通RAM のアドレスです。ここに格納できるのは16 バイト のみです。これらの変数を使う場合、プログラムでバンクを切り換える必要はありま せん。ここから先のレッスンでは、エンハンスト ミッドレンジではこの共通RAMに、
PIC18ではアクセスRAMに変数を格納します。ここに、以下の遅延ループを作成す
るための2つの変数を格納します。
例3-10:
braでループ先頭に戻り、処理を繰り返します。このループの実行には3命令サイク ル (デクリメントに1命令サイクル、braに2命令サイクル)かかります。そしてカ ウンタの動作によってこのループを 256 回実行するため、実行には合計768命令サ イクルがかかります。それでも、人間の目にはほとんど認識できないほどの速さです。
そこで、このループの外側にもう 1 つのループを追加してさらに速度を落とします。
内周ループの実行時間が768サイクルで、外周ループの3 サイクルを足したもの を256回繰り返します。すなわち、実行時間は(768+3) * 256 = 197376命令/125K命 令/s = 1.579 sです。
Instruction time 1 FOSC
---4 --- 1
500kHz ---4 --- 8 µS
= = =
cblock 0x70 ;shared memory location that is accessible from all banks Delay1 ; Define two file registers for the delay loop in shared memory Delay2
endc
bsf LATC, 0 ; turn LED on OndelayLoop:
decfsz Delay1,f ; Waste time.
bra OndelayLoop ; The Inner loop takes 3 instructions per loop * 256 loops = 768 instructions
decfsz Delay2,f ; The outer loop takes an additional 3 instructions per lap * 256 loops bra OndelayLoop ; (768+3) * 256 = 197376 instructions / 125K instructions per second = 1.579 ;sec.
bcf PORTC,0 ; Turn off LED C0 - NOTE: do not need to switch banks with 'banksel' since ;bank0 is still selected
OffDelayLoop:
decfsz Delay1,f ; same delay as above bra OffDelayLoop
decfsz Delay2,f bra OffDelayLoop
bra MainLoop ; Do it again...
PICkit™ 3 スタータキット ユーザガイド
DS41628B_JP - p.50 2015 Microchip Technology Inc.
goto命令とbra命令が2命令サイクルを必要とするのは、このプロセッサがパイプラ イン構造をしているためです。プロセッサは現在の命令を実行中に次の命令をフェッチ します。プログラムが発生すると、gotoまたはbraの後のフェッチ済み命令は実行 されません。その代わりにNOPが実行され、その間に分岐先の命令をフェッチします。
変数Delay1とDelay2は0から255へロールオーバします。このため、デクリメ ントする前に変数Delay1とDelay2に値を代入しておく必要はありません。
3.3.6.2 PIC18
エンハンスト ミッドレンジ コアには全バンクで共有できる汎用RAM (共通RAM)が 16バイトありますが、PIC18にはこれに相当する領域として0x00->0x5Fがあります。
この96バイトはバンク指定なしでアクセスできます。
例3-11:
3.3.7 C言語
3.3.7.1 両デバイス共通
変数カウンタを使ったCによる遅延ループでは、実際の遅延時間は予測できません。
C コードの場合、PIC MCU に書き込む前にコンパイラがユーザコードをアセンブリ に変換するステップが入ります。ループの実行時間は、コンパイラの効率およびプロ グラムの記述方法によって異なります。従って、遅延ループ用のライブラリ関数を使 う事を推奨します。
このレッスンでは両方の方法を紹介します。このレッスンのコードの最後でコメント アウトしたセクションは、XC8コンパイラ内蔵の高精度な遅延関数を使っています。
これ以降のレッスンでは、この内蔵遅延マクロを使います。
例3-12:
この高精度なルーチンを使うには、PIC MCU のプロセッサ速度を定義しておく必要 があります。
例3-13:
変数delayを作成し、デクリメントしてからLEDをトグルしています。^はピンの 値と「1」をXOR演算しており、これによってピンの値をトグルしています。コンパ イラの最適化レベルを変えると生成されるコードが変化し、遅延時間が増減します。
cblock 0x00 ; Access RAM
Delay1 ; Define two file registers for the delay loop in shared memory Delay2
endc
#define _XTAL_FREQ 500000 //Used by the HI-TECH delay_ms(x) macro
delay = 7500;
while (1) {
while(delay-- != 0)continue; //each instruction is 8us (1/(500KHz/4)) LATCbits.LATC0 ^= 1; //toggle the LED
delay = 7500; //assign a value since it is at 0 from the delay loop