lbed

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

ここでは、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つです。

  • SW1のモードをPullUpにセットする *1
  • スイッチを押すと0になるので、NOT !を使って1にする

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

#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番ピンで反時計回りにピン番号が割り振られています。

  • 1: ADDR I2Cのslave addressを決めるpin: オープンで0x4Cになっています
  • 2: GND
  • 3: VDD 3.3V(2.7~5.5Vに対応)
  • 4: SCL(4.7KΩでプルアップ)
  • 5: 未接続
  • 6: SDA(4.7KΩでプルアップ)

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

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

  • J1_1: 3.3V
  • J3_2: GND
  • J4_3: PB3(SDA)
  • J2_2: PB2(SCL)

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に移植した場合の変更点と問題点を整理します。

  • C++のスタートアップルーチンが自前なので、C++内部で配列の初期設定を行っている場合に正しく処理されない(未解決)
  • ユーザクラスの中で、SerialやI2Cのメンバー変数を持つ場合、最後にDegitalOut, DegitalInのメンバーを宣言しなくてはならない(未解決)
  • wait関数が使えない*5
  • mbed.hをlbed.hに変更する

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

アナログ入力の実験

アナログ入力は、StellarisWareのライブラリを使用するため、 StellarisLaunchPadWorkbook を参考にAnalogInクラスを作成してみます。

LM4F120には、12bit 1MサンプリングのADCモジュール*8が2個内蔵され、12個のアナログ入力チャネルで共有されています。*9

LM4F120 LaunchPad UserManual Table 2-5によると、12個のアナログ入力チャネルは、以下の様にピン配置されています。

analog_pin_assign.png

電圧を測る

アナログ入力クラスAnalogInを使って可変抵抗の電圧を測ってみます。

可変抵抗の両端を3.3VとGNDに接続し、真ん中のピンをJ3の9番目のピンPE_3に接続します。

analogin_setting.png

テストプログラムTestAnalogIn.cppは、以下の様にします。

#include"lbed.h"


int main(void) {
     Serial           pc(PA_1, PA_0);
     AnalogIn      in(PE_3);
     DigitalOut      myled(LEDG);
     pc.baud(19200);
     pc.println("Hello");
     while (1) {
          unsigned short val = in.read_u16();
          pc.printf("Sensor = %d\n", (int)val);
          myled = !myled;
          wait_ms(1000);
     }
     return 0;
}

いつものようにArudino IDEのシリアルモニターを起動して実行すると、以下のように可変抵抗を回すと 1から4095の範囲の値が表示されます。

analogin_out.png

AnalogInクラスの実装

AnalogInのsetupをPE_3に対してのみ表すと以下の様になります。

void AnalogIn::setup(PinName pin, const char* name)
{
     SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
     SysCtlADCSpeedSet(SAMPLE_SPS);
     ADCSequenceDisable(ADC0_BASE, SEQUENCE_NUM);

     if (pin >= PE_0 && pin <= PE_5) {
          _gpio = GPIO_PORTE_BASE;
          SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
          switch (pin) {
               case PE_3:
                    _channel = ADC_CTL_CH0;
                    _pin = 3;
                    break;
          // 他のピンも同様に設定
          }
          AnalogIn::_e_mask |= 1 << _pin;
          // ピンタイプをADCにセット
          GPIOPinTypeADC(_gpio, AnalogIn::_e_mask);
     }

// PB, PDは省略

     // ADC0をシーケンス3を割り込み不可にする
     ADCSequenceDisable(ADC0_BASE, SEQUENCE_NUM);
     // ADC0のシーケンスを最大優先にセット
     ADCSequenceConfigure(ADC0_BASE, SEQUENCE_NUM, ADC_TRIGGER_PROCESSOR, 0);
     // 1ステップで1サンプリングにセット
     ADCSequenceStepConfigure(ADC0_BASE, SEQUENCE_NUM, 0, _channel | ADC_CTL_IE | ADC_CTL_END);
     // ADC0をシーケンス3を割り込み可能にする
     ADCSequenceEnable(ADC0_BASE, SEQUENCE_NUM);
     // ADC0をシーケンス3の割り込みフラグをクリア
     ADCIntClear(ADC0_BASE, SEQUENCE_NUM);
}

アナログ値の読み込みは、以下の様にしました。

unsigned short AnalogIn::read_u16()
{
     if (_gpio) {
          ADCIntClear(ADC0_BASE, SEQUENCE_NUM);
          ADCProcessorTrigger(ADC0_BASE, SEQUENCE_NUM);
          while(!ADCIntStatus(ADC0_BASE, SEQUENCE_NUM, false)) {
          }
          ADCSequenceDataGet(ADC0_BASE, SEQUENCE_NUM, &_value);
          return ((short)(_value&0x0FFF));
     }
     else
          return 0;
}

