2013/08/11からのアクセス回数 6895 LPC1343学習基板に戻って †cortex-m3の勉強のために買った「ARMマイコンパーフェクト学習基板の使い方」をlbedを使うとどのくらい簡単に なるかを試してみます。 実験に使用するのは、以下の3つです。
汎用入出力GPIOの使い方編 †lbedにはまだ汎用入力クラスがありませんので、この機会に作成しました。 詳しくは、lbed/08a-ARM学習基板用追加クラスを参照してください。 実験開始 †ジャンパP4の3と4、31と32にジャンパーをセットし、タッチエリアに触れるとLEDが点灯するテストプログラムを作ります。 タッチセンサーPCF8883の出力オープン・ドレインHigth *1 なので、LPC1343の入力ピンは、プルダウンモードにセットする必要があります。 *2 テストプログラムTestSwitch.cppは、以下の様になります。 タッチセンサーに触れるとLEDが点灯し、離すとLEDが消えれば成功です。 #include <cr_section_macros.h> #include <NXP/crp.h> // Variable to store CRP value in. Will be placed automatically // by the linker when "Enable Code Read Protect" selected. // See crp.h header for more information __CRP extern const unsigned int CRP_WORD = CRP_NO_CRP ; #include "lbed.h" int main(void) { wait_init(); DigitalOut myled(LED2); DigitalIn sw(P2_4); sw.mode(PullDown); while(1) { myled = sw; } } USB仮想シリアルポートを使う †LPC1343では、USBをサポートしており、例題にもCDC(USB仮想シリアルポート) を使っています。 そこで、SerialクラスのサブクラスとしてSerialCDCを作ってUSB CDCを使えるように しました。 *3 USBの実験にはトランジスタが必要 †当初、ARM学習基板のトランジスタを付けないでUSB CDCのテストをしたら、 上手く動作しませんでした。本を調べて見るとUSB D+ラインのプルアップ制御に トランジスタ2SA1015が必要だと分かり、急遽取り付けました。 テスト用のソース †テスト用のプログラムとしてTestCDC.cppを以下の様に作成しました。 USB CDCが認識されるまで、少し時間がかかるため、最初にキー入力 を待つことにしました。 *4 #include <cr_section_macros.h> #include <NXP/crp.h> // Variable to store CRP value in. Will be placed automatically // by the linker when "Enable Code Read Protect" selected. // See crp.h header for more information __CRP extern const unsigned int CRP_WORD = CRP_NO_CRP ; #include "lbed.h" int main(void) { wait_init(); DigitalOut myled( LED2); SerialCDC pc(USBTX, USBRX); pc.baud(9600); // キー入力を待つ pc.read(); pc.println("Hello"); while (1) { char c = pc.read(); pc.write(c + 1); myled = !myled; } return 0; } Arduinoのシリアルモニターに出力した様子を以下に示します。 &ref(): File not found: "CDC_Screen.png" at page "lbed/08-ARM学習基板で実験"; アナログ入力を試す †アナログ入力クラスAnalogInも追加しました。 *5 ジャンパ線の設定 †ARM学習基板は、いろんなチップのてんこ盛りなので、実験をするにはジャンパ線で 接続しなくてはなりません。 *6 本の8章、図1からピンの設定の図を引用します。 P3には、U2から取った基準電圧1.25Vと電圧測定用のOPアンプU3を使って、電圧が供給されています。
に接続されています。 次に、P3とLPC1343の接続について、本の8章、図2を引用します。 OPアンプは、非反転増幅回路となっており、ゲインGは、 \( G_{NI} = \frac{R_1 + R_2}{R_1}\) となることから、P3のCh6, Ch7に入力された電圧は、AGNDとの差が 11倍に増幅された値にAGNDを加算された値がADコンバータの入力電圧 となります。 *7 アナログ回路の動作確認 †P3の3番ピンCh6に安定電源1.8Vを半固定抵抗で1.308Vに減圧して入力としました。 $$ V = (1.308 - 1.25)*11 + 1.25 = 1.888 $$ が、Ch6の予想結果です。 テストプログラムTestADC.cppは、Printクラスの#if文を有効にして使いました。*8 #include <cr_section_macros.h> #include <NXP/crp.h> // Variable to store CRP value in. Will be placed automatically // by the linker when "Enable Code Read Protect" selected. // See crp.h header for more information __CRP extern const unsigned int CRP_WORD = CRP_NO_CRP ; #include "lbed.h" int main(void) { wait_init(); DigitalOut myled(LED2); SerialCDC pc(USBTX, USBRX); pc.baud(9600); // キー入力を待つ pc.read(); AnalogIn agnd(P1_2); AnalogIn dc(P1_10); while(1) { int d0 = agnd.read_u16(); int v0 = 1250*1024/d0; int d1 = dc.read_u16(); int v1 = v0*d1/1024; pc.printf("Ch3:%04d[VDD=%04dmV] Ch6:%04d[%04dmV]\n", d0, v0, d1, v1); myled = ! myled; wait_ms(1000); } } 出力結果は、以下の様になっています。1.896mVと予想と近い結果が出ています。 温度センサーIC LM75Bを使ってみる †ARM学習基板には、I2Cのテストをするために温度センサーLM75Bが付いています。 これを使って、温度を取得してみます。 LM75Bのアドレスは、A2, A1, A0の接続によってユーザが変えることができるようになっています。 *9 本の11章の図4は、大きなミスプリがあります。 右がプロローグの図2から引用したものです。この図からLM75Bのアドレスは、1001001(0x49)となります。 LM75Bクラス †次にLM75BのクラスをLBED_LPC13xx_USERLIB/src/LM75B.cppに実装します。 本当に驚くくらい簡単でしょう! #include "lbed.h" #include "LM75B.h" LM75B::LM75B(PinName sda, PinName scl) : i2c(sda, scl) { char cmd[2]; // LM73設定 cmd[0] = 0x00; // register 0 cmd[1] = 0x28; // 温度レジスタを選択 i2c.write( LM75_ADDR, cmd, 2); } LM75B::~LM75B() { } float LM75B::read() { char cmd[2]; i2c.read( LM75_ADDR, cmd, 2); // Send command string unsigned int int_val = cmd[0] <<3 | cmd[1]>>5; return float(int_val*125/1000.0); } LCDとの接続 †アナログ入力の例では、USB CDCを使ったので、今回はLCDを使います。 ARM学習基板のP4とStar OrangeボードのPinとLPC1343ボードピンの対応は、以下の様になります。
テストプログラムと結果 †テストプログラムTestLM75B.cppは、以下の様になります。 #include<cr_section_macros.h> #include<NXP/crp.h> __CRP extern const unsigned int CRP_WORD = CRP_NO_CRP; #include"lbed.h" #include "TextLCD.h" #include "LM75B.h" int main(void) { wait_init(); DigitalOut myled(LED2); TextLCD lcd(p26, p22, p14, p6, p5, p25); // rs, e, d4-7 LM75B lm75b(p28, p27); lcd.print("Hello World"); while (1) { float t = lm75b.read(); lcd.locate(0, 1); lcd.print("Temp:"); lcd.print(t); myled = !myled; wait_ms(1000); } return 0; } 時計に付いてきた温度計を結果を比べてみました。時計が27.0度、LM75Bが27.50度でした。 リアルタイムクロック(SPI接続)を使う †リアルタイムクロックのテストには苦労しました。 *10 ジャンパ線の設定は以下の様になります。 リアルタイムクロックのクラス †リアルタイムクロックPCF2123のクラスは、 Linux Kernel Doxygen を参考にしました。 #include "lbed.h" #include "PCF2123.h" PCF2123::PCF2123(PinName mosi, PinName miso, PinName sclk, PinName sel) : _spi(mosi, miso, sclk) , _sel(sel) { _sel = 0; _spi.frequency(SPI_FREQUENCY); wait_ms( 1 ); } PCF2123::~PCF2123() { } unsigned char PCF2123::read(unsigned char reg) { int val = _spi.write(reg); return val; } void PCF2123::command(unsigned char reg, unsigned char dat) { _sel = 1; _spi.write(reg); _spi.write(dat); _sel = 0; } void PCF2123::read_rtc() { _sel = 1; read((READ_BIT|SUBADDR|REG_SC)); S = read(READ_NEXT); M = read(READ_NEXT); H = read(READ_NEXT); D = read(READ_NEXT); W = read(READ_NEXT); N = read(READ_NEXT); Y = read(READ_NEXT); _sel = 0; } void PCF2123::reset() { // initialize RTC command((WRITE_BIT|SUBADDR|REG_CTRL1),RESET); // Send a software reset command wait_us(1); command((WRITE_BIT|SUBADDR|REG_CTRL1), 0x20); // Stop the counter first wait_us(1); _sel = 1; int ret = read((READ_BIT|SUBADDR|REG_CTRL1)); _sel = 0; command((WRITE_BIT|SUBADDR|REG_CTRL1), 0x00); // Start the counter wait_us(1); } void PCF2123::set_time( unsigned char s, unsigned char m, unsigned char h, unsigned char d, unsigned char w, unsigned char n, unsigned char y) { reset(); command((WRITE_BIT|SUBADDR|REG_CTRL1), 0x20); // Stop the counter first wait_us(1); _sel = 1; read((WRITE_BIT|SUBADDR|REG_SC)); read(s); read(m); read(h); read(d); read(w); read(n); read(y); _sel = 0; wait_us(1); command((WRITE_BIT|SUBADDR|REG_CTRL1), 0x00); // Start the counter wait_us(1); } void PCF2123::set_alarm( unsigned char h, unsigned char m ) { command((WRITE_BIT|SUBADDR|REG_CTRL2), 0x02); // アラーム割り込みを可能にする wait_us(1); command((WRITE_BIT|SUBADDR|ALARM_DAY), 0x80); // 日付は使用しない wait_us(1); command((WRITE_BIT|SUBADDR|ALARM_HOUR), h); wait_us(1); command((WRITE_BIT|SUBADDR|ALARM_MINITE), m); wait_us(1); command((WRITE_BIT|SUBADDR|ALARM_WEEK), 0x80); // 曜日は使用しない wait_us(1); } 動作確認 †シリアルCDCでリアルタイムクロックの値を表示するテストプログラムで動作を確認しました。 #include<cr_section_macros.h> #include<NXP/crp.h> __CRP extern const unsigned int CRP_WORD = CRP_NO_CRP; #include"lbed.h" #include "PCF2123.h" int main(void) { wait_init(); DigitalOut myled(LED2); SerialCDC pc(USBTX, USBRX); pc.baud(9600); // キー入力を待つ pc.read(); pc.println("Real Time Clock Test."); PCF2123 rtc(MOSI, MISO, SCKL, P0_2); rtc.set_time(0x45, 0x59, 0x23, 0x31, 0x06, 0x12, 0x10); int pS = 0xff; while (1) { rtc.read_rtc(); if (pS != rtc.S) { pc.printf("%d%d/%d%d/%d%d[%d] ", (rtc.Y>>4)&0xf, rtc.Y&0xf, (rtc.N>>4)&0xf, rtc.N&0xf,(rtc.D>>4)&0xf, rtc.D&0xf, rtc.W&0x7); pc.printf("%d%d:%d%d:%d%d\r\n",(rtc.H>>4)&0xf, rtc.H&0xf, (rtc.M>>4)&0xf, rtc.M&0xf,(rtc.S>>4)&0xf, rtc.S&0xf); myled = !myled; pS = rtc.S; } wait_ms(100); } return 0; 出力結果は、以下の様になります。 ディープ・パワー・ダウンモードを試すARM学習基板の本では、いきなりタイマー割り込みで再起動する例題がでていますが、ここでは30秒後に最初にディープ・パワー・ダウンモードに入って、復帰するだけの簡単なプログラムで試してみます。 以下のように、p4の1番ピン(GND)、7番ピン(PIO1_4)をタクトスイッチにつなぎます。これでスイッチを押すとGNDレベルになるので、スリープモードから復帰して再起動します。 #include <cr_section_macros.h> #include <NXP/crp.h> // Variable to store CRP value in. Will be placed automatically // by the linker when "Enable Code Read Protect" selected. // See crp.h header for more information __CRP extern const unsigned int CRP_WORD = CRP_NO_CRP ; #include "lbed.h" void DeepPowerDown(void) { int d; LPC_SYSCON->PDAWAKECFG = LPC_SYSCON->PDRUNCFG; LPC_SYSCON->PDSLEEPCFG=0xFFFFFFFF; //アナログブロックをOFF状態にする SCB->SCR |= 0x04; // システム制御レジスタのSLEEPDEEPビットをセット(クロック停止可能なときにクロック停止) LPC_PMU->PCON |= 0x2; // DEEP PowerDownモードビットをセット d = LPC_PMU->PCON; __WFI(); // 割り込み待ちで停止 } int main(void) { wait_init(); DigitalOut myled(LED2); myled = 1; int wakeupCounter = 30; while(1) { if (--wakeupCounter == 0) { myled = 0; DeepPowerDown(); } wait_ms(1000); } } リアルタイムクロックの時刻を受け取る †次に、復帰したときにリアルタイムクロックから時刻を取得してみます。 意外にもコンデンサーに貯めた電気でリアルタイムクロックが半年以上も *11 動作することが分かりました。 $$ \frac{C (V_0 - V_1)}{I} = 1.5 \times ( 3.3 - 2.0)/100 \times 10^{-9} = 19500000 (秒) $$ 起動して、CDCに時刻を10回表示してまた、スリープモードに入る例を作ってみました。 *12 毎回起動後にCDCのターミナルを起動して、1文字入力してならないと動かないようになっています。 *13 int main(void) { int wakeupCounter = 10; wait_init(); DigitalOut myled(LED2); SerialCDC pc(USBTX, USBRX); pc.baud(9600); // キー入力を待つ pc.read(); PCF2123 rtc(MOSI, MISO, SCKL, P0_2); // 1回目に時刻をセットした後は、コメントにするとRTCが動いていることが確認できる // rtc.set_time(0x45, 0x59, 0x23, 0x31, 0x06, 0x12, 0x10); int pS = 0xff; pc.println("After 10 seconds, I will be into deep sleep!"); myled = 1; while(1) { rtc.read_rtc(); if (pS != rtc.S) { pc.printf("wake up count = %d\n", wakeupCounter--); pc.printf("20%02x/%02x/%02x[%d] ", rtc.Y, rtc.N, rtc.D, rtc.W&0x7); pc.printf("%02x/%02x/%02x\n", rtc.H, rtc.M&0x7F, rtc.S&0x7F); pS = rtc.S; } if (wakeupCounter == 0) { myled = 0; DeepPowerDown(); } wait_ms(100); } } アラームをセット †最後に、アラームをセットして、再起動をする例題を示して、締めくくりにします。 int main(void) { int wakeupCounter = 10; wait_init(); DigitalOut myled(LED2); SerialCDC pc(USBTX, USBRX); pc.baud(9600); // キー入力を待つ pc.read(); PCF2123 rtc(MOSI, MISO, SCKL, P0_2); // 1回目に時刻をセットした後は、コメントにするとRTCが動いていることが確認できる rtc.set_time(0x40, 0x42, 0x10, 0x01, 0x00, 0x09, 0x13); // 分単位の割り込みを発生するようにセット rtc.command((PCF2123::WRITE_BIT|PCF2123::SUBADDR|PCF2123::REG_CTRL2), 0x82); // アラームのセット(時間、分は、16進で指定する) // rtc.set_alarm(0x10, 0x33); int pS = 0xff; pc.println("After 10 seconds, I will be into deep sleep!"); myled = 1; while(1) { rtc.read_rtc(); if (pS != rtc.S) { pc.printf("wake up count = %d\n", wakeupCounter--); pc.printf("20%02x/%02x/%02x[%d] ", rtc.Y, rtc.N, rtc.D, rtc.W&0x7); pc.printf("%02x/%02x/%02x\n", rtc.H, rtc.M&0x7F, rtc.S&0x7F); pS = rtc.S; } if (wakeupCounter == 0) { myled = 0; DeepPowerDown(); } wait_ms(100); } } 最新のソース †最新のソースは、Githubの以下のURLからダウンロードできます。 コメント †
Tweet |