lbed

2013/08/11からのアクセス回数 6626

LPC1343学習基板に戻って

cortex-m3の勉強のために買った「ARMマイコンパーフェクト学習基板の使い方」をlbedを使うとどのくらい簡単に なるかを試してみます。

実験に使用するのは、以下の3つです。

汎用入出力GPIOの使い方編

lbedにはまだ汎用入力クラスがありませんので、この機会に作成しました。 詳しくは、lbed/08a-ARM学習基板用追加クラスを参照してください。

実験開始

ジャンパP4の3と4、31と32にジャンパーをセットし、タッチエリアに触れるとLEDが点灯するテストプログラムを作ります。

タッチセンサーPCF8883の出力オープン・ドレインHigth *1 なので、LPC1343の入力ピンは、プルダウンモードにセットする必要があります。 *2

TouchSensor.png

テストプログラム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が必要だと分かり、急遽取り付けました。

Add_TRG.png

テスト用のソース

テスト用のプログラムとして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からピンの設定の図を引用します。

Fig8_1.png

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

Fig8_2.png

アナログ回路の動作確認

P3の3番ピンCh6に安定電源1.8Vを半固定抵抗で1.308Vに減圧して入力としました。 $$ V = (1.308 - 1.25)*11 + 1.25 = 1.888 $$ が、Ch6の予想結果です。

ADC_Test.png

テストプログラム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と予想と近い結果が出ています。

ADC_Screen.png

温度センサーIC LM75Bを使ってみる

ARM学習基板には、I2Cのテストをするために温度センサーLM75Bが付いています。 これを使って、温度を取得してみます。

LM75Bのアドレスは、A2, A1, A0の接続によってユーザが変えることができるようになっています。 *9

本の11章の図4は、大きなミスプリがあります。

Fig11_4.pngFig0_2.png

右がプロローグの図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ボードピンの対応は、以下の様になります。

P4StarOrangePC1343機能
1p1p1GND
39p40p403.3V
33p24p26rs
31p26p22e
23p27p14d4
21p28p6d5
19p29p5d6
17p30p25d7

LM75B.png

テストプログラムと結果

テストプログラム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度でした。

Temp.png

リアルタイムクロック(SPI接続)を使う

リアルタイムクロックのテストには苦労しました。 *10

ジャンパ線の設定は以下の様になります。

RTC_setting.png

リアルタイムクロックのクラス

リアルタイムクロック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;

出力結果は、以下の様になります。

RTC_screen.png

ディープ・パワー・ダウンモードを試す

ARM学習基板の本では、いきなりタイマー割り込みで再起動する例題がでていますが、ここでは30秒後に最初にディープ・パワー・ダウンモードに入って、復帰するだけの簡単なプログラムで試してみます。

以下のように、p4の1番ピン(GND)、7番ピン(PIO1_4)をタクトスイッチにつなぎます。これでスイッチを押すとGNDレベルになるので、スリープモードから復帰して再起動します。

DeepSleepWakeUp.png

#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 (秒) $$

RTCwithC.png

起動して、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からダウンロードできます。

コメント


(Input image string)

*1 ON時にHightレベルが出力され、OFF時にはハイ・インピーダンスになります
*2 あるいは、プルダウン抵抗を付ける方法もあります
*3 詳しくは、lbed/08a-ARM学習基板用追加クラスを参照してください。
*4 コンパイルには、ForSerialCDC_usbhw.cをTestCDC.cppと同じディレクトリ入れて下さい
*5 詳しくは、lbed/08a-ARM学習基板用追加クラスを参照してください。
*6 その分、各チップを別の実験に使うことができるようにCPUと切り離されています
*7 トラ技2013/8号のエレキ数式も便利です
*8 サイズが大きくなりますので、注意してください
*9 '1001 + A2, A1, A0がアドレスになります。
*10 ssp.cのSSP_IOConfigでPIO2_11を使うように#if文を変更しなくてはなりません。
*11 225.7日
*12 main関数のみ表示
*13 すぐに起動するようにするとLPC1343が書き込み途中にスリープしてしまうのを防ぐため。

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc