セクションの複数ページをまとめています。 印刷またはPDF形式で保存...
関数
- 1: システム関数
- 1.1: millis()
- 1.2: delay()
- 1.3: delayMicroseconds()
- 1.4: random()
- 2: 汎用デジタルIO
- 2.1: pinMode()
- 2.2: digitalWrite()
- 2.3: digitalRead()
- 2.4: attachIntDio()
- 2.5: detachIntDio()
- 2.6: digitalReadBitmap()
- 3: ユーティリティ関数
- 3.1: printfの実装
- 3.2: pack_bits()
- 3.3: collect_bits()
- 3.4: Byte array utils
- 3.5: pack_bytes()
- 3.6: expand-bytes()
- 3.7: CRC8, XOR, LRC
- 3.8: div10(), div100(), div1000()
- 3.9: Scale utils
- 3.10: pnew()
1 - システム関数
1.1 - millis()
uint32_t millis()
システム時刻はTickTimerの割り込みで更新されます。
1.2 - delay()
void delay(uint32_t ms)
ms
にて与えられた期間待ち処理を行います。
時間の計測はTickTimerのカウントによって行っています。また長い時間待ちを行う場合はCPUのクロックを低下してポーリング処理を行います。
delay()
を呼び出してから約5ms経過するごとにTWELITEマイコン内部のウォッチドッグ処理を行います。
※例えばwhile(1) delay(1);
を実行した場合は、delay()
内部で5ms経過しないためウォッチドッグ処理が行われず、一定時間後リセットが実行されます。
setup(), wakeup()
関数内では、TickTimerがまだ動作していないため、whileループによる時間待ち処理になります。この場合、指定値との誤差は大きくなります。このループカウンタは32Mhzに合わせて調整しています。これら関数内でCPUクロックを変化させた場合は、そのクロックに比例した誤差が発生します。
パラメータに1,2といった短い時間を指定した場合は、誤差が大きくなる場合があります。
1.3 - delayMicroseconds()
void delayMicroseconds(uint32_t microsec)
microsec
にて与えられた期間待ち処理を行います。
時間の計測はTickTimerのカウントによって行っています。また長い時間待ちを行う場合はCPUのクロックを低下してポーリング処理を行います。
setup(), wakeup()
関数内では、TickTimerがまだ動作していないため、whileループによる時間待ち処理になります。この場合、指定値との誤差は大きくなります。このループカウンタは32Mhzに合わせて調整しています。これら関数内でCPUクロックを変化させた場合は、そのクロックに比例した誤差が発生します。
パラメータに10以下といった短い時間を指定した場合は、誤差が大きくなる場合があります。
1.4 - random()
uint32_t random(uint32_t maxval)
uint32_t random(uint32_t minval, uint32_t maxval)
1行目は0..(maxval-1)
の値を戻します。maxvalの値が最大値ではないことに注意してください。
2行目はminval..maxval-1
の値を戻します。
2 - 汎用デジタルIO
汎用デジタルIO(DIO)の操作には以下の関数を利用します。
pinMode()
digitalWrite()
digitalRead()
attachIntDio()
detachIntDio()
定数
ピン名と番号
定義 | 名称 |
---|---|
const uint8_t PIN_DIGITAL::DIO0 .. 19 | DIOピン0~19 |
const uint8_t PIN_DIGITAL::DO0 .. 1 | DOピン0,1 |
ピンのモード(DIO0..19)
以下の列挙値は型名 E_PIN_MODE
で取り扱われます。
定義 | プルアップ | 名称 |
---|---|---|
PIN_MODE::INPUT | 無 | 入力 |
PIN_MODE::OUTPUT | 無 | 出力 |
PIN_MODE::INPUT_PULLUP | 有 | 入力 |
PIN_MODE::OUTPUT_INIT_HIGH | 無 | 出力(初期状態HIGH) |
PIN_MODE::OUTPUT_INIT_LOW | 無 | 出力(初期状態LOW) |
PIN_MODE::WAKE_FALLING | 無 | 入力、起床ピン、立下り |
PIN_MODE::WAKE_RISING | 無 | 入力、起床ピン、立上り |
PIN_MODE::WAKE_FALLING_PULLUP | 有 | 入力、起床ピン、立下り |
PIN_MODE::WAKE_RISING_PULLUP | 有 | 入力、起床ピン、立上り |
PIN_MODE::DISABLE_OUTPUT | 有 | 入力状態に戻す |
ピンのモード(DO0,1)
以下の列挙値は型名 E_PIN_MODE
で取り扱われます。
定義 | 名称 |
---|---|
PIN_MODE::OUTPUT | 出力 |
PIN_MODE::OUTPUT_INIT_HIGH | 出力(初期状態HIGH) |
PIN_MODE::OUTPUT_INIT_LOW | 出力(初期状態LOW) |
PIN_MODE::DISABLE_OUTPUT | 出力設定をやめる |
ピンの状態
以下の列挙値は型名 E_PIN_STATE
で取り扱われます。
定義 | 値 | 名称 |
---|---|---|
PIN_STATE::HIGH | 1 | HIGHレベル(=Vccレベル) |
PIN_STATE::LOW | 0 | LOWレベル(=GNDレベル) |
ピンの立ち上がり、立ち下がり
以下の列挙値は型名 E_PIN_INT_MODE
で取り扱われます。
定義 | 名称 |
---|---|
PIN_INT_MODE::FALLING | 立ち下り |
PIN_INT_MODE::RISING | 立ち上がり |
2.1 - pinMode()
void pinMode(uint8_t u8pin, E_PIN_MODE mode)
この関数では DIO0..19 と、DO0,1のピンの状態を変更できます。設定内容は E_PIN_MODE
の列挙値のDIOの解説とDOの解説を参照してください。
DO0,1は特殊なピンで、原則として他の目的で利用されるものですが、出力としても設定可能です。ただしハード的な制約があるピンですので、利用には注意が必要です。
両方のピンは、電源投入時にHIGHレベルが担保される必要があります。不安定な電圧をとったりするような回路構成の場合、モジュールが起動しないなどの問題が出ます。
2.2 - digitalWrite()
static inline void digitalWrite(uint8_t u8pin, E_PIN_STATE ulVal)
事前にpinMode()
にて設定対象のピンを出力用に設定しておきます。1番目のパラメータは、設定対象のピン番号を指定します。2番目のパラメータはHIGH
かLOW
のいずれかを指定します。
E_PIN_STATE
型となっています。E_PIN_STATE
からint
型への変換演算子は定義していませんので、数値による直接の入力はできないようになっています。2.3 - digitalRead()
static inline E_PIN_STATE digitalRead(uint8_t u8pin)
事前に入力に設定したピンの入力値をLOW
またはHIGH
で得ることができます。
E_PIN_STATE
型からint
型への変換演算子は定義していないため、数値型への直接的な代入はできません。2.4 - attachIntDio()
void attachIntDio(uint8_t u8pin, E_PIN_INT_MODE mode)
事前に入力設定したピンに対して、1番目のパラメータは割り込みを有効にしたいピン番号で、2番目は割り込み方向(立ち上がり、立ち下がり)を指定します。
例
DIO5のピンがHIGHからLOWに変化したときに割り込みが発生する設定を行います。
void setup() {
the_twelite.app.use<myAppClass>();
pinMode(PIN_DIGITAL::DIO5, PIN_MODE::INPUT_PULLUP);
attachIntDio(PIN_DIGITAL::DIO5, PIN_INT_MODE::FALLING);
}
void loop() {
;
}
myAppClass.hpp
class myAppClass: public mwx::BrdPal, MWX_APPDEFS_CRTP(myAppClasslMot)
{
};
ビヘイビアmyAppClass
の基本定義。詳細は省略しています。
myAppClass.cpp
/*****************************************************************/
// MUST DEFINE CLASS NAME HERE
##define __MWX_APP_CLASS_NAME myAppClass
##include "_mwx_cbs_cpphead.hpp"
/*****************************************************************/
MWX_DIO_INT(PIN_DIGITAL::DIO5, uint32_t arg, uint8_t& handled) {
static uint8_t ct;
digitalWrite(PIN_DIGITAL::DIO12, (++ct & 1) ? HIGH : LOW);
handled = false; // if true, no further event.
}
MWX_DIO_EVENT(PIN_DIGITAL::DIO5, uint32_t arg) {
Serial << '*';
}
/*****************************************************************/
// common procedure (DO NOT REMOVE)
##include "_mwx_cbs_cpptail.cpp"
// MUST UNDEF CLASS NAME HERE
##undef __MWX_APP_CLASS_NAME
} // mwx
/*****************************************************************/
ビヘイビアmyAppClass
の割り込みハンドラの記述。DIO5の割り込み発生時にDIO12の出力設定を反転させ、割り込みハンドラが終了してから発生するイベントではシリアルポートSerial
に*
を表示します。
2.5 - detachIntDio()
void detachIntDio(uint8_t u8pin)
2.6 - digitalReadBitmap()
uint32_t digitalReadBitmap()
LSB側から順にDIO0 … DIO19 の順に値が格納されます。
HIGH
側のピンには 1 が、LOW
側のピンには 0 が設定されます。
3 - ユーティリティ関数
3.1 - printfの実装
printf()
に近い実装を用意しています。
int mwx_printf(const char* format, ...)
int mwx_snprintf(char* buffer, size_t count, const char* format, ...)
mwx_printf()
はSerial
オブジェクトに対してprintf
出力を行います。Serial.printfmt()
と同じ処理になります。
mwx_snprintf()
はバッファに対してsnprintf
を行います。
mwx::stream
でデータ生成を参照してください。3.2 - pack_bits()
constexpr uint32_t pack_bits(...)
パラメータは可変数引数で指定でき、各パラメータはビット位置を指定する0..31の整数を指定する。例えばpack_bits(1,3,6)
と指定すると((1UL<<1)|(1UL<<3)|(1UL<<6))
を返します。
constexpr
は定数による計算が可能な場合はコンパイル時に定数展開します。背景
IOポート(DI,DO)の状態など各種ビットマップに値を参照・設定する場面があり、その記述を簡素化するため。
3.3 - collect_bits()
constexpr uint32_t collect_bits(uint32_t bm, ...)
パラメータbmに指定する値から、その後の可変数パラメータで指定する0..31のビット位置に対応する値を取り出します。取り出した値はパラメータ順に並べビットマップとして戻り値になります。
ビットマップの並び順は、最初のパラメータを上位ビットとし末尾のパラメータがbit0になります。
uint32_t b1 = 0x12; // (b00010010)
uint32_t b2 = collect_bits(b1, 4, 2, 1, 0);
// bit4->1, bit2->0, bit1->1, bit0->0
// b2=0x10 (b1010)
例ではb1のビット4,2,1,0を取り出すと (1,0,1,0) になります。これをb1010として0x10のように計算されます。
背景
IOポート(DI,DO)の状態など各種ビットマップに値を参照・設定する場面があり、その記述を簡素化するため。
3.4 - Byte array utils
読み出し
バイト配列から、uint8_t
ビッグエンディアン並びとして、uint16_t
, uint32_t
の値を取得する。
inline uint8_t G_BYTE(const uint8_t*& p) {
return *(p)++;
}
inline uint16_t G_WORD(const uint8_t*& p) {
uint32_t r = *p++;
r = (r << 8) + *p++;
return r;
}
inline uint32_t G_DWORD(const uint8_t*& p) {
uint32_t r = *p++;
r = (r << 8) + *p++;
r = (r << 8) + *p++;
r = (r << 8) + *p++;
return r;
}
p
は読み出したバイト数分だけインクリメントされる。
書き込み
ポインタq
で指定するバイト配列にuint8_t
,ビッグエンディアンでuint16_t
,uint32_t
の値を書き込む。
inline uint8_t& S_OCTET(uint8_t*& q, uint8_t c) {
*q++ = c;
return *q;
}
inline uint8_t& S_WORD(uint8_t*& q, uint16_t c) {
*(q) = ((c) >> 8) & 0xff; (q)++;
*(q) = ((c) & 0xff); (q)++;
return *q;
}
inline uint8_t& S_DWORD(uint8_t*& q, uint32_t c) {
*(q) = ((c) >> 24) & 0xff; (q)++;
*(q) = ((c) >> 16) & 0xff; (q)++;
*(q) = ((c) >> 8) & 0xff; (q)++;
*(q) = ((c) & 0xff); (q)++;
return *q;
}
q
は書き込んだバイト数分だけインクリメントされる。
背景
無線パケットのデータペイロードの生成・分解時の操作を簡略化するため。
より簡略化したpack_bytes()
, expand_bytes()
も利用できます。
3.5 - pack_bytes()
uint8_t* pack_bytes(uint8_t* b, uint8_t* e, ...)
pack_bytes
はコンテナクラスのbegin()
,end()
イテレータをパラメータとし、続くパラメータで指定されるデータをバイト列としてコンテナに書き込みます。
可変引数パラメータに与えるデータは以下に示すとおりです。
データ型 | バイト数 | 解説 |
---|---|---|
uint8_t | 1 | |
uint16_t | 2 | ビッグエンディアン並びで格納される |
uint32_t | 4 | ビッグエンディアン並びで格納される |
uint8_t[N] | N | uint8_t 型の固定長配列 |
std::pair<char*,N> | N | char* ,uint8_t* 型の配列と配列長のペア。make_pair() で生成できる。 |
smplbuf_u8& pack_bytes(smplbuf_u8& c, ...)
pack_bytes
はコンテナオブジェクトをパラメータとし、続くパラメータで指定されるデータをバイト列としてコンテナに書き込みます。コンテナの.push_back()
メソッドで末尾に追加します。
可変引数パラメータに与えるデータは以下に示すとおりです。
データ型 | バイト数 | 解説 |
---|---|---|
uint8_t | 1 | |
uint16_t | 2 | ビッグエンディアン並びで格納される |
uint32_t | 4 | ビッグエンディアン並びで格納される |
uint8_t[N] | N | uint8_t 型の固定長配列 |
std::pair<char*,N> | N | char* ,uint8_t* 型の配列と配列長のペア。make_pair() で生成できる。 |
smplbuf_u8? | .size() | uint8_t 型のsmplbuf<> コンテナ。コンテナ長(.size() )のデータを格納する。 |
例
auto&& rx = the_twelite.receiver.read();
smplbuf<uint8_t, 128> buf;
mwx::pack_bytes(buf
, uint8_t(rx.get_addr_src_lid()) // src addr (LID)
, uint8_t(0xCC) // cmd id (0xCC, fixed)
, uint8_t(rx.get_psRxDataApp()->u8Seq) // seqence number
, uint32_t(rx.get_addr_src_long()) // src addr (long)
, uint32_t(rx.get_addr_dst()) // dst addr
, uint8_t(rx.get_lqi()) // LQI
, uint16_t(rx.get_length()) // payload length
, rx.get_payload() // payload
);
この例では受信パケットの各属性やペイロードを別のバッファbuf
に再格納しています。
背景
無全パケットのデータペイロードの生成やデータの取り出しで用いられるuint8_t
型のバイト配列の記述を簡素化するため。
auto&& rx = the_twelite.receiver.read();
uint8_t data[128];
data[0] = rx.get_addr_src_lid();
data[1] = 0xCC;
data[2] = rx.get_psRxDataApp()->u8Seq;
data[4] = rx.get_addr_src_long() & 0x000000FF;
data[5] = (rx.get_addr_src_long() & 0x0000FF00) >> 8;
data[6] = (rx.get_addr_src_long() & 0x00FF0000) >> 16;
data[7] = (rx.get_addr_src_long() & 0xFF000000) >> 24;
...
上記はもっとも単純な記述だが、以下のようにByte array utilsを用いてバイト配列を生成できる。
auto&& rx = the_twelite.receiver.read();
uint8_t data[128], *q = data;
S_OCTET(q, rx.get_addr_src_lid());
S_OCTET(q, 0xCC);
S_OCTET(q, rx.get_psRxDataApp()->u8Seq);
S_DWORD(q, rx.get_addr_src_long());
S_DWORD(q, rx.get_addr_dst());
S_OCTET(q, rx.get_lqi());
S_WORD(q, rx.get_length());
for (auto x : rx.get_payload()) {
S_OCTET(q, x);
}
3.6 - expand-bytes()
const uint8_t* expand_bytes(
const uint8_t* b, const uint8_t* e, ...)
expand_bytes()
は、パラメータにuint8_t*
型のイテレータの組み合わせを指定します。これは解析対象の先頭と末尾の次のイテレータの指定となります。e
の位置まで解析が進んだ場合はエラーとなりnullptr
を返します。
展開にエラーがない場合は、次の読み出しを行うイテレータを戻します。
可変数パラメータには以下を指定します。
バイト数 | データ長 | 解説 |
---|---|---|
uint8_t | 1 | |
uint16_t | 2 | ビッグエンディアン並びとして展開する |
uint32_t | 4 | ビッグエンディアン並びとして展開する |
uint8_t[N] | N | uint8_t 型の固定長配列 |
std::pair<char*,N> | N | char* ,uint8_t* 型の配列と配列長Nのペアmake_pair() で生成できる |
例
auto&& rx = the_twelite.receiver.read();
char fourchars[5]{};
auto&& np =
expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
, make_pair((uint8_t*)fourchars, 4)
);
// read rest of payload
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];
expand_bytes(np, rx.get_payload().end()
, u8DI_BM_remote
, au16AI_remote[0]
, au16AI_remote[1]
, au16AI_remote[2]
, au16AI_remote[3]
, au16AI_remote[4]
);
この例では、まず4バイトの文字列を読み出しています。ここではmake_pair()
を用いて明示的に4バイト分のデータを読み出します。
戻されたイテレータnp
をもとに、次のデータを読み出します。次のデータはuint8_t
型、あとはuint16_t
型が5つ続いています。
背景
無全パケットのデータペイロードの生成やデータの取り出しで用いられるuint8_t
型のバイト配列の記述を簡素化するため。
auto&& rx = the_twelite.receiver.read();
char fourchars[5]{};
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];
uint8_t *p = rx.get_payload().begin();
fourchars[0] = p[0];
fourchars[1] = p[1];
fourchars[2] = p[2];
fourchars[3] = p[3];
fourchars[4] = 0;
p += 4;
u8DI_BM_remote = (p[0] << 8) + p[1]; p+=2;
au16AI_remote[0] = (p[0] << 8) + p[1]; p+=2;
...
上記はもっとも単純な記述だが、以下のようにByte array utilsを用いてバイト配列から読み出せる。
auto&& rx = the_twelite.receiver.read();
char fourchars[5]{};
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];
uint8_t *p = rx.get_payload().begin();
fourchars[0] = G_BYTE(p);
fourchars[1] = G_BYTE(p);
fourchars[2] = G_BYTE(p);
fourchars[3] = G_BYTE(p);
fourchars[4] = 0;
u8DI_BM_remote = G_WORD(p);
au16AI_remote[0] = G_WORD(p);
...
3.7 - CRC8, XOR, LRC
uint8_t CRC8_u8Calc(uint8_t *pu8Data, uint8_t size, uint8_t init=0)
uint8_t CRC8_u8CalcU32(uint32_t u32c, uint8_t init=0)
uint8_t CRC8_u8CalcU16(uint16_t u16c, uint8_t init=0)
uint8_t XOR_u8Calc(uint8_t *pu8Data, uint8_t size)
uint8_t LRC_u8Calc(uint8_t* pu8Data, uint8_t size)
CRC8, XOR, LRC(アスキー形式で使用)の計算を行います。
CRC8_u8CalcU16(), CRC8_u8CalcU32()
はu16c, u32c
をビッグエンディアン並びとして、CRC8を計算します。
X^8+X^5+X^4+1
(Polynomial Valueを0x31)をとしたものを使用しています。これはCRC8-CCITT や CRC8-Maximと呼ばれることがあります。背景
無線パケットのデータ列、アスキー形式のチェックサム(LRC)、各種センサーのデータチェック用に利用されるため、ライブラリ手続きとして追加した。
3.8 - div10(), div100(), div1000()
struct div_result_i32 {
int32_t quo; // quotient
int16_t rem; // remainder
uint8_t b_neg; // true if negative
uint8_t digits_rem; // digits of remainder
};
div_result_i32 div10(int32_t val);
div_result_i32 div100(int32_t val);
div_result_i32 div1000(int32_t val);
センサー値などで100倍した値をuint16_t
型にして受け渡しする場合がありますが、除算回路がないマイコンでの計算処理には相応の時間がかかるため、加算・減算・乗算とビットシフトを用いた近似計算と補正により計算を行います。
val
に計算したい値、rem
は余りの格納変数、neg
は符号を格納する変数を渡します。
戻り値は商の値(常に正)、rem
には余りの値(常に正)、neg
は負ならtrue
が格納されます。
計算アルゴリズムの制約(桁あふれ)からdiv100()
とdiv1000()
での計算可能な値域が決まっています。div100()
は-99999~99999までの値に対応し、div1000()
は-999999~999999までの値に対応します。
商を得る近似計算式
div100()
int dv = val * 1311 >> 17;
div1000()
int dv = val * 131 >> 17;
使用例
auto d1 = div100(sns_val.u16temp_object);
auto d2 = div100(sns_val.u16temp_object);
Serial
<< crlf << format("..Object = %c%2d.%02d"
, d1.b_neg ? '-' : '+', d1.quo, d1.rem)
<< format(" Ambient = %c%2d.%02d"
, d2.b_neg ? '-' : '+', d2.quo, d2.rem);
計算速度
10分の1程度になります。
結果の出力
// 変換オプション
struct DIVFMT {
static const int STD = 0; // displays with minimul digits (no padding, no positive sign)
static const int PAD_ZERO = 1; // set padding character as '0' instead of ' '.
static const int SIGN_PLUS = 2; // put '+' sign if value is positive or 0.
static const int PAD_ZERO_SIGN_PLUS = 3; // PAD_ZERO & SIGN_PLUS
static const int SIGN_SPACE = 4; // put ' ' sign if value is positive or 0.
static const int PAD_ZERO_SIGN_SPACE = 5; // PAD_ZERO & SIGN_SPACE
};
// 文字列変換結果を格納するためのクラス
class _div_chars {
...
const char* begin() const {...}
const char* end() const {...}
const char* c_str() const { return begin(); }
operator const char*() const { return begin(); }
};
// format()メソッド
_div_chars div_result_i32::format(
int dig_quo = 0, uint32_t opt = DIVFMT::STD) const;
// Serialへのインタフェースの実装
template <class D> class stream {
...
inline D& operator << (const mwx::_div_chars&& divc);
inline D& operator << (const mwx::div_result_i32&&);
inline D& operator << (const mwx::div_result_i32&);
};
割り算の結果を格納するdiv_result_i32
クラスにはformat()
メソッドを用い_div_chars
クラスオブジェクトを得ることが出来る。_div_chars()
クラスオブジェクトは文字列バッファを内包していてconst char*
型として文字列バッファにアクセスするメソッドが用意されている。また、Serial
オブジェクトに対する<<
演算子も実装している。
format()
メソッドのパラメータの1番目dig_quo
は出力桁数(符号部を含まず)を指定します。出力桁数に足りない場合(以下、不足桁)は空白または0
で埋めます。2番目のパラメータopt
は書式を指定します。
optパラメータ | 内容 |
---|---|
DIVFMT::STD | 標準的な出力で、不足桁は空白で埋め、負の値に限り- を付加します。 |
DIVFMT::PAD_ZERO | 不足桁は0 で埋めます。 |
DIVFMT::SIGN_PLUS | 正の値にも+ 符号を付加します。 |
DIVFMT::PAD_ZERO_SIGN_PLUS | 不足桁は0 で埋め、正の値にも+ 符号を付加します。 |
DIVFMT::SIGN_SPACE | 正の値の場合は+ 符号の替わりに空白を付加します。 |
DIVFMT::PAD_ZERO_SIGN_SPACE | 不足桁は0 で埋め、正の値の場合は+ 符号の替わりに空白を付加します。 |
例
//// div_result_i32オブジェクトから直接出力
Serial << div100(-1234) << crlf;
// 結果: -12.34
//// 3桁で出力します
Serial << div100(3456).format(3, DIVFMT::PAD_ZERO_SIGN_PLUE) << crlf;
// 結果: +034.56
//// c_str()を使ってconst char*を得る
char str1[128];
auto x = div100(-5678);
mwx_snprintf(str1, 16, "VAL=%s", x.format.c_str()); // const char*
Serial << str1;
// 結果: VAL=-56.78
背景
TWELITE BLUE/RED では除算はコストが高い演算であるため、目的を限定した除算アルゴリズムを追加した。
ライブラリ内では温度・湿度といった一部のセンサー値、100倍の値(25.12℃なら2512)を用いて表現しているため、100で割った商と余りを得るための簡素な手続きを定義した。
dev_result_i32::format()
については、書式出力を行う際の煩雑さを避けるためである。
3.9 - Scale utils
x*1000/255
)の替わりに乗算とビットシフトによって近似計算します。
static inline uint8_t scale_1000_to_127u8(uint16_t x)
static inline uint16_t scale_127u8_to_1000(uint8_t x)
static inline uint8_t scale_1000_to_255u8(uint16_t x)
static inline uint16_t scale_255u8_to_1000(uint8_t x)
static inline uint8_t scale_1000_to_256u8(uint16_t x)
static inline uint16_t scale_256u16_to_1000(uint16_t x)
scale_1000_to_127u8()
0..1000 を 0..127 にスケールします。(16646*x+65000)>>17
を用いて近似計算します。
scale_127u8_to_1000()
0..127 を 0..1000 にスケールします。(2064000UL*x+131072)>>18
を用いて近似計算します。
scale_1000_to_255u8()
0..1000 を 0..255 にスケールします。(33423*x+65000)>>17
を用いて近似計算します。
scale_255u8_to_1000()
0..255 を 0..1000 にスケールします。(1028000UL*uint32_t(x)+131072)>>18
を用いて近似計算します。
scale_1000_to_256u8()
0..1000 を 0..256 にスケールします。(33554*x+66000) >> 17
を用いて近似計算します。
注: x=999,1000は計算値が256になりますがuint8_t
の範囲として255を返します。
scale_256u16_to_1000()
0..256 を 0..1000 にスケールします。(1028000UL*uint32_t(x)+131072)>>18
を用いて近似計算します。
背景
ハードウェアに設定すべき値は0..255といった2進数が前提になることが多く、ユーザアプリケーションで取り扱う数は0..1000といった10進数基準のほうが扱いやすい。これらのスケール変換に除算を行わない式を定義した。
3.10 - pnew()
template <class T, class... Args>
T* pnew(T& obj, Args&&... args) {
return (T*)new ((void*)&obj) T(std::forward<Args&&>(args)...);
}
例えば以下のように利用します。コンストラクタ引数を与えることもできます。
class my_class {
int _a;
public:
my_class(int a = -1) : _a(a) {}
};
my_class obj_1; // このコンストラクタは呼ばれない!
my_class obj_2; // このコンストラクタは呼ばれない!
void setup() {
mwx::pnew(obj_1); // my_class obj_1; に相当
mwx::pnew(obj_2, 2); // my_class obj_2(2); に相当
...
}
背景
コンパイラの制約のためグローバルオブジェクトのコンストラクタが呼び出されないため、これを初期化する方法は配置newを使用する方法が挙げられます。しかしながら、配置new(placement new)の構文は煩雑に見えるため。
他にstd::unique_ptr
(または eastl::unique_ptr
)を用いる方法もある。
std::unique_ptr<my_class> obj_3;
void setup() {
obj_3.reset(new my_class(3));
// TWELITE マイコンでは new は1回だけ確保し delete はできないため、
// 事実上グローバルオブジェクトと同等です。
}