rp2040_2.html (23454B)
1 <h1>RP2040 SDKなし2 Clock、UART</h1> 2 <time>2024-02-22</time><br /> 3 <time>2024-02-27</time>: リセット解除の間違いを修正。 4 <p> 5 今回はClockとUARTを設定してパソコンに繋ぎ、\ 6 キーボードからの入力をオウム返しするプログラムを作成する。 7 <p> 8 <p> 9 前回: <a href="rp2040_1.html">RP2040 SDKなしでLチカ</a><br> 10 ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex2 11 </p> 12 13 <h2>動作環境</h2> 14 <ul> 15 <li>Void Linux 16 <ul> 17 <li>cross-arm-none-eabi-binutils-2.32_2</li> 18 <li>GNU Make 4.4.1</li> 19 <li>minicom version 2.7.1</li> 20 </ul> 21 </li> 22 <li><a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a> 23 </li> 24 </ul> 25 26 <h2>Clock</h2> 27 28 <h3>リング発振回路</h3> 29 <p>\ 30 RP2040にはリング発振回路というのが内蔵されている。\ 31 これは自分の出力を反転させようとするもので、\ 32 不安定だが高速で消費電力の少ないクロックとして用いられる。\ 33 RP2040は電源を入れると、このリング発振回路を動作用のクロックとして\ 34 用いている。この発振回路の周波数は、チップの製造過程での誤差、動作時の電圧、\ 35 動作温度によって変動するので、正確な周波数が必要な用途には向かない。\ 36 </p> 37 38 <h3>水晶発振子</h3> 39 <p>\ 40 秋月電子通商で購入したRP2040マイコンボードには外部クロックとして、\ 41 12MHzの水晶発振子が付属する。水晶発振子はリング発振回路より電力を消費するが、\ 42 より正確である。\ 43 </p> 44 45 <h3>PLL</h3> 46 <p>\ 47 水晶振動子を入力として、周波数を数倍にしたものを出力するもの。\ 48 電気的な話はよく知らない。データシートの「2.18.2. Calcurating PLL parameters」\ 49 によると、入力周波数を<code>FREF</code>としたときの出力周波数は\ 50 <code>(FREF / REFDIV) × FBDIV / (POSTDIV1 × POSTDIV2)</code>となる。\ 51 これらの変数はそれぞれ設定用のレジスタに値を保存することで変更できる。\ 52 </p> 53 54 <h2>UART</h2> 55 <p> 56 Universal Asynchronous Receiver/Transmitterの略。\ 57 2本の線だけで通信できる。\ 58 プロトコルは詳しく知らないが、rp2040がよしなにやってくれる。\ 59 rp2040では同時に二個まで利用できる。\ 60 どのGPIOピンを使うかもある程度自由に選べる。\ 61 どのピンが使えるかはデータシートの「2.19.2. Function Select」に書かれている。\ 62 今回はGPIO0とGPIO1を使う。\ 63 パソコンとの接続には、秋月電子通商で売っている\ 64 <a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a>\ 65 を使用した。\ 66 UARTで接続するためのパソコン側のソフトウェアはminicomを使用した。\ 67 僕の環境ではシリアル変換モジュールをパソコンにUSB接続すると、\ 68 <code>/dev/ttyUSB0</code>として認識されるので、\ 69 </p> 70 <pre><code>\ 71 $ minicom -D /dev/ttyUSB0 72 </code></pre> 73 <p> 74 とすると接続できる。 75 </p> 76 77 <h2>main.s</h2> 78 <h3>初期設定</h3> 79 <p> 80 後で見るように、UARTの動作には多分水晶発振子とPLLが必要なので、\ 81 まずはそれを設定する。\ 82 起動後、メインのプログラムが読み込まれるまでの<code>boot2</code>は\ 83 前回と同じものである。\ 84 <code>main.s</code>ではまず前回と同様に初期スタックポインタとエントリーポイント\ 85 を定義する: 86 </p> 87 <pre><code> .section .vectors 88 vectors: 89 .word 0x20040000 // initial SP 90 .word (reset+1) // entry point 91 </code></pre> 92 <p> 93 続いて利用するサブシステムのリセットを解除する。\ 94 PLLとUARTが追加されている。\ 95 今回使うUARTはUART0だけである。\ 96 なお、UARTはclock_periが有効化されるまでリセット状態の解除が完了しない\ 97 ようなので、unreset_chkからは外してある:\ 98 </p> 99 <pre><code> .section .text 100 reset: 101 // unreset gpio, pll_sys, uart0 102 ldr r0, =(1 << 22 | 1 << 12 | 1 << 5) // uart0 | pll_sys | io_bank0 103 ldr r3, resets_base 104 ldr r1, atomic_clr 105 str r0, [r3, r1] // RESETS: RESET 106 mov r1, #1 107 lsl r1, #22 108 bic r0, r1 // uart stays in reset state until clock_peri is enabled 109 unreset_chk: 110 ldr r1, [r3, #0x8] // RESETS: RESET_DONE 111 bic r0, r1 112 bne unreset_chk 113 114 /* ... */ 115 116 atomic_clr: 117 .word 0x00003000 118 resets_base: 119 .word 0x4000c000 120 </code></pre> 121 122 <h3>GPIOの設定</h3> 123 <p> 124 次にGPIOの役割を設定する。\ 125 前回はLEDを点滅させるためにGPIO25をSIOに設定したが、\ 126 今回はGPIO0とGPIO1をUART0にする: 127 </p> 128 <pre><code> // set gpio functions 129 ldr r3, io_bank0_base 130 mov r0, #2 // uart0 131 mov r1, #0x4 132 str r0, [r3, r1] // IO_BANK0: GPIO0_CTRL 133 mov r1, #0xc 134 str r0, [r3, r1] // IO_BANK0: GPIO1_CTRL 135 136 /* ... */ 137 138 io_bank0_base: 139 .word 0x40014000 140 </code></pre> 141 142 <h3>Clockの設定</h3> 143 <p> 144 Clockの設定をする。\ 145 まずは水晶発振子を起動する。\ 146 水晶発振子は起動してから周波数が安定するまで少し時間がかかるようで、\ 147 その間待たないといけない。\ 148 この時間は1msあれば十分だとデータシートに書いている。\ 149 この待ち時間はXOSC: STARTUPレジスタに、256サイクル単位で記述する。\ 150 データシートによると初期のリング発振子は最大で12MHzなので、\ 151 <code>(12 * 10^6 * 1 * 10^-3) / 256 = 47</code>をこのレジスタにセットする。\ 152 ところでデータシートではこの計算はリング発振子ではなく\ 153 水晶発振子の周波数で書かれている。\ 154 起動直後でまだ使えない水晶発振子の周波数を使うのはなんでやろ。\ 155 SDKでも<code>pico-sdk/src/rp2_common/hardware_xosc/xosc.c</code>で、 156 </p> 157 <pre><code>\ 158 #define STARTUP_DELAY (((((XOSC_MHZ * MHZ) / 1000) + 128) / 256) * PICO_XOSC_STARTUP_DELAY_MULTIPLIER) 159 </code></pre> 160 <p> 161 と定義されている(PICO_XOSC_STARTUP_DELAY_MULTIPLIERは1)。\ 162 とりあえず47に設定しているが、試しに0や1にしても動いた。よくわからん。\ 163 </p> 164 <p> 165 待ち時間を設定したら発振子を起動する。\ 166 XOSC: CTRLに起動用のコマンド的なものを入力し、周波数が安定するのを待つ。\ 167 </p> 168 <p> 169 以上を実装したのが以下のコード: 170 </p> 171 <pre><code> // setup xosc 172 ldr r3, xosc_base 173 mov r0, #47 // start up delay for 12MHz rosc (xosc?) 174 str r0, [r3, #0xc] // XOSC: STARTUP 175 ldr r0, =(0xfab << 12 | 0xaa0) 176 str r0, [r3, #0] // XOSC: CTRL 177 wait_xosc: 178 ldr r0, [r3, #0x4] // XOSC: STATUS 179 lsr r0, r0, #31 // STABLE bit 180 beq wait_xosc 181 182 /* ... */ 183 184 xosc_base: 185 .word 0x40024000 186 </code></pre> 187 188 <h3>PLLの設定</h3> 189 <p> 190 水晶発振子が起動できたので、次にPLLを設定する。\ 191 CPUが133MHzまで対応しているので133MHzになるようにした。\ 192 </p> 193 <p> 194 PLLは入力となる振動(ここでは水晶発振子の振動)を加工して\ 195 周波数を上げたり下げたりする。\ 196 出力の周波数は以下の式で決まる: 197 </p> 198 <pre>(FREF / REFDIV) * FBDIV / (POSTDIV1 * POSTDIV2)</pre> 199 <p> 200 FREFは入力の周波数(ここでは12MHz)で、その他の変数はプログラマが設定できる。\ 201 ただしデータシートによると(FREF / REFDIV)は5MHz以上でないといけないので、\ 202 REFDIVは1である。\ 203 また、FBDIVは16〜320、POSTDIV1とPOSTDIV2は1〜7で、POSTDIV1とPOSTDIV2に違う値を\ 204 代入する場合、POSTDIV1に大きい方を入れたほうが消費電力が少なくなるとのことなので、\ 205 133MHzにするには、FBDIV=133、POSTDIV1=6、POSTDIV=2とすればいい\ 206 (POSTDIV1=4、POSTDIV2=3も可能だが、pico-sdkに付属するvcocalc.pyというスクリプト\ 207 のコメントには、この2つの値の差が大きい方がいいと書いている)。 208 </p> 209 <p> 210 PLL設定の手順は、FBDIVの設定、PLLとVCOの起動、VOCが安定するまで待機、\ 211 POSTDIV1とPOSTDIV2の設定、Post Dividerの起動、そして最後にシステムとUARTのクロック\ 212 を今設定したPLLに変更、である。\ 213 以上を実装したのが以下のコード: 214 </p> 215 <pre><code> // setup pll_sys 133MHz 216 ldr r3, pll_sys_base 217 // set feedback divider 218 mov r0, #133 219 str r0, [r3, #0x8] // PLL: FBDIV_INT 220 // power on pll and vco 221 ldr r0, =(1 << 5 | 1) // VCOPD | PD 222 ldr r1, atomic_clr 223 add r1, r1, #0x4 224 str r0, [r3, r1] // PLL: PWR 225 // wait vco to lock 226 wait_vco: 227 ldr r0, [r3, #0] // PLL: CS 228 lsl r0, r0, #31 229 beq wait_vco 230 // setup post dividers 231 ldr r0, =(4 << 16 | 3 << 12) 232 str r0, [r3, #0xc] // PLL: PRIM 233 // power on post divider 234 mov r0, #8 // POSTDIVPD 235 str r0, [r3, r1] // PLL: PWR 236 237 // set system clock clksrc_pll_sys 238 ldr r3, clocks_base 239 ldr r0, =(0x0 << 5 | 0x1) 240 str r0, [r3, #0x3c] // CLOCKS: CLK_SYS_CTRL 241 // enable clk_peri 242 mov r0, #1 243 lsl r0, r0, #11 244 str r0, [r3, #0x48] // CLOCKS: CLK_PERI_CTRL 245 246 /* ... */ 247 248 atomic_clr: 249 .word 0x00003000 250 clocks_base: 251 .word 0x40008000 252 pll_sys_base: 253 .word 0x40028000 254 </code></pre> 255 256 <h3>UARTの設定</h3> 257 <p> 258 データシートによるとUART設定の手順は以下の通り: 259 </p> 260 <ul> 261 <li>リセットの解除</li> 262 <li>clock_periの設定</li> 263 <li>UARTの有効化</li> 264 <li>FIFOの有効化</li> 265 <li>転送速度の設定</li> 266 <li>フォーマットの設定</li> 267 </ul> 268 <p> 269 上の2つは既に終えている。\ 270 残りの部分はこの順番どおりに設定しても動かなかった。\ 271 C言語で書かれたサンプルを見ると、クロックを設定した後、\ 272 転送速度の設定、UARTの有効化、FIFOの有効化の順になっている。\ 273 そのとおりにすると動いた。\ 274 理由はよく理解していないが、変数を設定してから起動するほうが素直ではある。\ 275 </p> 276 <p> 277 転送速度はminicomのデフォルトである115200 baudに設定する。\ 278 データシート「4.2.7.1. Baud Rate Calculation」の計算式において、\ 279 クロック周波数を125MHzから133MHzに変えて計算して、\ 280 BRDI=72、BDRF=0.157(=10/64)となる。\ 281 この数値をUART: UARTIBRD、UART: UARTFBRDレジスタにそれぞれ代入する。 282 </p> 283 <p> 284 UARTの有効化はUART: UARTCRレジスタのUARTENビットをセットすることで行う。\ 285 C言語のサンプルでは同じレジスタのRXE、TXEビットもセットしているが、\ 286 この2つはもともと1になっているのでほっといてよさそう。\ 287 </p> 288 <p> 289 FIFOの有効化はUART: UARTLCR_HレジスタのFENビットをセットすることで行う。\ 290 また、同じレジスタの他のビットで、データーのフォーマットを設定できる。\ 291 ここではminicomのデフォルトに合わせてWLENを8bitにする。\ 292 </p> 293 <p> 294 以上をまとめると以下のようになる: 295 </p> 296 <pre><code> // setup uart0 297 ldr r3, uart0_base 298 // set baudrate 115200 299 // BDRI = 72, BDRF = 0.157 (10 / 64) 300 mov r0, #72 301 str r0, [r3, #0x24] // UART: UARTIBRD 302 mov r0, #10 303 str r0, [r3, #0x28] // UART: UARTFBRD 304 // enable uart0 305 mov r0, #1 // UARTEN 306 ldr r1, atomic_set 307 add r1, r1, #0x30 308 str r0, [r3, r1] // UART: UARTCR 309 // enable FIFO and set format 310 ldr r0, =(3 << 5 | 1 << 4) // WLEN = 8, FEN = 1 311 str r0, [r3, #0x2c] // UART: UARTLCR_H 312 313 /* ... */ 314 315 atomic_set: 316 .word 0x00002000 317 uart0_base: 318 .word 0x40034000 319 </code></pre> 320 321 <h3>UARTの入出力</h3> 322 <p> 323 設定が終わったので実際にUARTの入出力を処理するコードを書く。\ 324 まずUARTからの出力は、出力したいバイトをUART: UARTDRに書き込むことで\ 325 行う。\ 326 その際、書き込まれたデータは一時的に出力用FIFOに保持されるので、このFIFO\ 327 が満杯でないことを確認する必要がある。\ 328 FIFOの状態はUART: UARTFRレジスタで確認できる。\ 329 このレジスタのTXFFの値が1であればデータを書き込めないので、\ 330 0になるまで待機する。\ 331 関数名は<code>putbyte</code>にした。\ 332 また出力したいデータは<code>r0</code>レジスタにの下位8ビットに\ 333 入れられているものとした。\ 334 書き込めるデーターは8ビットだけなので、<code>0xff</code>と論理積をとってから\ 335 書き込んでいる: 336 </p> 337 <pre><code>putbyte: 338 ldr r3, uart0_base 339 mov r1, #1 340 lsl r1, r1, #5 // TXFF 341 txff: 342 ldr r2, [r3, #0x18] // UART: UARTFR 343 tst r1, r2 344 bne txff 345 mov r1, #0xff 346 and r0, r0, r1 347 str r0, [r3, #0] // UART: UARTDR 348 bx lr 349 350 /* ... */ 351 352 uart0_base: 353 .word 0x40034000 354 </code></pre> 355 356 <p> 357 入力はUART: UARTDRの下位8ビットを読むことで得られる。\ 358 UARTからの入力は、一時的に入力用FIFOに保存される。\ 359 このFIFOが空の状態でデータを読んでも意味がないので、\ 360 FIFOが空でないことを確認する必要がある。\ 361 これはUART: UARTFRレジスタのRXFEを読むことで確認できる。\ 362 本来は入力があったときに割り込みを発生させて、それまでは\ 363 CPUを休ませるか別の処理をさせておくべきだが、とりあえずここでは\ 364 ループでFIFOの状態を確認し続けている。\ 365 関数名は<code>getbyte</code>にした。 366 読み込んだデータは<code>r0</code>レジスタに保存している:\ 367 </p> 368 <pre><code>getbyte: 369 ldr r3, uart0_base 370 mov r1, #1 371 lsl r1, r1, #4 // RXFE 372 rxfe: 373 ldr r2, [r3, #0x18] // UART: UARTFR 374 tst r1, r2 375 bne rxfe 376 ldr r0, [r3, #0] // UART: UARTDR 377 mov r1, #0xff 378 and r0, r0, r1 379 bx lr 380 381 /* ... */ 382 383 uart0_base: 384 .word 0x40034000 385 </code></pre> 386 <p> 387 あとはこの2つの関数をループの中で交互に呼び出せば、\ 388 オウム返しするだけのプログラムが完成する: 389 </p> 390 <pre><code>loop: 391 bl getbyte 392 bl putbyte 393 b loop 394 </code></pre> 395 396 <h2>リング発振回路でUARTは動くんかな?</h2> 397 <p>\ 398 UARTの通信には正確なクロックが必要である。\ 399 その為上では<code>clk_peri</code>として水晶発振子とPLLを用いた。\ 400 ところがpico-examplesのhello_uartでは<code>main()</code>関数で\ 401 水晶発振子を設定していない。そこでリング発振回路を用いてみたのだが、\ 402 どうもうまく通信できない。出力されている正確な周波数も分からないので\ 403 あきらめることにした。オシロスコープなんていうものは持っていない。\ 404 </p> 405 406 <h3>pico-sdk</h3> 407 <p> 408 ところがどうも調べているとSDKを使った場合、\ 409 デフォルトではクロック周波数は125MHzになっているらしい。\ 410 どうやら水晶発振子もPLLも<code>main()</code>が呼ばれる前に\ 411 設定されているようである。\ 412 </p> 413 <p> 414 pico-examplesのサンプルプログラムはビルドすると自動で逆アセンブリした\ 415 ファイルを出力してくれる。これを見ると、最初の256バイトは前回説明した\ 416 boot2のコードで、その後ろにベクターテーブルが続く。\ 417 ベクターテーブルの最初は初期スタックポインタで、<code>0x20042000</code>\ 418 になっている。次はエントリーポイントで、<code>0x100001f7</code>である:\ 419 </p> 420 <pre><code>\ 421 10000100 <__VECTOR_TABLE>: 422 10000100: 20042000 .word 0x20042000 423 10000104: 100001f7 .word 0x100001f7 424 </code></pre> 425 <p> 426 Thumbモードなので実際のエントリーポイントは<code>1</code>引いた、\ 427 <code>0x100001f6</code>である。この場所ではまず自分のCPUIDを調べて、\ 428 <code>1</code>であれば待機状態に移行する。\ 429 RP2040はデュアルコアである。起動直後はCPUIDが<code>0</code>のコアだけで処理をして、\ 430 CPUIDが<code>1</code>のコアはプログラマが必要に応じて起動することになっている。\ 431 このためCPUIDが<code>1</code>のコアは起動してすぐに待機状態に入ることがデータシート\ 432 に書かれている。しかしこの処理はユーザーの書いたプログラムじゃなくて\ 433 内蔵ROMにある起動用プログラムが担当するみたいに書かれてるんやけど、\ 434 なんでSDKではユーザープログラムの一部として組み込んでるんかな? 435 </p> 436 <pre><code>\ 437 100001f6 <_reset_handler>: 438 100001f6: 481d ldr r0, [pc, #116] ; (1000026c <hold_non_core0_in_bootrom+0xe>) 439 100001f8: 6800 ldr r0, [r0, #0] 440 100001fa: 2800 cmp r0, #0 441 100001fc: d12f bne.n 1000025e <hold_non_core0_in_bootrom> 442 </code></pre> 443 <p>\ 444 上のコードの最初の<code>ldr</code>は、<code>0xd0000000</code>\ 445 (M0PLUS: CPUIDレジスタ)をロードしている。\ 446 最後の飛び先<code>0x1000025e</code>はCPUIDが<code>1</code>のCPUを待機させる処理\ 447 である:\ 448 </p> 449 <pre><code>\ 450 1000025e <hold_non_core0_in_bootrom>: 451 1000025e: 4809 ldr r0, [pc, #36] ; (10000284 <hold_non_core0_in_bootrom+0x26>) 452 10000260: f001 fb9c bl 1000199c <rom_func_lookup> 453 10000264: 4700 bx r0 454 10000266: 0000 .short 0x0000 455 /* ... */ 456 10000284: 00005657 .word 0x00005657 457 </code></pre> 458 <p>\ 459 内蔵フラッシュに書きこまれた関数を呼びだしている。呼びだしに使うコードは\ 460 <code>0x00005657</code>(<code>'W' | 'V' << 8</code>)である。\ 461 データシートを見ると、この関数は<code>_wait_for_vector()</code>という名前で、\ 462 CPUIDが1のCPUを寝かしつけるのに使われると書いている。\ 463 この部分のソースコードをpico-sdkで探すと\ 464 <code>pico-sdk/src/rp2_common/pico_standard_link/crt0.S</code>\ 465 というのが見付かった:\ 466 </p> 467 <pre><code>\ 468 $ find pico-sdk/src -type f | xargs grep -l _reset_handler 469 pico-sdk/src/rp2_common/pico_standard_link/crt0.S 470 </code></pre> 471 <p>\ 472 このファイルによると: 473 </p> 474 <pre><code> // Only core 0 should run the C runtime startup code; core 1 is normally 475 // sleeping in the bootrom at this point but check to be sure 476 </code></pre> 477 <p>\ 478 だそうである。やっぱり無駄やん。内蔵フラッシュのプログラムにバグがあっても\ 479 このコードのせいで見付かりにくくなってない?知らんけど。\ 480 </p> 481 482 <p>\ 483 続いて<code>.data</code>領域と<code>.bss</code>領域のコピー、初期化のようである。\ 484 多分OSの本かなんかで習ったメモリマップの話:\ 485 </p> 486 <pre><code>\ 487 100001fe: a40d add r4, pc, #52 ; (adr r4, 10000234 <data_cpy_table>) 488 10000200: cc0e ldmia r4!, {r1, r2, r3} 489 10000202: 2900 cmp r1, #0 490 10000204: d002 beq.n 1000020c <_reset_handler+0x16> 491 10000206: f000 f812 bl 1000022e <data_cpy> 492 1000020a: e7f9 b.n 10000200 <_reset_handler+0xa> 493 1000020c: 4918 ldr r1, [pc, #96] ; (10000270 <hold_non_core0_in_bootrom+0x12>) 494 1000020e: 4a19 ldr r2, [pc, #100] ; (10000274 <hold_non_core0_in_bootrom+0x16>) 495 10000210: 2000 movs r0, #0 496 10000212: e000 b.n 10000216 <bss_fill_test> 497 498 10000214 <bss_fill_loop>: 499 10000214: c101 stmia r1!, {r0} 500 501 10000216 <bss_fill_test>: 502 10000216: 4291 cmp r1, r2 503 10000218: d1fc bne.n 10000214 <bss_fill_loop> 504 </code></pre> 505 506 <p>\ 507 最後にいろいろ呼びだす:\ 508 </p> 509 <pre><code>\ 510 1000021a <platform_entry>: 511 1000021a: 4917 ldr r1, [pc, #92] ; (10000278 <hold_non_core0_in_bootrom+0x1a>) 512 1000021c: 4788 blx r1 513 1000021e: 4917 ldr r1, [pc, #92] ; (1000027c <hold_non_core0_in_bootrom+0x1e>) 514 10000220: 4788 blx r1 515 10000222: 4917 ldr r1, [pc, #92] ; (10000280 <hold_non_core0_in_bootrom+0x22>) 516 10000224: 4788 blx r1 517 10000226: be00 bkpt 0x0000 518 10000228: e7fd b.n 10000226 <platform_entry+0xc> 519 /* ... */ 520 10000278: 10001819 .word 0x10001819 521 1000027c: 100002dd .word 0x100002dd 522 10000280: 10001909 .word 0x10001909 523 </code></pre> 524 <p>\ 525 一つめの<code>blx</code>は<code>0x10001818</code>(<code>runtime_init</code>)を、\ 526 二つめは<code>0x100002dc</code>(<code>main</code>)を、\ 527 最後のは<code>0x10001908</code>(<code>exit</code>)を、\ 528 それぞれ呼んでいる。\ 529 この<code>runtime_init</code>はアセンブリでは分かりにくいので\ 530 ソースコードを探してみると、以下のものが見付かった:\ 531 </p> 532 <pre><code>\ 533 $ find pico-sdk/src -type f | xargs grep -l runtime_init 534 pico-sdk/src/rp2_common/pico_runtime/runtime.c 535 pico-sdk/src/rp2_common/pico_standard_link/crt0.S 536 pico-sdk/src/common/pico_sync/include/pico/mutex.h 537 </code></pre> 538 <p>\ 539 最後の<code>mutex.h</code>は関係なさそう。\ 540 二つめの<code>crt0.S</code>は呼びだしてるだけ。\ 541 一つめの<code>runtime.c</code>が多分探しているものである。\ 542 これを見るとまず各種周辺機器を一度リセットし、リセット状態を解除している。\ 543 使わんやつも初期化してない?\ 544 その後<code>clocks_init()</code>を呼んでいる。\ 545 この関数は<code>pico-sdk/src/rp2_common/hardware_clocks/clocks.c</code>\ 546 で定義されている。これを見ると、<code>xosc_init()</code>を呼んで\ 547 水晶発振子を初期化した後、<code>clk_peri</code>を125MHzに設定している:\ 548 </p> 549 <pre><code> clock_configure(clk_peri, 550 0, 551 CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, 552 125 * MHZ, 553 125 * MHZ); 554 </code></pre> 555 <p>\ 556 やっぱり水晶発振子じゃないとあかんのかな。\ 557 </p> 558 559 <h2>CMake</h2> 560 <p>\ 561 上ではビルドしたバイナリを逆アッセンブルして読んだ。\ 562 わざわざこんなことをしなくてもMakefile読めばなにがどう\ 563 なって最終生成物に辿りつくのか分かればいいのだが、そうもいかない。\ 564 このSDKとpico-examplesにはビルドシステムとしてCMakeなるものが使われている。\ 565 これがどうも複雑でよく分からない。勉強する気にもならん。\ 566 上で見た<code>crt0.S</code>や<code>runtime.c</code>といったファイルも\ 567 <code>hello_uart</code>で本当に使われているものなのかもよく分からない。\ 568 こんな煩雑なものは本当に必要なのかな。無駄に複雑にしてるだけとちゃうんかな。\ 569 特に僕は勉強用に使ってるので、ソースコードの依存関係をもっと\ 570 分かりやすくしてくれないと、内部でなにがどうなってるのか理解しにくい。\ 571 何度か頑張って読もうとしたが、面白くないのでやめた。\ 572 数百行のファイルをあっちからこっちから<code>include</code>してるし、\ 573 大文字ばかりの変数だらけで目が痛い。\ 574 こんなものを扱えるというのはえらいええ頭してはるんやね。\ 575 </p> 576 577 578 <h2>参考</h2> 579 <ul> 580 <li> 581 <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a> 582 </li> 583 <li> 584 <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a> 585 </li> 586 <li> 587 <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a> 588 </li> 589 <li> 590 <a href="https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%BB%E3%82%AA%E3%82%B7%E3%83%AC%E3%83%BC%E3%82%BF">リング・オシレータ.Wikipedia</a> 591 </li> 592 <li> 593 <a href="https://www5.epsondevice.com/ja/information/technical_info/osc.html">水晶発振器とは? 原理と仕組み、水晶振動子との違い、選び方のポイントを解説.エプソン水晶デバイス</a> 594 </li> 595 </ul>