最適な出力のために、Google Chrome(15以降)または Microsoft Edge(79以降)を推奨いたします。
2024-11-14 現在Wire
2線シリアル (I2C) master (controller) としての読み書き (mwx::periph_wire)
2線シリアル(I2C) master (controller) としての読み書きを行います。
定義
別名定義
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;
注意事項
API 中に STOP ビットの扱いが厳格でない呼び出しを行うものもあります。
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)
ハードウェアの初期化を行います。
初期化せずにWireの操作を行うとTWELITE無線モジュールがハングアップします。
スリープからの起床時は、スリープ直前で動作していた場合、直前の状態に復帰します。
パラメータ | 解説 |
---|
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種類あります。いずれかを選択して利用します。
その他
プローブ(デバイスの存在判定)
bool probe(uint8_t address)
address
で指定したデバイスが応答するかを確認します。デバイスが存在する場合は true
が戻ります。
setClock()
void setClock(uint32_t speed)
本来はバス周波数を変更するための手続きですが、何も処理をしません。
1 - Wire (メンバ関数版)
Wire (メンバ関数を使用する方法)
メンバ関数を利用した方法は、抽象度が比較的低く、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: 失敗 |
2 - Wire (ヘルパークラス版)
Wire (ヘルパークラスを使用する方法)
ヘルパークラス版はより抽象度が高い実装です。読み書きに対応するオブジェクト 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書き出しに用いるワーカーオブジェクトを取得します。
ワーカーオブジェクト (writer)
<<
演算子
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バイト書き出す。
ワーカーオブジェクト (reader)
>>
演算子
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;