IoT何をいまさら(100) ATSAMD51、アナログコンパレータ AC を使ってみる

Joseph Halfmoon

Microchip社 ATSAMD51は、ADコンバータ、DAコンバータだけでなくアナログコンパレータ(AC)も搭載しています。2つの電圧を比較して大きい、小さいを判断できる回路。各種設定はソフトウエアですが、完全ハードウエアのコンパレータ的動作も可能。ただし今回は諸般の事情あり、割り込み受けです。

(実験に使用したソース全文は末尾に)

アナログコンパレータは専用のアナログICあり、またオペアンプで作ることもでき、アナログ世界とデジタルとを結ぶ重要なコンポーネントじゃないかと思います。

常づねモダンなペリフェラルセットを搭載しているMCU(マイコン)だと感服しております、Microchip社のArm Cortex-M4コア搭載ATSAMD51 マイコンは、アナログコンパレータを2チャンネル搭載です。2つ使えばアナログ信号がある電圧範囲に入っているとかいないとかも識別できます。何かと便利じゃないかと思いつつも、今回実験するにあたり、いくつか困ったことがありました。

1点目はデータシートの記述です。外付けのアナログコンパレータであれば、通常2本の入力端子、1本の出力端子を持ちます。ATSAMD51マイコン搭載のアナログコンパレータも最大2本の外部入力と1本の外部出力を持つことができます。ただ、マイコン内蔵のペリフェラルなので、必ずしも外部入出力に接続する必要はありません。入力には内部信号を割り当てることができ、出力は割り込みやイベントに接続することができます。しかし今回、最初目論見ましたのは、

入力2端子、出力1端子を全部外部接続したら普通のアナログコンパレータ

としてテストできるんじゃね、ということでした。そのため、アナログコンパレータの入力と出力がチップのどの端子に接続できるのか調べました。データシートには、多数の機能を多数の端子にマッピングするため

I/O Multiplexing

という5ページにまたがる巨大な表が掲載されています。それを端から眺めて調べました。ACへの入力端子は直ぐに発見できました。ADコンバータやDAコンバータなどと同じカテゴリに分類されています。入力はOK。しかし、ACの出力端子が見当たりませぬ。データシート全文(2000ページ以上あります)を検索したり、関係ありそうな部分を読んだりしていてようやく判明しました。表の末尾の下にある注釈を読めばよかったのです。1行引用させていただきます。

The AC has analog signals on the peripheral function B and digital signals on the peripheral function M.

Peripheral function Bはアナログなやつらのグループで何度も眺めていた部分。Peripheral funciton Mは、GCLKという各種ペリフェラル動作の元になるクロック関係のグループです。これを見てようやく調べるところが分かりアナログコンパレータの出力端子を発見。見つかってよかった。けれど、トータル2129ページのデータシートから上の一文を見つけ出すのはかなり辛かったです。だいたいGLCKと混ざっているなんて予想外だったし(分類のところのGCLKの下に小さくACと書いてありました。頭からここには無いと思い込んでいた私が悪い。)

チップ上での端子名が分かったので意気揚々とボード上での端子を調べていて第2の、そして決定的なガッガリに直面しました。ターゲットボードはSeeed Studio社のWio Terminalです。その回路図見ていたら、ACの出力端子にアサイン可能な2端子とも、ボード内部の回路で使われているじゃあ~りませんか。しょうがない、ノージンジャー。直接出力は断念と相成りました。

ACの出力端子が使えずとも、ATSAMD51の強力なイベントシステムを駆使すれば、ACの動作をソフトウエアの介在なしに外部に出力できるのでは、と考えました。過去、外界とは無関係なTRNGのイベントをイベント経由で端子に出力させるようなこともやってみています。ACでもできるんじゃね。しかし、これも空振りでした。ACが発生するイベントはコンバージョンの開始イベントみたいで、コンパレータの出力そのものの反映ではないようです。

結局のところ、Wio Terminal上でコンパレータの出力を外部端子に出力するのは、ACの変化で割り込みをかけ、割り込みハンドラの中で端子に出力する、という方法に落ち着きました。ソフトウエアが介在する分、ACのハードウエア動作と外部端子の反応までの時間がかかるし、時間もバラつく可能性があります。まあ、信号が遅ければ、気にしなくても良いか?

ACの設定

2本のアナログ信号電圧の大なり、小なりを判定するだけのACですが、設定に関しては至れり尽くせりというか、いろいろソフトウエアで設定できる項目が多いのです。ざっくりとして以下のような感じ。

比較対象の電圧ソースの選択。外部の信号同士を比較することも可能ですが、内部の電圧源と外部信号を比較することもできるのです。内蔵のDAコンバータの出力を比較信号にとることもできれば、内蔵のバンドギャップリファレンスも使えます。それ以外にもVDO SCALERという抵抗ラダーも内蔵しており、電源電圧に対して64段階の電圧比較も可能。今回は、VDO SCALER使って電源電圧の真ん中辺に比較電圧を設定してみました。いろいろ選択の範囲が広い分、使用するソースによってそれぞれ適切な設定が必要です。メンドイかも。

また、ノイズなどに対して安定的に動作させるために、コンパレータの特性にヒステリシスを持たせたり(これも3段階可能)、多数決論理のフィルタ(投票するのは3人、5人と選択できます)を通したりできるのです。これらの設定も必要。今回は、ヒステリシスは最大、フィルタは無しにしてみました。この辺をどう設定するか監視するアナログ信号次第でしょう。

