- 追加された行はこの色です。
- 削除された行はこの色です。
[[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よりは良いのではないかと思います。))
** コメント [#xddd351a]
#vote(おもしろかった,そうでもない,わかりずらい)
皆様のご意見、ご希望をお待ちしております。
#comment_kcaptcha