今回は、ADコンバータプログラムの設定部分の解説です。
ADコンバータプログラム概要
ADコンバータ機能を利用したスイッチ回路の設計と実装が終わりましたので、今回からプログラムの作成をします。
第7回の説明で、ADコンバータ機能を利用する場合、ピンの設定を「アナログ」の「入力」に設定する必要があることを説明しました。実際にADコンバータ機能を利用する場合、追加の設定もありますので、今回の記事ではADコンバータ機能を利用する際の設定について詳しく説明します。2進数の考え方も出てきますので、必要な場合は復習しておきましょう。
ADコンバータ機能を利用する場合、以下の2種類の設定が必要です。
- ADコンバータを利用するピンを「アナログ」の「入力」設定にする
- ADコンバータ動作の設定をする
(1)の「アナログ入力」設定は第7回ですでに説明しましたが、ここでもう一度復習しておきましょう。
基礎編で作成したプログラムでは、main関数の最初の方のピンの設定部分で、ANSELAレジスタでデジタル設定にして、TRISAレジスタで入力ピンか出力ピンかの設定をしていましたよね。ADコンバータ機能を利用する場合、ピンの設定をANSELAレジスタで「アナログ」に、TRISAレジスタで「入力」に設定します。
次に、(2)のADコンバータ動作の設定を行います。例えばADコンバータで読み取った電圧値をレジスタにどのように格納するのか、などです。詳細の設定内容は、ADコンバータがどのように動いているか理解してから説明します。
ADコンバータの内部動作概要
(2)のADコンバータの動作に関する設定内容を理解するには、ADコンバータが具体的に内部でどのように動作しているのか、ある程度の理解が必要です。そこで、内部動作の概要について説明します。
以下の図は、PICマイコン内部のADコンバータモジュール部の概略を説明したものです。
ADコンバータ機能とその設定を理解するために重要なポイントを説明します。
PIC12F1822の内部にはADコンバータを行うモジュール(ハードウエア)は「1個」しかありません。一方、データシートをみると、ADコンバータは4ピン(3, 5, 6, 7番ピン)で使用できることになっています。この状況からわかることは、ADコンバータを利用する場合、どのピンをADコンバータの入力ピンとしてアナログ値を読み取るか、ということをPICマイコンに教えてあげる必要がある、ということです。これが必要な設定のひとつです。
次は「基準電圧」です。最初に謝らなくてはいけません。ごめなさい!
今までの説明で、「ADコンバータはピンの電圧を例えば0.5V、というようにアナログ値で読み取ります!」とドヤ顔でしたが、正確には違うんです。
ここで、ADコンバータの実際の働きについて説明します。この動作はPICマイコンに限らず、他のマイコンやArduino、Raspberry PiのADコンバータも同じですので十分理解するようにしていただければと思います。
ADコンバータは入力ピンのアナログ電圧値を直接読み取るのではなく、上の図にあるように基準電圧を参照してアナログ値を読み取ります。といっても何のことやら、って感じですよね。
もう少し具体的に説明します。ADコンバータは、「基準電圧」を1024等分した時、「入力ピンの電圧」がどのぐらいの値になるか、という形で読み取ります。ADコンバータは、入力電圧の具体的な値(例えば0.2Vなど)を直接読み取るのではなく、基準電圧を参考に、その基準電圧と比較してどのぐらいの割合かを読み取るわけです。
ところで1024等分、という数字ですが、これは10-bitで読み取るために1024等分になっています。実際にPIC12F1822のデータシートを見てみましょう。
(Microchip社 PIC12F1822データシートより引用、加工)
このページはPIC12F1822の仕様概要が書かれていますが、枠で囲んだ部分にADコンバータの仕様が記載されています。ここに「10-bit resolution(分解能10-bit)」と書かれています。電圧を10-bit、つまり1024等分して読み取ることができますよ、という意味です。
それでは基準電圧の説明に戻りましょう。「基準電圧」が5Vで「入力ピンの電圧」が2Vの場合、ADコンバータをした結果は、2 ÷ 5 × 1024 = 409 (少数切り捨て)ということで「409」という結果になります。プログラムで409という結果がら得られますので、プログラムで計算すれば何Vかわかるわけです。
ところで、この「基準電圧」ですが、ちょっと厄介なところがあります。第7回で説明した温度センサは、例えばセンサの出力電圧が0.24Vの場合は気温24度、ということになります。このように絶対値として何Vか知りたいケースがあります。このようなケースで、基準電圧として「電源電圧」を使用した場合を考えてみます。
今回製作しているタイマーは電池で動作させています。普通、電池の電圧は1.5Vと理解されていると思いますが、実際には電池は使っているうちにどんどん電圧が低下していきます。買ってきたばかりの電池の電圧は1.6V程度、電池切れ状態の場合は1.2V〜1.3Vといったところです。
先ほどのようにADコンバータ機能を利用する場合、基準電圧として電源電圧、つまり電池の電圧を利用した場合、例えばADコンバータで読み取った値が「100」といっても、基準電圧の1024分の100、ということがわかるだけで、正確な電圧はわかりません。もし電源電圧(=基準電圧)が4.5Vだったら、4.5 ÷ 1024 × 100 = 約0.44Vですが、そもそも「4.5」という値が違うはずのなで正確な電圧はわかりません。
そこで、ADコンバータが参照する基準電圧を選択する設定が用意されています。
例えばこの今回のスイッチの状態に応じた電圧を読み取りたい場合、電源電圧の半分ぐらいかどうかがわかればいいので、ADコンバータの基準電圧は電源電圧で構いません。
一方、温度センサのような電圧の絶対値を読み取りたい場合は、基準電圧は変動しない正確な電圧にする必要があります。
ここで疑問が出てきます。ADコンバータで読み取った値から正確な電圧が分かる分には困ることはないわけだから、基準電圧は変動しない正確な値にしてましって、このような基準電圧を選択する設定はなくてもいいのでは? という疑問です。
基準電圧を正確な値にしたい場合、PICマイコン内部に正確な基準電圧を発生する回路が入っていますのでそれを使用するか、外部から供給することができます。ただし、PICマイコン内部の基準電圧は2.048Vか4.096Vです。例えばアナログ値として0〜5Vを読み取りたい場合はどちらの基準電圧でも読み取ることができません。この場合は正確な5Vを外部から供給すれば解決できますが、その場合は外部に回路を追加する必要がある、つまりコストがかかってしまいます。
ということで、基準電圧をどうするかは設計者に任させれていますので、基準電圧の選択の設定をする必要があります。
次に、読み取った値について説明します。
ピンの電圧を読み取ったら、結果の数値は「ADRES」というレジスタに格納されます(A/D converter RESultの意味)。
ADコンバータで読み取る数値範囲は0〜1023の10bitです。一方、コンピュータの世界は8-bitの倍数で扱いますので、ADRESレジスタは16-bitのレジスタとなります。ADRESレジスタは16-bitつまり2バイトで構成されていて、上位バイトがADRESH (ADRES High byte)、下位バイトがADRESL (ADRES Low byte)と名前がつけられています。
10-bitの値をこの16-bitレジスタに格納するわけですが、普通に考えると右詰で格納するのが自然ではないかと思います。例えばADコンバータで読み取った値が10進数で「987」の場合、2進数では「1111011011」になります。これを右詰で格納すると、
このようになりますので、「ADRES」を16-bitの数値として読み取ればいいわけです。
ところで、10-bitの数字を16-bitの箱に格納する場合、もう一つの格納方法がありますよね。左詰で格納する方法です。
このように格納した場合、16-bitのADRESの数値としては、10進数で「63168」になってしまいます。このように格納すると数値が正しく読めないように思いますが、この格納方法は別のメリットがあります。
左詰で数値を格納した場合、上位8-bitであるADRESHのみをADコンバータの数値として扱う、ということができます。10-bitの結果の一番下の2-bitは捨てて、上位8-bitを数字として扱う、という方法です。???って感じですよね。2進数だとよくわからないと思いますので、10進数の別の例で説明します。
何かの値段の大体の合計計算する場合、例えば「31,225円」「12,600円」「14,320円」の大体の合計金額を計算する場合、真面目に足し算しないですよね。この場合、カンマ以下の100円台の数字は一旦無視して、「31」と「12」と「14」を足せば大体の合計金額がわかります。31 + 12 + 14 = 57ですので、これらの金額の合計は大体「57,000円ぐらい」端数足されるのでもうちょっと正確には「58,000円ぐらい」でしょうか。このように10進数の場合は下位3桁を捨てても大体の計算ができます。
2進数でも同じで、10-bitの数値の下位2-bitを捨てて(2-bit分の正確性は捨てて)、上位8-bitを数値として扱うことができます。この場合は、電源電圧を8-bit(=256分割)した、と考えて計算することになります。
このように8-bitで扱うことにより、16-bitで数値を扱うよりも計算が少なくなりますので、計算時間が短くなり、消費電力も少なくなります。
以上がADコンバータの内部動作からみた必要な設定項目です。他にも設定項目がありますので、次に必要な設定項目について詳細を説明します。
ADコンバータ設定方法
ADコンバータ機能の設定は、「ADCON0」と「ADCON1」レジスタで行います。(AD converter CONfigurationの意味)
それぞれのレジスタの設定項目詳細説明です。
最初は「ADCON0」レジスタです。
(Microchip社 PIC12F1822データシートより引用)
ADCON0レジスタの「CHS」では、どのピンの電圧値を読み取るかを設定します(Channel Selectの意味)。設定値は以下になります。
設定値(2進数) | 意味 |
---|---|
00000 | 7番ピン(AN0) |
00001 | 6番ピン(AN1) |
00010 | 5番ピン(AN2) |
00011 | 3番ピン(AN3) |
次の「GO/DONE」はADコンバータで数値を読み取る指示を行うビットです。このビットは通常状態では「0」です。プログラムでこのビットを「1」にするとADコンバータモジュールが動き出して電圧値を数値としてADRESに格納します。格納できたらこのビットは「0」になります。ADコンバータでアナログ値を読み取り場合、ちょっと時間がかかるのでこのような動作になります。
最後の「ADON」はADコンバータ機能を有効化するビットです。このビットを1にするとADコンバータが有効になります。ADコンバータは有効化すると電力を消費するため、使わない場合はこのビットは「0」にして無効化しておきます。
次に「ADCON1」レジスタです。
(Microchip社 PIC12F1822データシートより引用)
最初の「ADFM」はADコンバータで読み取った数値をADRESに左詰で格納するか右詰で格納するかの指定です(AD Result Format Selectの意味です。設定値は以下になります。
設定値 | 意味 |
---|---|
0 | 左詰め |
1 | 右詰め |
次の「ADCS」はADコンバータでアナログ値をデジタル値に変換するときのクロックの設定(動作スピードの設定)です。基本的に早い方がいいですが、選択できるスピードが限られていますので、その中から一番早い値を選ぶようにします。ADコンバータの速度??という感じですが、この説明はさらに細かい説明が必要になりますので、設定概要のみ理解いただければと思います。
以下の表は、PICマイコンのクロック周波数に対して、選択できるADCSの値の対応表です。
(Microchip社 PIC12F1822データシートより引用)
今回のPICマイコンプログラムはクロック周波数を1MHzにしていますので、選択できる一番早い動作スピードは枠で囲った部分になります。このときのADCSの値は「000」ですので、この値に設定します。
最後の「ADPREF」は基準電圧の選択です。以下の意味になります。
設定値(2進数) | 意味 |
---|---|
00 | 電源電圧 |
01 | 未定義 |
10 | 外部電圧電源 |
11 | 内部電圧電源 |
今回製作しているスイッチ回路は、抵抗で電源電圧を半分にした値を読み取りますので、基準電圧は電源電圧で問題ありません。ADPREFに00を設定することになります。
以上がADコンバータ機能の設定項目です。ずいぶんいろいろな設定がありましたね…(なんか面倒ですね…)
ADコンバータ処理方法
以上でADコンバータの設定方法の説明が終わりましたので、処理プログラムの概要を説明します。
まずADコンバータの設定部分は以下のようになります。
// ADコンバータピン設定
OSCCON = 0b01011010; // 内部クロック周波数を1MHzに設定
ANSELA = 0b00000100; // RA2をアナログ、RA2以外はデジタルに設定
TRISA = 0b00001100; // RA2とRA3を入力、それ以外は出力に設定
// ADコンバータ機能設定
ADCON0 = 0b00001001; // RA2(AN2)をADコンバータピンに設定し、ADコンバータ機能をEbableにする
ADCON1 = 0b10000000; // 結果数値は右寄せ、クロックはFosc/2(000)、基準電源はVDD
ここまで設定できたら、あとはADコンバータに電圧読み取りをしてもらいます。読み取りしてもらう場合、
GO = 1;
とADCON0のGOビットに1を代入するとADコンバータが開始されます。数値読み取りにはちょっと時間がかかりますので、終わるまで待ちます。読み取りが終わるとGOビットが0になりますので、
while( GO );
で待ちます。ADコンバータの読み取りか終わるとGOが0になりますので、このwhile文を抜けます。抜けたあとは、結果はADRES(16-bit・数値右寄せ)で格納されていますので、ADRESの値に応じた処理を行います。
以上がADコンバータの概略です。次回はこの内容を元に押されたスイッチに応じて、タイマー時間の増減を行うプログラムを作成します。
更新履歴
日付 | 内容 |
---|---|
2017.7.23 | 新規投稿 |
2018.12.2 | 電池電圧の説明補足 |
こんにちは。ADコンバータと言いますか、C言語の知識の問題なのかもしれませんが、
ADコンバータ関数を
unsigned int adconv(){
unsigned int temp;
__delay_us(5);
GO_nDONE = 1 ; // アナログ値読取り開始指示
while(GO_nDONE) ; // 読取り完了まで待つ
__delay_us(15);
temp = ADRESH ;
temp = ( temp << 8 ) | ADRESL ;
return temp ;
}
のように作り、
これをmainの中で10回呼び出して足した後
for (int i = 0; i<10; i++)
{
voltage = adconv();
voltage2 = voltage + voltage2;
}
voltage2 = voltage2 / 10;
のように平均を取ってノイズを抑えようとしたのですが、
なぜか最終的なvoltage2 の値が0~1136になってしまうんですよね。
10bitのADコンバータなので10回足して10で割れば1023が最大だと思うのですが…
voltage = adconv(); ←この時点では正しく0~1023までの値が格納されているようです。
使用しているマイコンはpic16F1827です。何が原因かわかりますでしょうか?
すみません、解決しました
for (int i = 0; i<10; i++)
{
voltage = adconv();
voltage2 = voltage + voltage2;
}
これをmainの中で回すとvoltage2が足され続けてオーバーフローしてたみたいです。
for文の前で毎回voltageとvoltage2を初期化するようにしたらうまくいきました。
ただなぜ1136が表示されていたのかは謎です。
ご質問どうもありがとうございました。
というより自己解決されたんですね。
一人で考えているとわからなくても、何か他のことをするとすんなり解決できたりしますので、今回はここへの書き込みで何かがきっかけで解決されたのでしょうかね。
voltage2の初期化忘れが原因とのことですが、C言語に慣れてきても意外に初期化は忘れがちですよね。
ところで、1136が表示される理由ですが、voltage2をどの変数型で宣言されているかわかりませんが、voltage2の桁あふれによってたまたまその値が表示されたのだと思います。
例えば、
のように、voltageを符号なし8ビット変数として宣言して、最初にvoltage = 200、次にvoltageに200を加算すると、計算上は400になりますが、上のコードの場合、voltageは144になります。
voltageは255までしか数えられないため、超えた場合はまた0からカウントするため、200 + 200 – 256 = 144となります。
いつもこちらのサイトにはお世話になっております。
質問させてください。
基準電圧が5VでLM35DZの温度センサーが最大の1Vを出力してきたとしたら
ADRESにはいくつの数値が格納されるのでしょうか?
1024???それとも5Vに対して1Vなので5分の1ぐらいの204???
いつもレベルの低い質問ばかりですいません。
よろしくお願いいたします。
ご質問どうもありがとうございます。
後者になりますね。
ADCは、基準電圧を1024等分して測定電圧がどの値になるか、という測定をします。基準電圧が5Vの場合で、測定電圧が1Vですと、1024の5分の1の値になり、204という読み取り値になります。
レベルの低い質問、などとおっしゃらないでください。誰でも最初は知らないことばかりですし、色々試していくうちに知識が増えていきますので、楽しみながら進めていただければと思います。
さっそくの回答ありがとうございました。
少しずつ知識と経験を増やしていって上手にマイコンを制御できるよう
精進したいと思います。また不明点問い合わせさせていただくと思います
のでその時はよろしくお願いします。
こんにちは。還暦すぎで始めたPICですが、このサイトにきてやっと少しだけ理解することができました。全部理解できてはいませんが一通り試してからもう一度最初に戻り勉強します。とても分かりやすい説明で助かります。
だいちゃん様、
コメントいただきどうもありがとうございました。
電子工作はArduinoやRaspberryPiの方が簡単にできますが、PICマイコンが使えるようになると、マイコンそのものは安いですし小さいので工作の自由度が上がると思います。
ただ、工作が一手間必要だったり、プログラムがちょっと複雑だったりしますが、これも楽しみの一つ、と思って色々なモノづくりに挑戦されてみてください。
記事を読み返すと、誤植があったり文章がおかしいところ、説明が??のところもありますので、わからないことがありましたらコメント欄、お問い合わせページからご連絡いただければと思います。