32bit型をラップしたAPI戻り値クラス。MSB(bit31)は失敗、成功のフラグ。bit0..30は戻り値を格納するために使用します。
class MWX_APIRET {
uint32_t _code;
public:
MWX_APIRET() : _code(0) {}
MWX_APIRET(bool b) {
_code = b ? 0x80000000 : 0;
}
MWX_APIRET(bool b, uint32_t val) {
_code = (b ? 0x80000000 : 0) + (val & 0x7fffffff);
}
inline bool is_success() const { return ((_code & 0x80000000) != 0); }
inline bool is_fail() const { return ((_code & 0x80000000) == 0); }
inline uint32_t get_value() const { return _code & 0x7fffffff; }
inline operator uint32_t() const { return get_value(); }
inline operator bool() const { return is_success(); }
};
MWX_APIRET()
MWX_APIRET(bool b)
MWX_APIRET(bool b, uint32_t val)
デフォルトコンストラクタはfalse
,0
の組み合わせで構築します。
またbool
型とuint32_t
型をパラメータとする明示的な構築も可能です。
bool
型のコンストラクタを実装しているため、以下のようにtrue
/false
を用いることができます。
MWX_APIRET myfunc() {
if (...) return true;
else false;
}
inline bool is_success()
inline operator bool()
MSBに1
がセットされていればtrue
を返す。
inline bool is_fail()
MSBが0
の場合にtrue
を返す。
inline uint32_t get_value()
inline operator uint32_t()
bit0..30の値部を取得する。
コンテナクラス(smplbuf
, smplque
)のテンプレート引数として指定し、内部で利用するメモリの確保または領域指定します。
このクラスはユーザコードから直接呼び出すものではありませんが、内部的にコンテナの宣言に用いられています。
内容
alloc_attach<T>
すでにあるバッファを指定する
alloc_local<T, int N>
Nバイトのバッファを内部に静的確保する
alloc_heap<T>
指定したサイズをヒープに確保する
alloc_attach
やalloc_heap
ではメモリ確保クラスに応じた初期化メソッド (begin()
)を実行する必要があります。
void attach(T* p, int n) // alloc_attach
void init_local() // alloc_local
void init_heap(int n) // alloc_heap
バッファーp
・サイズn
で初期化します。
uint16_t alloc_size()
バッファのサイズを返す。
想定したallocクラスと違うメソッド呼び出し記述に対して、static_assertのように、コンパイルエラーを発生させるためのメソッドです。
3軸の加速度センサーの値を格納するための構造体ですが、コンテナクラスに格納したときの利便性を上げるための手続きを追加しています。
struct axis_xyzt {
int16_t x;
int16_t y;
int16_t z;
uint16_t t;
};
/*戻り型は長いテンプレート型名なのでauto&&と記載します*/
auto&& get_axis_x_iter(Iter p)
auto&& get_axis_y_iter(Iter p)
auto&& get_axis_z_iter(Iter p)
axis_xyzt
を格納したコンテナクラスのイテレータをパラメータとして、X, Y, Z 軸のいずれかの要素にアクセスするイテレータを生成します。
以下の例では、buf.begin()
, buf.end()
をX軸用のイテレータとしてアルゴリズムstd::minmax_element
に用いています。
#include <algorithm>
void myfunc() {
// コンテナクラス
smplbuf_local<axis_xyzt, 10> buf;
// テスト用にデータを投入
buf[0] = { 1, 2, 3, 4 };
buf[1] = { 2, 3, 4, 5 };
...
// 最大、最小値を得るアルゴリズム
auto&& minmax = std::minmax_element(
get_axis_x_iter(buf.begin()),
get_axis_x_iter(buf.end()));
Serial << "min=" << int(*minmax.first)
<< ",max=" << int(*minmax.second) << mwx::crlf;
}
/*戻り型は長いテンプレート型名なのでauto&&と記載します*/
auto&& get_axis_x(T& c)
auto&& get_axis_y(T& c)
auto&& get_axis_z(T& c)
axis_xyzt
を格納したコンテナクラスのXYZ軸のいずれかの軸を取り出した仮想的なコンテナクラスを生成する関数です。この生成したクラスにはbegin()
とend()
メソッドのみ実装されています。このbegin()
とend()
メソッドで取得できるイテレータは前節get_axis_{x,y,z}_iter()のイテレータと同じものになります。
#include <algorithm>
void myfunc() {
// コンテナクラス
smplbuf_local<axis_xyzt, 10> buf;
// テスト用にデータを投入
buf[0] = { 1, 2, 3, 4 };
buf[1] = { 2, 3, 4, 5 };
...
// キューの中の X 軸を取り出す
auto&& vx = get_axis_x(que);
// 範囲for文の利用
for (auto&& e : vx) { Serial << int(e) << ','; }
// 最大、最小値を得るアルゴリズム
auto&& minmax = std::minmax_element(
vx.begin(), vx.end());
Serial << "min=" << int(*minmax.first)
<< ",max=" << int(*minmax.second) << mwx::crlf;
}
このクラスはTWENETのtsRxDataApp
構造体のラッパークラスです。
このクラスオブジェクトは、ビヘイビアのコールバック関数またはthe_twelite.receiver.read()
により取得できます。
packet_rx
では、特にパケットのデータペイロードをsmplbuf
コンテナで取り扱えるようにし、expand_bytes()
などのユーティリティ関数によりペイロードの解釈記述を簡素化しています。
smplbuf_u8_attach& get_payload()
パケットのデータペイロードを取得する。
const tsRxDataApp* get_psRxDataApp()
TWENET Cライブラリの受信構造体を得る。
uint8_t get_length()
ペイロードのデータ長を返す。.get_payload().size()
と同じ値になる。
uint8_t get_lqi()
LQI値 (Link Quality Indicator)を得る。
uint32_t get_addr_src_long()
uint8_t get_addr_src_lid()
送信元のアドレスを得る。
get_addr_src_long()
は送信元のシリアル番号で、MSB(bit31)が必ず1になります。
get_addr_src_lid()
は送信元の論理IDで0x00
-0xFE
までの値をとります(<NWK_SIMPLE>
で指定する論理IDです)。
uint32_t get_addr_dst()
宛先アドレスを得ます。
宛先アドレスは、送信元で指定され、宛先の種別によって値の範囲が変わります。
値
解説
MSB(bit31)がセットされている
宛先としてシリアル番号を指定しています。
0x00
-0xFF
宛先として論理ID(8bit)が指定されています。
このクラスはTWENET CライブラリのtsTxDataApp
構造体のラッパクラスで、このクラスをベースとした派生クラスのオブジェクトをネットワークビヘイビアより取得して利用します。
if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
pkt << tx_addr(0x00)
<< tx_retry(0x1)
<< tx_packet_delay(0,50,10);
pack_bytes(pkt.get_payload()
, make_pair("APP1", 4)
, uint8_t(u8DI_BM)
);
pkt.transmit();
}
ネットワークビヘイビアの .prepare_tx_packet()
によって行います。
if (auto&& pkt = the_twelite.network.use<NWK_SIMPLE>().prepare_tx_packet()) {
...
}
上記の例ではthe_twelite.network.use<NWK_SIMPLE>()
によってネットワークビヘイビアのオブジェクトを取り出します。このオブジェクトの.prepare_tx_packet()
によってオブジェクトpkt
が生成されます。型名はauto&&で推論されていますがpacket_tx
の派生クラスになります。
このpkt
オブジェクトは、まず、()
内の条件判定にてtrue
かfalse
を返します。false
が返ってくるのは、送信用のキューが一杯でこれ以上要求が追加できない時です。
無線パケットには宛先情報など相手に届けるための様々な設定を行います。設定には設定内容を含むオブジェクトを<<演算子の右辺値に与えます。
pkt << tx_addr(0x00)
<< tx_retry(0x1)
<< tx_packet_delay(0,50,10);
以下に設定に用いるオブジェクトについて記載します。
各設定の利用可否や意味合いは、ネットワーク ビヘイビアの仕様によります。
tx_addr(uint32_t addr)
宛先アドレスaddr
を指定します。宛先アドレスの値については、ネットワークビヘイビアの仕様を参照してください。
MSB(bit31=0x80000000
)がセットされるアドレスは、無線モジュールのシリアル番号宛という意味になります。
0x00
..0xEF
は、8bitの論理IDを意味します。0xFEは子機宛(0x01
..0xEF
)の同報通信(ブロードキャスト)、0xFF
は親機子機関係なく同報通信(ブロードキャスト)します。
tx_retry(uint8_t u8count, bool force_retry = false)
再送回数の指定を行います。再送回数はu8countで指定します。force_retryは、送信が成功しようがしまいが、指定回数の再送を行う設定です。
ネットワークビヘイビア<NWK_SIMPLE>
では、同じ内容のパケットをu8count+1
回送信します。
force_retry
の設定は無視されます。
tx_packet_delay(uint16_t u16DelayMin,
uint16_t u16DelayMax,
uint16_t u16RetryDur)
パケットを送信するまでの遅延と再送間隔を設定します。u16DelayMin
とu16DelayMax
の2つの値をミリ秒[ms]で指定します。送信要求をしてからこの間のどこかのタイミングで送信を開始します。再送間隔をu16RetryDur
の値[ms]で指定します。再送間隔は一定です。
内部処理の都合で指定通りのタイミングで送信処理が始まらない場合もあります。また、IEEE802.15.4の処理でもパケット創出までの時間ブレが発生します。これらのタイミングのブレは、多くのシステムではパケットの衝突回避を行う上で有効な手立てとなります。
厳格なタイミングでのパケット送信は、IEEE802.15.4の規格の性質上、例外的な使用方法とお考え下さい。
この指定は有効です。
最初の送信から1秒を超えて再送され到達した同一パケットについては、新たなパケットが到達したとして重複除外が為されません。再送間隔を長く設定したり、中継でのパケット到達遅延により1秒を超えて同じパケットを受信する場合があります。
重複パケットの処理の設定は<NWK_SIMPLE>
ビヘイビアの初期化で設定できます。
tx_process_immediate()
パケット送信を「できるだけ速やかに」実行するように要求する設定です。TWENETでのパケット送信処理は、1msごとに動作するTickTimer起点で行われています。この設定をすることで、要求後速やかにパケット送信要求が処理されます。もちろん、tx_packet_delay(0,0,0)
以外の設定では意味がない指定になります。
他のパケット送信処理が行われている場合は、通常の処理になります。
この指定は有効です。
tx_ack_required()
無線パケット通信では、送信完了後、送信相手先からACK(アック)という短い無線電文を得て、送信成功とする送信方法があります。このオプションを設定することで、ACK付き送信を行います。
<NWK_SIMPLE>
では、この指定は無効です。コンパイルエラーになります。
<NWK_SIMPLE>
は、シンプルに動作する中継ネットワークの実装を目的としており、ACK付きの通信は行いません。
tx_addr_broadcast()
ブロードキャストの指定を行います。
<NWK_SIMPLE>
では、この指定は無効です。コンパイルエラーになります。
替わりに宛先アドレスtx_addr(0xFF)
(ブロードキャスト)またはtx_addr(0xFE)
(子機宛のブロードキャスト)を指定します。
tx_packet_type_id(uint8_t)
0..7の指定ができるTWENET無線パケットのタイプIDを指定します。
<NWK_SIMPLE>
では、この指定は無効です。コンパイルエラーになります。
<NWK_SIMPLE>
ではタイプIDを内部的に使用しています。ユーザは使用できません。
シリアル書式入出力 (mwx::serial_parser)
シリアル書式の入出力のために用います。内部に解釈済みのバイナリ系列を保持するバッファを持ち、入力時は1バイトずつ系列を読み出し書式に従い内部バッファに格納し、系列の解釈が完了した時点で完了状態になるものです。反対に出力時は内部バッファから所定の出力書式に従いバッファを出力します。
メモリバッファ取り扱い方法(alloc
)に応じて3種類のクラス名が定義されています。
// serparser_attach : 既存のバッファを用いる
serparser_attach
// serparser : Nバイトのバッファを内部に確保する
serparser_local<N>
// serparser_heap : ヒープ領域にバッファを確保する
serparser_heap
begin()
の初期化のパラメータで渡す書式の種別です。ここではアスキー形式とバイナリー形式の2種類があります。
定数
種別
uint8_t PARSER::ASCII = 1
アスキー形式
uint8_t PARSER::BINARY = 2
バイナリー形式
バイナリ形式の取り扱いはアスキー形式に比べ、必要なツールや確認方法を含め一般に取り扱いが煩雑になります。通常はアスキー形式をお使いください。
アスキー形式は、バイナリで構成されたデータ列を文字列で表現する方法です。
例えばバイト列で 00A01301FF123456
をアスキー形式で表現すると、以下のようになります。先頭は :
で B1
がチェックサム、終端は [CR:0x0d][LF:0x0a]
となります。
:00A01301FF123456B1[CR][LF]
終端のチェックサムを省略できます。チェックサムからCRLFの系列をX
に置き換えます。文字化けによる誤ったデータ系列には弱くなりますが、実験などでデータを送付したいときに便利です。
:00A01301FF123456X
======
元データのバイト数
バイト数
解説
ヘッダ
1
:
(0x3A) コロンを指定します。
データ部
N
2N
元データの各バイトをアスキー文字列2文字(A-F は大文字)で表現します。
例えば 0x1F は 1
(0x31) F
(0x46) と表現します。
チェックサム
2
データ部の各バイトの和を8ビット幅で計算し2の補数をとります。つまりデータ部の各バイトの総和+チェックサムバイトを8ビット幅で計算すると0になります。
チェックサムバイトをアスキー文字列2文字で表現します。
例えば 00A01301FF123456
では 0x00 + 0xA0 + ... + 0x56 = 0x4F となり、この二の補数は0xB1 です。(つまり 0x4F + 0xB1 = 0x00)
フッタ
2
[CR] (0x0D) [LF] (0x0A) を指定する。
通常はアスキー形式を利用してください。
マイコン間通信での実装を考えるとバイナリ形式のほうが効率的ですが、実験などでの送受信の確認にはバイナリ通信に対応した特別なターミナルなどを準備する必要があり、チェックサムの計算も必須です。アスキー形式より利用の難易度は高くなります。
バイナリ形式は、バイナリで構成されたデータ列にヘッダとチェックサムを付加して送付する方法です。
例えば 00A01301FF123456
をバイナリ形式で表現すると、以下のようになります。
0xA5 0x5A 0x80 0x08 0x00 0xA0 0x13 0x01 0xFF 0x12 0x34 0x56 0x3D
======
元データのバイト数
形式におけるバイト数
解説
ヘッダ
2
0xA5 0x5A
を指定します。
データ長
2
データ長はビッグエンディアン形式の2バイトで、MSB (0x8000) を設定した上、データ部の長さを指定します。
例えばデータ部の長さが 8 バイトなら0x80 0x08
を指定します。
データ部
N
N
元データを指定します。
チェックサム
1
データ部の各バイトの XOR を計算します。
例えばデータ部が 00A01301FF123456
なら 0x00 xor 0xA0 xor ... 0x56 = 0x3D となります。
フッタ
(1)
チェックサムが事実上の終端です。無線モジュールからの出力では 0x04
(EOT) が付加されます。
// serparser_attach : 既存のバッファを用いる
serparser_attach p1;
uint8_t buff[128];
p1.begin(ARSER::ASCII, buff, 0, 128);
// serparser : Nバイトのバッファを内部に確保する
serparser p2<128>;
p2.begin(PARSER::ASCII);
// serparser_heap : ヒープ領域にバッファを確保する
serparser_heap p3;
p3.begin(PARSER::ASCII, 128);
宣言にはメモリの確保クラスを指定します。この指定は煩雑であるため、上述のように別名定義を行っています。
クラス名(別名定義) メモリ確保
内容
serparser_attach
すでにあるバッファをbegin()
にて指定する
serparser_local<N>
Nバイトのバッファを内部に確保する
serparser_heap
begin()
メソッドのパラメータで指定したサイズをヒープに確保する
メモリ確保クラスに応じたbegin()
メソッドを呼び出します。
void begin(uint8_t ty, uint8_t *p, uint16_t siz, uint16_t max_siz)
ty
で指定する形式で、p
で指定したバッファを用います。バッファの最大長はmax_siz
で、バッファの有効データ長をsiz
で指定します。
この定義は、特に、データ列を書式出力したい場合に用います(>>
演算子参照)
void begin(uint8_t ty)
ty
で指定する形式で初期化を行います。
void begin(uint8_t ty, uint16_t siz)
ty
で指定する形式で、siz
で指定したサイズをヒープに確保して初期化します。
一度確保したヒープ領域は解放できません。
BUFTYPE& get_buf()
内部バッファを返す。バッファは smplbuf<uint8_t, alloc>
型となります。
inline bool parse(uint8_t b)
入力文字を処理する。書式入力の入力文字列を1バイト受け取り書式に従い解釈します。例えばASCII書式では:00112233X
のような系列を入力として受け取りますが : 0 0 ... X
の順で1バイトずつ入力し、最後の X
を入力した時点で書式の解釈を完了し、完了済みと報告します。
parse()
のパラメータは入力バイト、戻り値は解釈完了であればtrue
を戻します。
while (Serial.available()) {
int c = Serial.read();
if (SerialParser.parse(c)) {
// 書式解釈完了、b に得られたデータ列(smplbuf<uint8_t>)
auto&& b = SerialParser.get_buf();
// 以下は得られたデータ列に対する処理を行う
if (b[0] == 0xcc) {
// ...
}
}
}
operator bool()
true
ならparse()
により読み出しが完了した状態で、false
なら解釈中となります。
while (Serial.available()) {
int c = Serial.read();
SerialParser.parse(c);
if(SerialParser) {
// 書式解釈完了、b に得られたデータ列(smplbuf<uint8_t>)
auto&& b = SerialParser.get_buf();
// ...
}
}
内部バッファを書式形式でストリーム(Serial)に対して出力します。
uint8_t u8buf[] = { 0x11, 0x22, 0x33, 0xaa, 0xbb, 0xcc };
ser_parser pout;
pout.begin(ARSER::ASCII, u8buf, 6, 6); // u8bufの6バイトを指定
Serial << pout;// Serialに書式出力 -> :112233AABBCC??[CR][LF]
内部が配列構造のコンテナクラスです。初期化時にバッファの最大サイズを決定しますが、その最大サイズまでの範囲で可変長の配列として振る舞います。
template <typename T, int N> smplbuf_local
template <typename T> smplbuf_attach
template <typename T> smplbuf_heap
smplbuf
は要素の型T
とメモリの確保方法alloc
で指定したメモリ領域に対して配列の操作を提供するコンテナクラスです。alloc
の指定は煩雑であるためusing
を用いた別名定義が行っています。
オブジェクトの宣言例です。宣言の直後に初期化用のメソッド呼び出しを行います。いずれも初期化直後の最大サイズは128バイトで、サイズは0です。必要に応じてサイズを拡張しながら使用します。
// 内部に固定配列
smplbuf_local<uint8_t, 128> b1;
b1.init_local();
// すでにある配列を利用する
uint8_t buf[128];
smplbuf_attach<uint8_t> b2;
b2.attach(buf, 0, 128);
// ヒープに確保する
smplbuf_heap<uint8_t> b3;
b3.init_heap(128);
上記のuint8_t
型に限り別名定義があります。
template <int N>
smplbuf_u8
// smplbuf<uint8_t, alloc_local<uint8_t, N>>
smplbuf_u8_attach
// smplbuf<uint8_t, alloc_attach<uint8_t>>
smplbuf_u8_heap
// smplbuf<uint8_t, alloc_heap<uint8_t>>
通常の配列のように[]演算子などを用いて要素にアクセスできますし、イテレータを用いたアクセスも可能です。
smplbuf_u8<32> b1;
b1.reserve(5);
b1[0] = 1;
b1[1] = 4;
b1[2] = 9;
b1[3] = 16;
b1[4] = 25;
for(uint8_t x : b1) {
Serial << int(x) << ",";
}
smplbuf
はストリーム(stream)インタフェースも有しているため、いくつかのストリーム用のメソッドを使用することができます。
smplbuf_u8<32> b1;
b1 << uint8_t(0x01)
<< uint32_t(0x1234abd);
push_back()
メソッドを定義しています。末尾にデータを追記するタイプのアルゴリズムが使用可能になります。
smplbuf_local<T,N>
smplbuf_local<T,N>::init_local()
smplbuf_attach<T>
smplbuf_attach<T>::attach(T* buf, uint16_t size, uint16_t N)
smplbuf_heap<T>
smplbuf_heap<T>::init_heap(uint16_t N)
// 例
// 内部に固定配列
smplbuf_local<uint8_t, 128> b1;
b1.init_local();
// すでにある配列を利用する
uint8_t buf[128];
smplbuf_attach<uint8_t> b2;
b2.attach(buf, 0, 128);
// ヒープに確保する
smplbuf_heap<uint8_t> b3;
b3.init_heap(128);
型T
でサイズN
のコンテナを宣言します。宣言後に初期化のメソッドを呼び出します。
smplbuf_attach
では、使用するバッファの先頭ポインタT* buf
と配列の初期サイズsize
と最大サイズN
を指定します。
smplbuf_local
のみ、ローカルオブジェクトとして宣言する場合は、初期化メソッド.init_local()
を省略できます。
alloc_local
でグローバルオブジェクトを生成する場合は、smplbuf
コンテナの使用前に.init_local()
メソッドを呼び出します。
void in_some_func() {
smplbuf_local<uint8_t, 5> b1;
b1.init_local();
b1 = { 0, 1, 2, 3, 4 };
smplbuf_local<uint8_t, 5> b2{0, 1, 2, 3, 4};
}
初期化子リスト(イニシャライザリスト){ ... }
によるメンバーの初期化をできます。smplbuf_local
のローカル宣言でのコンストラクタでの利用を除き、初期化のメソッドを呼び出した後に有効です。
代入演算子の右辺値 (smplbuf_local
, smplbuf_attach
, smplbuf_heap
)
コンストラクタ(smplbuf_local
のローカル宣言、グローバル宣言は不可)
inline bool append(T&& c)
inline bool append(const T& c)
inline void push_back(T&& c)
inline void push_back(const T& c)
末尾にメンバーc
を追加します。append()
の戻り値はbool
で、バッファが一杯で追加できないときにfalse
が返ります。
pop_back()
は末尾のエントリを抹消します。
inline bool empty()
inline bool is_end()
inline uint16_t size()
inline uint16_t capacity()
empty()
は配列に要素が格納されていない場合にtrue
を戻します。is_end()
は反対に配列サイズ一杯まで要素が格納されているときにtrue
を戻します。
size()
は配列の要素数を返します。
capacity()
は配列の最大格納数を返します。
inline bool reserve(uint16_t len)
inline void reserve_head(uint16_t len)
inline void redim(uint16_t len)
reserve()
は配列のサイズを拡張します。配列が格納されていない領域はデフォルトで初期化されます。
reserve_hear()
は配列の先頭部に指定したサイズの領域を確保します。コンテナオブジェクトからは参照できない領域となります。例えばパケットペイロードのヘッダ部分を読み飛ばした部分配列にアクセスするようなコンテナとして利用できるようにします。確保した領域を戻しすべてアクセスできるようにコンテナを戻すには確保時と同じ負の値を与えます。
redim()
は利用領域のサイズを変更します。reserve()
と違い、未使用領域の初期化を行いません。
inline T& operator [] (int i)
inline T operator [] (int i) const
要素にアクセスします。
i
に負の値を与えるとバッファー末尾からの要素となります。-1
の場合は末尾の要素、-2
は末尾から一つ手前となります。
inline std::pair<T*, T*> to_stream()
//例
smplbuf_local<uint8_t, 10> b1;
b1.init_local();
b1 = { 0x30, 0x31, 0x32, 0x33 };
Serial << b1.to_stream();
// Output->0123
ストリームへの出力目的で利用します。
inline size_t write(int n)
inline static void vOutput(char out, void* vp)
inline void flush(void)
上記の実装を行っています。
<<
演算子を用いて配列にデータを書き込む
printfのアルゴリズムを用いた関数を持ちて配列にデータを書き込む
flush()
によりバッファの末尾にヌル文字を書きこむ(配列のサイズは変更しません)
FIFOキューを構造のコンテナクラスです。
template <typename T, int N, class Intr> smplbuf_local
template <typename T, class Intr> smplbuf_attach
template <typename T, class Intr> smplbuf_heap
smplque
は要素の型T
とメモリの確保方法alloc
で指定したメモリ領域に対してFIFOキューの操作を提供するコンテナクラスです。alloc
の指定は煩雑であるためusing
を用いた別名定義が行っています。
要素型は原則として数値や数値などを格納する構造体を想定しています。デストラクタによる破棄手続きが必要なオブジェクトを格納することを想定していません(キューから要素を抹消する際にオブジェクトを抹消する処理をしていないため)。
宣言時に割り込み禁止設定を行うクラスIntr
を登録することが出来ます。このクラスは指定しない場合は、割り込み禁止制御を行わない通常の動作となります。
オブジェクトの宣言例です。宣言の直後に初期化用のメソッド呼び出しを行います。いずれも初期化直後の最大サイズは128バイトで、サイズは0です。必要に応じてサイズを拡張しながら使用します。
// 内部に固定配列
smplque_local<uint8_t, 128> q1;
q1.init_local();
// すでにある配列を利用する
uint8_t buf[128];
smplque_attach<uint8_t> q2;
q2.attach(buf, 128);
// ヒープに確保する
smplque_heap<uint8_t> q3;
q3.init_heap(128);
FIFOキューですのでpush()
,pop()
,front()
といったメソッドを用いて操作します。
smplque_local<int, 32> q1;
q1.init_local();
q1.push(1);
q1.push(4);
q1.push(9);
q1.push(16);
q1.push(25);
while(!q1.empty()) {
Serial << int(q1.front()) << ',';
q1.pop();
}
// output -> 1,4,9,16,25,
イテレータによるアクセスも可能です。
smplque_local<int, 32> q1;
q1.init_local();
q1.push(1);
q1.push(4);
q1.push(9);
q1.push(16);
q1.push(25);
for(int x : q1) {
Serial << int(x) << ',';
}
auto&& minmax = std::minmax_element(q1.begin(), q1.end());
Serial << "min=" << int(*minmax.first)
<< ",max=" << int(*minmax.second);
// output -> 1,4,9,16,25,min=1,max=25[]
smplbuf_local<T,N>
smplbuf_local<T,N>::init_local()
smplbuf_attach<T>
smplbuf_attach<T>::attach(T* buf, uint16_t N)
smplbuf_heap<T>
smplbuf_heap<T>::init_heap(uint16_t N);
//例
// 内部に固定配列
smplque_local<uint8_t, 128> q1;
q1.init_local();
// すでにある配列を利用する
uint8_t buf[128];
smplque_attach<uint8_t> q2;
q2.attach(buf, 128);
// ヒープに確保する
smplque_heap<uint8_t> q3;
q3.init_heap(128);
型T
でサイズN
のコンテナを宣言します。宣言後に初期化のメソッドを呼び出します。
smplque_local
のみ、ローカルオブジェクトとして宣言する場合は、初期化メソッド.init_local()
を省略できます。
smplque_local
のグローバルオブジェクトを生成する場合は、smplbuf
コンテナの使用前に.init_local()
メソッドを呼び出します。
inline void push(T&& c)
inline void push(T& c)
inline void pop()
inline T& front()
inline T& back()
inline T& pop_front()
push()
はエントリをキューに追加します。
pop()
はエントリをキューから抹消します。
front()
は先頭のエントリ(一番最初に追加されたもの)を参照します。
back()
は末尾のエントリ(一番最後に追加されたもの)を参照します。
pop_front()
は先頭のエントリを戻り値として参照し、同時にそのエントリをキューから抹消します。
inline bool empty()
inline bool is_full()
inline uint16_t size()
inline uint16_t capacity()
empty()
は配列に要素が格納されていない場合にtrue
を戻します。is_full()
は反対に配列サイズ一杯まで要素が格納されているときにtrue
を戻します。
size()
はキューに格納されている要素数を返します。
capacity()
はキューの最大格納数を返します。
inline void clear()
キューのすべての要素を抹消します。
inline T& operator[] (int i)
要素にアクセスします。0
が最初に追加した要素です。
inline smplque::iterator begin()
inline smplque::iterator end()
begin()
とend()
によるイテレータを取得できます。イテレータの先頭はキューの最初に登録した要素です。イテレータを用いることで範囲for文やアルゴリズムが利用できます。
応用としてaxis_xyzt構造体の特定のメンバーに注目したイテレータによるアクセスがあります。
入出力ストリーム
入出力ストリームを処理する上位クラスです。
CRTP (Curiously Recurring Template Pattern) 手法を用いたポリモーフィズムにより、いくつかのクラス(Serial, Wire, SPI, smplbuf
) にインタフェースを提供します。
CRTP では下位クラスは template class Derived : public stream<Derived>;
のように定義し、上位クラスからも下位クラスのメソッドを参照します。
本クラスでは print
メソッド、<<
演算子などの共通処理の定義を行い、下位クラスで実装した write()
メソッドなどを呼び出すことで、仮想関数を用いるのと近い実装を行っています。
下位クラスでは、以下に列挙する関数を実装します。
int available()
// example
while(Serial.available()) {
int c = Serial.read();
// ... any
}
入力が存在する場合は 1、存在しない場合は 0 を返します。
パラメータ
解説
戻り値 int
0: データなし 1:データあり
本実装の戻り値はバッファ長ではありません。
void flush()
// example
Serial.println("long long word .... ");
Serial.flush();
出力をフラッシュ(出力完了まで待つ)します。
int read()
// example
int c;
while (-1 != (c = read())) {
// any
}
ストリームより1バイトデータを入力します。データが存在しない場合は -1
を戻します。
size_t write(int c)
// example
Serial.write(0x30);
ストリームに1バイト出力します。
パラメータ
解説
n
出力したい文字。
戻り値 size_t
出力が成功すれば 1、失敗すれば 0。
static void vOutput(char out, void* vp)
1バイト出力を行うスタティック関数です。クラスメソッドではないため、メンバー変数等の情報は利用できません。替わりにパラメータとして渡される vp にクラスインスタンスへのポインタを渡します。
このスタティック関数は内部的に利用されfctprintf()
の1バイト出力関数として関数ポインタが渡ります。これを用いてprint
メソッドなどを実装しています。
パラメータ
解説
out
出力したい文字
vp
クラスインスタンスへのポインタ 通常は、元のクラスにキャストして write() メソッドを呼び出す
void mwx::stream::putchar(char c)
// example
Serial.putchar('A');
// result -> A
1バイト出力します。
size_t print(T val, int base = DEC) // T: 整数型
size_t print(double val, int place = 2)
size_t print(const char*str)
size_t print(std::initializer_list<int>)
// example
Serial.print("the value is ");
Serial.print(123, DEC);
Serial.println(".");
// result -> the value is 123.
Serial.print(123.456, 1);
// result -> 123.5
Serial.print({ 0x12, 0x34, 0xab, 0xcd });
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.
各種整形出力を行います。
パラメータ
解説
val
整形出力したい数値型
base
出力形式
BIN 二進数 / OCT 8進数 / DEC 10進数 / HEX 16進数
place
小数点以下の桁数
戻り値 size_t
書き出したバイト数
size_t printfmt(const char* format, ...);
// example
Serial.printfmt("the value is %d.", 123);
// result -> the value is 123.
printf 形式での出力を行います。
TWESDK/TWENET/current/src/printf/README.md 参照
// examples
Serial << "this value is" // const char*
<< int(123)
<< '.';
<< mwx::crlf;
// result -> this value is 123.
Serial << fromat("this value is %d.", 123) << twe::crlf;
// result -> this value is 123.
Serial << mwx::flush; // flush here
Serial << bigendian(0x1234abcd);
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.
Serial << int(0x30) // output 0x30=48, "48"
<< '/'
<< uint8_t(0x31); // output '1', not "48"
// result -> 48/1
smplbuf<char,16> buf = { 0x12, 0x34, 0xab, 0xcd };
Serail << but.to_stream();
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.
Seiral << make_pair(buf.begin(), buf.end());
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.
Serial << bytelist({ 0x12, 0x34, 0xab, 0xcd });
// will output 4byte of 0x12 0x34 0xab 0xcd in binary.
引数型
解説
char
1バイト出力 (数値としてフォーマットはしない)
int
整数出力 (printf の "%d")
double
数値出力 (printf の "%.2f")
uint8_t
1バイト出力する(char型と同様)
uint16_t
2バイト出力する(ビッグエンディアン順)
uint32_t
4バイト出力する(ビッグエンディアン順)
format()
printf 形式での出力
mwx::crlf
改行 CRLF の出力
mwx::flush
出力のフラッシュ
bigendian()
数値型をビッグエンディアン順で出力する。(右辺値)
std::pair<T*, T*>
バイト型の begin(), end()
ポインタを格納したペア。make_pair
により生成できる。T
は uint8_t
型を想定する。(右辺値)
bytelist()
std::initializer_list
を用いるバイト列の出力
smplbuf<T>::to_stream()
smplbuf<T>
のデータを出力する
T
は uint8_t
型を想定する
// examples
Serial << "this value is" // const char*
<< int(123)
<< '.';
<< mwx::crlf;
// result -> this value is 123.
// example: SPI transmission
uint8_t val;
if (auto&& x = SPI.get_rwer()) {
(x << (0x61)) >> null_stream(1); // discard read byte
(x << (0x00)) >> val; // read a byte to val
}
入力処理を行います。
setup()
内では実行できません。
引数型
解説
uint8_t
1バイト入力
uint16_t
2バイト入力(ビッグエンディアン順)
uint32_t
4バイト入力(ビッグエンディアン順)
null_stream(int n)
nバイト読み捨てる
uint8_t get_error_status()
void clear_error_status()
void set_timeout(uint8_t centisec)
// example
Serial.set_timeout(100); // set timeout as 1000ms.
uint32_t u32val;
Serial >> u32val;
入力タイムアウトとエラーを管理します。set_timeout()
によりタイムアウト時間を指定し、>>演算子により入力処理を行います。所定時間内までに入力が得られない場合は get_error_status()
によりエラー値を読み出せます。clear_error_status()
によりエラー状況をクリアします。
引数型
解説
centisec
1/100秒単位でタイムアウト時間を設定します
値
意味
0
エラーなし
1
エラー状況
mwx::stream に printf の書式を入力
mwx::stream
の << 演算子に対してフォーマット書式を書き出すヘルパークラスです。ライブラリ内では Using format=mwx::mwx_format;
として別名定義しています。
Serial << format("formatted print: %.2f", (double)3123 / 100.) << mwx::crlf;
// formatted print: 31.23[改行]
可変数引数リストに登録できる引数は最大4つ。
コンストラクタで受け取った引数リストを、パラメータパックの展開機能を用いてクラス内部変数に格納する
operator <<
が呼び出された時点で、fctprintf()
を呼び出し、ストリームにデータを書き出す
format(const char *fmt, ...)
コンストラクタでは、書式のポインタとパラメータを保存します。続く <<
演算子による呼び出しでフォーマットを解釈して出力処理を行います。
パラメータ
解説
fmt
フォーマット書式。
TWESDK/TWENET/current/src/printf/README.md 参照
...
フォーマット書式に応じたパラメータ。 ※ 最大数は4で、5つ以上のパラメータではコンパイルエラーとなる。
※ 書式との整合性はチェックしないため、不整合な入力に対しては安全ではない。
fmt
は本オブジェクトが破棄されるまで、アクセス可能であることが必要です。
twe::stream にビッグエンディアン順で数値型のデータを出力する
twe::stream に改行コードを出力する
mwx::stream
の <<
演算子に対して改行コード (CR LF) を出力するためのヘルパークラスのインスタンスです。
Serial << "hello world!" << mwx::crlf;
twe::stream へのバッファ出力をフラッシュする。
mwx::stream
の出力バッファをフラッシュする。flush()
メソッドを呼び出すヘルパークラスへのインスタンス。
for (int i = 0; i < 127; ++i) {
Serial << "hello world! (" << i << ")" << twe::endl << twe::flush;
}
シリアルポートの場合は出力完了までポーリング待ちを行う
mwx::simpbuf
バッファの場合は 0x00
を末尾に出力する(サイズは変更しない)