lbed

2013/11/02からのアクセス回数 6570

ここでは、LM4F120 LaunchPadを使っていろいろな実験をして、lbedの使い方を説明します。

シリアル通信の実験

LM4F120 LaunchPadには、2個のLM4F120が搭載されており、1つはデバッガ兼シリアル通信用、 もう一つがターゲットのLM4F120と豪勢な構成となっています。

このようにデバッグと通信に専用にCPUが割り当てられているとUSBケーブルに接続しただけで パソコンの通信ソフトに接続できるので、CDCのようにシリアルの通信のテスト毎に接続が切れ てしまうようなことがなく、とても自然に通信とデバッグができます。

LM4F120_LaunchPad2.png

Serialクラスの実装

LM4F120 LaunchPadのStellarisWareライブラリとサンプルプログラムを使うことで、簡単にSerialクラスを 実装することができます。

Serial.cppは次のようになっています。

#include "Serial.h"
#include "PinNames.h"

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/gpio.h"

Serial::Serial()
     : _tx(-1)
     , _rx(-1)
{
     setup(PA_1, PA_0, "default");
}

Serial::Serial(PinName tx, PinName rx, const char *name)
     : _tx(-1)
     , _rx(-1)
{
     setup(tx, rx, name);
}

void Serial::setup(PinName tx, PinName rx, const char *name)
{
     _tx = tx;
     _rx = rx;
     _available = false;
     SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
     SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
     GPIOPinConfigure(GPIO_PA0_U0RX);
     GPIOPinConfigure(GPIO_PA1_U0TX);
     GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
}

void Serial::baud(unsigned int baudrate) {
     unsigned long sysclock = SysCtlClockGet();
     UARTConfigSetExpClk(UART0_BASE, sysclock, baudrate,
                        (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                         UART_CONFIG_PAR_NONE));
     _available = true;                                        }

void Serial::begin(unsigned int baudrate) {
     baud(baudrate);
}

int Serial::write(const char c) {
     UARTCharPut(UART0_BASE, c);
     return 1;
}

int Serial::read() {
     return UARTCharGet(UART0_BASE);
}

int Serial::available() {
     return _available ? 1 : 0;
}

動作確認

実際にSerialクラスを使ってシリアル通信を行ってみます。

テストプログラムは、以下の様になります。

#include"lbed.h"

DigitalOut myled(LEDG);

int main(void) {
     Serial pc(PA_1, PA_0);
     pc.baud(19200);
     pc.println("Hello");
     while (1) {
          char c = pc.read();
          pc.write(c + 1);
          myled = !myled;
     }
     return 0;
}

シリアル通信には、やはりArduinoのシリアルモニターを使いました。 これなら、どんなPCでも同じように使えるので、便利です。

最初にHelloと出力して入力を待ちます。 ここで、abcと入力すると一つ後の文字bcdを出力します。

Serial_out.png

スイッチ入力の実験

DegialInのクラスを使ってSW1を押したときに、LEDBが点灯するプログラムを作ってみましょう。

ポイントは以下の2つです。

プログラムは、とても簡単です。

#include "lbed.h"

int main(void) {
     DigitalIn     sw1(SW1);
     sw1.mode(PullUp);
    DigitalOut     myled(LEDG);
    while(1) {
        myled = !sw1;     // SW1を押すとLow=0になるので、押したときにLEDを付けるために!を付ける。
        wait_ms(200);
    }
}

動作確認

デバッガを起動して、プログラムをLM4F120 LaunchPadに書き込み、Resumeメニューを選択、または三角の青いアイコンをクリックするとmain関数の先頭で停まります。

ここで、もう一度Resumeを実行して、SW1を押したり、離したりしてみて下さい。

SW_out.png

温度を測る

I2Cインターフェースを持った温度センサーLM73B*2を使って、温度を測ってみましょう。

LM73は、白の三角がついたところが、1番ピンで反時計回りにピン番号が割り振られています。

となっています。SCL, SDAは、プルアップ抵抗が必要で、ここでは手持ちの4.7KΩを使用しました。

LM4F120 LaunchPadの接続は、以下の4本を使用します。

LM73_setting.png

テストプログラム

テストプログラムTestLM73.cppは、以下の様になります。 *3

どうもDegitalOutは、シリアルクラスの影響を受けるみたいで、pcの後に型宣言しています。

#include"lbed.h"
#include "LM73.h"

int main(void) {
     LM73 lm73(PB_3, PB_2);
     Serial pc(PA_1, PA_0);
     pc.baud(19200);
     // 注意)Serialの影響を受けるので、最後にLEDを生成した
     DigitalOut myled(LEDG);

     while (1) {
          float t = lm73.read();
          pc.printf("temp=%d.%02d\n", int(t), (int(t*100)%100));
          myled = !myled;
          wait_ms(1000);
     }
}

実際に動かしてシリアルモニターに出力させてみました。

LM73_out.png

I2Cクラスの実装

StellarisWareライブラリで、I2Cを利用する例題はいくつか見つかりましたが、2バイト以上を送る例が少なく、

LM4F120 LaunchPad特有のI2C初期設定が分からず、動作するまでかなり時間が掛かりました。 *4

I2Cクラスは、以下の様に作成しました。

