RP2040のADCの誤差の確認と簡単な補正プログラムのテスト
◆まえがき
Raspberry Pi pico のCPUのRP2040 のADコンバーターは12ビットですが、バグ(エラッタ RP2040-E11)があるため実力は 7-8ビットくらいしかありません。
この記事ではその状況を確認すると共に、簡単な補正プログラムを作って効果を確認してみます。なお、この問題は現時点のもので、エラッタが修正されればこの記事は意味の無いものになるはず、と言うかそうなって欲しいと思います。
◆ADコンバーターの誤差
RP2040のデーターシートの 4.9.4. INL and DNLの項で解説があります。(release 1.8)
上図はRP2040データーシートから抜粋したものです。特定の値で(512, 536, 2560, 3584)で特性が不連続になっています。
◆現象の確認
状態を確認するために簡単な回路とプログラムを用意しました。
・回路図
GP16の出力をR1とC1で積分してADコンバーター(GP26)に入力しています。適当な周期でGP16をトグルすることで問題の電圧付近のADCの挙動を探るのが狙いです。
◆外観
・プログラム
// RP2040のADCの誤差補正テスト 20220623PicoAdcTest.ino // void setup() { pinMode(16, OUTPUT); pinMode(23, OUTPUT); Serial.begin(115200); analogReadResolution(12); // ADCのフルスケールを12ビットに設定 digitalWrite(23, HIGH); // ノイズ対策のため電源をPWMモードに設定 digitalWrite(16, LOW); delay(500); } void loop() { int d, dc; digitalWrite(16, HIGH); for (int i = 0; i < 30; i++) { // 30, 90, 150, 210から選ぶ d = analogRead(26); // ADC0の値を読む dc = adcCorrection(d); Serial.print(d); Serial.print(", "); Serial.println(dc); delay(10); } digitalWrite(16, LOW); for (int i = 0; i < 210; i++) { // 210, 150, 90, 30 d = analogRead(26); dc = adcCorrection(d); Serial.print(d); Serial.print(", "); Serial.println(dc); delay(10); } } int adcCorrection(int x) { // RP2040のADCの非直線性補正(RP2040-E11対策) int y; if (x >= 3584) y = x + 32; // fsの7/8のポイントのオフセット補正 else if (x == 3583) y = x + 29; // 遷移のステップを少し埋める else if (x == 3582) y = x + 27; // 同上 else if (x >= 2560) y = x + 24; // fsの5/8 else if (x == 2559) y = x + 21; else if (x == 2558) y = x + 19; else if (x >= 1536) y = x + 16; // fsの3/8 else if (x == 1535) y = x + 13; else if (x == 1534) y = x + 11; else if (x >= 512) y = x + 8; // fsの1/8 else if (x == 511) y = x + 5; else if (x == 510) y = x + 3; else y = x; // 該当しなければそのままの値 return y; }
◆状態の確認
シリアルプロッタを使って特性を見ると便利です。
・511付近
グラフはADCの生の値のプロットです。つまり補正結果はコメントアウトして出力しないようにしています。
入力電圧(横軸)が変化しても、特定の値(このグラフでは511)でADCの値(縦軸)が引っかかって変化していません。
・511付近(電源をPFMに設定)
話が脱線しますが、これは電源の制御をPWMからPFMに設定した状態です(digitalWrite(23, HIGH);をコメントアウト)。大きなノイズが乗っていてとても使い物になる感じではありません。ちなみに、デフォルトはこの状態なので怖いです。
◆単純な補正
プログラムでステップの補正を行った状態です
・511付近
青線が補正前、赤線が補正後です。段付きの前後でシフトしていた特性が直線になっています。
もちろん、段が付いた部分の情報を再現することは出来ません。また、切り欠きになっている部分で一度に値が8増えるので大きなノイズが発生しそうです。
◆少し切り欠きを緩和
ここからが上に示したプログラムの補正を全部使った状態です。
・511付近
切り欠きの前後に中間的な値を入れて大きな変化が起きないように改良したものです。具体的には一度に+8させていたのを +3, +2, +3 と3回に分けて変化させています。なお、こんなことやってるのでミッシングコードが発生します。
それでも段付きは発生しますが、誤差(仮想直性からはみ出した三角形の面積)は小さくなっているはずです。
・3548付近
下の方の引っ掛かりで発生した誤差が累積されているので、補正の有無による値の差が大きくなっていますが、補正の内容は同じです。
◆動画
◆まとめ
RP2040のADCのエラッタは、特定の入力電圧で電圧の刻みが大きく(約8LSB)なっていることが確認出来ました。この問題についてはいろいろな方が記事で言及されていますが、改めて自分で確認することが出来て良かったです。
簡単な補正プログラム (adcCorrection) を作ってみました。これを使うとリニアリティが良く見えるようになるかも知れません。しかしAD変換のステップが飛んだことで失われた情報が再現出来る訳では無いので、やらない方が良い場合だってありそうです。
補正プログラムを通すとフルスケールが4095から、4127に増えてしまいます。これが問題になる場合は最大値が4095になるように再度マップすると良いと思います。
- 関連記事
-
- 外側に回転子を持つ誘導モーターを動かす
- ラズピコを外部の電源レギュレーターで動かす
- RP2040のADCの誤差の確認と簡単な補正プログラムのテスト
- Raspberry Pi Picoで72LEDのクリスマスツリーイルミネーションを作る
- 3軸ジャイロを使ってアバターの表情を動かす、「きょろきょろ君」を作って見た