LEDの明るさを変える

LM4F120には、ドライバーで使われているPWMモジュールが付属していない *10 ため、StellarisライブラリーのPWMは使えず、タイマー機能を使って代用する必要があります。

PWMのピン割り当ては、以下の様になっています。 *11

PWM_pin.png

LEDとの接続

PWMピンの一つJ3_10(LEDRと兼用)をLEDのアノード(線の長い方)と直列につないだ470Ωの抵抗に接続し、 カソードをGND(J3_2)に接続します。

PWM_setting.png

テストプログラム

動作を確認するテストプログラムは、PwmOutのインスタンスledを生成し、80m秒毎に0.02ずつ値を大きくするもので、これを4秒間隔で繰り返します。

#include "lbed.h"

int main(void) {
    PwmOut led(LEDR);
    led = 0.5;
    while(1) {
        led = led + 0.02;
        if(led >= 1.0) {
            led = 0.0;
        }
        wait_ms(80);
   }
}

PwmOutクラスの実装

PwmOutのsetupをLEDRに対してのみ表すと以下の様になります。

*12

void PwmOut::setup(float period) {
     _period = period;
     unsigned long p = (uint32_t)(SystemCoreClock*_period);
     // 16bitの範囲内で制御できるようにprescaleを変更する
     if (p > (unsigned long)0xFFFF)
          _scale = p/0x10000 + 1;
     else
          _scale = 1;
     unsigned long matchTime = (unsigned long)((_period - _pulsewidth)*SystemCoreClock/_scale)-1;
     switch (_pin) {
     case LEDR:     // T0CCP1
          SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
          GPIOPinConfigure(GPIO_PF1_T0CCP1);
          GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_1);
          SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
          _base = TIMER0_BASE;
          _timer = TIMER_B;
          TimerConfigure(_base, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_B_PWM);
          break;
     // 他のピンは、省略
     }
     TimerPrescaleSet(_base, _timer, _scale -1);          // prescaleをセット
     TimerPrescaleMatchSet(_base, _timer, _scale -1);
     TimerLoadSet(_base, _timer, p/_scale -1);          // periodをセット
     TimerMatchSet(_base, _timer, matchTime);
     TimerEnable(_base, _timer);
}

PWMで音を出してみる

PWMを使ってArduinoのToneクラスを作ってみます。

圧電スピーカとの接続

圧電スピーカは、直接PWDの出力ピント接続します。 今回は、PB5をPWDの出力ピンに使用します。

LaunchPadとの接続は、J1_2(PB5)とJ3_2(GND)を圧電スピーカに接続するだけです。

Tone_setting.png

動かしてみる

テストプログラムTestTone.cppは、PCから入力された1から5に応じてそれぞれ、 ド、レ、ミ、ソ、ラを出力します。

TestTone.cppは、以下の通りです。

#include     "lbed.h"
#include     "Tone.h"

int main(void) {
     int tones[]={262,294,330,392,440};  // C, D, E, G, A
     int toneDuration = 500;

     Serial          pc(PA_1, PA_0);
     Tone          tone(PB_5);          // T1CCP1
     DigitalOut     myled(LEDR);
     pc.baud(19200);
     tone.noTone();

     pc.println("Input number");
     while (1) {
          char c = pc.read();
          myled = !myled;
          if (c >= '1' && c <= '5') {
               int index = c - '1';
               tone.tone(tones[index], toneDuration);
          }
     }
     return 0;
}

Toneクラスの実装

Toneクラスは、PwmOutクラスを使ってとても簡単に作成できます。

Tone.cppは、以下の通りです。

#include "Tone.h"
#include "lbed.h"

Tone::Tone(PinName pin) : _out(pin)
{
}

void Tone::tone(unsigned int frequency)
{
     float period = 1.0/frequency;
     noTone();
     _out.period(period);
     _out.write(0.5);
}

void Tone::tone(unsigned int frequency, unsigned long duration)
{
     noTone();
     tone(frequency);
     wait_ms(duration);
     noTone();
}

void Tone::noTone(void)
{
     _out.disable();
}

SPI通信

SPIのテストプログラム

が動作したので、SPIクラスを作りました。