#include "platform.h"
#include "PinNames.h"
#include "I2C.h"

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_i2c.h"
#include "driverlib/i2c.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"

I2C::I2C(PinName sda, PinName scl, const char *name) {
     _name = (char *) name;
     // The I2C0 peripheral must be enabled before use.
     SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
     // For this example I2C0 is used with PortB[3:2].
     SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
     // Select the I2C function for these pins.
     GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);     //     I2CSCL
     GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);     //     I2CSDA
     // Enable and initialize the I2C0 master module. True=400Kbps, False=100Kbps
     I2CMasterInitExpClk(I2C0_MASTER_BASE, SysCtlClockGet(), false);
}

int I2C::read(int address, char *data, int length, bool repeated) {
     unsigned char addr = (unsigned char)address>>1;
     I2CMasterSlaveAddrSet( I2C0_MASTER_BASE, addr, true);   // false = write, true = read
    if (length == 1) {
         I2CMasterControl( I2C0_MASTER_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
        // Wait until done transmitting
        while( I2CMasterBusy(I2C0_MASTER_BASE));
        *data = I2CMasterDataGet(I2C0_MASTER_BASE);
    }
    else {
         for (int i = 0; i < length; i++) {
              if (i == 0)
                   I2CMasterControl( I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
              else if (i == length-1)
                   I2CMasterControl( I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
              else
                   I2CMasterControl( I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
            // Wait until done transmitting
            while( I2CMasterBusy(I2C0_MASTER_BASE));
            *data++ = I2CMasterDataGet(I2C0_MASTER_BASE);
         }
    }
    return length;
}

int I2C::write(int address, const char *data, int length, bool repeated) {
     unsigned char addr = (unsigned char)address>>1;
     I2CMasterSlaveAddrSet( I2C0_MASTER_BASE, addr, false);   // false = write, true = read
    if (length == 1) {
         I2CMasterDataPut( I2C0_MASTER_BASE, *data);
         I2CMasterControl( I2C0_MASTER_BASE, I2C_MASTER_CMD_SINGLE_SEND);
        // Wait until done transmitting
        while( I2CMasterBusy(I2C0_MASTER_BASE));
    }
    else {
         for (int i = 0; i < length; i++) {
              I2CMasterDataPut( I2C0_MASTER_BASE, *data++);
              if (i == 0)
                   I2CMasterControl( I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_START);
              else if (i == length-1)
                   I2CMasterControl( I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
              else
                   I2CMasterControl( I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
            // Wait until done transmitting
            while( I2CMasterBusy(I2C0_MASTER_BASE));
         }
    }
    return length;
}

// これらの関数の使い方がよく分からないので、ダミー関数
int I2C::read(int ack) {
     return -1;
}
int I2C::write(int ack) {
    return -1;
}

既存のmbedのライブラリの移植

これまで自分が作ってきたユーザライブラリを使っていましたが、mbedの既存のユーザ ライブラリをlbedに移植した場合の変更点と問題点を整理します。

I2cLCD.cppの移植

勝純一さんが公開されているI2cLCDを例にlbedへの移植をしてみます。

I2cLCD_setting.png

wait関数の変更は、wait_msとwait_usを使って置き換えます。

wait_ms(1); wait_us(640); //wait(0.00164f);     // This command takes 1.64 ms

I2cLCD.hとI2cLCD.cppのメンバー変数の順序を以下の様に変更します。

    // DigitalOut, I2Cの順で宣言されているのをI2C, DigitalOutに変更
    // DigitalOut _rs;
    // I2C    _i2c;
    I2C    _i2c;
    DigitalOut _rs;
// _rs, _i2cの順に初期化しているのを_i2c, _rsに変更
// I2cLCD::I2cLCD(PinName sda, PinName scl, PinName rp) :  _rs( rp ), _i2c( sda , scl ) {
I2cLCD::I2cLCD(PinName sda, PinName scl, PinName rp) :  _i2c( sda , scl ), _rs( rp ) {

staticな配列icon_dataの初期化をauto変数に変更します。

static unsigned char icon_data[]=
{
     0x00, 0x10,
途中省略
     0x0F, 0x10,
};

をputiconメンバ関数のなかで設定するように変更 *6

void I2cLCD::puticon(int flg)
{
    unsigned char icon_data[] = {
    0x00, 0x10,
途中省略
    0x0F, 0x10,
    };

このように手順が分かれば、ある程度スムーズに移植できると思います。 *7

コメント

選択肢 投票
おもしろかった 0  
そうでもない 0  
わかりずらい 0  

皆様のご意見、ご希望をお待ちしております。


(Input image string)

*1 LM4F120 LaunchPadのユーザマニュアルのAppendix Aに出ている回路をみるとSW1は、0Ωの抵抗を通してPF4に接続していますので、設定でプルアップにする必要があります
*2 エレキジャックNo.8の付録
*3 自作のprintfは、floatに対応していないので、少数点以下2桁を整数で求めています
*4 GPIOPinTypeI2CSCLが他のMPUと異なります
*5 doubleからfloatへの変換でハードフォルトになる
*6 今回は使用している場所が一カ所だったので、このような変更でも対応できました
*7 まだまだ、アドホックな実装ですが、使いにくいStellarisWareよりは良いのではないかと思います。

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