[[lbed]] #contents 2013/11/02からのアクセス回数 &counter; ここでは、LM4F120 LaunchPadを使っていろいろな実験をして、lbedの使い方を説明します。 ** シリアル通信の実験 [#n8e82a75] LM4F120 LaunchPadには、2個のLM4F120が搭載されており、1つはデバッガ兼シリアル通信用、 もう一つがターゲットのLM4F120と豪勢な構成となっています。 このようにデバッグと通信に専用にCPUが割り当てられているとUSBケーブルに接続しただけで パソコンの通信ソフトに接続できるので、CDCのようにシリアルの通信のテスト毎に接続が切れ てしまうようなことがなく、とても自然に通信とデバッグができます。 &ref(LM4F120_LaunchPad2.png); *** Serialクラスの実装 [#u2dd559a] LM4F120 LaunchPadのStellarisWareライブラリとサンプルプログラムを使うことで、簡単にSerialクラスを 実装することができます。 Serial.cppは次のようになっています。 #pre{{ #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; } }} *** 動作確認 [#o4f0da11] 実際にSerialクラスを使ってシリアル通信を行ってみます。 テストプログラムは、以下の様になります。 #pre{{ #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を出力します。 &ref(Serial_out.png); ** スイッチ入力の実験 [#ie1da269] DegialInのクラスを使ってSW1を押したときに、LEDBが点灯するプログラムを作ってみましょう。 ポイントは以下の2つです。 - SW1のモードをPullUpにセットする ((LM4F120 LaunchPadのユーザマニュアルのAppendix Aに出ている回路をみるとSW1は、0Ωの抵抗を通してPF4に接続していますので、設定でプルアップにする必要があります)) - スイッチを押すと0になるので、NOT !を使って1にする プログラムは、とても簡単です。 #pre{{ #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); } } }} *** 動作確認 [#w5835807] デバッガを起動して、プログラムをLM4F120 LaunchPadに書き込み、Resumeメニューを選択、または三角の青いアイコンをクリックするとmain関数の先頭で停まります。 ここで、もう一度Resumeを実行して、SW1を押したり、離したりしてみて下さい。 &ref(SW_out.png); ** 温度を測る [#uf74ea5d] I2Cインターフェースを持った温度センサーLM73B((エレキジャックNo.8の付録))を使って、温度を測ってみましょう。 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) &ref(LM73_setting.png); *** テストプログラム [#j327e69e] テストプログラムTestLM73.cppは、以下の様になります。 ((自作のprintfは、floatに対応していないので、少数点以下2桁を整数で求めています)) どうもDegitalOutは、シリアルクラスの影響を受けるみたいで、pcの後に型宣言しています。 #pre{{ #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); } } }} 実際に動かしてシリアルモニターに出力させてみました。 &ref(LM73_out.png); *** I2Cクラスの実装 [#c2ced26d] StellarisWareライブラリで、I2Cを利用する例題はいくつか見つかりましたが、2バイト以上を送る例が少なく、 LM4F120 LaunchPad特有のI2C初期設定が分からず、動作するまでかなり時間が掛かりました。 ((GPIOPinTypeI2CSCLが他のMPUと異なります)) I2Cクラスは、以下の様に作成しました。 #pre{{ #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のライブラリの移植 [#n482170b] これまで自分が作ってきたユーザライブラリを使っていましたが、mbedの既存のユーザ ライブラリをlbedに移植した場合の変更点と問題点を整理します。 - C++のスタートアップルーチンが自前なので、C++内部で配列の初期設定を行っている場合に正しく処理されない(未解決) - ユーザクラスの中で、SerialやI2Cのメンバー変数を持つ場合、最後にDegitalOut, DegitalInのメンバーを宣言しなくてはならない(未解決) - wait関数が使えない((doubleからfloatへの変換でハードフォルトになる)) - mbed.hをlbed.hに変更する *** I2cLCD.cppの移植 [#h5b132aa] 勝純一さんが公開されている[[I2cLCD>http://mbed.org/users/jksoft/notebook/i2clcd_lib/]]を例にlbedへの移植をしてみます。 &ref(I2cLCD_setting.png); wait関数の変更は、wait_msとwait_usを使って置き換えます。 #pre{{ wait_ms(1); wait_us(640); //wait(0.00164f); // This command takes 1.64 ms }} I2cLCD.hとI2cLCD.cppのメンバー変数の順序を以下の様に変更します。 #pre{{ // DigitalOut, I2Cの順で宣言されているのをI2C, DigitalOutに変更 // DigitalOut _rs; // I2C _i2c; I2C _i2c; DigitalOut _rs; }} #pre{{ // _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変数に変更します。 #pre{{ static unsigned char icon_data[]= { 0x00, 0x10, 途中省略 0x0F, 0x10, }; }} をputiconメンバ関数のなかで設定するように変更 ((今回は使用している場所が一カ所だったので、このような変更でも対応できました)) #pre{{ void I2cLCD::puticon(int flg) { unsigned char icon_data[] = { 0x00, 0x10, 途中省略 0x0F, 0x10, }; }} このように手順が分かれば、ある程度スムーズに移植できると思います。 ((まだまだ、アドホックな実装ですが、使いにくいStellarisWareよりは良いのではないかと思います。)) ** アナログ入力の実験 [#o5ae9ca3] アナログ入力は、StellarisWareのライブラリを使用するため、 [[StellarisLaunchPadWorkbook>http://software-dl.ti.com/trainingTTO/trainingTTO_public_sw/GSW-Stellaris-LaunchPad/StellarisLaunchPadWorkbook.pdf]] を参考にAnalogInクラスを作成してみます。 LM4F120には、12bit 1MサンプリングのADCモジュール((ADC0, ADC1))が2個内蔵され、12個のアナログ入力チャネルで共有されています。((詳しくは、http://software-dl.ti.com/trainingTTO/trainingTTO_public_sw/GSW-Stellaris-LaunchPad/StellarisLaunchPadWorkbook.pdf のADC12を参照してください)) LM4F120 LaunchPad UserManual Table 2-5によると、12個のアナログ入力チャネルは、以下の様にピン配置されています。 &ref(analog_pin_assign.png); *** 電圧を測る [#s1dc99c0] アナログ入力クラスAnalogInを使って可変抵抗の電圧を測ってみます。 可変抵抗の両端を3.3VとGNDに接続し、真ん中のピンをJ3の9番目のピンPE_3に接続します。 &ref(analogin_setting.png); テストプログラムTestAnalogIn.cppは、以下の様にします。 #pre{{ #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の範囲の値が表示されます。 &ref(analogin_out.png); *** AnalogInクラスの実装 [#c30b22da] AnalogInのsetupをPE_3に対してのみ表すと以下の様になります。 #pre{{ 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); } }} アナログ値の読み込みは、以下の様にしました。 #pre{{ 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の明るさを変える [#x3519295] LM4F120には、ドライバーで使われているPWMモジュールが付属していない ((http://e2e.ti.com/support/microcontrollers/stellaris_arm/f/471/t/195128.aspx 参照。)) ため、StellarisライブラリーのPWMは使えず、タイマー機能を使って代用する必要があります。 PWMのピン割り当ては、以下の様になっています。 ((一部のピンがカラーLEDとタイマーが同じため、同時にはPWM制御できません。)) &ref(PWM_pin.png); *** LEDとの接続 [#s3dd863c] PWMピンの一つJ3_10(LEDRと兼用)をLEDのアノード(線の長い方)と直列につないだ470Ωの抵抗に接続し、 カソードをGND(J3_2)に接続します。 &ref(PWM_setting.png); *** テストプログラム [#t98b0c3c] 動作を確認するテストプログラムは、PwmOutのインスタンスledを生成し、80m秒毎に0.02ずつ値を大きくするもので、これを4秒間隔で繰り返します。 #pre{{ #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クラスの実装 [#f9772472] PwmOutのsetupをLEDRに対してのみ表すと以下の様になります。 (( Stellarisのライブラリを使っているので、実装はいたってシンプルです)) #pre{{ 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で音を出してみる [#z6ec77e1] PWMを使ってArduinoのToneクラスを作ってみます。 *** 圧電スピーカとの接続 [#r54633ec] 圧電スピーカは、直接PWDの出力ピント接続します。 今回は、PB5をPWDの出力ピンに使用します。 LaunchPadとの接続は、J1_2(PB5)とJ3_2(GND)を圧電スピーカに接続するだけです。 &ref(Tone_setting.png); *** 動かしてみる [#m165ba75] テストプログラムTestTone.cppは、PCから入力された1から5に応じてそれぞれ、 ド、レ、ミ、ソ、ラを出力します。 TestTone.cppは、以下の通りです。 #pre{{ #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クラスの実装 [#e1bba43b] Toneクラスは、PwmOutクラスを使ってとても簡単に作成できます。 Tone.cppは、以下の通りです。 #pre{{ #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通信 [#nc76ca7a] [[SPIのテストプログラム>arm/LM4F120F LaunchPad SPI通信に挑戦]] が動作したので、SPIクラスを作りました。 | 機能 | レジスター | ピン番号 |h | ssi0Rx | PA_4 | J2_8 | | ssi0Tx | PA_5 | J1_8 | | ssi0clk | PA_2 | J2_10 | | ssi1Rx | PD_2 | J3_5 | | ssi1Tx | PD_3 | J3_6 | | ssi1clk | PD_0 | J3_3 | | ssi2Rx | PB_6 | J2_7 | | ssi2Tx | PB_7 | J2_6 | | ssi2clk | PB_4 | J1_7 | *** Arduinoとの接続 [#xdf0539b] 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の結線は、以下の様になっています。 &ref(SPI_setting.png); *** 動作確認用プログラム [#fd22a215] SPIをクラスを使うと、Hello Worldのテストプログラムの以下の様に簡単になります。 #pre{{ #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); } } }} シリアルモニターに以下の様に出力されます。 &ref(TestSPI_out.png); ** OLEDに接続 [#b4cb05e2] SPIの動作が確認できましたので、最大の目標であるMARMEX_OBの液晶モジュールに接続してみましょう。 今回使用するのは、 [[MARY-OB基板(OLED Board)>http://www.marutsu.co.jp/images/mm12/081202/0000000200110331_2.jpg]] です。 今回も、mbedのサイトから [[MARMEX_OB基板OLEDライブラリ (MARMEX_OB_oled)>http://mbed.org/users/nxpfan/notebook/MARMEX_OB_oled_lib/]] を使わせてもらいました。 *** MARY-OB基板(OLED Board)との接続 [#q2f4e2f3] MARY-OB基板(OLED Board)のコネクターとピンの定義は以下の通りです。 &ref(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)に接続 *** テストプログラム [#k80467b4] テストプログラムTestOLED.cppは以下の様にします。 #pre{{ #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) { } } }} *** 動作確認 [#kf397e6b] 無事動いて、MARY-OB基板(OLED Board)に以下の様に表示されました。 &ref(OLED_out.png); ** キーパッドを使う [#f8f64615] テンキーは入力デバイスとして便利です。そこでArduinoのKeypadクラスをlbedに移植してみることにしました。 ポイントは以下の2点です - インタフェースは変えずに、ピン番号指定の処理をDegitalOutに置き換える - pinMode, mille等のlbedにない、関数を追加する *** キーパッドとの接続 [#xacc1730] 今回は、スイッチサイエンスの [[3x4のマトリックス式ボタンパッド>http://www.switch-science.com/products/detail.php?product_id=1298]] を使用しました。 マトリックス式ボタンパッドのコネクタは、左から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 | &ref(keypad_setting.png); *** テストプログラムと動作確認 [#m951b06c] テストプログラムは、以下の様になります。 #pre{{ #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移植のポイント [#sf375270] Arduinoのライブラリをlbedに移植するときのポイントは、できるだけソース変更しないことです。 *** lbedに存在しない関数の追加 [#waeb9de9] lbedに存在しない関数の内、 - pinMode: 入力・出力を切り替える - mille: ミリ秒のカウントを返す pinModeは、DigitalOut.hに以下の様に追加しました。 #pre{{ // 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の修正 [#a2c60f18] Keypad内部で保持している行(rowPins)と列(columPins)をbyteポインタからDigitalOutの配列に変更しました。 ((今回は4 x 4 の固定としました)) #pre{{ //byte *rowPins; //byte *columnPins; DigitalOut rowPins[4]; DigitalOut columnPins[4]; }} そしてこれからが、今回の移植のメインイベントで、以下のdefine文で不足している関数を入れ替えました。 #pre{{ #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をセットします。 #pre{{ // 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); } }} *** 最新版のソース [#d00a002a] ** 最新版のソース [#d00a002a] Githubにlbedを登録しました。 - https://github.com/take-pwave/lbed ** コメント [#xddd351a] #vote(おもしろかった,そうでもない,わかりずらい) 皆様のご意見、ご希望をお待ちしております。 #comment_kcaptcha