2011年5月14日土曜日
LPC1769の内蔵ブートローダを使って書き込みに失敗する環境がある(lpc21isp使用時)
概要
ARM Cortex-M3で遊ぼうということで設計したNXP LPC1769搭載基板。グダグダと色々なファームウェア部品を作ったりして楽しんでいるわけです。
このボードには「いつでもどこでもLPC1769」という裏コンセプトがあり、
- 「だいたいパスポートサイズ」
- 「USBバスパワーで動作する」
- 「内蔵ブートローダで書き込みができる」
ノートパソコンとこのボードとUSBケーブルさえあれば、開発を楽しむ事ができる仕組みです。
「ちょっとしたデモ」であれば十分対応できます。
この特徴を生かして今日の今までノートパソコンを使った開発で楽しんでいました。
ふと思い立って自宅の大画面環境で作業した方が効率も良いし・・・と思った時の事です。
なんと、LPC1769の内蔵ブートローダを使った書き込みに失敗します。
何度やっても再現性100%で。
どちらもVMware Player上に仕立てたUbuntu 10.10で行っています。
プロジェクトのリポジトリから書き込みスクリプトを含む全てのリソースをチェックアウトして作業しています。
異なる可能性があるとすれば、ローカル環境設定とツールのバージョンくらいでしょう。
これに依存する何かが書き込み時の起こっているとは考えにくかったのですが、「おかしい、おかしい」と言っているだけでは前に進みません。
そこで先日購入したロジックアナライザでちょっと見てみることにしました。
正しく動作する環境
まずは正しく動作する環境の方です。lpc21ispのバージョンは1.79です。
次に正しく動作しない環境の方です。
lpc21ispのバージョンは1.64です。
さて、何か変ですね?
ちょっと見比べてみましょう。
上側は動作する時、下側は動作しない時の要求と応答です。
整理してみます。
| lpc21ispのバージョン | 状況 | lpc21isp | LPC1769 | Synchronized確認トークンの補足説明 |
|---|---|---|---|---|
| 1.79 | 正しく動作する | Synchronized\n | Synchronized\nOK\r\n | lpc21ispとLPC1769で一致している。 |
| 1.64 | 正しく動作しない | Synchronized\r\n | Synchronized\rOK\r\n | lpc21ispは\r\nをセパレータとして使用している。LPC1769はそれとは異なるセパレータを返す。(ように見える) |
「ように見える」と書いたのは、ここがポイントだからなのですが、それは後述します。
この手の書き込みプロトコルは、ターゲット上の内蔵ブートローダとホスト側のアプリケーションの協調動作で成り立ちます。
例えばアプリケーション側から「XXXだぜ!」と要求を受けて、内蔵ブートローダが「おっけー!」という具合です。
例に漏れずLPCシリーズの内蔵ブートローダもそのようになっています。
上記2つの環境間で異なるのはトークンセパレータのようです。
では、どちらが本来の実装なのでしょうか?
データシートを見てみます。
「全てのISPコマンドは単一のASCII文字列で送られなければならない。」
「文字列はキャリッジリターン と/か ラインフィードによって終端されなければならない。」
原文の「and/or」を「と/か」と訳しました。
要するに正解は
- CR
- LF
- CR + LF
何かこの辺りの問題がありそうなのはわかりました。
では、立ち返って現象を見てみます。
user@ubuntu:~/Projects/TOPPERS-ASP-for-BlackTank-LPC1769$ ./devwrite.sh
lpc21isp version 1.64
File TOPPERS-ASP_BlackTank-LPC1769.hex:
loaded...
converted to binary format...
image size : 115032
Synchronizing (ESC to abort). OK
No answer on Oscillator-Command
「オシレータコマンドの返答がない。」と言っていますね。
これが最後のやり取りのようです。
応答がないため、アプリケーション(lpc21isp)がそれ以降の処理を中止して終了するわけです。
でも、実際にはLPC1769から応答が返ってきています。
先ほど見た「Synchronized」は非同期シリアル通信の通信速度を自動判別する部分です。
これは書き込み動作の前の最初の操作ですから、オシレータコマンドとは関連ありません。
オシレータコマンドのやりとりを探すために、トリガがかかってからのサンプル数を増やしてみます。
以下は正しく動作しない環境でのやりとりです。
やりとりは以下のようになります。
- アプリケーション:「4000で頼む。」
- 内蔵ブートローダ:「4000ね。おっけー!」
面白いのはアプリケーション側からやってくるセパレータによって、内蔵ブートローダ自身が用いるセパレータも変えてくるという点です。
但し、上記動作を見て下さい。
アプリケーション側が2つのセパレータを送った場合、内蔵ブートローダは先頭のキャラクタのみを採用するようです。
また、「OK\r\n」という固定的な返答も見られる事から、混在しているようにも見えます。
この動作は、アプリケーション側がデータシートに書いてある通り「CR」でも、「LF」でも、「CR + LF」でも正しく動作するよう実装してあれば問題ありません。
ですが、私の手元にあった古いlpc21isp (Version 1.64)ではそうなっていないということがわかりました。
本来であれば、このコマンドに続いてアプリケーション側から書き込み用のデータが送られてきます。
しかし、失敗する環境では「返答がない。」と判定されていました。
恐らくセパレータの解釈に問題があるのでしょう。
もしかしたら、受信側のセパレータの解釈に問題がありながら、送信側で対応したのかもしれません。その証拠に2つのバージョンでは送信するセパレータが異なる事がわかります。
この件に関して言えば、最近のバージョン (Version 1.79)では修正されているので心配ありません。
LPC1768やLPC1769を使う方の御参考までに。
おわりに
この手の問題は実際に色んな調査をしてみなければ原因がわからないことが多いので大変です。今回のケースでは「あぁ、lpc21ispが古いのだなぁ」と片付ける事もできます。
ですが、「2つのバージョンでどのように異なり、その差異の何が問題となるのか?」を調べることで安心して使用できるようになります。
ウェブ上にある情報は断片的なものが多いのですが、「どのような問題があり」、「それをどのように解決したのか?」を自分のために整理すると意外に面白い発見になるのかもしれません。
2011年2月13日日曜日
eLuaをLPC1768で手軽に試してみる。mbedとLPCXpressoでお手軽eLuaライフ。
eLuaとはLuaプログラミング言語のフル実装版を組み込みの世界に持ち込むというもの。
Embedded Luaを略してeLuaと呼ばれています。
従来、スクリプト言語をマイコンに移植する場合、機能を削減した仕様で実装されることが多いのですが、eLuaは「フル実装」が一つの特徴になっています。
これによって例えば、パソコン上である程度の機能を実装した後で、ターゲットデバイス上で最終的なデバッグをするなど、様々な開発スタイルをとることもできます。
今回はeLuaをソースコードからコンパイルして実行させてみることにしました。
ホストの環境はUbuntu 10.10を使用しました。
まずはソースコードをSubversionリポジトリから取り出します。
ビルドにはmakeではなく、sconsを使います。
Ubuntu 10.10でsudo apt-get install sconsを試みましたが、私の環境ではパッケージのインストールができませんでした。
そこでhttps://launchpad.net/ubuntu/maverick/+source/scons/2.0.0-1からscons_2.0.0.orig.tar.gzをダウンロードしてインストールします。
sconsはツールチェインを自動的に見つけてコンパイルしてくれるので楽ちんです。
(うまく行くときは。)
ちなみにcpu=lpc1768とした場合、ターゲットボードはmbedが選択されるようになっています。
上記でビルドが完了すると思います。
elfができますので、binに変換します。
これで準備完了。
まずはmbed LPC1768にコピーして試してみます。
ターミナルにはminicomを使用しました。
eLua側の通信速度設定はデフォルトで115200になっています。
minicomもこれに合わせます。
mbedのリセットボタンを押して以下のような表示が出てくれば動作しています。
/romと/semiというディレクトリが見えます。
/semiはmbedのローカルファイルシステムです。
LIFE.LUAはライフゲームのスクリプトです。
試しに実行してみます。
テキストベースのライフゲームが実行されます。
このライフゲームは2000世代までになると終了するようになっています。
それではこれをLPCXpresso LPC1768上で実行してみます。
Embedded Luaを略してeLuaと呼ばれています。
従来、スクリプト言語をマイコンに移植する場合、機能を削減した仕様で実装されることが多いのですが、eLuaは「フル実装」が一つの特徴になっています。
これによって例えば、パソコン上である程度の機能を実装した後で、ターゲットデバイス上で最終的なデバッグをするなど、様々な開発スタイルをとることもできます。
今回はeLuaをソースコードからコンパイルして実行させてみることにしました。
ホストの環境はUbuntu 10.10を使用しました。
まずはソースコードをSubversionリポジトリから取り出します。
svn checkout svn://svn.berlios.de/elua/trunk
ビルドにはmakeではなく、sconsを使います。
Ubuntu 10.10でsudo apt-get install sconsを試みましたが、私の環境ではパッケージのインストールができませんでした。
そこでhttps://launchpad.net/ubuntu/maverick/+source/scons/2.0.0-1からscons_2.0.0.orig.tar.gzをダウンロードしてインストールします。
sudo python setup.py install
sconsはツールチェインを自動的に見つけてコンパイルしてくれるので楽ちんです。
(うまく行くときは。)
ちなみにcpu=lpc1768とした場合、ターゲットボードはmbedが選択されるようになっています。
scons cpu=lpc1768
上記でビルドが完了すると思います。
elfができますので、binに変換します。
arm-none-eabi-objcopy -O binary elua_lua_lpc1768.elf elua_lua_lpc1768.bin
これで準備完了。
まずはmbed LPC1768にコピーして試してみます。
ターミナルにはminicomを使用しました。
eLua側の通信速度設定はデフォルトで115200になっています。
minicomもこれに合わせます。
mbedのリセットボタンを押して以下のような表示が出てくれば動作しています。
eLua v0.8 Copyright (C) 2007-2011 www.eluaproject.net
eLua#
試しにlsと打ってみてください。
eLua# ls /rom Total on /rom: 0 bytes /semi MBED.HTM 340 bytes ELUA_L~1.BIN 189860 bytes LIFE.LUA 2684 bytes Total on /semi: 192884 bytes eLua#
/romと/semiというディレクトリが見えます。
/semiはmbedのローカルファイルシステムです。
LIFE.LUAはライフゲームのスクリプトです。
試しに実行してみます。
eLua# lua /semi/LIFE.LUA
テキストベースのライフゲームが実行されます。
このライフゲームは2000世代までになると終了するようになっています。
LPCXpresso LPC1768上には外付けのフラッシュストレージは存在しません。
先程の/semiに該当する部分です。
そこでプラットフォーム設定からこれを除外します。
先程の/semiに該当する部分です。
そこでプラットフォーム設定からこれを除外します。
trunk/src/platform/lpc17xx/platform_conf.hの#define BUILD_SEMIFSをコメントアウトします。
実行したいファイルはtrunk/romfsに予めコピーしておきます。
// #define BUILD_SEMIFS
実行したいファイルはtrunk/romfsに予めコピーしておきます。
再度コンパイルを実行して書き込みます。
出来上がったバイナリはLPCXpressoの128KB制限を超えてしまいます。
そこでここではJTAGKey2P + OpenOCDで書き込みます。
それでは確認してみましょう。
出来上がったバイナリはLPCXpressoの128KB制限を超えてしまいます。
そこでここではJTAGKey2P + OpenOCDで書き込みます。
shinta@greenpad:~$ telnet localhost 4444Trying ::1...Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.Open On-Chip Debugger> halt> flash probe 0flash 'lpc2000' found at 0x00000000> flash write_image erase /home/shinta/Projects/elua/trunk/elua_lua_lpc1768.binauto erase enabledwrote 196608 bytes from file /home/shinta/Projects/elua/trunk/elua_lua_lpc1768.bin in 28.987883s (6.623 KiB/s)
それでは確認してみましょう。
と思いましたが・・・
LPCXpressoのデバッガとターゲットを切り離してJTAGKey2Pを接続したのでシリアルが使えません。
LPCXpressoのデバッガとターゲットを切り離してJTAGKey2Pを接続したのでシリアルが使えません。
あ、なるほど。
LPCXpressoのデバッガとターゲット間が切り離し辛いにも関わらず2.54mmピッチの間隔しかあいていません。
かといって、安易に切り離してしまうと、シリアル経由で何かやりたい時に不便です。
これを考えると、半田ジャンパを取り除いた後で2列ピンヘッダを立てると良いかもしれません。
書き込み時はジャンパを取り除いて他のツールでできるし、デバッグはジャンパを付けてシリアル経由でできます。
それでもジャンパを付けたり取ったりするのは面倒ですね。
やっぱりシリアルインターフェースを付けますかね。
LPCXpressoのデバッガとターゲット間が切り離し辛いにも関わらず2.54mmピッチの間隔しかあいていません。
かといって、安易に切り離してしまうと、シリアル経由で何かやりたい時に不便です。
これを考えると、半田ジャンパを取り除いた後で2列ピンヘッダを立てると良いかもしれません。
書き込み時はジャンパを取り除いて他のツールでできるし、デバッグはジャンパを付けてシリアル経由でできます。
それでもジャンパを付けたり取ったりするのは面倒ですね。
やっぱりシリアルインターフェースを付けますかね。
2011年1月29日土曜日
LPCXpresso LPC1768を使ってUARTを制御してみよう!
先日に引き続きLPCXpressoを使ってCortex-M3を体験する話題です。
前回までにCMSISを使った下地を整える作業を行いました。
今回はUARTをレジスタ経由で制御してみます。
資料は前回までと同様にUM10360を使用します。
今回はUART1を制御対象に選びました。
User manualにも基本的な設定がわかりやすく示されています。
次のページにはピンの説明とレジスタの説明が続きます。
もう後は手順に従ってレジスタの値を操作するだけです。
今回は割り込みを使わずに何かを送ってみるという事で作業してみましょう。
それでは先ほどのドキュメントの記述に合わせてmain関数の中に実装を加えます。
試しに「LPC_SC->」と入力してみましょう。
IDEに候補が出てきます。あぁ、なんて贅沢なんだ。
ここで言うSCはSystem Controlの略です。
まずはUART1の電源とクロック制御を有効にします。
(そうです。ペリフェラル毎に電源を落とし、クロックの供給を止めることで省電力化できるようになっています。)
このビットはリセット時に1となっているので、あえて立てる必要は無いと言えば無いのですが、操作対象ペリフェラルがどのレジスタと依存関係があるのか知るのは重要です。
これでペリフェラルの電源が入りました。
次にペリフェラルに供給するクロックを選択します。
UART1に供給されるクロックはPCLKSEL0レジスタで選択します。
このビットは初期値は00です。
この値は以下から選択することができます。
CCLKはClock generation for the LPC17xxのブロックを見ると何の事だかわかります。
前回までにCMSISを使った下地を整える作業を行いました。
今回はUARTをレジスタ経由で制御してみます。
資料は前回までと同様にUM10360を使用します。
今回はUART1を制御対象に選びました。
User manualにも基本的な設定がわかりやすく示されています。
- 電源:PCONPレジスタ(Table 46)のPCUART1ビットをセットする。
- ペリフェラルクロック:PCLKSEL0レジスタ(Table 40)のPCLK_UART1を選択する。
- ボーレート:U1LCRレジスタ(Table 298)のDLABを1にセットする。これによりボーレート設定の為のDLLレジスタ(Table 292)とDLMレジスタ(Table 293)へのアクセスが有効になる。また、必要があれば分数分周レジスタ(Table 305)の分数ボーレートを設定する。
- UART FIFO:FIFOを有効にするためにU0FCR(Table 297)にあるFIFO enableビット(ビット0)を使用する。
- ピン:UARTピンをPINSELレジスタを使って選択し、PINMODEレジスタを使ってピンモードを設定する。
- 割り込み:UART割り込みを有効にするためにU1LCR(Table 298)にあるDLABレジスタを0に設定する。これによりU1IER(Table 294)へのアクセスが有効になる。割り込みはNVICが使用する然るべき割り込み設定レジスタを設定することで有効になる。
- DMA:UART1転送と受信関数はGPDMAコントローラによって操作することができる。
次のページにはピンの説明とレジスタの説明が続きます。
こちらはそのレジスタの説明。
詳細は次のページから更に続きます。
今回は割り込みを使わずに何かを送ってみるという事で作業してみましょう。
それでは先ほどのドキュメントの記述に合わせてmain関数の中に実装を加えます。
試しに「LPC_SC->」と入力してみましょう。
IDEに候補が出てきます。あぁ、なんて贅沢なんだ。
ここで言うSCはSystem Controlの略です。
まずはUART1の電源とクロック制御を有効にします。
(そうです。ペリフェラル毎に電源を落とし、クロックの供給を止めることで省電力化できるようになっています。)
このビットはリセット時に1となっているので、あえて立てる必要は無いと言えば無いのですが、操作対象ペリフェラルがどのレジスタと依存関係があるのか知るのは重要です。
これでペリフェラルの電源が入りました。
次にペリフェラルに供給するクロックを選択します。
UART1に供給されるクロックはPCLKSEL0レジスタで選択します。
このビットは初期値は00です。
この値は以下から選択することができます。
CCLKはClock generation for the LPC17xxのブロックを見ると何の事だかわかります。
ペリフェラルに供給されるクロックはCCLK/4、CCLK、CCLK/2、CCLK/8の4つの中から選択できるようになっています。(但し、CAN1、CAN2、CANはCCLK/8ではなく、CCLK/6になっています。)
これでUART1に対するクロックの供給設定も完了です。
次にUART1のLCR(Line Control Register)を設定します。
このレジスタにはワード長やストップビット、パリティなどの設定が含まれます。
今回は「8ビット長、1ストップビット、パリティなし」で設定します。
また、ボーレートレジスタにアクセスするためにDLABビットも1にします。
では、「ボーレートはどうやって設定するの?」という話になります。
最初の説明で
- U1LCRレジスタ(Table 298)のDLABを1にセットする。
- これによりボーレート設定の為のDLLレジスタ(Table 292)とDLMレジスタ(Table 293)へのアクセスが有効になる。
- また、必要があれば分数分周レジスタ(Table 305)の分数ボーレートを設定する。
Fractional Divider RegisterはDLMとDLLで設定しきれない値(割り切れない数値)を設定するためのものです。所望のボーレートに対して1.1%以内にするためのものです。
LPC17xxのユーザマニュアル(UM10360)にはこの値を決定するフローが記されています。
- PCLKと設定したいボーレートを手元に用意します。
- DLestを求めます。 DLest = PCLK / (16 x ボーレート)
- 求めたDLestが整数であれば、DIVADDVAL=0, MULVAL=1を設定して完了です。
で、求めたDLestが整数でない時は補正値を算出することになります。
- FRestという変数を持ち出します。最初にこの変数に1.5を代入します。
- DLestを求めます。DL=INT(PCLK/(16 x ボーレート x FRest))
- FRestを次の式で計算します。 FRest = PCLK/(16 x BR x DLest)
- もし1.1<FRest<1.9が成り立てば計算は終了です。(成り立たない場合はFRestの値を1.1から1.9選んで再度ステップ2に戻ります。)
- DIVADDVALとMULVALの値をドキュメントからピックアップします。
- 後はDLM=DLest[15:8], DLL=DLest[7:0]を代入して完了です。
テーブルは以下のようになっています。
後は上記に従って計算するだけです。
もちろん先ほどのフローをプログラム化しても良いでしょう。
ここでは手計算します。
まず、PCLKの元になるCCLKを調べる必要があります。
今回のプロジェクトではCMSISが使われています。
スタートアップで「CMSISを使うならSystemInit()」という実装があります。
この中でクロック分周期に対する設定が行われています。
CCLKCFGが0x00000003と設定されています。
今回はこの値をこのまま使う事にします。
CPUクロックはpllclkの4分周と設定されていることがわかります。
ここで再度確認しましょう。
CPUクロックは以下のパラメータによって決まります。
図中の右側にあるCCLKCFGは先ほど調べたパラメータです。
pllclkを知るために残りのCLKSRCSELとPLL0CFGとPLL0CONが必要になります。
ここで、PLL0の周波数を決めるパラメータを見てみます。
PLL0CFGを見るとPre-dividerとMultiplierの値がわかります。
「うそ!」と思った方、慌てないでください。
これはあくまでPLLの出力です。
この出力を分周してCPUクロックを作り出します。
LPC1768の最大クロック周波数は100M[Hz]です。
分周するパラメータがCCLKSELです。
CCLKSELの設定状況より、今回は4分周が選択されていることがわかります。
ちなみに、CCLKCFGレジスタの説明の中にもある通り、PLL0を接続している場合には「分周なし」と「2分周」は最大クロック周波数を超えるので有効な選択肢ではありません。
以上でクロックの系統がわかりました。
これを図示したのが以下です。
長くなりましたが、先ほどのFig 51. Algorithm for setting UART dividersに戻りましょう。
フローに従って計算してみます。
算出したFRestに従ってテーブルから必要な値を拾い出します。
それでは、最後にUART1のペリフェラルを設定するテストコードを示します。
DLABビットはDivisor Latch Access Bitと言ってDivisor Latchへのアクセスを行う時にのみ使用します。UART1のレジスタマップでwhen DLAB=0とかwhen DLAB=1とか書いてあるのはこのためです。
実際に実行して波形を見てみます。
永遠に0x55を送信するコードで見てみましょう。
0x55にするのは1ビット毎にトグルする波形が
オシロでUART1のTXをあたります。
今回はパリティビットがありませんので、
今回はUARTを題材にどのようにペリフェラルの設定を行っていくのかを具体的な例で示しました。
ここで挙げたコードが重要なのではなく、1つのペリフェラルを設定するのに、順々に手順をおっていく過程が重要です。
ぶ厚いドキュメントを見るとそれだけで嫌になるかもしれませんが、読むべきところは関係している数ページ分くらいです。
今回はそれを体感頂くことができればと思い、少し長くなりましたが実例を挙げてご紹介しました。
ペリフェラルが思った通りに動作しない時にはレジスタの設定順序などに依存関係がある場合もあります。そのような記述がないかどうか見てみるのも良いでしょう。
また、ドキュメントに書いてある通りに初期化をするのがコツです。
結果的に同じだからと判断して最初から最適化する事を狙うのではなく、試しにドキュメント通りに実装して見る事をお勧めします。
後は上記に従って計算するだけです。
もちろん先ほどのフローをプログラム化しても良いでしょう。
ここでは手計算します。
まず、PCLKの元になるCCLKを調べる必要があります。
今回のプロジェクトではCMSISが使われています。
スタートアップで「CMSISを使うならSystemInit()」という実装があります。
この中でクロック分周期に対する設定が行われています。
CCLKCFGが0x00000003と設定されています。
今回はこの値をこのまま使う事にします。
CPUクロックはpllclkの4分周と設定されていることがわかります。
ここで再度確認しましょう。
CPUクロックは以下のパラメータによって決まります。
図中の右側にあるCCLKCFGは先ほど調べたパラメータです。
pllclkを知るために残りのCLKSRCSELとPLL0CFGとPLL0CONが必要になります。
ここで、PLL0の周波数を決めるパラメータを見てみます。
PLL0CFGを見るとPre-dividerとMultiplierの値がわかります。
- N=0x0005 + 1
- M=0x0063 + 1
「うそ!」と思った方、慌てないでください。
これはあくまでPLLの出力です。
この出力を分周してCPUクロックを作り出します。
LPC1768の最大クロック周波数は100M[Hz]です。
分周するパラメータがCCLKSELです。
CCLKSELの設定状況より、今回は4分周が選択されていることがわかります。
CPUのクロック周波数CCLKは400,000,000 / 4 = 100,000,000[Hz]が選択されることになります。
結果的に最大クロックを設定していたのだと言う事がわかります。
ちなみに、CCLKCFGレジスタの説明の中にもある通り、PLL0を接続している場合には「分周なし」と「2分周」は最大クロック周波数を超えるので有効な選択肢ではありません。
以上でクロックの系統がわかりました。
これを図示したのが以下です。
長くなりましたが、先ほどのFig 51. Algorithm for setting UART dividersに戻りましょう。
フローに従って計算してみます。
- PCLK = CCLK / 4 = 100 / 4 = 25M[Hz]
- ボーレートBR = 9600を選択。
- DLest = PCLK / (16 x BR) = 25M / (16 x 9600) = 162.760417
- FRest = 1.5
- DLest = INT(PCLK / (16 x BR x FRest)) = INT(25M / (16 x 9600 x 1.5)) = 108
- FRest = PCLK / (16 x BR x DLest) = 25M / (16 x 9600 x 108) = 1.507041
算出したFRestに従ってテーブルから必要な値を拾い出します。
- DIVADDVAL = 1 (Table 306より)
- MULVAL = 2 (Table 306より)
最後にピンモードを設定します。
これで準備完了です。
それでは、最後にUART1のペリフェラルを設定するテストコードを示します。
DLABビットはDivisor Latch Access Bitと言ってDivisor Latchへのアクセスを行う時にのみ使用します。UART1のレジスタマップでwhen DLAB=0とかwhen DLAB=1とか書いてあるのはこのためです。
実際に実行して波形を見てみます。
永遠に0x55を送信するコードで見てみましょう。
0x55にするのは1ビット毎にトグルする波形が
オシロでUART1のTXをあたります。
今回はパリティビットがありませんので、
- スタートビット
- データ(8ビット)
- ストップビット
1ビットが104[us]で送信されています。
1,000,000[us]=1[s]で9615.385ビット送信できることがわかります。
設定の狙いの値(9600bps)と実測データからエラーを計算すると約0.16%ですから、ほぼ狙い通りの値に設定できていることがわかります。
今回はUARTを題材にどのようにペリフェラルの設定を行っていくのかを具体的な例で示しました。
ここで挙げたコードが重要なのではなく、1つのペリフェラルを設定するのに、順々に手順をおっていく過程が重要です。
ぶ厚いドキュメントを見るとそれだけで嫌になるかもしれませんが、読むべきところは関係している数ページ分くらいです。
今回はそれを体感頂くことができればと思い、少し長くなりましたが実例を挙げてご紹介しました。
ペリフェラルが思った通りに動作しない時にはレジスタの設定順序などに依存関係がある場合もあります。そのような記述がないかどうか見てみるのも良いでしょう。
また、ドキュメントに書いてある通りに初期化をするのがコツです。
結果的に同じだからと判断して最初から最適化する事を狙うのではなく、試しにドキュメント通りに実装して見る事をお勧めします。
ラベル:
LPC1768,
LPCXpresso LPC1768
2011年1月19日水曜日
LPC1768(Cortex-M3)のペリフェラルをレジスタ経由で制御してみる(の前に・・・)
昨日の記事でCortex-M3の導入方法について簡単に触れました。
先日の流れのまま最後のページまで到達するには途方もなく時間がかかるので、何らかのペリフェラルを例にとって実際に制御してみたいと思います。まずは「動かして楽しむ!」ですね!
今回は制御対象にUART1を選択しました。
まずはペリフェラルのクロックを確認します。
UART1のペリフェラルはPCLK_UART1というシンボルが付けられています。
そうだ。
大切な事を忘れていました。
ARM社はマイクロコントローラソフトウェアインターフェース規格を定めています。
従来、レジスタ操作などはデバイスベンダによって色々な流儀があったりして面倒でした。
簡単にいうと、これらを統一して扱いやすくするものがCMSIS(Cortex Microcontroller Software Interface Standard)です。
これは後ほど使います。
今はこんなものもあるという触りだけ・・・。
えーと、あ、そうそうUART1の話でした。
どこから始めるのかという話になるのですが、今回はレジスタによるデバイス制御に主眼を置いていますのでLPCXpressoのIDEを活用します。
また、スタートアップに関する処理はIDEが生成するものを使用します。
まずはプロジェクトの生成から。
プロジェクト名を適当に入れて下さい。
CMSISは今回使いません。(2011年01月21日追記:やっぱり使いましょう!Use CMSISにチェックを入れておいて下さい!)
この辺りは初期値で良いです。
ここも。
最後にプロセッサを選択します。
今回はLPCXpresso LPC1768を使用しているのでリストからLPC1768を選択しました。
赤色になっているものは「このライセンスでは全部のROMを使う事はできないよ。」と言っていると思って頂ければ良いです。
上記の簡単な選択が終了するとプロジェクトが生成されます。
ソースディレクトリにはCランタイムスタートアップとmainが生成されています。
それでは少しだけCランタイムスタートアップを見てみましょう。
色々見るべきところはあるのですが、リセットベクタに対する関数はここで定義されています。
ここに記述されている.isr_vectorはリンカスクリプトで定義されています。
リセットベクタに書かれた関数ResetISRの実装は以下のようになっています。
dataとbssセグメントはメモリ(内蔵SRAM)上に配置されます。
初期値を持つデータはフラッシュからSRAMにここでコピーされます。
また、bssもここでゼロに初期化されます。
これら処理が終わった後でmain関数が呼び出される仕組みになっています。
また、__USE_CMSISが定義されている場合にはCMSISのSystemInit()が呼び出されるようになっています。選択したCランタイムライブラリによっては__libc_init_array()なども呼び出されます。
このようにLPCXpressoのIDEではスケルトンでここまで簡単にできてしまうようになっています。
いきなりmainから記述できてしまう。(良いやら悪いやら・・・。)
ということで、今回はペリフェラルを制御する一歩か二歩手前にお気軽に到達してみました。
ラベル:
LPC1768,
LPCXpresso,
LPCXpresso LPC1768,
NXP
登録:
コメント (Atom)