クラスオブジェクトは、MWXライブラリであらかじめ定義されたオブジェクトで、TWENETを取り扱うthe_twelite
、ペリフェラルの利用のためのオブジェクトが定義されています。
各オブジェクトは.setup()
, .begin()
メソッドの呼び出しを行って初期化する必要があります。
(UART0
を利用するSerial
オブジェクトのみ初期化は必要ありません)
セクションの複数ページをまとめています。 印刷またはPDF形式で保存...
クラスオブジェクトは、MWXライブラリであらかじめ定義されたオブジェクトで、TWENETを取り扱うthe_twelite
、ペリフェラルの利用のためのオブジェクトが定義されています。
各オブジェクトは.setup()
, .begin()
メソッドの呼び出しを行って初期化する必要があります。
(UART0
を利用するSerial
オブジェクトのみ初期化は必要ありません)
the_twelite
オブジェクトは、TWENETの利用手続きをまとめたもので、無線の基本設定やスリープ等の手続きなど無線マイコンを操作するための手続きが含まれます。the_twelite
はsetup()
関数内で設定と開始the_twelite.begin()
を行います。setup()
以外では設定は行えません。
void setup() {
...
the_twelite
<< TWENET::appid(APP_ID)
<< TWENET::channel(CHANNEL)
<< TWENET::rx_when_idle();
...
the_twelite.begin();
}
上記の例では、アプリケーションIDの設定、通信チャネルの設定、受信回路の設定を行っています。
様々な手続きが含まれます。
// シリアル番号を得る
uint32_t u32hwser = the_twelite.get_hw_serial();
// チャネルを 11 に設定する
the_twelite.change_channel(11);
// 1秒のスリープを行う
the_twelite.sleep(1000);
// リセットを行う
the_twelite.reset_system();
また無線ネットワークを取り扱うクラスやボード対応をまとめたクラス、ユーザ記述のイベントドリブン処理を行うクラスを登録できるようになっています。このクラスを登録することにより、専用化した機能を手軽に利用できるようになります。これらのクラスを本解説中では「ビヘイビア」と呼称します。
void setup() {
/*** SETUP section */
// use PAL_AMB board support.
auto&& brd = the_twelite.board.use<PAL_AMB>();
...
// Register Network
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
nwk << NWK_SIMPLE::logical_id(u8ID);
上記の例では環境センサーパル<PAL_AMB>
と、シンプル中継ネットワーク<NWK_SIMPLE>
の2種類を登録しています。これらを登録することにより環境センサーパル上のセンサーなどハードウェアを簡易に取り扱うことが出来ます。また煩雑な無線パケットの取り扱いについて中継の処理や重複で届いたパケットの自動破棄などの機能を暗黙に持たせることが出来ます。
MWXライブラリには、ここで紹介したメソッド以外にも定義されています。
アクト記述には直接関係ないもの、設定しても有効に機能しないもの、内部的に使用されているものが含まれます。
<<
演算子 (設定)オブジェクトthe_twelite
の初期設定を行うために<<
演算子を用います。
以下に挙げる設定用のクラスオブジェクトを入力とし、設定をしなければデフォルト値が適用されます。
TWENET::appid(uint32_t id)
パラメータid
に指定したアプリケーションIDを設定します。これは必須指定です。
設定の読み出しは uint32_t the_twelite.get_appid()
で行います。
TWENET::channel(uint8_t ch)
パラメータch
に指定したチャネル番号(11
..26
)を設定します。
設定の読み出しはuint8_t the_twelite.get_channel()
で行います。
TWENET::tx_power(uint8_t pw)
パラメータpw
に指定した出力設定を(0
..3
)を設定します。デフォルトは(3:出力減衰無し)です。
設定値の読み出しはuint8_t the_twelite.get_tx_power()
で行います。
TWENET::rx_when_idle(uint8_t bEnable)
パラメータbEnable
が1
であれば常に受信回路を動作させ、他からの無線パケットを受信できるようになります。デフォルトは0
で、もっぱら送信専用となります。
設定値の読み出しはuint8_t the_twelite.get_rx_when_idle()
で行います。
TWENET::chmgr(uint8_t ch1 = 18, uint8_t ch2 = 0, uint8_t ch3 = 0)
チャネルマネージャを有効にします。チャネルを複数指定すると複数チャネルでの送受信を行います。ch2
,ch3
に0を指定すると、その指定は無効になります。
STG_STD
インタラクティブモードの設定値を反映します。
auto&& set = the_twelite.settings.use<STG_STD>();
...
set.reload(); // 設定値をロード
the_twelite << set; // インタラクティブモードの設定値を反映
反映される項目は以下です。
app_id
channel
tx_power
begin()
void begin()
事前に設定(<<
演算子参照)や、ビヘイビアの登録を済ませた後に実行します。通常はsetup()
関数内の一番最後に記述します。
the_twelite
設定完了setup()
関数が終了した後にも実行されます。多くの処理はsetup()
が終了した後に実行するようになっているため、ここでは初期化以外の処理を行わないようにしてください。
void setup() {
// use PAL_AMB board support.
auto&& brd = the_twelite.board.use<PAL_AMB>();
// settings
the_twelite
<< TWENET::appid(APP_ID)
<< TWENET::channel(CHANNEL)
<< TWENET::rx_when_idle();
// Register Network
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
nwk << NWK_SIMPLE::logical_id(u8ID);
// somo others
// begin the TWENET!
the_twelite.begin();
}
change_channel()
inline bool change_channel(uint8_t u8Channel)
チャネル設定を変更します。失敗時にはチャネルは変更されずfalse
を戻します。
get_channel_phys()
uint8_t get_channel_phys()
現在設定中のチャネル番号(11..26)を取得する。MAC層のAPIより取得します。
get_hw_serial()
inline uint32_t get_hw_serial()
モジュールのシリアル番号を取得します。
sleep()
inline void sleep(
uint32_t u32Periodms,
bool_t bPeriodic = true,
bool_t bRamOff = false,
uint8_t u8Device = TWENET::SLEEP_WAKETIMER_PRIMARY)
モジュールをスリープさせる。
パラメータ | 解説 |
---|---|
u32Periodms | スリープ時間[ms] |
bPeriodic | 前回の起床時間をもとに次の起床時間を再計算する。 ※次の起床タイミングが迫っているなどの理由で、現在のタイミングからになる場合もあります。 |
bRamoff | true に設定すると、RAMを保持しないスリープになる(起床後はwakeup() ではなくsetup() から再初期化する必要がある) |
u8Device | スリープに用いるウェイクアップタイマーの指定。TWENET::SLEEP_WAKETIMER_PRIMARY または TWENET::SLEEP_WAKETIMER_SECONDARY を指定する。 |
on_sleep()
メソッドが呼び出され、スリープ前の手続きを行います。スリープ復帰後は反対に on_wakeup()
メソッドにより復帰処理が行われます。is_wokeup_by_dio()
bool is_wokeup_by_dio(uint8_t port)
スリープからの復帰要因が指定したディジタルピンである場合にtrue
を返します。
is_wokeup_by_wktimer()
bool is_wokeup_by_wktimer()
スリープからの復帰要因がウェイクアップタイマーである場合にtrue
を返します。
reset_system()
inline void reset_system()
システムをリセットします。リセット後はsetup()
からの処理となります。
stop_watchdog()
inline void stop_watchdog()
ウォッチドッグタイマーを停止します。長時間のポーリング待ちを行うような場合はタイマーを停止します。
restart_watchdog()
inline void restart_watchdog()
ウォッチドッグタイマーを再開します。
twe_twelite
には3つのビヘイビアを登録でき、これらを格納する以下のクラスオブジェクトを定義されています。
network
: ネットワークを実装するビヘイビアです。通常は<NWK_SIMPLE>
を登録します。network2
: ネットワークを実装するビヘイビアです。最初に network
でペイロードのデータ構造などの判定により受理しなかったパケットを、別のネットワーク ビヘイビアで処理させたい場合に使用します。(参考: NWK_LAYERED と NWK_SIMPLEの併用)board
: ボード対応のビヘイビアです。ボード上の各デバイス利用手続きが付加されます。app
: ユーザアプリケーションを記述したビヘイビアです。割り込みやイベント記述、ステートマシンによる状態遷移によるふるまいの記述が可能です。また複数のアプリケーション記述を定義しておいて、起動時に全く振る舞いの違うアプリケーションを選択する記述が容易に行えます。settings
: 設定(インタラクティブモード)を実行するためのビヘイビアです。<SET_STD>
を登録します。use<B>()
// 例
auto&& brd = the_twelite.board.use<PAL_AMB>();
ビヘイビア<B>
を登録します。登録はsetup()
内で行います。戻り値は登録したビヘイビアに対応するオブジェクトの参照です。
登録後は登録時と同じ書式でオブジェクトの取得を行います。
グローバル変数としてビヘイビアのオブジェクトを宣言することを想定していません。利用都度use<B>()
を用いてください。
void loop() {
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
}
ただし、グローバル変数にオブジェクトのポインタを定義して以下のように記述することは可能です。(MWXライブラリでは原則としてポインタ型の利用を最小限にとどめ参照型を利用する方針ですので、下記のような記述は推奨しません)
NWK_SIMPLE *pNwk = nullptr;
setup() {
auto&& nwk = the_twelite.network.use<NWK_SIMPLE>();
pNwk = &nwk;
}
void transmit() {
if (auto&& pkt = pNwk->prepare_tx_packet()) {
...
}
}
the_twelite
には上述のboard
, network
, app
の3つのクラスオブジェクトが定義されていますが他に以下が定義されています。
tx_status
送信完了状態を通知する。
transmit_complete()
コールバックで管理します。is_complete()
bool is_complete(uint8_t cbid)
指定したIDのパケットが送信完了したときにtrue
を返す。
is_success()
bool is_success(uint8_t cbid)
指定したIDのパケットが送信完了し、かつ送信成功したときにtrue
を返す。
receiver
受信パケットを取得する。
the_twelite.receiver
は推奨されません。
従来loop()
内での記述を意図して the_twelite.receiver
による処理を行っていましたが、キューによる遅延処理である点で原理的に取りこぼしが発生し、また記述も煩雑になりがちである点から on_rx_packet()
を追加しました。
receive()
コールバックで取得します。read()
メソッドで得られる受信パケットデータは、続くパケットが受信処理時に上書きされる設計となっています。available
直後に読み出してなにか短い処理をする場合は問題になることはありませんが、原則として読み出し→アプリケーションが使うため必要なデータのコピー→loop()
の終了を速やかに行います。例えばloop()
中で長いdelay()
を行うと受信パケットの取りこぼしなどが発生します。available()
bool available()
まだ読み出していない受信パケットが存在する場合にtrue
を返す。
read()
packet_rx& read()
パケットを読み出します。
定数 | 種別 | 標準アプリでのピン名 |
---|---|---|
uint8_t PIN_ANALOGUE::A1 = 0 | ADC1ピン | AI1 |
uint8_t PIN_ANALOGUE::A2 = 1 | ADC2ピン | AI3 |
uint8_t PIN_ANALOGUE::A3 = 2``uint8_t PIN_ANALOGUE::D0 = 2 | ADC3ピン (DIO0) *1 | AI2 |
uint8_t PIN_ANALOGUE::A4 = 3``uint8_t PIN_ANALOGUE::D1 = 3 | ADC4ピン (DIO1) *1 | AI4 |
uint8_t PIN_ANALOGUE::VCC = 4 | Vcc 電源電圧 |
*1 ディジタル、アナログ共用のADC2/ADC3ピンは利用手続きと利用制限があります。
ADC開始前に利用するピンをプルアップ無しとします。これを実行しないと常にプルアップ電圧をADCで観察することになります。
pinMode(PIN_DIGITAL::DIO0, PIN_MODE::INPUT);
pinMode(PIN_DIGITAL::DIO1, PIN_MODE::INPUT);
通常の回路構成では、スリープ時には電流リークが発生します。 ソフトウェアの記述のみで回避することは出来ません。
スリープ時の電流リーク回避には、アナログ回路部分のGNDをFETスイッチなどで切り離し、スリープ中はフローティング状態にします。またスリープ前には入力かつプルアップ状態にピンを設定します。
setup()
void setup(
bool bWaitInit = false,
uint8_t kick_ev = E_AHI_DEVICE_TICK_TIMER,
void (*fp_on_finish)() = nullptr)
ADCの初期化を行います。setup()では、半導体内部のレギュレータの始動、周期実行するためのタイマーデバイスの指定、指定チャネルすべてのADCが終了したときに呼び出されるコールバック関数の指定します。
パラメータ | 解説 |
---|---|
bWaitInit | true を指定すると、半導体内部のレギュレータの初期化を待つ。 |
kick_ev | 周期実行に指定するタイマーデバイスを指定する。指定可能なデバイスは、以下の5種類で、初回以外は割り込みハンドラ内でADが開始される。E_AHI_DEVICE_TICK_TIMER (TickTimer)``E_AHI_DEVICE_TIMER0 .. 4 (Timer0 .. 4) |
fp_on_finish | 指定されたポートすべてのADCが終了後に、割り込みハンドラ内から呼び出されるコールバック関数。ADC計測値をFIFOキューなどに別途格納したい場合に利用する。 |
begin()
void begin(uint8_t bmPorts, uint8_t capt_tick = 1)
1番目のパラメータにはADCを行いたいポートを指定します。ポートの指定はピンの定義で述べたポート番号に対応するビットをセットしたビットマップになります。例えば PIN_ANALOGUE::A2
とPIN_ANALOGUE::VCC
の2つのピンの値を得たい場合は (1 <<PIN_ANALOGUE::A1 | 1<<PIN_ANALOGUE::VCC )
を指定します。pack_bits
を用いpack_bits(PIN_ANALOGUE::A1,PIN_ANALOGUE::VCC)
のように記述することもできます。
begin()
の呼び出し後、速やかに最初のADC処理が開始され、その終了割り込から次のピンの処理を開始します。すべての処理が終われば(指定されている場合)コールバック関数が呼び出されます。次のタイマー割り込みが発生まで待ってから新たなADC処理を開始します。
2番目のパラメータは、ACを開始するまでのタイマー割り込みの回数を指定します。例えばTickTimer
は1msごとに呼び出されますが、パラメータに16
を指定すれば 16msごとの処理になります。
void begin()
デフォルトのADCピン(PIN_ANALOGUE::A1
,PIN_ANALOGUE::A2
)を指定してADC処理を開始します。end()
では中断したADC処理を再開します。
end()
void end()
ADC処理を終了し、半導体内部のレギュレータを停止します。
available()
inline bool available()
ADCの値が取得後にtrue
になります。本関数により確認した後は次のADC完了まではfalse
です。
read()
, read_raw()
inline int16_t read(uint8_t s)
inline int16_t read_raw(uint8_t s)
ADC値を読み出します。パラメータには読み出したいADCピンを指定します。read()
はmVに変換した読み値、read_raw()
はADCの値(0..1023)を戻します。
read()
で読み出すことを推奨します。read_raw()
の値からmVに変換するには、特別な変換式を適用する必要があるためです。loop()
の処理中であっても値が更新されるためです。ADCの割り込みハンドラはsetup()
の呼び出し時にperiph_analogue::ADC_handler()
に設定されます。
半導体のペリフェラルライブラリで別途ハンドラを指定すると正常に動作しなくなります。
ADCがbegin()
により周期実行状態であれば、スリープ復帰後もADC処理を再開します。
スリープからの復帰時は setup(true)
を自動的に呼び出すため、E_AHI_DEVICE_TICK_TIMER
以外のタイマを使用する際などには wakeup()
内で明示的に再初期化してください。
例えば、次のコードを挿入すると、復帰時にE_AHI_DEVICE_TIMER0
を指定して再初期化します。
void wakeup()
{
Analogue.setup(true, E_AHI_DEVICE_TIMER0, adc_handler);
Analogue.begin(pack_bits(PIN_ANALOGUE::A1), 1);
}
setup()
void setup(uint8_t max_history);
パラメータのmax_history
は、begin()
で設定可能な参照回数の最大値です。ここではメモリーの確保と初期化を行います。
begin()
void begin(uint32_t bmPortMask,
uint8_t u8HistoryCount,
uint16_t tick_delta);
Buttons
の動作を開始します。1番目のパラメータbmPortMask
は監視対象のデジタル入力のビットマップを指定します。bit 0がDIO 0, … , bit N がDIO Nに対応します。複数指定することができます。2番目のu8HistoryCount
は値の確定をするのに必要な回数です。3番目のtick_delta
は値の確認を行う間隔をmsで指定します。
値の確定にはu8HistoryCount*tick_delta
[ms]かかることになります。例えばu8HistoryCount
=5, tick_delta
=4の場合は、状態の確定に最低約20msかかります。
確認はTickTimer
のイベントハンドラで行っています。割り込みハンドラではないので、処理等の遅延の影響を受けますが、メカ式ボタン等のチャタリング抑制には十分です。
end()
void end()
Buttons
の動作を終了します。
available()
inline bool available()
変化が検出されたときにtrue
を返します。read()
を実行するとクリアされます。
read()
bool read(uint32_t& u32port, uint32_t& u32changed)
availableになったとき呼び出します。u32port
は現在の入力DIOのビットマップ、u32changed
は変化が検出されたDIOのビットマップです。
Buttonsが動作していない場合はfalse
を返します。
Buttonsが動作を開始した時点では、DIOの入力状態は未確定です。値が確定した時点でavailableになります。このときread()
で読み出すビットマップのMSB(bit31)が1にセットされます。
動作確定を要するため、入力値が定常的に変化するポートを監視する目的では利用できません。
スリープ前にButtonsが稼働状態であれば、復帰後に再開します。再開後、初回確定を行います。
TWELITE 無線マイコンの内蔵EEPROMに対して読み書きを実行します。
内蔵EEPROMはアドレス0x000~0xEFFまでの3480バイトが利用可能です。
先頭部分は設定(インタラクティブモード)に利用されるため、併用する場合は後半のアドレスの利用を推奨します。設定(インタラクティブモード)でどの程度の領域を消費するかは、その実装に依存します。最小限度の設定であっても先頭から256バイトまでは利用されるため、それ以降の利用を推奨します。
read()
uint8_t read(uint16_t address)
EEPROMからaddress
に対応するデータを読み出します。
write()
void write(uint16_t address, uint8_t value)
EEPROMからaddress
に対してvalue
を書き込みます。
update()
void update(uint16_t address, uint8_t value)
write()
と同じく書き込みを行いますが、先にaddress
にあるデータを読み出してvalue
と違う場合のみ、書き込みを行います。EEPROMの書き換え寿命を考慮し、書換回数を減らしたいときに用います。
get_stream_helper()
auto&& get_stream_helper()
// 戻り値型は長くなるためauto&&と省略しています。
後述のmwx::stream
を用いた読み書きを行うために、ヘルパーオブジェクトを取得します。
mwx::stream
インタフェースを用いた入出力stream_helper
ヘルパーオブジェクトを経由して、mwx::stream
による演算子やメソッドを用います。mwx::stream
を用いるとuint16_t
やuint32_t
型といった整数型の読み書き、uint8_t
の固定長配列型の読み書き、format()
オブジェクトによる書式整形などが可能になります。
auto&& strm = EEPROM.get_stream_helper();
// ヘルパーオブジェクトの型名は長くなるためauto&&により解決しています。
このオブジェクトに対して<<
演算子などmwx::stream
で定義されたインタフェースを利用できます。
strm.seek(1024); // 1024バイト目に移動
strm << format("%08x", 12345678); // 12345678を16進数の8文字で記録
strm << uint32_t(0x12ab34cd); // 0x12ab34cd の4バイトを記録
uint8_t msg_hello[16] = "HELLO WORLD!";
strm << msg_hello; // バイト列 "HELLO WORLD!" を記録(終端なし)
// 結果
// 0400: 30 30 62 63 36 31 34 65 12 ab 34 cd 48 45 4c 4c
// 0 0 b c 6 1 4 e 0x12ab34cd H E L L
// 0410: 4f 20 57 4f 52 4c 44 21 00 00 00 00 ff ff ff ff
// O SP W O R L D !
.seek()
を用いてEEPROMのアドレスを1024に移動しています。
上記では8バイト文字列(00bc614e
)、4バイト整数(0x12ab34cd
)、16バイトバイト列(HELLO WORLD!...
)、1バイト終端文字を書き込んでいます。
strm.seek(1024);
uint8_t msg1[8];
strm >> msg1;
Serial << crlf << "MSG1=" << msg1;
// MSG1=00bc614e
uint32_t var1;
strm >> var1;
Serial << crlf << "VAR1=" << format("%08x", var1);
// VAR1=12ab34cd
uint8_t msg2[16]; // "HELLO WORLD!"の文字数
strm >> msg2;
Serial << crlf << "MSG2=" << msg2;
// MSG2=HELLO WORLD!
.seek()
を用いてEEPROMのアドレスを1024に移動しています。
先ほど書き出したデータ列を読み出します。順番に8バイト文字、4バイト整数、16バイト文字列を>>
演算子を用いて読み出します。
パルスカウンターは2系統あります。PC0はPulseCounter0
, PC1はPulseCounter1
に割り当てられます。また**PulseCounter
はPulseCounter1
**の別名です。
begin()
void begin(uint16_t refct = 0,
E_PIN_INT_MODE edge = PIN_INT_MODE::FALLING,
uint8_t debounce = 0)
オブジェクトを初期化し、計数を開始します。1番目のパラメータrefct
は割り込みやavailable判定の基準となるカウント数です。この数を超えたときにアプリケーションに報告されます。またrefct
には0を指定することもできます。この場合は、スリープ起床要因にはなりません。
2番目のパラメータedge
は割り込みが立ち会がり(PIN_INT_MODE::RISING
)か立下り(PIN_INT_MODE::FALLING
)を指定します。
3番目のdebounce
は、0,1,2,3の値をとります。1,2,3の設定はノイズの影響を小さくするため値の変化の検出に連続した同じ値を要する設定です。
設定 | 連続サンプル数 | 最大検出周波数 |
---|---|---|
0 | - | 100Khz |
1 | 2 | 3.7Khz |
2 | 4 | 2.2Khz |
3 | 8 | 1.2Khz |
end()
void end()
検出を中止します。
available()
inline bool available()
指定カウント数(begin()
のrefct
)が0の場合は、カウントが1以上でtrue
を返します。
指定カウント数(begin()
のrefct
)が1以上の場合は、検出回数が指定カウント数を超えた場合にtrue
となります。
read()
uint16_t read()
カウント値を読み出します。読み出し後にカウント値を0にリセットします。
mwx::stream
を実装し TWELITE の UART0 で入出力します。Serial
オブジェクトはシステム起動時に UART0, 115200 bps で初期化され、ライブラリ内で初期化処理が行われます。ユーザコード上は、setup()
から利用できます。Serial1
オブジェクトは、ライブラリ内で用意されていますが、初期化処理は行っていません。UART1を有効化するためには、必要な初期化手続き Serial1.setup(), Serial1.begin()
を行ってください。setup(), wakeup()
やスリープ直前の flush
処理で、出力が不安定になる場合があります。setup()
void setup(uint16_t buf_tx, uint16_t buf_rx)
オブジェクトの初期化を行う。
Serial
(UART0) は ライブラリ内で setup()
の呼び出しが自動で行われます。ユーザによる呼び出しを行う必要はありません。
また、Serial
(UART0) のバッファサイズは、コンパイル時に決定されます。マクロ MWX_SER_TX_BUFF
(未指定時は 768), MWX_SER_RX_BUFF
(未指定時 256) により変更できます。
パラメータ | 解説 |
---|---|
buf_tx | TX用のFIFOバッファサイズ |
buf_rx | RX用のFIFOバッファサイズ |
begin()
void begin(unsigned long speed = 115200, uint8_t config = 0x06)
ハードウェアの初期化を行う。
Serial
(UART0) は ライブラリ内で begin()
の呼び出しが自動で行われます。ユーザによる呼び出しを行う必要はありません。パラメータ | 解説 |
---|---|
speed | UART のボーレートを指定する。 |
config | serial_jen::E_CONF::PORT_ALT ビットを指定したときは、UART1をDIO14,15で初期化します。指定しない場合はDIO11(TxD),9(RxD)で初期化します。 |
指定したボーレートの下2桁の数値は0に丸めて処理します。またハードウェアの制限により指定したボーレートより誤差が生じます。
ボーレートの計算には除算が発生し計算時間がかかる場合があります。9600,38400,115200を指定する場合は、除算をせずに計算を行います。処理の詳細は、constexpr uint16_t _serial_get_hect_baud(uint32_t baud)
を参照してください。
end()
(未実装)ハードウェアの使用を停止する。
get_tsFile()
TWE_tsFILE* get_tsFile();
Cライブラリで利用する TWE_tsFILE*
形式での構造体を得る。
_sSerial
構造体が定義されています。初期化時(begin()
)にヒープから内部で使用するバッファ領域を確保するmwx::serial_parser<mwx::alloc_heap<uint8_t>>
型として定義されています。
詳細はクラス serparser
を参照してください。
定数 | 意味 |
---|---|
const uint8_t SPI_CONF::MSBFIRST | MSB を先頭ビットにする |
const uint8_t SPI_CONF::LSBFIRST | LSB を先頭ビットにする |
const uint8_t SPI_CONF::SPI_MODE0 | SPI MODE 0 に設定する |
const uint8_t SPI_CONF::SPI_MODE1 | SPI MODE 1 に設定する |
const uint8_t SPI_CONF::SPI_MODE2 | SPI MODE 2 に設定する |
const uint8_t SPI_CONF::SPI_MODE3 | SPI MODE 3 に設定する |
SPIバスの利用手続きはbegin()
メソッドによります。
begin()
void begin(uint8_t slave_select, SPISettings settings)
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
ハードウェアの初期化を行います。
パラメータ | 解説 |
---|---|
slave_select | 対象のペリフェラルのセレクトピンを指定する。0 : DIO19``1 : DIO0 (DIO 19 は予約されます)``2 : DIO1 (DIO 0,19 は予約されます) |
settings | SPIのバス設定を指定します。clock [hz]でSPIバスの周波数を指定します。指定した周波数に近いディバイザが選択されます。16Mhzまたは16Mhzを偶数で割った値になります。bitOrder はSPI_CONF::MSBFIRST かSPI_CONF::LSBFIRST を指定します。dataMode はSPI_CONF::SPIMODE0..3 を指定します。 |
void setup() {
...
SPI.begin(0, SPISettings(2000000, SPI_CONF::MSBFIRST, SPI_CONF::SPI_MODE3));
...
}
void wakeip() {
...
SPI.begin(0, SPISettings(2000000, SPI_CONF::MSBFIRST, SPI_CONF::SPI_MODE3));
...
}
end()
void end()
SPIのハードウェアの利用を終了します。
読み書きの手続きは、以下の2種類あります。いずれかを選択して利用します。
beginTransaction(), endTransaction(), transfer(), transfer16(), transfer32()
transceiver
次のサンプルコードは、Analog Devices の温度センサ ADT7310 から1秒おきに温度を取得し、シリアルポートへ出力します。
#include <TWELITE>
#include <SM_SIMPLE>
enum class STATE : uint8_t {
INTERACTIVE = 255,
INIT = 0,
INIT_WAIT,
SENSOR,
LOOP_WAIT
};
SM_SIMPLE<STATE> step;
struct SensorData {
uint8_t highByte;
uint8_t lowByte;
uint16_t rawValue;
int32_t tempValue16th;
div_result_i32 temperature;
} sensorData;
void setup() {
step.setup(); // 状態マシンの初期化
}
void loop() {
do {
switch (step.state()) {
case STATE::INIT: // 初期状態
SPI.begin(0 /* DIO19をチップセレクトとして使用 */
, { 400000UL /* クロック周波数 */
, SPI_CONF::MSBFIRST
, SPI_CONF::SPI_MODE3
}
);
// ソフトウェアリセット
SPI.beginTransaction();
for (int i = 0; i < 4; i++) {
SPI.transfer(0xFF);
}
SPI.endTransaction();
// Continuous Readモード開始
SPI.beginTransaction();
SPI.transfer(0x54);
SPI.endTransaction();
step.set_timeout(300); // 待機時間の設定
step.next(STATE::INIT_WAIT);
break;
case STATE::INIT_WAIT: // 待機
if (step.is_timeout()) {
step.next(STATE::SENSOR);
}
break;
case STATE::SENSOR: // センサーデータの読み取り
SPI.beginTransaction();
sensorData.highByte = SPI.transfer(0x00); // ダミーデータを送信してクロック信号を生成
sensorData.lowByte = SPI.transfer(0x00); // ダミーデータを送信してクロック信号を生成
SPI.endTransaction();
sensorData.rawValue = (((uint16_t)sensorData.highByte << 8) | sensorData.lowByte) >> 3;
if (sensorData.rawValue & 0x1000) {
sensorData.tempValue16th = int32_t(sensorData.rawValue) - 0x2000;
} else {
sensorData.tempValue16th = sensorData.rawValue;
}
// div100()を使用して温度計算
sensorData.temperature = div100((sensorData.tempValue16th * 100) / 16);
// 結果をシリアル出力
Serial << crlf << sensorData.temperature.format() << "°C";
step.set_timeout(1000); // 次のキャプチャまで待機
step.next(STATE::LOOP_WAIT);
break;
case STATE::LOOP_WAIT: // 待機
if (step.is_timeout()) {
step.next(STATE::SENSOR);
}
break;
default:
break;
}
} while (step.b_more_loop());
}
ここでは、メンバ関数版のインタフェースを利用しています。
begin()
メソッドによりハードウェアの初期化を行った後、beginTransaction()
によりバスの読み書きができるようになります。beginTransaction()
を実行するとSPIのセレクトピンが選択されます。読み書きはtransfer()
関数を用います。SPIは読み出しと書き込みを同時に実行します。beginTransaction()
void beginTransaction()
void beginTransaction(SPISettings settings)
バスの利用開始を行います。SPIのセレクトピンをセットします。
settings
パラメータを与えて呼び出した場合は、バスの設定を行います。
endTransaction()
void endTransaction()
バスの利用を終了します。SPIのセレクトピンを解除します。
transfer()
, transfer16()
, transfer32()
inline uint8_t transfer(uint8_t data)
inline uint16_t transfer16(uint16_t data)
inline uint32_t transfer32(uint32_t data)
バスの読み書きを行います。trasnfer()
は8bit、transfer16()
は16bit、transfer32()
は32bitの転送を行います。
transceiver
を生成することでバスの利用を開始し、オブジェクトを破棄することでバスの利用を終了します。if文の判定式内でオブジェクトの生成を行うことで、オブジェクトの有効期間はif節内のスコープに限定され、if節を抜けた時点でオブジェクトは破棄され、その時点でバスの利用終了の手続きを行います。
uint8_t c;
if (auto&& trs = SPI.get_rwer()) { // オブジェクトの生成とデバイスの通信判定
// このスコープ(波かっこ)内が trs の有効期間。
trs << 0x00; // 0x00 を mwx::stream インタフェースで書き出し
trs >> c; // 読み出したデータをcに格納。
}
// if 節を抜けるところで wrt は破棄され、バスの利用終了
また、読み書きオブジェクトは、mwx::stream
インタフェースを実装しているため<<
演算子などを利用できます。
mwx::stream
インタフェースによる読み書き手続きを統一します読み込み処理とその終了手続きをスコープ内 if() { ... }
で行うためのヘルパークラスを用いた読み込み方法。
inline uint8_t _spi_single_op(uint8_t cmd, uint8_t arg) {
uint8_t d0, d1;
if (auto&& x = SPI.get_rwer()) {
d0 = x.transfer(cmd); (void)d0;
d1 = x.transfer(arg);
// (x << (cmd)) >> d0;
// (x << (arg)) >> d1;
}
return d1;
}
上記では get_rwer()
メソッドにより生成された x
オブジェクトを用いて1バイトずつ読み書きを行っています。
if(...)
内で x
オブジェクトを生成します。同時にSPIバスのセレクトピンをセットします。(型は、型推論によるユニバーサル参照 auto&&
で解決しています。)x
オブジェクトには operator bool ()
が定義されており、判定式の評価として利用される。SPIバスの場合は常に true
となる。x
オブジェクトには uint8_t transfer(uint8_t)
メソッドが定義されていて、これを呼び出すことでSPIに対して8bitの読み書き転送を行。if() { ... }
スコープの末尾で x
のデストラクタが呼び出され、SPIバスのセレクトピンを解除します。get_rwer()
periph_spi::transceiver get_rwer()
SPIバスの読み書きに用いるワーカーオブジェクトを取得します。
transfer()
, transfer16()
, transfer32()
uint8_t transfer(uint8_t val)
uint16_t transfer16(uint16_t val)
uint32_t transfer32(uint32_t val)
それぞれ8bit,16bit,32bitの転送を行い、読み取り結果を書き込んだデータ幅と同じデータ幅で返す。
<<
演算子
operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c)
operator << (uint32_t c)
int
型,uint8_t
型は8bitの転送を行います。
uint16_t
型、uint32_t
型は、それぞれ16bitの転送、32bitの転送を行います。
転送結果は最大16バイトの内部FIFOキューに格納され >>
演算子により読み出します。バッファが大きくないので、転送都度読み出すことを想定します。
>>
演算子
operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)
null_stream(size_t i = 1)
operator >> (null_stream&& p)
直前の転送と同じデータ幅の変数を指定します。
読み出した結果が不要の場合はnull_stream()
オブジェクトを使用します。i
で指定したデータバイト分だけ読み飛ばします。
TickTimer
はTWENETの内部制御用に利用され、暗黙に実行されています。タイマーの周期は1msです。loop()
中でTickTimerイベントにより1msごとの処理を記述する目的でavailable()
メソッドのみを定義しています。必ず1ms刻みでavailableになる訳ではない点に注意してください。
ユーザプログラムの記述内容や、システム内部の割り込み処理などが要因で、大きな遅延が発生することもあり、イベントが飛ばされるような場合もあります。
void loop() {
if (TickTimer.available()) {
if ((millis() & 0x3FF) == 0) { // これは処理されない場合がある
Serial << '*';
}
}
}
available()
inline bool available()
TickTimer
割り込み発生後にセットされ、その直後のloop()
でtrue
になります。loop()
終了後にクリアされます。
組み込みオブジェクト名は Timer0..4
ですが、このページでは TimerX
と記載します。
setup()
void setup()
タイマーを初期化します。この呼び出しにより必要なメモリ領域の確保を行います。
begin()
void begin(uint16_t u16Hz, bool b_sw_int = true, bool b_pwm_out = false)
タイマーを開始します。1番目のパラメータは、タイマーの周期でHzで指定します。2番目のパラメータをtrue
にするとソフトウェア割り込みが有効になります。3番目のパラメータをtrue
にするとPWM出力を有効にします。
change_hz()
で周波数を変更することが出来ます。change_hz()
ではbegin()
の指定より細かい指定が可能です。
change_duty()
でPWM出力のデューティー比を変更できます。
割り込みハンドラの処理を記述するには、アプリケーションビヘイビアの定義が必要です。
end()
void end()
タイマーの動作を停止します。
available()
inline bool available()
タイマー割り込みが発生した直後のloop()
でtrue
になり、loop()
が終了すればfalse
になります。
change_duty()
void change_duty(uint16_t duty, uint16_t duty_max = 1024)
デューティー比の設定です。1番目のパラメータにデューティ比を指定します(小さい値を指定すると波形の平均はGNDレベルに近づき、大きい値を指定するとVccレベルに近づく)。2番目のパラメータはデューティ比の最大値を指定します。
duty_max
は1024,4096,16384
のいずれかの指定を推奨します。
内部でのカウント値の計算に除算が発生しますが、これら3つに限りビットシフトによる演算を行っていますが、これ以外の値では計算量の大きい除算処理が実行されます。
change_hz()
void change_hz(uint16_t hz, uint16_t mil = 0)
タイマーの周波数を設定します。2番目のパラメータは周波数の小数点3桁分の値を整数で指定します。例えば 10.4 Hz としたい場合は hz=10, mil=400
と指定します。
using TwoWire = mwx::periph_twowire<MWX_TWOWIRE_RCVBUFF>;
mwx::periph_wire<MWX_TWOWIRE_RCVBUFF>
はTwoWire
として参照可能です。
以下の定義型で引数や戻り値の型を記載します。
typedef uint8_t size_type;
typedef uint8_t value_type;
write(), writer::operator() ()
には、本解説以外にもいくつか引数が定義されてます。
uint8_t cmds[]={11,12};
...
Wire.write(cmds);
initializer_list<>
型Wire.write({11,12})
Wire
インスタンスの生成ライブラリ内でインスタンスの生成と必要な初期化は行われます。ユーザコードでは Wire.begin()
を呼び出すことで利用可能となります。
requestFrom()
メソッドを用いる場合、データを一時保管するための FIFO キューのサイズを指定できます。コンパイル時にマクロMWX_TWOWIRE_BUFF
に必要なバイト数を指定してコンパイルする。デフォルトは 32 バイトです。
例:
-DMWX_TWOWIRE_BUFF=16
begin()
void begin(
const size_type u8mode = WIRE_100KHZ,
bool b_portalt = false)
ハードウェアの初期化を行います。
パラメータ | 解説 |
---|---|
u8mode | バス周波数を指定する。デフォルトは100Khz(WIRE_CONF::WIRE_100KHZ )周波数は WIRE_CONF::WIRE_??KHZ で指定し?? には50 ,66 ,80 ,100 ,133 ,160 ,200 ,266 ,320 ,400 を指定できる。 |
b_portalt | ハードウェアのピン割り当てを変更する。 |
void setup() {
...
Wire.begin();
...
}
void wakeup() {
...
Wire.begin();
...
}
読み書きの手続きは、以下の2種類あります。いずれかを選択して利用します。
requestFrom(), beginTransmission(), endTransmission(), write()
reader, writer
bool probe(uint8_t address)
address
で指定したデバイスが応答するかを確認します。デバイスが存在する場合は true
が戻ります。
setClock()
void setClock(uint32_t speed)
本来はバス周波数を変更するための手続きですが、何も処理をしません。
メンバ関数を利用した方法は、抽象度が比較的低く、C言語ライブラリで提供されるような一般的なAPI体系に倣っています。二線シリアルバスの操作手続きがより直感的です。
ただしバスの利用の開始と終了を明示的に意識して記述する必要があります。
requestFrom()
size_type requestFrom(
uint8_t u8address,
size_type length,
bool b_send_stop = true)
指定バイト数分を一括で読み出します。読みだした結果はキューに保存されるため、直後にキューが空になるまで .read()
メソッドを呼び出すようにしてください。
パラメータ | 解説 |
---|---|
u8address | 読み出し対象のI2Cアドレス |
length | 読み出しバイト数 |
b_send_stop=true | true の時、読み込み終了時にSTOP ビットを設定する。 |
戻り値型 size_type | 読み出したバイト数。 0 は読み出しの失敗。 |
int len = Wire.requestFrom(0x70, 6);
for (int i = 0; i < 6; i++) {
if (Wire.available()) {
au8data[i] = Wire.read();
Serial.print(buff[i], HEX);
}
}
// skip the rest (just in case)
// while (Wire.available()) Wire.read(); // normally, not necessary.
書き出し処理は、beginTransmission()
を実行後、write()
メソッドにより行います。一連の書き出しが終了したら endTranmission()
を呼びます。
#define DEV_ADDR (0x70)
const uint8_t msg[2] =
{SHTC3_SOFT_RST_H, SHTC3_SOFT_RST_L};
Wire.beginTransmission(DEV_ADDR);
Wire.write(msg, sizeof(msg));
Wire.endTransmission();
beginTransmission()
void beginTransmission(uint8_t address)
書き出しの転送を初期化する。書き出し処理終了後、速やかに endTransmission()
を呼び出す。
パラメータ | 解説 |
---|---|
u8address | 書き出し対象のI2Cアドレス |
write(value)
size_type write(const value_type value)
1バイトの書き出しを行う。
パラメータ | 解説 |
---|---|
value | 書き込むバイト |
戻り値 size_type | 書き込んだバイト数。0 はエラー。 |
write(*value, quantity)
size_type write(
const value_type* value,
size_type quantity)
バイト列の書き出しを行います。
パラメータ | 解説 |
---|---|
*value | 書き込むバイト列 |
size_type | バイト数 |
戻り値 size_type | 書き込んだバイト数。0はエラー。 |
endTransmission()
uint8_t endTransmission(bool sendStop = true)
書き出しの終了処理を行います。
パラメータ | 解説 |
---|---|
sendStop = true | STOPビットを発行します。 |
戻り値 uint8_t | 0: 成功 4: 失敗 |
reader, writer
を生成することがバスの利用開始となり、オブジェクトを破棄するとバス利用の終了手続きを行います。if文の判定式内でオブジェクトの生成を行うことで、オブジェクトの有効期間はif節内のスコープに限定され、if節を抜けた時点でオブジェクトは破棄され、その時点でバスの利用終了の手続きを行います。
if (auto&& wrt = Wire.get_writer(...)) { // オブジェクトの生成とデバイスの通信判定
// このスコープ(波かっこ)内が wrt の有効期間。
wrt << 0x00; // 0x00 を mwx::stream インタフェースで書き出し
}
// if 節を抜けるところで wrt は破棄され、バスの利用終了
また読み書きオブジェクトはmwx::stream
インタフェースを実装しているため<<
演算子などを利用することができます。
mwx::stream
インタフェースによる読み書き手続きの統一読み込み処理とその終了手続きをスコープ内 if() { ... }
で行うためのヘルパークラスを用いた読み込み方法です。
const uint8_t DEV_ADDR = 0x70;
uint8_t au8data[6];
if (auto&& rdr = Wire.get_reader(DEV_ADDR, 6)) {
for (auto&& c: au8data) {
c = rdr();
}
}
// same above
uint16_t u16temp, u16humd;
uint8_t u8temp_csum, u8humd_csum;
if (auto&& rdr = Wire.get_reader(SHTC3_ADDRESS, 6)) {
rdr >> u16temp;
rdr >> u8temp_csum;
rdr >> u16humd;
rdr >> u8humd_csum;
}
上記では get_readr()
メソッドにより生成された rdr
オブジェクトを用いて1バイトずつ読み出しします。 メソッドのパラメータには読み込みたい二線シリアル ID を指定します。
if(...)
内で rdr
オブジェクトを生成。(型は、型推論によるユニバーサル参照 auto&&
で解決しています。)rdr
オブジェクトには operator bool ()
が定義されており、判定式の評価として利用される。指定された ID により通信が可能であれば true
となる。rdr
オブジェクトには int operator () (void)
演算子が定義されていて、これを呼び出すことで2線シリアルバスから1バイトのデータを読み出す。読み込みに失敗したときは -1
が戻り、成功した場合は読み込んだバイト値が戻る。if() { ... }
スコープの末尾で rdr
のデストラクタが呼び出され、二線シリアルバスの STOP
を行う。get_reader(addr, read_count=0)
periphe_wire::reader
get_reader(uint8_t addr, uint8_t read_count = 0)
I2C 読み出しに用いるワーカーオブジェクトを取得します。
パラメータ | 解説 |
---|---|
addr | 読み込み用のI2Cアドレス |
read_count | 読み出しバイト数(この値を指定すると最後の転送で STOP ビットを発行する)。0を指定した場合は STOP ビットなしとなる(デバイスによっては動作するものもあります) |
writer
)書き出し処理とその終了手続きをスコープ内 if() { ... }
で行うためのヘルパークラスを用いた読み込み方法です。
const uint8_t DEV_ADDR = 0x70;
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
wrt(SHTC3_TRIG_H);
wrt(SHTC3_TRIG_L);
}
// same above
if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
wrt << SHTC3_TRIG_H; // int type is handled as uint8_t
wrt << SHTC3_TRIG_L;
}
上記では get_writer()
メソッドにより生成された wrt
オブジェクトを用いて1バイトずつ書き出す。 メソッドのパラメータには読み出したい二線シリアル ID を指定します。
if(...)
内で wrt
オブジェクトを生成する。(型名は長くなるため auto で解決)wrt
オブジェクトには operator bool ()
が定義されており、判定式の評価として利用される。指定された ID により通信が可能であれば true
となる。wrt
オブジェクトには int operator () (void)
演算子が定義されていて、これを呼び出すことで2線シリアルバスに1バイトのデータを書き出しす。失敗したときは -1
が戻り、成功した場合は書き込んだバイト値が戻る。if() { ... }
スコープの末尾で wrt
のデストラクタが呼び出され、二線シリアルバスの STOP
を行う。get_writer()
periph_wire::writer
get_writer(uint8_t addr)
I2C書き出しに用いるワーカーオブジェクトを取得します。
パラメータ | 解説 |
---|---|
addr | 書き出し用のI2Cアドレス |
<<
演算子
operator << (int c)
operator << (uint8_t c)
operator << (uint16_t c)
operator << (uint32_t c)
int
型,uint8_t
型は8bitの転送を行います。データ並び順はビッグエンディアン形式(上位バイトが先に転送される)です。
()
演算子
operator() (uint8_t val)
operator() (int val)
1バイト書き出す。
>>
演算子
operator >> (uint8_t& c)
operator >> (uint16_t& c)
operator >> (uint32_t& c)
operator >> (uint8_t(&c)[N]) // Nバイトの固定配列
それぞれのデータ型のサイズ分だけ読み出します。データ並び順はビッグエンディアン形式(先に転送されたほうが上位バイトに格納される)です。
()
演算子
int operator() (bool b_stop = false)
//例
uint8_t dat[6];
if (auto&& rdr = Wire.get_reader(0x70)) {
for(uint8_t& x : dat) {
x = rdr();
}
}
1バイト読み出します。エラーがある場合は-1を戻し、正常時は読み出したバイト値を戻します。
b_stop
をtrue
にすると、その読み出しにおいてSTOPビットを発行します。
以下の例は、環境センサーパルの温湿度センサーSHTC3の計測例です。
Wire.begin();
// reset (may not necessary...)
if (auto&& wrt = Wire.get_writer(0x70)) {
wrt << 0x80; // SHTC3_SOFT_RST_H
wrt << 0x05; // SHTC3_SOFT_RST_L
}
delay(5); // wait some
// start read
if (auto&& wrt = Wire.get_writer(0x70)) {
wrt << 0x60; // SHTC3_TRIG_H
wrt << 0x9C; // SHTC3_TRIG_L
}
delay(10); // wait some
// read result
uint16_t u16temp, u16humd;
uint8_t u8temp_csum, u8humd_csum;
if (auto&& rdr = Wire.get_reader(0x70, 6)) {
rdr >> u16temp;
rdr >> u8temp_csum;
rdr >> u16humd;
rdr >> u8humd_csum;
}
// checksum 0x31, init=0xFF
if (CRC8_u8CalcU16(u16temp, 0xff) != u8temp_csum) {
Serial << format("{SHTC3 T CKSUM %x}", u8temp_csum); }
if (CRC8_u8CalcU16(u16humd, 0xff) != u8humd_csum) {
Serial << format("{SHTC3 H CKSUM %x}", u8humd_csum); }
// calc temp/humid (degC x 100, % x 100)
int16_t i16Temp = (int16_t)(-4500 + ((17500 * int32_t(u16temp)) >> 16));
int16_t i16Humd = (int16_t)((int32_t(u16humd) * 10000) >> 16);
Serial << "temp=" << int(i16Temp)
<< ",humid=" << int(i16Humd) << mwx::crlf;