また、連続動作かワンショットか、端子の極性をSWAPするかなどいろいろオプションがあります。そして、割り込みやイベントに対する接続設定もあるので、真面目に設定していくと考えなければいけない項目実に多数。

それだけいろいろ出来るということですが、メンドイことの裏返し。今回設定の詳細は、末尾の setupAC0() 関数をご覧くだされ。

動作確認

末尾のプログラムをビルド(VSCode+PlatformIO環境)した後、オブジェクトをアップロードして起動、アナログコンパレータの試験のためにDigilent AnalogDiscovery2を以下のように接続しました。

  • アナログコンパレータの外部入力(PA04端子)に信号ジェネレータから三角波を印加。周波数100Hz、振幅1V、オフセット1.5V。同じ端子に波形観察のためオシロのCH1(黄)を接続
  • アナログコンパレータの割り込みハンドラ内でアナログコンパレータの出力と同じレベルを外部端子(PA06端子)に出力しています。この端子を観察するために、オシロのCH2(青)を接続。

接続した様子が以下に。

AC_DUT取得したオシロ波形は以下です。出力方形波(青)の立ち上がりと立下りに時間カーソルをあわせ、その時間位置での黄色(入力三角波)の電圧レベルを横の電圧カーソルで示しています。

AC_INTR_TIM_WAVE先ほども述べたように、コンパレータの入力である三角波からハードウエアだけを介して矩形波出力が出ているわけではなく、途中、割り込みハンドラを介しているのでタイミングは「成り行き」といわざるを得ません。しかしそれでも200mVくらいのヒステリシスがあるように見える(実際の設定は150mV)ので、それなりにコンパレータの設定を反映しているみたいです(本当か?)

設定めんどいけれど、結構使えそう。

IoT何をいまさら(99) ATSAMD51、DMAで1バイト転送成功までの長い道 へ戻る

IoT何をいまさら(101) ATSAMD51、ICM、ハッシュ計算をする専用DMAC へ進む

実験に使用したソース全文

PC上の VSCode+PlatformIOで、ターゲットボード Seeed Wio Terminal、プラットフォームはArduinoとして設定して確認したコードです。

#include <Arduino.h>

// GPIO13 (WIO:A6/D6, SAMD51:PA04) 
// GPIO26 (WIO:A8/D8, SAMD51:PA06) 
#define GPIO13  (6)
#define GPIO26  (8)

uint32_t counter;

void AC_Handler(void) {
  if (AC->STATUSA.bit.STATE0 == 1) {
    digitalWrite(GPIO26, 1);
  } else {
    digitalWrite(GPIO26, 0);
  }
  AC->INTFLAG.reg = 0x13; //Clear all AC INT Flags
}

void setupAC0(uint8_t scl) {
  GCLK->PCHCTRL[32].reg = 0x41;  // Enable GCLK_AC <= Generator 1 (48MHz)
  MCLK->APBCMASK.bit.AC_ = 1; //Enable CLK_AC_APB
  NVIC->ISER[3] |= 0x04000000;  // Enable AC Interrupt #122 [3] bit 26
  PORT->Group[0].PMUX[2].reg = 0x01; //bit7-4: PA05, bit3-0: PA04, Function B(0x1)=Analog
  PORT->Group[0].PINCFG[4].bit.PMUXEN = 1; //PA04 Analog
  AC->CTRLA.bit.ENABLE = 0; // Disable AC
  while(AC->SYNCBUSY.bit.ENABLE == 1) {}
  AC->CTRLA.bit.SWRST = 1; // Software reset
  while(AC->SYNCBUSY.bit.SWRST == 1 ) {}
  AC->SCALER->bit.VALUE = scl;  // Set VDD volatage scaler value
  AC->COMPCTRL[0].bit.OUT = 0;  // No route to OUTPUT port
  AC->COMPCTRL[0].bit.FLEN = 0; // No filtering
  AC->COMPCTRL[0].bit.HYST = 2; // Hysteresis level = 150mV
  AC->COMPCTRL[0].bit.HYSTEN = 1; // Histeresis Enable
  AC->COMPCTRL[0].bit.SPEED = 3; // High speed
  AC->COMPCTRL[0].bit.SWAP = 0; // NO SWAP
  AC->COMPCTRL[0].bit.MUXPOS = 0; // IO pin 0 = PA04(A6)
  AC->COMPCTRL[0].bit.MUXNEG = 0x5; // VDD scaler
  AC->COMPCTRL[0].bit.RUNSTDBY = 0; // Disable during sleep
  AC->COMPCTRL[0].bit.INTSEL = 0; // Interrupt on toggle
  AC->COMPCTRL[0].bit.SINGLE = 0; // Continuous mode.
  AC->COMPCTRL[0].bit.ENABLE = 1; // Enable CH0
  while(AC->SYNCBUSY.bit.COMPCTRL0 == 1 ) {}
  AC->INTFLAG.reg = 0x13; //Clear all AC INT Flags
  AC->INTENSET.bit.COMP0 = 1; // Enable CH0 interrupt
  AC->CTRLA.bit.ENABLE = 1; // Enable AC
  while(AC->SYNCBUSY.bit.ENABLE == 1) {}
}

void setup() {
  Serial.begin(9600);
  while(!Serial);
  Serial.printf("ATSAMD51 AC test.\r\n");
  pinMode(GPIO26, OUTPUT);
  digitalWrite(GPIO26, 0);
  counter = 0;
  setupAC0(31);
}

void loop() {
  Serial.printf("%d \r\n",++counter);
  delay(1000);
}