機能レジスターピン番号
ssi0RxPA_4J2_8
ssi0TxPA_5J1_8
ssi0clkPA_2J2_10
ssi1RxPD_2J3_5
ssi1TxPD_3J3_6
ssi1clkPD_0J3_3
ssi2RxPB_6J2_7
ssi2TxPB_7J2_6
ssi2clkPB_4J1_7

Arduinoとの接続

Arduinoとの接続は、SPIのテストプログラムと同じで以下の様にしました。

LaunchPadのピンは、以下のピンを使用します。

  • J3_6 (PD3) ssi1Tx (MOSIに相当)
  • J3_5 (PD2) ssi1Rx (MISOに相当)
  • J3_3 (PD0) ssi1Clk (SCKL)
  • J3_2 (GND)

Arduinoのピンは、以下のピンを使用します。

  • D11 (MOSI)
  • D12 (MISO)
  • D13 (CLK)
  • GND

AdruinoとLaunchPadの結線は、以下の様になっています。

SPI_setting.png

動作確認用プログラム

SPIをクラスを使うと、Hello Worldのテストプログラムの以下の様に簡単になります。

#include "lbed.h"

int main(void) {
     SPI     spi(PD_3, PD_2, PD_0);
     char     *pcChars = "Hello World!\n";
     char     c;
     while((c = *pcChars++)) {
          spi.write(c);
     }
}

シリアルモニターに以下の様に出力されます。

TestSPI_out.png

OLEDに接続

SPIの動作が確認できましたので、最大の目標であるMARMEX_OBの液晶モジュールに接続してみましょう。 今回使用するのは、 MARY-OB基板(OLED Board) です。

今回も、mbedのサイトから MARMEX_OB基板OLEDライブラリ (MARMEX_OB_oled) を使わせてもらいました。

MARY-OB基板(OLED Board)との接続

MARY-OB基板(OLED Board)のコネクターとピンの定義は以下の通りです。

oled_connector.png

LaunchPadとの接続は、以下のようにします。

  • J3_2(GND)をCN1の1番に接続
  • J3_1(5V)をCN1の2番に接続
  • J1_1(3.3V)をCN1の3番に接続
  • J2_6(PD_3 ssi1Tx)をCN4の4番(OLED_SDIN)に接続
  • J2_3(PD_0 ssi1clk)をCN4の2番(OLED_SCLK)に接続
  • J1_8をCN3の2番(OLED_CSN)に接続
  • J1_9をCN3の1番(OLED_RESN)に接続
  • J1_10をCN2の1番(OLED_VCC_ON)に接続

テストプログラム

テストプログラムTestOLED.cppは以下の様にします。

#include <math.h>
#include "lbed.h"
#include "MARMEX_OB_oled.h"

int main(void) {
    MARMEX_OB_oled oled(PD_3, PD_0, J1_8, J1_9, J1_10);     // mosi, sclk, cs, rst, power_control

    oled.background( 0x000000 );
    oled.cls();

    int colorbar_width  = MARMEX_OB_oled::WIDTH / 8;
    for ( int i = 0; i < 8; i++ )
        oled.fill( colorbar_width * i, 0, colorbar_width, MARMEX_OB_oled::HEIGHT,
                  ((i & 0x4) ? 0xFF0000 : 0x000000) | ((i & 0x2) ? 0x00FF00 : 0x000000) | ((i & 0x1) ? 0x0000FF : 0x000000) );

    oled.fill(  50,  50,  64,  64, 0xCCCCCC );;
    oled.locate(0, 3);
    oled.print("Hello World!");

    for (int i = 0; i < MARMEX_OB_oled::WIDTH; i++ ) {
         oled.pixel( i, 80 + sinf( (float)i / 5.0 ) * 10, 0x000000 );
    }

    while(1) {
    }
}

動作確認

無事動いて、MARY-OB基板(OLED Board)に以下の様に表示されました。

OLED_out.png

キーパッドを使う

テンキーは入力デバイスとして便利です。そこでArduinoのKeypadクラスをlbedに移植してみることにしました。

ポイントは以下の2点です

  • インタフェースは変えずに、ピン番号指定の処理をDegitalOutに置き換える
  • pinMode, mille等のlbedにない、関数を追加する

キーパッドとの接続

今回は、スイッチサイエンスの 3x4のマトリックス式ボタンパッド を使用しました。

マトリックス式ボタンパッドのコネクタは、左から1番とし、以下の様に接続します。

キーパッドのピンLaunchPadのピン
1:1行目J1_9
2:2行目J1_8
3:3行目J1_7
4:4行目J1_6
5:1列目J1_5
6:2列目J1_4
7:3列目J1_3

keypad_setting.png

テストプログラムと動作確認

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

