目次
前
次
I/O操作
利用するForthインタプリタは、ARMの中のCPUコアである
Cortex-M0を利用したLPC1114にインプリメントされてます。
I/O操作はSFR(Special Function Register)への値設定か
値取得で実現できます。
LPC1114のピンアサインは、以下。
傍に赤の線をつけたピンをGPIO(General Purpose Input and
Output)として利用可能。
ワンチップマイコンの常で、ピンに複数の機能を
割り当てられるので、場面に応じた設定が必要。
データシートから、単純なI/Oとして利用するため
必要なパラメータ設定に関係する部位を探します。
メモリマップを眺めると、以下。
CPUチップをひとつのシステムとみれば、configurationで
動作仕様を指定できるはずと、レジスタを探していきます。
アドレス0x40044000に、IOCONFIGというラベルがありました。
NXPのARMプロセッサのメモリマップでは、アドレス32ビットの
上位16ビットで大まかな機能分野を指定し、下位16ビットで
より細かい内容に絞り込むよう。
IOCONFIGで、GPIOとして利用できるようにするとともに
内部抵抗を活用したプルアップ、プルダウンの指定まで
も可能となります。
PIO1_8をGPIOで利用してみます。
デフォルトで、GPIOの設定になっているのですが
細かい設定をしたいときには、次のようにコードを入力。
\ define LABEL
$40044014 constant IOCON_PIO1_8
$C0 IOCON_PIO1_8 !
\ Set P1.8 as GPIO without Pullup/Pulldown.
\ $C0 are reserved bits that must be set.
IOCONFIGレジスタのビット割り当てに関しては、後で説明。
Forthのコードでは、以下のことがわかります。
- 定数設定ワードは、「constant」
- 16進数指定は、接頭辞で「$」をつける
- レジスタ、メモリへの値代入ワードは、「!」
- コメントには、「\」を先行させる
日本語語順でワードを並べると、コードになります。
「$40044014を、定数でラベルIOCON_PIO1_8に割当て」
と言ってみれば、理解できるはず。
「$C0をIOCON_PIO1_8に転送」という日本語の
表現が、そのままForthのコードと等価と理解
できます。
ピンアサインで確認すると、以下。
PIO1_8が、GPIOとして利用できるようになったなら
LEDを接続して、点灯、消灯させてみることに。
方向を設定するには、Data Direction Registerを
論理値の出力は、Data Registerを利用します。
Data Direction Registerは、メモリマップでみると
$50010000から始まるどこかにあるレジスタのはず。
Data Direction Registerのアドレスは、$50018000。
このレジスタは、32ビット中の下位16ビットでピンの
入出力を設定し、論理値の'1'を与えると出力に。
PIO1_8では、PORT1という範囲で$50018000にしてある
ので、下位16ビットの8ビット目を'1'にすれば出力
になります。
注目しているのは、8ビット目だけとすると、$100
(10進数で256)を指定すればよいはず。
コードでは、以下。
\ define LABEL
$50018000 constant GPIO1_DDR
\ Set P1.8 as Output
$100 GPIO1_DDR !
論理値の出力には、指定レジスタに論理値を書き込む
だけですが、LPCシリーズでは、他のピンに影響を与え
ないで、該当ピンだけを操作可能。
2進数で該当ビットだけを1にし、その2進数を4倍
した値が、そのピンのData Registerのアドレスになる。
PIO1_8のデータレジスタのアドレスは、次の計算で
求められます。(ビット番号は、0から始めます。)
entry address $50010000
offset 0001_0000_0000 -> 0100_0000_0000 = $400
PIO1_8 Data Register Address => $50010400
レジスタは32ビットなので、4バイト分の
オフセットを加え、バイトアドレスに換算
と考えればよいです。
論理値を出力するコードは、以下。
\ define LABEL
$50010400 constant PIO1_8D
\ Set 1
$100 PIO1_8D !
\ Set 0
$000 PIO1_8D !
LEDをブリンクする自前のワード定義は、次の
ようにします。
\ define LABEL
$50018000 constant GPIO1_DDR
\ Set P1.8 as Output
$100 GPIO1_DDR !
\ assing target pin number register
$50010400 constant PIO1_8D
\ delay
: xdelay
1000000 0 do loop
;
\ primitive
: blink
$100 PIO1_8D !
xdelay
$000 PIO1_8D !
xdelay
;
\ 10 times blink
: blinky
10 0 do blink loop
;
LPC1114は、ポート0、1しか持ってないので
Data Direction Registerは、次の定義でよい
でしょう。
$50008000 constant GPIO0_DDR
$50018000 constant GPIO1_DDR
Data Registerは、複数ピンを扱うのか単独ピンを
扱うのかで、ラベル定義がかわるので、その都度
考えなければなりません。
以下のリストで限定するとわかりやすくなります。
GPIO
Match Compare Output
Input Capture
Analog Digital Converter
GPIOを除き、使えるピンは固定されているので
図で分けておき、一目でわかるようにすれば
よいでしょう。
ADC
Match Compare Output
Input Capture
利用しているForthインタプリタで、ピンの
configrationを知りたければ、対応レジスタ
の内容を見ればよいはず。
ラベルを定義し、そのラベルをアドレスと見なして
内容を16進数で表示すれば、情報を得られるはず。
コードは、以下。
\ define LABEL
$40044000 constant SFR_IOCON
\ show address with hexadecimal
SFR_IOCON hex.
\ get context from target address and show it with hexadecimal
SFR_IOCON 2@ hex.
SFR_IOCON @ hex.
TeraTermを使うときは、次のようにクリップボードにコピーした
内容を貼り付ける操作で、タイプミスを防げます。
端末操作で、SFRの該当レジスタの内容を表示すると、以下。
ワード「hex.」を利用すると、16進数表示になるので
SFRのレジスタアドレスをスタックに入れて、内容を
取得後に、わかりやすいフォーマットで表示できます。
システムのconfigrationも確認してみます。
SFRのアドレスがあるので、ラベルをアサインして内容を
表示させます。
\ define LABEL
$40048000 constant SFR_SYSTEM
\ get context
SFR_SYSTEM @ hex.
コードを貼付け後、表示すると以下。
表示内容からわかることは、内蔵モジュールに対して
電源、供給するクロックをどうするかの指定です。
次の図を参照すると、よくわかります。
Forthインタプリタ関係のファイルをダウンロードし
展開したときに、次のサンプルコードを見つけました。
: 48MHz ( -- )
0 $40048070 ! \ MAINCLKSEL: Switch to IRC "Internal RC oscillator" clock
0 $40048074 ! \ MAINCLKUEN: Toggle this bit from zero
1 $40048074 ! \ to one to update clock selection
1 7 lshift $40048238 bis! \ PDRUNCFG: Set power-down bit for PLL
\ Simply keep reset values here
\ 0 $40048040 ! \ SYSPLLCLKSEL: Set IRC clock
\ 0 $40048044 ! \ SYSPLLCLKUEN: Toggle this bit from zero
\ 1 $40048044 ! \ to one to update clock selection
\ FCLKOUT = M * FCLKIN = FCCO / (2*P)
\ * Specify the input clock frequency FCLKIN.
\ * Calculate M to obtain the desired output frequency FCLKOUT with M = FCLKOUT / FCLKIN.
\ * Find a value so that FCCO = 2 * P * FCLKOUT.
\ * FCCO needs to be between 156 and 320 MHz.
\ * Ensure that FCLKOUT < 100 MHz.
\ Here: FCLKIN = 12 MHz
\ M = 48 MHz / 12 MHz = 4
\ FCCO = 2 * 2 * 48 MHz = 192 MHz
3 \ Feedback divider value, ranging from 0 (M=1) to 31 (M=32)
1 5 lshift or \ Post divider ratio: 0: P=1 1: P=2 3: P=4 4: P=8
$40048008 ! \ SYSPLLCTRL: Configure PLL dividers
1 7 lshift $40048238 bic! \ PDRUNCFG: Clear power-down bit for PLL
begin $4004800C @ until \ SYSPLLSTAT: Wait for lock of PLL
$4003C010 @ 3 bic 2 or $4003C010 ! \ FLASHCFG: Set wait states for frequencies up to 50 MHz
4 $40048098 ! \ UARTCLKDIV: Divide 48 MHz / 4 = 12 MHz to get desired baud rate of 115200 baud.
3 $40048070 ! \ MAINCLKSEL: Switch to PLL output
0 $40048074 ! \ MAINCLKUEN: Toggle this bit from zero
1 $40048074 ! \ to one to update clock selection
48000 flash-khz ! \ Update current clock frequency, it is needed to write flash.
;
一度、上のコードを動かすと、48MHzにできるので
PLL関係の操作その他は、このスクリプトに任せてしまう
ことにしておきます。
48MHzにシステムクロックを変更後、次のコードで
点滅が高速になったことを確認しました。
$50010800 constant PIO1_9D
: blinkz
10 0 do
$100 PIO1_8D !
$000 PIO1_9D !
xdelay
$000 PIO1_8D !
$200 PIO1_9D !
xdelay
loop
$000 $50010C00 !
;
2ビットを一度に処理するのなら、$50013FFCを使う
方法もありだとして、次のワードでも確認しました。
$50013FFC constant PIO1
: blinku
10 0 do
$100 PIO1 !
xdelay
$200 PIO1 !
xdelay
loop
0 PIO1 !
;
GPIOのポート1で全体を使うなら、$50013FFCとする方が
複数ビットの操作は簡単になります。
デフォルトでは、ポート0、1ともにGPIOとなっているので
次の操作で、各ピンの入出力とリード、ライトの操作ができ
ます。
\ SFR
$50008000 constant GPIO0_DDR
$50018000 constant GPIO1_DDR
$50003FFC constant GPIO0_DR
$50013FFC constant GPIO1_DR
\ set data direction
$300 GPIO0_DDR !
$0FF GPIO1_DDR !
\ data read
GPIO0_DR @ .
\ data write
$A5 GPIO1_DR !
利用LEDは、次の基板上にあるもの。
インバータを利用して、正論理でLEDを点灯します。
回路は、以下。
この基板を使って、デジタルI/Oで使える
ビットを調べると、次のようになりました。
P0.2
P0.3
P0.6
P0.7
P0.8
P0.9
P1.4
P1.5
P1.8
P1.9
P0.4、P0.5はIICバスで使えるので、システムとしては
デフォルトでI/Oにアサインしないのでしょう。
P1.0からP1.3は、A/Dコンバータの入力とタイマーカウンタ
のインプットキャプチャやコンペアマッチ出力での利用を
想定して、デジタルI/Oとして使えないようにしていると
考えられます。
パワーオンリセット後、指定しなければ、どのモードにも
ならないように設定されています。
GPIO、A/D変換器、タイマーカウンタのモードで使うかを
指定してから、利用という流れをとります。
P1.6、P1.7はシリアルインタフェースで使っています。
シリアルインタフェースがないと、端末接続でForthの
インタプリタと情報交換できないので、当然の仕様。
Forthインタプリタ設計者は、P0.10、P0.11はタイマーカウンタ
での利用を想定しているのかも、知れません。
GPIO、A/D変換、その他のモードで使うときは、利用前
指定と、事前準備を挟むと覚えればよいでしょう。
目次
前
次