GR-SAKURA で遊んでみる(2)
先日購入したGR-SAKURA の JTAG I/F の場所にピンヘッダを半田付けした。
これで、E1 エミュレータを用いたデバッグが可能になる。
開発に必要なツール群は、以下の URL より入手した。
【無償評価版】RXファミリ用C/C++コンパイラパッケージ V2 (統合開発環境なし) V2.02.00:
http://japan.renesas.com/support/downloads/download_results/C1000000-C9999999/tools/evaluation_software_cc_rx.jsp
Renesas E-Series USBドライバ V.1.02 Release 00
http://japan.renesas.com/support/downloads/download_results/C1000000-C9999999/tools/upgrades_usb_driver_for_e-series.jsp
フラッシュ開発ツールキット V.4.09 Release 02 アップデート
http://japan.renesas.com/support/downloads/download_results/C1000000-C9999999/tools/evaluation_software_cc_rx.jsp
(OS を搭載したPC 上で)セルフ開発環境の整備後に最初に書くであろうプログラムは
#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { printf("Hello World!\n"); exit(EXIT_SUCCESS); }
というものであるが、OS も何も書き込まれていない白紙状態のボードで、新規にコードを書き起こして遊ぶ際には大抵 LED 点滅プログラムを出発点とする。
単に LED を点滅させるだけでも
- I/Oポート
- タイマ
- 割り込み
- PLL
の機能を試すことができ、後々作成するプログラムにも流用できる。
そこで、LED の点滅を行うプログラムを実際に書いてみた。
blink.src:
1 SCKCR .equ 000080020h 2 SCKCR2 .equ 000080024h 3 SCKCR3 .equ 000080026h 4 5 PLLCR_ADDR .equ 000080028h 6 PLLCR2_ADDR .equ 00008002ah 7 MOSCCR_ADDR .equ 000080032h 8 MOSCWTCR_ADDR .equ 0000800a2h 9 PLLWTCR_ADDR .equ 0000800a6h 10 11 IR12_ADDR .equ 00008701ch 12 IPR004_ADDR .equ 000087304h 13 IER03_ADDR .equ 000087203h 14 15 PRCR_ADDR .equ 0000803feh 16 MSTPCRA_ADDR .equ 000080010h 17 MSTPCRB_ADDR .equ 000080014h 18 MSTPCRC_ADDR .equ 000080018h 19 20 PORT0_PDR_ADDR .equ 00008c000h 21 PORT1_PDR_ADDR .equ 00008c001h 22 PORT2_PDR_ADDR .equ 00008c002h 23 PORT3_PDR_ADDR .equ 00008c003h 24 PORT4_PDR_ADDR .equ 00008c004h 25 PORT5_PDR_ADDR .equ 00008c005h 26 PORT6_PDR_ADDR .equ 00008c006h 27 PORT7_PDR_ADDR .equ 00008c007h 28 PORT8_PDR_ADDR .equ 00008c008h 29 PORT9_PDR_ADDR .equ 00008c009h 30 PORTA_PDR_ADDR .equ 00008c00ah 31 PORTB_PDR_ADDR .equ 00008c00bh 32 PORTC_PDR_ADDR .equ 00008c00ch 33 PORTD_PDR_ADDR .equ 00008c00dh 34 PORTE_PDR_ADDR .equ 00008c00eh 35 PORTF_PDR_ADDR .equ 00008c00fh 36 PORTG_PDR_ADDR .equ 00008c010h 37 PORTJ_PDR_ADDR .equ 00008c012h 38 PORTA_PODR_ADDR .equ 00008c02ah 39 40 CMSTR0_ADDR .equ 000088000h 41 CMT0_CMCR_ADDR .equ 000088002h 42 CMT0_CMCNT_ADDR .equ 000088004h 43 CMT0_CMCOR_ADDR .equ 000088006h 44 45 .section ResetPRG,code 46 .glb _start 47 .glb intvect 48 _start: 49 ; disable interrupts 50 clrpsw i 51 52 mvtc #000020000h, usp 53 mvtc #00001fc00h, isp 54 mvtc #intvect, intb 55 56 ; stop unused peripherals 57 bsr stop_modules 58 59 ; initialize non-existent I/O port 60 ; see following: 61 ; 62 ; RX63N Group, RX631 Group 63 ; User's Manual:Hardware 64 ; 21.5.1 Products fewer than 176 Pins 65 bsr init_nonexistent_port 66 67 ; enable CMT0 68 mov.l #PRCR_ADDR, r1 69 mov.w #0a503h,[r1] 70 mov.l #MSTPCRA_ADDR, r1 71 mov.l [r1], r2 72 bclr #15, r2 73 mov.l r2, [r1] 74 75 ; if the main clock stabilization waiting time(tMAINOSC) is 20 ms, 76 ; we should set MOSCWTCR.MSTS[4:0] to 14 77 ; (262,144 cycle > 20.00 ms = 240,000 cycles), 78 ; so we need to wait 79 ; tMAINOSC + (262,144 + 16384)/ fMAIN 80 ; = 20 + (262,144 + 16,384) / 12,000 = approx. 43.23 ms 81 mov.l #MOSCWTCR_ADDR, r1 82 mov.b #0eh, [r1] ; MOSCWTCR.MSTS[4:0]=14 83 84 ; run the main clock oscillator 85 mov.l #MOSCCR_ADDR, r1 86 bclr #0, [r1] ; MOSCCR.MOSTP=0 87 ; confirm that MOSCCR.MOSTP has actually been updated 88 ?: movu.b [r1], r2 89 tst r2, r2 90 bnz ?- 91 92 mov.l #44, r1 93 bsr mdelay 94 95 ; configure the PLL clock: 96 ; to generate the PLL clock from main clock by dividing by 1 and 97 ; multiplying by 8(fPLL = 12.0 * 8 = 96.0 MHz), 98 ; we should set PLLWTCR.PSTS to 9 99 ; (n = 65536 cycles = 682.7 us), 100 ; so we need to wait 101 ; tPLL1 + (n + 131,072) / fPLL 102 ; = 0.5 + (65,536 + 131,072)/ 96,000 = 2.548 ms 103 mov.l #PLLCR_ADDR, r1 104 mov.w #00700h, [r1] 105 mov.l #PLLWTCR_ADDR, r1 106 mov.b #9, [r1] 107 mov.l #PLLCR2_ADDR, r1 108 bclr #0, r1 ; PLLCR2.PLLEN=0 109 110 mov.l #3, r1 111 bsr mdelay 112 113 mov.l #SCKCR, r1 114 mov.l #022c23311h, [r1] ; ICLK -> 1/4, PCLK -> 1/8 115 mov.l #SCKCR2, r1 116 mov.w #00002h, [r1] 117 mov.l #SCKCR3, r1 118 mov.w #0400h, [r1] ; use PLL clock 119 mov.l #0000803feh, r1 120 mov.w #0a500h,[r1] ; write protection for clock control registers 121 122 ; turn off user LEDs 123 mov.l #PORTA_PODR_ADDR, r1 124 bclr #0, [r1] ; Set PA0 output level to L 125 bclr #1, [r1] ; Set PA1 output level to L 126 bclr #2, [r1] ; Set PA2 output level to L 127 bclr #6, [r1] ; Set PA6 output level to L 128 mov.l #PORTA_PDR_ADDR, r1 129 bset #0, [r1] ; Set PA0 direction to output 130 bset #1, [r1] ; Set PA1 direction to output 131 bset #2, [r1] ; Set PA2 direction to output 132 bset #6, [r1] ; Set PA6 direction to output 133 134 ; initialize section "B" 135 mov.l #(TOPOF B), r1 136 mov.l #0, r2 137 mov.l #(SIZEOF B), r3 138 sstr.b 139 140 mov.l #CMT0_CMCOR_ADDR, r1 141 mov.w #005dch, [r1] ; Set CMCOR 142 mov.l #CMT0_CMCR_ADDR, r1 143 mov.w #00040h, [r1] ; Set CMCR 144 mov.l #CMT0_CMCNT_ADDR, r1 145 mov.w #00000h, [r1] ; Clear CMCNT 146 147 ; enable interrupts 148 mov.l #IPR004_ADDR, r1 149 mov.b #001h, [r1] ; Set IPR004 150 mov.l #IER03_ADDR, r1 151 bset #4, [r1] ; Set IER03.IEN4 152 setpsw i ; Set I bit 153 154 mov.l #CMSTR0_ADDR, r1 155 mov.w #00001h, [r1] ; Start CMT0 156 157 bra $ 158 159 mdelay: 160 pushm r1-r3 161 162 mov.l #CMT0_CMCOR_ADDR, r2 163 mov.w #00010h, [r2] 164 mov.l #CMT0_CMCR_ADDR, r2 165 mov.w #00040h, [r2] 166 mov.l #CMT0_CMCNT_ADDR, r2 167 mov.w #00000h, 4[r2] 168 169 mov.l #IPR004_ADDR, r2 170 bset #0, [r2] ; Set IPR004 171 mov.l #IER03_ADDR, r2 172 bset #4, [r2] ; Set IER03.IEN4 173 174 tst r1, r1 175 L1: bz L2 176 177 mov.l #CMSTR0_ADDR, r2 178 mov.w #00001h, [r2] ; Start CMT0 179 180 ?: mov.l #IR12_ADDR, r2 181 movu.b [r2], r3 182 tst r3, r3 183 bz ?- 184 mov.b #000h, [r2] 185 186 mov.l #CMSTR0_ADDR, r2 187 mov.w #00000h, [r2] ; Stop CMT0 188 189 sub #1, r1 190 bra L1 191 192 L2: mov.l #IER03_ADDR, r2 193 bclr #4, [r2] ; Clear IER03.IEN4 194 mov.l #IPR004_ADDR, r2 195 bclr #0, [r2] ; Clear IPR004 196 197 popm r1-r3 198 rts 199 200 stop_modules: 201 ; disable write protection for low power consumption module 202 mov.l #PRCR_ADDR, r1 203 mov.w #0a502h,[r1] 204 205 ; stop unused peripherals(DMA and EXDMA) 206 mov.l #MSTPCRA_ADDR, r1 207 mov.l [r1], r2 208 bset #28, r2 ; DMA 209 bset #29, r2 ; EXDMA 210 mov.l r2, [r1] 211 212 mov.l #PRCR_ADDR, r1 213 mov.w #0a500h, [r1] 214 rts 215 216 init_nonexistent_port: 217 ; set non-existent I/O ports' direction to output 218 push.l r1 219 mov.l #PORT0_PDR_ADDR, r1 220 mov.b #00fh, [r1] ; P00-P03 221 mov.l #PORT1_PDR_ADDR, r1 222 mov.b #003h, [r1] ; P10-P11 223 mov.l #PORT2_PDR_ADDR, r1 224 mov.b #000h, [r1] ; none 225 mov.l #PORT3_PDR_ADDR, r1 226 mov.b #000h, [r1] ; none 227 mov.l #PORT4_PDR_ADDR, r1 228 mov.b #000h, [r1] ; none 229 mov.l #PORT5_PDR_ADDR, r1 230 mov.b #0c0h, [r1] ; P56-P57 231 mov.l #PORT6_PDR_ADDR, r1 232 mov.b #0ffh, [r1] ; P60-67 233 mov.l #PORT7_PDR_ADDR, r1 234 mov.b #0ffh, [r1] ; P70-77 235 mov.l #PORT8_PDR_ADDR, r1 236 mov.b #0ffh, [r1] ; P80-87 237 mov.l #PORT9_PDR_ADDR, r1 238 mov.b #0ffh, [r1] ; P90-97 239 mov.l #PORTA_PDR_ADDR, r1 240 mov.b #000h, [r1] ; none 241 mov.l #PORTB_PDR_ADDR, r1 242 mov.b #000h, [r1] ; none 243 mov.l #PORTC_PDR_ADDR, r1 244 mov.b #000h, [r1] ; none 245 mov.l #PORTD_PDR_ADDR, r1 246 mov.b #000h, [r1] ; none 247 mov.l #PORTE_PDR_ADDR, r1 248 mov.b #000h, [r1] ; none 249 mov.l #PORTF_PDR_ADDR, r1 250 mov.b #03fh, [r1] ; PF0-PF5 251 mov.l #PORTG_PDR_ADDR, r1 252 mov.b #0ffh, [r1] ; PG0-OG7 253 mov.l #PORTJ_PDR_ADDR, r1 254 mov.b #020h, [r1] ; PJ5 255 pop r1 256 rts 257 258 .section IntPRG,code 259 .glb unhandled_interrupt 260 .glb unhandled_exception 261 .glb cmt0_interrupt 262 263 unhandled_exception: 264 ; turn on LED1 265 mov.l #PORTA_PODR_ADDR, r1 266 bset #0, [r1] 267 mov.l #PORTA_PDR_ADDR, r1 268 bset #0, [r1] 269 bra $ 270 271 unhandled_interrupt: 272 ; turn on LED2 273 mov.l #PORTA_PODR_ADDR, r1 274 bset #1, [r1] 275 mov.l #PORTA_PDR_ADDR, r1 276 bset #1, [r1] 277 bra $ 278 279 cmt0_interrupt: 280 pushm r1-r2 281 mov.l #count, r1 282 mov.l [r1], r2 283 tst r2, r2 284 bnz out 285 mov.l #001f4h, r2 286 287 ; toggle output level 288 mov.l #PORTA_PODR_ADDR, r1 289 bnot #6, [r1] 290 291 mov.l #count, r1 292 out: sub #1, r2 293 mov.l r2, [r1] 294 295 popm r1-r2 296 rte 297 298 .section B,DATA,align=4 299 count: .blkl 1 300 301 .end
vect.src:
1 .section C$VECT,ROMDATA,align=4 2 .glb unhandled_interrupt 3 .glb cmt0_interrupt 4 .glb intvect 5 6 intvect: 7 .lword unhandled_interrupt 8 .lword unhandled_interrupt (中略) 34 .lword unhandled_interrupt 35 .lword cmt0_interrupt 36 .lword unhandled_interrupt 37 .lword unhandled_interrupt (中略) 262 .lword unhandled_interrupt 263 264 265 .section FIXEDVECT,ROMDATA,align=4 266 .glb fixedvect 267 .glb unhandled_exception 268 .glb _start 269 270 fixedvect: 271 .lword unhandled_exception 272 .lword unhandled_exception 273 .lword unhandled_exception 274 .lword unhandled_exception 275 .lword unhandled_exception 276 .lword unhandled_exception 277 .lword unhandled_exception 278 .lword unhandled_exception 279 .lword unhandled_exception 280 .lword unhandled_exception 281 .lword unhandled_exception 282 .lword _start 283 284 .end
blink.src がボードの初期化処理および LED の点滅処理を、vect.src が可変ベクタテーブルおよび固定ベクタテーブルの内容を記述したものである。
これらのプログラムを GR-SAKURA 向けにアセンブルする場合、asrx コマンドを -cpu=rx600 オプションを付けて呼び出せばよい*1。以下にアセンブルおよびリンクの具体例を示す。
> asrx -cpu=rx600 blink.src RX Family Assembler V2.01.00.04 [01 Aug 2013] Copyright (C) 2008, 2013 Renesas Electronics Corporation All rights reserved. ( blink.src ) macro processing now --- assembler processing now --- TOTAL ERROR(S) 00000 TOTAL WARNING(S) 00000 TOTAL LINE(S) 00301 LINES CODE 0000000513(00000201H) ResetPRG DATA 0000000004(00000004H) B CODE 0000000073(00000049H) IntPRG > asrx -cpu=rx600 vect.src RX Family Assembler V2.01.00.04 [01 Aug 2013] Copyright (C) 2008, 2013 Renesas Electronics Corporation All rights reserved. ( vect.src ) macro processing now -- assembler processing now -- TOTAL ERROR(S) 00000 TOTAL WARNING(S) 00000 TOTAL LINE(S) 00284 LINES ROMDATA 0000001024(00000400H) C$VECT ROMDATA 0000000048(00000030H) FIXEDVECT > rlink -S9 -form=stype -start=B/00000000,ResetPRG,IntPRG,C$VECT/fff80000,FIXEDVECT/ffffffd0 blink.obj vect.obj Renesas Optimizing Linker W1.01.00 [19 Jul 2013] Copyright (C) 2011, 2013 Renesas Electronics Corporation The evaluation period has expired. Renesas Optimizing Linker Completed
生成された blink.mot は、GR-SAKURA ボードを E1 エミュレータに接続後、フラッシュ開発ツールキット(FDT)を用いて書き込むことができる*2。
プログラムの書き込み後、E1 を取り外し、USB または DC ジャックから電源を供給すると LED が 0.5 秒間隔で点燈/消燈を繰り返す。
ついでに、make(1) にてビルド可能なものを以下の URL に置いた。
http://he4.seikyou.ne.jp/home/tsukimizake/gr-sakura-dev/blink.zip
プログラムの処理内容
初期化処理の内容は
- 割り込みを無効化
- スタックポインタの設定
- 可変ベクタテーブルの設定
- 使用しない周辺機能の無効化
- 存在しないポートの初期化
- クロックの初期化
- セクションの初期化*3
というものである。いずれも初期化処理としてはごく一般的なものである。
ただし注意が必要な箇所があり、それらについては次節にて後述する。
初期化処理の終了後はタイマを動作させて無限ループに入る。
154 mov.l #CMSTR0_ADDR, r1 155 mov.w #00001h, [r1] ; Start CMT0 156 157 bra $
LED の点燈/消燈処理は、コンペアマッチタイマ 0(CMT0)の割り込みハンドラにさせている。
280 cmt0_interrupt: 281 pushm r1-r2 282 mov.l #count, r1 283 mov.l [r1], r2 284 tst r2, r2 285 bnz out 286 mov.l #001f4h, r2 287 288 ; toggle output level 289 mov.l #PORTA_PODR_ADDR, r1 290 bnot #6, [r1] 291 292 mov.l #count, r1 293 out: sub #1, r2 294 mov.l r2, [r1] 295 296 popm r1-r2 297 rte
消費電力低減機能
最近の MCU は、消費電力を下げる目的で、リセット直後はほとんどの機能を停止させている(クロックを供給していない)ものが多い。RX もそのような MCU の一つで、リセット直後ではコンペアマッチタイマすら使用できない。
このため、モジュールストップコントロールレジスタ A(MSTPCRA)を事前に書き換え、クロックを供給する必要があるのだが、この MSTPCRA はプログラム暴走時に意図せぬ書き換えが起きないよう保護の対象となっている。MSTPCRA を書き換えるためには、さらにプロテクトレジスタの書き換えも必要である(つまり二重に鍵が掛けられている状態である)。
blink.src の 67 行目から 73 行目にかけてのコードはこれらの処理を実行し、コンペアマッチタイマ 0(CMT0)へのクロック供給を開始するものである。
67 ; enable CMT0 68 mov.l #PRCR_ADDR, r1 69 mov.w #0a503h,[r1] 70 mov.l #MSTPCRA_ADDR, r1 71 mov.l [r1], r2 72 bclr #15, r2 73 mov.l r2, [r1]
PLL クロックの使用
GR-SAKURA ボードでは、RX に 12MHz の水晶発振子が外付けされており、これをメインクロックとして用いることができる。今回のプログラムでは、12MHz のメインクロックを PLL により 1 分周・8 逓倍したものを生成し、それを 4 分周したものをシステムクロック(ICLK)として、8 分周したものを周辺クロック(PCLK)として使用した。したがって ICLK は 24 MHz、PCLK は 12 MHz となる。
(RX に限らず)使用するクロックを切り替える際には、切り替え先のクロックが安定するのをソフトウェアで待つ必要がある。RX の場合は、この時間をメイン発振器ウェイトコントロールレジスタ(MOSCWTCR)の [4:0] の bit で指定する。このビットに指定可能な値と、その値が何サイクルに相当するかの対応を以下に示す。
b4 | b3 | b2 | b1 | b0 | 機能 |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 待機時間=2サイクル |
0 | 0 | 0 | 0 | 1 | 待機時間=4サイクル |
0 | 0 | 0 | 1 | 0 | 待機時間=8サイクル |
0 | 0 | 0 | 1 | 1 | 待機時間=16サイクル |
0 | 0 | 1 | 0 | 0 | 待機時間=32サイクル |
0 | 0 | 1 | 0 | 1 | 待機時間=64サイクル |
0 | 0 | 1 | 1 | 0 | 待機時間=512サイクル |
0 | 0 | 1 | 1 | 1 | 待機時間=1,024サイクル |
0 | 1 | 0 | 0 | 0 | 待機時間=2,048サイクル |
0 | 1 | 0 | 0 | 1 | 待機時間=4096サイクル |
0 | 1 | 0 | 1 | 0 | 待機時間=16,384サイクル |
0 | 1 | 0 | 1 | 1 | 待機時間=32,768サイクル |
0 | 1 | 1 | 0 | 0 | 待機時間=65,536サイクル |
0 | 1 | 1 | 0 | 1 | 待機時間=131,072サイクル |
0 | 1 | 1 | 1 | 0 | 待機時間=262,144サイクル |
0 | 1 | 1 | 1 | 1 | 待機時間=524,288サイクル |
ここで指定したサイクル数 + 16384 サイクル分だけメインクロックをカウントすると、MCU 内部へのメインクロック供給が開始される。
水晶発振子が安定するまでの時間()が 20.0 ms と仮定*4すると、周波数 12 MHz のメインクロックでは 240,000 サイクルに相当するので、上記の表より MOSCWTCR の [4:0] には 01110 を設定すればよい。 したがって、ソフトウェアで待機する時間は、
となる。
81 行目から 93 行目にかけてのコードは、これらの処理を行うものである。
81 mov.l #MOSCWTCR_ADDR, r1 82 mov.b #0eh, [r1] ; MOSCWTCR.MSTS[4:0]=14 83 84 ; run the main clock oscillator 85 mov.l #MOSCCR_ADDR, r1 86 bclr #0, [r1] ; MOSCCR.MOSTP=0 87 ; confirm that MOSCCR.MOSTP has actually been updated 88 ?: movu.b [r1], r2 89 tst r2, r2 90 bnz ?- 91 92 mov.l #44, r1 93 bsr mdelay
今回の場合は PLL を使用するので、加えて PLL の安定待ちも必要になる。PLL の発振安定時間は最大 0.5 ms なので、PLL ウェイトコントロールレジスタ(PLLWTCR)には 9 (約 0.6827 ms 相当)を設定する。したがって、ソフトウェアで待機する時間は、
となる(実際には約 3 ms 待機している)。
105 mov.l #PLLWTCR_ADDR, r1 106 mov.b #9, [r1] 107 mov.l #PLLCR2_ADDR, r1 108 bclr #0, r1 ; PLLCR2.PLLEN=0 109 110 mov.l #3, r1 111 bsr mdelay
この辺りの詳しい内容は、以下のアプリケーションノートに記載されている。
RX63Nグループ、RX631グループ 初期設定例:
http://documentation.renesas.com/doc/products/mpumcu/apn/rx/r01an1245jj0110_rx63n.pdf