#include "lbed.h"
#include "Keypad.h"

int main(void) {
     const byte rows = 4; //four rows
     const byte cols = 3; //three columns
     char keys[rows][cols] = {
       {'1','2','3'},
       {'4','5','6'},
       {'7','8','9'},
       {'*','0','#'}
     };
     byte     rowPins[rows] = {J1_9, J1_8, J1_7, J1_6};     //connect to the row pinouts of the keypad
     byte     colPins[cols] = {J1_5, J1_4, J1_3};               //connect to the column pinouts of the keypad
     Serial     pc(PA_1, PA_0);
     Keypad     keypad = Keypad( makeKeymap(keys), rowPins, colPins, rows, cols );
     pc.baud(19200);
     pc.println("Input number");
     wait_ms(500);
     while (1) {
          char c = keypad.getKey();
          if (c)
               pc.write(c);
     }
}

Keypad移植のポイント

Arduinoのライブラリをlbedに移植するときのポイントは、できるだけソース変更しないことです。

lbedに存在しない関数の追加

lbedに存在しない関数の内、

  • pinMode: 入力・出力を切り替える
  • mille: ミリ秒のカウントを返す

pinModeは、DigitalOut.hに以下の様に追加しました。

     // Arduinoのライブラリコンバートのため、pinModeを追加
     void pinMode(int mode) {
          switch (mode) {
          case Arduino_INPUT:
               _gpio->DIR &= ~_mask;
               DigitalIn::mode(PullNone);
               break;
          case Arduino_OUTPUT:
               DigitalIn::mode(PullNone);
               _gpio->DIR |= _mask;
               break;
          case Arduino_INPUT_PULLUP:
               _gpio->DIR &= ~_mask;
               DigitalIn::mode(PullUp);
               break;
          }
     }

Keypadの修正

Keypad内部で保持している行(rowPins)と列(columPins)をbyteポインタからDigitalOutの配列に変更しました。 *13

    //byte *rowPins;
    //byte *columnPins;
     DigitalOut          rowPins[4];
     DigitalOut          columnPins[4];

そしてこれからが、今回の移植のメインイベントで、以下のdefine文で不足している関数を入れ替えました。

#define pin_mode(pinNum, mode)          (pinNum).pinMode(mode)
#define pin_write(pinNum, level)     (pinNum) = level ? 1 : 0
#define pin_read(pinNum)               (pinNum)
#define bitWrite(b, c, bit)               b = bit ? (b | 1<<c) : (b & ~(1<<c))
#define bitRead(b, c)                    ((b & (1<<c)) >>c)

最後に、Keypadのコンストラクターでピン番号からDigitalOutをセットします。

     // rowPins = row;
     // columnPins = col;
     for (int i = 0; i < numRows; i++) {
          rowPins[i].setup(row[i], NULL);
     }
     for (int j = 0; j < numCols; j++) {
          columnPins[j].setup(col[j], NULL);
     }

最新版のソース

Githubにlbedを登録しました。

コメント

選択肢 投票
おもしろかった 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よりは良いのではないかと思います。
*8 ADC0, ADC1
*9 詳しくは、http://software-dl.ti.com/trainingTTO/trainingTTO_public_sw/GSW-Stellaris-LaunchPad/StellarisLaunchPadWorkbook.pdf のADC12を参照してください
*10 http://e2e.ti.com/support/microcontrollers/stellaris_arm/f/471/t/195128.aspx 参照。
*11 一部のピンがカラーLEDとタイマーが同じため、同時にはPWM制御できません。
*12 Stellarisのライブラリを使っているので、実装はいたってシンプルです
*13 今回は4 x 4 の固定としました

添付ファイル: filekeypad_setting.png 439件 [詳細] fileSPI_setting.png 452件 [詳細] fileTestSPI_out.png 451件 [詳細] fileOLED_out.png 462件 [詳細] fileoled_connector.png 449件 [詳細] filePWM_pin.png 409件 [詳細] filePWM_setting.png 452件 [詳細] fileTone_setting.png 441件 [詳細] fileanalog_pin_assign.png 437件 [詳細] fileanalogin_out.png 428件 [詳細] fileanalogin_setting.png 447件 [詳細] fileI2cLCD_setting.png 488件 [詳細] fileSW_out.png 414件 [詳細] fileLM4F120_LaunchPad2.png 454件 [詳細] fileSerial_out.png 439件 [詳細] fileLM73_setting.png 430件 [詳細] fileLM73_out.png 415件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2013-12-03 (火) 12:59:57 (1419d)
SmartDoc