www.mtkn.jp

Manuscripts for my personal webpage.
git clone https://git.mtkn.jp/www.mtkn.jp
Log | Files | Refs | LICENSE

rp2040_3_interrupt.html (6633B)


      1 <h1>RP2040 SDKなし3 割り込み</h1>
      2 <time>2025-08-27</time><br />
      3 <p>
      4 前回UARTで通信する際、FIFOにデータが届いていないかを確認しつづけていた(ポーリング)。
      5 これでは電力がもったいないので、データが届くまでCPUは休ませておいて、
      6 届いたときだけ処理を実行するように変更する(割り込み)。
      7 <p>
      8 <p>
      9 前回: <a href="rp2040_2.html">RP2040 SDKなし2 Clock、UART</a><br>
     10 ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex3_interrupt
     11 </p>
     12 
     13 <h2>動作環境</h2>
     14 <ul>
     15 <li>OpenBSD 7.7
     16 	<ul>
     17 	<li>arm-none-eabi-binutils-2.40</li>
     18 	<li>OpenBSD Make</li>
     19 	<li>minicom version 2.8</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>割り込み</h2>
     27 <p>
     28 ハードウェアが外部から信号を受けとると、CPUに現在の処理を中断させてその信号を処理させるようにできる。\
     29 これを割り込み処理という。\
     30 今回はUARTにデータが届いた時に同じデータをUARTに書き込むようにしたい。\
     31 </p>
     32 
     33 <h3>割り込みを有効化</h3>
     34 <p>
     35 割り込みの信号を受け取るために、割り込みを有効化する。\
     36 CPUが割り込みを受け付けるようにする設定と、\
     37 UARTが割り込み信号を発生させるようにする設定が必要である。\
     38 </p>
     39 <p>
     40 CPU側の設定は<code>M0PLUS: NVIC_ISER</code>レジスタの有効化したい割り込みのビットに1を書くことで行う。\
     41 有効化できる割り込みは全部で26個ある。\
     42 割り込みの種類はrp2040のデータシート[1]のTable 80. Interruptsに書いてある。\
     43 今回有効化するのはUART0の割り込みで、この表から20番が割り当てられていることが分かるので、\
     44 <code>M0PLUS: NVIC_ISER</code>に、下から20番目のビットを1にしたもの書き込む:\
     45 </p>
     46 <code><pre>	// enable uart interrupt in cpu
     47 	ldr r4, ppb_base
     48 	mov r5, #0xe1
     49 	lsl r5, #8
     50 	mov r0, #1
     51 	lsl r0, #20
     52 	str r0, [r4, r5] // M0PLUS: NVIC_ISER
     53 
     54 /* ... */
     55 
     56 ppb_base:
     57 	.word 0xe0000000
     58 </pre></code>
     59 <p>
     60 UART側の設定は、<code>UART: UARTIMSC</code>レジスタで行なう。\
     61 データを受け取ったときに割り込みを発生させたいので、\
     62 <code>RXIM</code>ビットに1を書き込む。\
     63 また、処理を開始する前に既にFIFOにデータがたまっている可能性があるので、\
     64 FIFOが空ではないときに割り込みを発生させるように、\
     65 <code>RTIM</code>にも1を書き込む:\
     66 </p>
     67 <code><pre>	// enable uart interrupt in uart0 subsystem
     68 	ldr r1, uart0_base
     69 	mov r2, #(1 &lt;&lt; 6 | 1 &lt;&lt; 4) // RTIM | RXIM
     70 	str r2, [r1, #0x38] // UART: UARTIMSC
     71 </pre></code>
     72 <p>
     73 また、FIFOにデータがきたらすぐに割り込みを発生させたいので、\
     74 <code>UART: UARTIFLS</code>レジスタの<code>RXIFLSEL</code>に0を書き込む。\
     75 これでFIFOの1/8が埋まったら割り込みが発生する。\
     76 </p>
     77 <code><pre>	// set fifo level to 0
     78 	mov r2, #0
     79 	str r2, [r1, #0x34] // UART: UARTIFLS
     80 </pre></code>
     81 <p>
     82 これだとFIFOに届いた瞬間には割り込みがおこらない気がするが、\
     83 pico-sdk(src/rp2_common/hardware_uart/include/hardware/uart.h)の\
     84 <code>uart_set_irqs_enabled</code>関数でもこうなってる。\
     85 FIFOを無効にすれば即座に割り込みがおこるかもしれないが、\
     86 データが失われる可能性もあるのかな。\
     87 </p>
     88 
     89 <h3>ベクターテーブルの設定</h3>
     90 <p>
     91 外部からの信号で割り込みが発生したときにCPUに行わせる処理はベクターテーブルにあらかじめ登録しておく必要がある。\
     92 ベクターテーブルの場所は<a href="rp2040_1.html">一回目</a>に登録した通り\
     93 <code>vectors</code>とラベルを付けた場所(main.sの先頭)である。\
     94 ARMの仕様書[2]によると、ベクターテーブルの0番目は初期のスタックポインタで、\
     95 そこから15個はARMによって使用用途が定められている。\
     96 16番以降の32個は各CPUの設計者が自由に決められる。\
     97 rp2040の割り込みについてはデータシート[1]のTable 80. Interruptsに一覧表が載っている。\
     98 今回使いたいのはUART0の割り込みなので16 + 20 = 36番目に、割り込み時に呼ばれたい関数のポインタ\
     99 (Thumb Modeなので関数のアドレスに1を足したもの)を書き込めばいい。\
    100 それ以外のものは今回は使わないので適当な数値でうめておく:\
    101 </p>
    102 <code><pre>	.global vectors
    103 vectors:
    104 	.word 0x20040000 // initial SP
    105 	.word (reset+1)  // entry point
    106 	.word 0xdeadbeef
    107 	.word 0xdeadbeef
    108 
    109 	.word 0xdeadbeef
    110 	.word 0xdeadbeef
    111 	.word 0xdeadbeef
    112 	.word 0xdeadbeef
    113 
    114 	.word 0xdeadbeef
    115 	.word 0xdeadbeef
    116 	.word 0xdeadbeef
    117 	.word 0xdeadbeef
    118 
    119 	.word 0xdeadbeef
    120 	.word 0xdeadbeef
    121 	.word 0xdeadbeef
    122 	.word 0xdeadbeef
    123 
    124 
    125 	.word 0xdeadbeef
    126 	.word 0xdeadbeef
    127 	.word 0xdeadbeef
    128 	.word 0xdeadbeef
    129 
    130 	.word 0xdeadbeef
    131 	.word 0xdeadbeef
    132 	.word 0xdeadbeef
    133 	.word 0xdeadbeef
    134 
    135 	.word 0xdeadbeef
    136 	.word 0xdeadbeef
    137 	.word 0xdeadbeef
    138 	.word 0xdeadbeef
    139 
    140 	.word 0xdeadbeef
    141 	.word 0xdeadbeef
    142 	.word 0xdeadbeef
    143 	.word 0xdeadbeef
    144 
    145 	.word 0xdeadbeef
    146 	.word 0xdeadbeef
    147 	.word 0xdeadbeef
    148 	.word 0xdeadbeef
    149 
    150 	.word (uart_interrupt_handler+1) // UART0_IRQ
    151 </pre></code>
    152 
    153 <h3>割り込み処理</h3>
    154 <p>
    155 割り込み発生時に呼ばれる関数はUARTのFIFOに届いたデータをそのまま送信するだけのものである:\
    156 </p>
    157 <code><pre>	// uart_interrupt_handler echos the data arrived at uart0
    158 uart_interrupt_handler:
    159 	push {lr}
    160 	bl getbyte
    161 	bl putbyte
    162 	pop {pc}
    163 </pre></code>
    164 <p>
    165 <code>getbyte</code>、<code>putbyte</code>は前回と同じもの。
    166 </code>
    167 
    168 <h2>メインループ</h2>
    169 <p>
    170 UARTにデータが届いていないときは特にすることがないので\
    171 CPUは寝かせておく。\
    172 ARMには割り込みが発生するまでなにもしない<code>wfe</code>という命令があるのでこれを使う:\
    173 </p>
    174 <code><pre>\
    175 loop:
    176 	wfe
    177 	b loop
    178 </pre></code>
    179 
    180 <h2>完成</h2>
    181 <p>
    182 これで完成。\
    183 動作は前回と全く同じだが、消費電力をおさえられる。\
    184 </p>
    185 
    186 <h2>参考</h2>
    187 <ul>
    188 <li>
    189 [1]. <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a>
    190 </li>
    191 <li>
    192 [2]. <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a>
    193 </li>
    194 <li>
    195 [3]. <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a>
    196 </li>
    197 </ul>