LPCXpressoとLPC800-MAXでマイコンを学ぶ(その8:UARTでPCと接続)
今回もサンプルを使ってUARTの動作を確認していきます。
UARTのサンプルプログラムを動かす
まずはサンプルプログラムを動かしてみます。サンプルは例のごとく、import project(s)からNXP_LPC8xx_SampleCodeBundle.zipを選択して「UART」をインポートします。メインはuarttest.cです。このサンプルプログラムはUARTでPCのターミナルソフトなどと接続して、PCのからの入力をループバックでエコーを返すプログラムです。それにはPCとLPC800-MAXを接続する必要があるので接続図を示します。
LPC800-MAXの0pinをシリアルアダプタのTX, 1pinをRX, GNDを接続します。注意としてはLPC800-MAXは3.3v駆動なので、USBシリアルアダプタを使う場合、3.3v対応のものを使うことです。PCのターミナルソフトではボーレートを9600に設定しておきます。あとはお決まりの、データビット8,、ストップビット1、 パリティ無し、フローコントール無しです。この状態でdebugビルドで動かしてみます(redlib(semihost)に変更しないとエラーになるので注意)
緑のLEDが点灯します(これはおまけに記述)。一方PCのターミナルには"Hello World!"が表示されます。ターミナルの方に文字を入力するとエコーバックされて文字が表示されます。
UARTの動作を見る
UARTのピンアサインの設定
コードは#ifでいくつかのブロックになっていますが、これはRXとTXのUARTブロックとピンアサインの話なので#if 1のとこだけ見ます。
/* connect the UART0 TXD abd RXD sigals to port pins(P0.4-P0.0)*/ regVal = LPC_SWM->PINASSIGN0 & ~( 0xFF << 0 ); LPC_SWM->PINASSIGN0 = regVal | ( 4 << 0 ); /* P0.4 is UART0 TXD, ASSIGN0(7:0) */ regVal = LPC_SWM->PINASSIGN0 & ~( 0xFF << 8 ); LPC_SWM->PINASSIGN0 = regVal | ( 0 << 8 ); /* P0.0 is UART0 RXD. ASSIGN0(15:8) */ regVal = LPC_SWM->PINASSIGN0 & ~( 0xFF << 16 ); LPC_SWM->PINASSIGN0 = regVal | ( 12 << 16 ); /* P0.12 is UART0 RTS, ASSIGN0(23:16) */ regVal = LPC_SWM->PINASSIGN0 & ~( 0xFFUL << 24 ); LPC_SWM->PINASSIGN0 = regVal | ( 13 << 24 ); /* P0.13 is UART0 CTS. ASSIGN0(31:24) */
この部分はUARTのRX, TX, RTS, CTSのピンアサインを行っています。
LPC800-MAXの回路図で見ると、TX, RXはそれぞれDigitalピンとしての1pin, 0pinに割りあたっています。ちょうどこのピン配置はArduinoと同じ配置にしてあります(当たり前だけど)
ここではPIOにUARTの端子を割り付けるという、いわゆるピンマルチの設定を行っています。LPC800シリーズでは機能の割にピン数が少ないため、デフォルト状態ではすべてGPIOに設定されていますが、これを内部のピンマルチ機能を使って、使いたい機能ブロックのピンをGPIOに割りつけるようになっています。通常ピンマルチは制約が多いのですが、LPC800シリーズではかなり自由に割り付けられるようになっていて、Switch Matrix機能と呼ばれています。詳しいことはLPC800シリーズ - 少ピンパッケージ、スイッチ・マトリックス内蔵 Cortex-M0+に書いてあります。設定を見てみます。ユーザーズマニュアルp122-p124にSwitch Matrixのレジスタ設定があります
Pin assigne register 0を見ると、該当するビット範囲(ワード設定)にPIOの番号を書き込むことでアサインされるようです。TXDをPIO0_4にアサインするには4を[7:0]で書き込みます。その他のピンアサインも同様に行っています。
UART通信の初期化
初期化でUARTのピンアサインを行ったので、UARTの初期化を行います。
UARTInit(LPC_USART0, 9600);
UARTInit関数です。UARTのコードもARMが提供しているところなので詳細は省きます。まぁ普通に使う分にはそのままでいいんじゃないかと。(厳密にはボーレートの生成クロックの選択など、シビアな設定がありますが)lpc8xx_uart.cで提供されている関数で、ボーレートと使うUARTのチャンネルを設定します。USART(非同期および同期が使える)はLPC812では2チャンネル、もしくは3チャンネル用意されています(パッケージで違う)あれれ?フローコントロールとか、パリティの設定が無いなーと思ったら・・・
(lpc8xx_uart.c) void UARTInit(LPC_USART_TypeDef *UARTx, uint32_t baudrate) { uint32_t UARTSysClk; UARTTxEmpty = 1; UARTClock_Init( UARTx ); UARTSysClk = SystemCoreClock/LPC_SYSCON->UARTCLKDIV; UARTx->CFG = DATA_LENG_8|PARITY_NONE|STOP_BIT_1; /* 8 bits, no Parity, 1 Stop bit */ // UARTx->CFG = DATA_LENG_7|PARITY_NONE|STOP_BIT_1; /* 7 bits, no Parity, 1 Stop bit */ // UARTx->CFG = DATA_LENG_8|PARITY_NONE|STOP_BIT_2; /* 8 bits, no Parity, 2 Stop bit */ // UARTx->CFG = DATA_LENG_8|PARITY_EVEN|STOP_BIT_1; /* 8 bits, even Parity, 1 Stop bit */ // UARTx->CFG = DATA_LENG_8|PARITY_ODD|STOP_BIT_1; /* 8 bits, odd Parity, 1 Stop bit */
コメントアウトされてる...orz UARTも詳細を自分で設定する場合は、このドライバ書き直す必要がありますね...(;´Д`)
受信・送信処理
実際の受信・送信処理は下記の部分です。UARTSend関数で"Hello World!"をUARTで送信して、永久ループで受信待ちになっています。送信はUARTのチャンネルと、送信データ、サイズです。
UARTSend(LPC_USART0, (uint8_t *)"Hello World!\r\n", 14); for ( i = 0; i < 0x10000; i++ ); while ( 1 ) { if ( UARTRxCount && !RxErrorCount ) { LPC_USART0->INTENCLR = RXRDY; /* Disable RXRDY */ UARTSend( LPC_USART0, (uint8_t *)UARTRxBuffer, UARTRxCount ); UARTRxCount = 0; LPC_USART0->INTENSET = RXRDY; /* Re-enable RXRDY */ } } }
受信ですが、これも実装が微妙な気がしますが(゜.゜)・・・UARTRxCountは受信データ数なので、受信データがあって、かつエラービットが立ってない場合処理を行います。
- 受信割り込みクリア
- 受信バッファの内容を、そのまま送信
- 受信データサイズクリア
- 受信割り込み有効
という流れになっています。これで、受信した(PCからの入力)データがエコーバックされる動作となっています。しかし、この実装はドライバで使う変数をここで処理するのはあまり良くないですね...(;´Д`) なんかサンプルコードがこんなのばっかなんだけどいいのかな。
おまけ。なぜLEDが点灯するのか?
さて、UARTの動作は理解できたかと思います。ひとつあれ?っと思う部分があったので補足します。LPC800-MAXでこのサンプルプログラムを走らせると、LEDが緑に点灯するんですね。GPIOの設定した覚えないのに...というわけで、そこを調べてみます。実はBlinkyの時からあった、この最初のデバッグに使うよ。という部分が答えでした。
/* Config CLKOUT, mostly used for debugging. */ regVal = LPC_SWM->PINASSIGN8; regVal &= ~( 0xFF << 16 ); regVal |= ( 17 << 16 ); LPC_SWM->PINASSIGN8 = regVal; /* P0.17 is CLKOUT, ASSIGN(23:16). */ CLKOUT_Setup( CLKOUTCLK_SRC_MAIN_CLK );
何をやってるのか調べてみると、CLCKOUTを特定のピンに外部出力していました。このプログラムの場合、PIO0_17にCLCKOUT、おそらく12MHzを出力しています。LPCXpressoとLPC800-MAXでマイコンを学ぶ(その3:LEDの動作(GPIO)を調べる)でやりましたが、LPC800-MAXのボードはPIO0_17は緑のLEDに接続していました。ここにCLCKOUTが出力されたことでPWMとして機能するため、緑色に点灯するというわけです。具体的にやっていることは
- Switch MatrixのレジスタでCLCKOUTピンをPIO0_17に設定
- CLCKOUTをメインクロックの12MHzに設定
CLKOUTは右真ん中あたりにあります。CLKOUT_Setup関数では、CLKOUTのソースクロックを設定します。関数内ではソースクロックの選択と、CLKOUTのためにCLKOUTDIVで分周しています。ただし、ちょっと不思議なのはCLKOUTDIVは初期設定が0なのでDisableのはずなのにLEDが点灯しているんですよね。はて
その後にCLKOUTDIV = 1を書き込むことで、12MHzが出力されるということのはずなのですが、オシロスコープがあればこの辺確かめられるはずだけど持ってない...orz