« 2.54mmピッチのピンヘッダに挿したソケットが抜ける | トップページ | BSch3V CE3ファイルからコメント文字をピックアップ »

2022年11月 6日 (日)

Arduino map関数をfloatに

map関数の処理は、
 C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\WMath.cpp
ヘッダーファイルは、
C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\Arduino.h
ここで記述されています。

中味がこれ。

long map(long x, long in_min, long in_max, long out_min, long out_max) {
 return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

ちょっとわかりにくいので、図にします。

M11_20221106164101

傾き「a」を出す計算が重要なわけです。

long値での割り算ですんで切り捨てで処理されます。
ですので、map関数が出した答えを使って四捨五入しても、
間に合いません。 ※<5>の処理
ということは、map関数自体をfloatにして、内部の割り算での
切り捨てをなくします。

/***** 線形補間  *****/
// floatで
float mapf(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

「long」を「float」に置き換えるだけ
簡単なのも。

先日のA/D値を補正する例だと、

/***** A/D値補正    *****/
// in ad : 0~1023 A/D値(10bit)
//   ch : ch1sitei
// 補正テーブルを使ってキャリブレーション
short adadj(word ad, byte ch)
{
float a;
long g;
  a = mapf((float)ad,       // floatにしたmap(線形補間)
      (float)pgm_read_word(&adj_tbl[ch].x0), // in_min
      (float)pgm_read_word(&adj_tbl[ch].x1), // in_max
      (float)pgm_read_word(&adj_tbl[ch].y0), // out_min
      (float)pgm_read_word(&adj_tbl[ch].y1)); // out_max
  g = lround(a);     // 四捨五入してlongに
              // 必要ならshort範囲をチェック
  return (short)g;    // shortにしてリターン
}

浮動小数点の四捨五入には、関数「lround()」という便利なの
があります。
少数以下を四捨五入してlongの整数に変換してくれます。
+/-を判断してくれるので、
  マイナス、右(数値が大)か左(数値が小)、どっちにそろえる
  ねん? も問題なし。
こんな結果になります。

 lround(+2.3) = 2 lround(+2.5) = 3 lround(+2.7) = 3
 lround(-2.3) = -2 lround(-2.5) = -3 lround(-2.7) = -3

昔のCだと、lroundは標準関数には無いんですよね。

これで、「A/D値 → 電圧値」の変換が、実値(A/D値の読みと
テスターで測定した電圧)で可能になります。
変換テーブルは実値をwordにして2バイトで。
電圧だと「12345mV」などと5桁で表現できます。

ただ・・・floatを使ってmath.hをインクルードすると
1.5kバイトほどプログラムが大きくなっちゃいます。


※関連
Arduino なんとかして誤用を正したい:A/Dの1/1023とmap関数
Arduino 10bit A/D値をmap関数でスケーリングする例
ミスが広まる 1/1023 vs 1/1024
5chサーミスタ温度計のA/D入力、map関数を使って補正

|

« 2.54mmピッチのピンヘッダに挿したソケットが抜ける | トップページ | BSch3V CE3ファイルからコメント文字をピックアップ »

Arduino」カテゴリの記事

コメント

map関数を浮動小数点にすると・・・
  y = map(x, 0, 1023, 0, 255);
この式でも、半値の x = 512 が
  y = 127.6246 → 128
となり、四捨五入して整数にすると合っている
ように見えます。

しかし、2倍の 2048 をxに入れると
  y = 510.4985 → 510
と、正しい 512 から離脱しているのがわかります。

3/4 の 768 でも、
  y = 191.4369
となり、四捨五入しても、正しい 192.000 とは異なって
しまいます。

10bit → 8bit の1/4問題、浮動小数点にして四捨五入
だけでは解決できません。

  y = map(x, 0, 1023, 0, 255);

そのものが、間違っているのです。

投稿: 居酒屋ガレージ店主(JH3DBO) | 2022年11月 8日 (火) 09時02分

コメントを書く



(ウェブ上には掲載しません)




« 2.54mmピッチのピンヘッダに挿したソケットが抜ける | トップページ | BSch3V CE3ファイルからコメント文字をピックアップ »