ADCの使い方 ----アナログウォッチドッグ----
2012.3.9 訂正 ADCを使うときのclockがHSIだけだというウソを訂正しました.
2012.1.21 訂正 ADC clockが1〜4MHzに制限されているのでそれに準拠し、訂正しました.
アナログウォッチドッグって何でしょうか?
ウォッチドッグというのは番犬のことです.
CPUでウォッチドッグというとウォッチドッグタイマってやつが有名です.ある制限時間以内にカウンタをクリアしないでいる
と、カウンタがoverflowしてアラートを出すという機能です.これはCPUが無限ループに入ってハングアップしていることを検知するために利用される機能
です.
アナログウォッチドッグというのは、AD変換値がXX以下になったらダメ、あるいはYY以上になったらダメというダメ状況を検知しアラートを出す機能です.STM8Sにはアナログウォッチドッグ機能があるので使ってみましょう.
ADCの使い方で10ch電圧計を解説しましたので、わたしとしては、この10ch電圧計にアナログウォッチドッグ機能を追加したいと思いました.しか
し、STM8Sの仕様書を読むと、STM8SのADCのハードウエア機能を利用してサクッとやることは不可能でした.どうして不可能なのかを、まず説明します.
AD変換モードについて
ADCには、一発変換モードとか、連続変換モードがあります.アナログウォッチドッグ機能が動くかどうかはこのAD変換モードによりますので、まずはAD変換モードについて説明します.
なお、以下はdocumentを読んだだけの情報であり、サンプルプログラムで動作確認したわけじゃないので、関数の扱いは間違えているかもしれませんのでご注意のほどよろしく.
single mode (一発変換モード)
●一発変換モードではAINxを一発だけ変換して、止まります.
●一発変換モードにするには、この関数を使います.
ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC1_CHANNEL_x,...........
●ADC1_StartConversion()で変換開始させます.
●ADC1_GetConversionValue()で変換結果をu16で取り出します.
●変換終了(EOC)で割り込みをかけさせることもできます.
ADC1_ITConfig( ADC1_IT_EOCIE , ENABLE)
●アナログウォッチドッグ機能はこのモードで使えます.
continuous mode (連続変換モード)
●連続変換モードではAINxをグルグルと変換し続けます.
●連続変換モードにするには、この関数を使います.
ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, ADC1_CHANNEL_x,...........
●その他は一発変換モードと同じです.
●アナログウォッチドッグ機能はこのモードで使えます.
bufferd comtinuous mode (バッファ連続変換モード)
●AINxをグルグルと変換し続けますが、10本のバッファに10回分のAD変換データを格納して、あとでそのバッファを読めばいいという仕組みのモードです.
●バッファ連続変換モードにするには、この関数を使います.
ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, ADC1_CHANNEL_x,...........
さらに、バッファをオンするためにこの関数を使います.
ADC1_DataBufferCmd(ENABLE)
●ADC1_StartConversion()で変換開始させます.
●ADC1_GetBufferValue(x)でx番目の値を取り込みます.
●バッファが満杯になったら、EOC割り込みを発生させることもできます.
ADC1_ITConfig( ADC1_IT_EOCIE , ENABLE)
●連続変換モードの一種ですから、手早くバッファを読み終えないとデータロストしてしまいます.
データロストしたことを警告するoverrunフラグもあります.
●アナログウォッチドッグ機能は、バッファを使うこのモードで使えません.
sigle scan mode (一発スキャンモード)
●AIN0〜AINxを連続してAD変換します.x<=9は任意に設定できます.
●一発スキャンモードにするには、この関数を使います.
ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC1_CHANNEL_x,...........
さらに、バッファをオンするためにこの関数を使います.
ADC1_DataBufferCmd(ENABLE)
●ADC1_StartConversion()で変換開始させます.
●ADC1_GetBufferValue(x)でx番目の値を取り込みます.
●バッファが満杯になったら、EOC割り込みを発生させることもできます.
ADC1_ITConfig( ADC1_IT_EOCIE , ENABLE)
●連続変換モードの一種ですから、手早くバッファを読み終えないとデータロストしてしまいます.
データロストしたことを警告するoverrunフラグもあります.
●アナログウォッチドッグ機能は、バッファを使うこのモードで使えません.
continuous scan mode (連続スキャンモード)
●AIN0〜AINxを連続してAD変換します.x<=9は任意に設定できます.
●連続スキャンモードにするには、この関数を使います.
ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, ADC1_CHANNEL_x,...........
さらに、バッファをオンするためにこの関数を使います.
ADC1_DataBufferCmd(ENABLE)
●そのほかは一発スキャンモードと同じです.
●アナログウォッチドッグ機能は、バッファを使うこのモードで使えません.
AD変換モードの説明からアナログウォッチドッグについて判ったことは、AD変換モードによっては、アナログウォッチドッグ機能を使えません.
10ch電圧計はどうかというと、連続スキャンモードで動いているので、アナログウォッチドッグ機能と両立できないってことがわかります.
というわけで、10ch電圧計にアナログウォッチドッグ機能を追加するのはあきらめます.
ついては、応用の広がりはあまり面白くはありませんが、ADCをアナログウォッチドッグのためだけに活用するサンプルプログラムを以下で説明することにします.
どんなものを作るか?
AIN9には実験のためボリウムを取り付けておきます.
アナログウォッチドッグ(AWD)はAIN9の電圧を監視します.
ADCは連続変換モードで動かしっぱなしになるようプログラミングします.
タイマ3割り込みによって0.1SecごとにAD変換値をTerminal Softに表示します.
AIN9のAD変換値 < 200 ならAWD割り込みを発してTerminal Softにアラートを表示します.
AIN9のAD変換値 > 800 ならAWD割り込みを発してTerminal Softにアラートを表示します.
Terminal softの表示例
ボリウムを回してADC電圧を 0V〜3.3Vまで上げていった場合のTerminalSoftの表示.
ADC= 156 mV= 502 WATCHDOG ALERT!!!
ADC= 163 mV= 525 WATCHDOG ALERT!!!
ADC= 166 mV= 534 WATCHDOG ALERT!!!
中略
ADC= 193 mV= 621 WATCHDOG ALERT!!!
ADC= 199 mV= 641 WATCHDOG ALERT!!! アラートはどばーっと表示されます
ADC= 199 mV= 641 WATCHDOG ALERT!!!
ADC= 244 mV= 786
ADC= 288 mV= 928
ADC= 331 mV=1066 通常は0.1Secごとに表示されます
ADC= 362 mV=1166
ADC= 397 mV=1279
ADC= 437 mV=1408
中略
ADC= 539 mV=1737
ADC= 541 mV=1743
ADC= 541 mV=1743
ADC= 540 mV=1740
ADC= 738 mV=2378
ADC= 768 mV=2475
ADC= 796 mV=2565 WATCHDOG ALERT!!! なんで796で反応しているのかは謎です
ADC= 800 mV=2578 WATCHDOG ALERT!!!
ADC= 805 mV=2594 WATCHDOG ALERT!!!
ADC= 801 mV=2581 WATCHDOG ALERT!!!
中略
ADC= 825 mV=2658 WATCHDOG ALERT!!!
ADC= 827 mV=2665 WATCHDOG ALERT!!!
ADC= 831 mV=2678 WATCHDOG ALERT!!!
ADC= 832 mV=2681 WATCHDOG ALERT!!!
ADC= 832 mV=2681 WATCHDOG ALERT!!!
基板写真
茶色の基板がボリウムを載せた基板.
右下のコネクタはLCDを取り付けたときのコネクタの名残なので無視してください.
ハードウエアの製作
@USBシリアル変換回路
まず、UARTをUSBに変換する回路を、STM8S-DISCOVERYにどうやって取り付けるかを説明します.
秋月のUSBシリアル変換基板をSTM8S-DISCOVERYに取り付けます.
回路図は下記です.配線は上記回路図の4本です.
●STM8SのPD5とPD6には、UARTの送信信号と受信信号が出てきますので、それをUSBシリア ル基板に配線しています.
●STM8S-DISCOVERYでは、電源はFLASH焼き回路から5V or 3.3Vが供給されますが、これを切断し、USBシリアル変換基板から3.3Vが供給されるようにします.
ただし5Vじゃダメってことではありません.3.3Vにしたのはなんとなくです.
そのために、ジャンパーをつぎの1)2)のように設定 してください.
1) USBシリアル変換基板のジャンパーピンは、下図の赤色の位置にします.
2) STM8S-DISCOVERYのJP1のジャンパーピンは、外しておきます.
USBシリアル変換基板の取付完了状態はこのようになります.
右の黄色の囲いがUSBシリアル変換回路です.同基板に刺さっているMini-USBがPCのUSBへ行きます.
中央の水色の囲いがSTM8Sマイコンです.USBシリアル変換回路と配線されています.
左の赤い囲いは、ブロック図には描きませんでしたがSTM8Sを焼くために必須な回路です.同基板に刺さっているUSBケーブルは、STM8Sを焼くためのケーブルです.やはりPCに接続します.
この回路は、PCのUSBポートを都合2ヶ占有することになります.
プログラムを焼いてしまえば、焼き用のUSBケーブルは不要になるわけで、下図のようにUSBを外してしまいたくなるのが人情です.
ところが、USBケーブルを外しただけでは、STM8Sにリセットがかかってしまって動かなくなってしまいます.
そこで、下の写真の丸の中にあるSB1とSB2をハンダごてでOPENにしてやればリセット線が切断されるので、STM8Sが動きます.
この方法は憶えておきましょう.
AAIN9へボリウムとりつけ
CN3にはAIN0〜AIN9が集まっています.
ボリウムを取り付ける場所はCN3のAIN9です.
ボリウムは1kΩ〜10kΩぐらいならOKです.
ソースコードの解説
サンプルプログラムはこちらからダウンロードできます.
main.c
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
文字列操作のためCの標準ライブラリをインクルードします.
#include "stm8s.h"
STMicroのライブラリをインクルードします.
#include "usrlib-uart.h"
Terminal Softと通信するためにUARTの自作ライブラリをインクルードします.
訂正 2012.3.9
#define fMASTER 16000000 // 16MHz
#define TIM3_FREQ 10 // 10Hz
#define TIM3_PRSC TIM3_PRESCALER_512
#define TIM3_ARR ((u16)(fMASTER/512/TIM3_FREQ)-1) // 16MHz/512/10-1=3124
0.1Secのタイマ割り込みで、ADCを動かします.つまりサンプル周波数10Hzというわけです.
そのためにTIM3から0.1Sec毎に割り込みをかけてもらいます.
TIM3のプリスケーラTIM3_PSRCとカウンタmax値TIM3_ARRをdefine文で定義しました.
やりたい分周比は、4MHz/128/3125 = 10 という計算です.
char UARTstrTX[100];
Terminal Softに文字列を送信するためのバッファです.100文字も要らないか?
void main(void)
{
メインルーチンの始まりです.
訂正 2012.3.9
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
外部水晶の16MHzをclockとして使います.
TIM3_DeInit();
TIM3_TimeBaseInit(TIM3_PRSC,TIM3_ARR); // 0.1sec timer
TIM3_ITConfig(TIM3_IT_UPDATE, ENABLE);
TIM3_Cmd(ENABLE);
0.1Secで割り込みをかけさせます.
ADC1_DeInit();
ADCの設定の始まりです.念のため初期化.
ADC1_Init(@ADC1_CONVERSIONMODE_CONTINUOUS,
AADC1_CHANNEL_9, BADC1_PRESSEL_FCPU_D4, CADC1_EXTTRIG_TIM, DDISABLE,
EADC1_ALIGN_RIGHT, FADC1_SCHMITTTRIG_9, GDISABLE);
@連続変換モードにするための引数です.
AAIN9をAD変換すると設定します.次の選択肢があります.
ADC1_CHANNEL_0
ADC1_CHANNEL_1
ADC1_CHANNEL_2
ADC1_CHANNEL_3
ADC1_CHANNEL_4
ADC1_CHANNEL_5
ADC1_CHANNEL_6
ADC1_CHANNEL_7
ADC1_CHANNEL_8
ADC1_CHANNEL_9
B訂正 2012.3.9
ADC clock を fMASTER/4=4MHzにします.次の選択肢があります.
ADC1_PRESSEL_FCPU_D2 Prescaler selection fADC1 = fcpu/2.
ADC1_PRESSEL_FCPU_D3 Prescaler selection fADC1 = fcpu/3.
ADC1_PRESSEL_FCPU_D4 Prescaler selection fADC1 = fcpu/4.
ADC1_PRESSEL_FCPU_D6 Prescaler selection fADC1 = fcpu/6.
ADC1_PRESSEL_FCPU_D8 Prescaler selection fADC1 = fcpu/8.
ADC1_PRESSEL_FCPU_D10 Prescaler selection fADC1 = fcpu/10.
ADC1_PRESSEL_FCPU_D12 Prescaler selection fADC1 = fcpu/12.
ADC1_PRESSEL_FCPU_D18 Prescaler selection fADC1 = fcpu/18.
ADCのclockは1〜4MHzという制限がありますので、適切なfADC1になるように設定する必要があります.
ここでは16MHz÷4=4MHzにしています.
ちなみに、FCPUという名前でdefineされているのでfCPUを分周するかのごとく誤解してしまいますが、正しくはfMASTERを分周します.これはSTM8Sのライブラリを書いた人の誤解による誤記と思われます.
fMASTERはこのサンプルプログラムでは16MHzですから、ADC clockを8MHz〜888kHzの範囲で選択できます.
ただし、AD変換レートが8MHz〜888kHzになるわけではありません.
なぜなら、1回のAD変換に14clock必要だからです.
CD外部トリガをDISABLEします.
E10bitのAD変換値を16bitに納めるために、右詰にします.左詰にしたければ_LEFTにします.
ADC1_ALIGN_LEFT Data alignment left.
ADC1_ALIGN_RIGHT Data alignment right.
FGAIN9のシュミット特性をDISABLEにします.DISABLEにしたほうが消費電力が小さくなるそうです.DISABLEにしなかったとしてもAD変換は正常に動くとわたしは感じています.
AINxの選択肢はこれです.
ADC1_SCHMITTTRIG_CHANNEL0
ADC1_SCHMITTTRIG_CHANNEL1
ADC1_SCHMITTTRIG_CHANNEL2
ADC1_SCHMITTTRIG_CHANNEL3
ADC1_SCHMITTTRIG_CHANNEL4
ADC1_SCHMITTTRIG_CHANNEL5
ADC1_SCHMITTTRIG_CHANNEL6
ADC1_SCHMITTTRIG_CHANNEL7
ADC1_SCHMITTTRIG_CHANNEL8
ADC1_SCHMITTTRIG_CHANNEL9
ADC1_ITConfig(ADC1_IT_AWDIE, ENABLE);
アナログウォッチドッグで割り込みをかけるように設定しています.
ADC1_AWDChannelConfig(ADC1_CHANNEL_9, ENABLE);
アナログウォッチドッグが監視するのをAIN9にするように設定しています.
ADC1_SetHighThreshold(800);
ADC1_SetLowThreshold(200);
アナログウォッチドッグする閾値を設定しています.上が800.下が200です.
ちなみに、ADCは10bitですので、AD変換値は0〜1023までです.
ADC1_Cmd(ENABLE);
ADC機能をオンに.
ADC1_StartConversion();
連続変換スタート.
UART2_DeInit();
UART2_Init((u32)115200,
UART2_WORDLENGTH_9D, UART2_STOPBITS_1, UART2_PARITY_ODD,
UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);
UART2_ITConfig(UART2_IT_RXNE_OR, ENABLE);
UART2_Cmd(ENABLE);
UARTの設定です.UARTの解説を参照してください.
GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST); // LED
インジケータとしてLEDをチカチカさせています.
enableInterrupts();
割り込みイネーブル.
while(1){ } /*Main loop*/
}
メインルーチンの最後の無限ループです.
u16 adc;
float mV;
void Tim3Update(void) interrupt 15 // 0.1Sec interrupt
{
GPIO_WriteReverse(GPIOD,GPIO_PIN_0); // LED flash
adc=ADC1_GetConversionValue(); // get ADC value
mV=(float)adc/1024.0*3300.0; // convert to voltage
sprintf(UARTstrTX,"ADC=%4d mV=%4d\r\n",(int)adc,(int)mV);
SerialPutString(UARTstrTX);
TIM3_ClearFlag(TIM3_FLAG_UPDATE);
return;
}
タイマ3の割り込み番号は15です.
0.1Secごとにタイマ3が割り込みを発生させて、ここへ飛んできます.
やってることは判ると思います.
AD変換値を取ってきて、Terminal Softに表示してます.
ADCはせわしなくAD変換していますけど、表示は0.1Secごとなので、無駄にAD変換しているわけです.
void AWD(void) interrupt 22 // analog watch dog interrupt
{
adc=ADC1_GetConversionValue(); // get ADC value
mV=(float)adc/1024.0*3300.0; // convert to voltage
sprintf(UARTstrTX,"ADC=%4d mV=%4d WATCHDOG ALERT!!!\r\n",(int)adc,(int)mV);
SerialPutString(UARTstrTX);
ADC1_ClearFlag(ADC1_FLAG_AWD);
}
アナログウォッチドッグの割り込み番号は22です.
これもAD変換値を取ってきて、Terminal Softに表示しています.アラートも表示します.
ADCはせわしなくAD変換しており、AWDを検知するとその都度ここへ飛んできます.
なので、アラートはダーッとたくさん表示されます.0.1Sec周期ではありません.
もっとも、ソフトウエアに支配される0.1Secごとにアラートを出してくれるのではあまり御利益がないわけでして、ハードウエア的にアラートを出しまくってくれるという様が見えているわけです.