今回は、ADコンバータ機能の設定方法を解説します。
ADコンバータのプログラミング概要
ADコンバータ機能を利用するスイッチ回路の設計と実装が終わりました。
今回からプログラムを作成していきましょう!
第7回の説明で、ADコンバータ機能を利用する場合、ピンの設定を「アナログ」の「入力」に設定する必要があることを説明しました。
ただ、この設定だけですぐにADコンバータ機能を利用できるわけではないんです。ADコンバータ機能を利用する場合、さらにいろいろな設定が追加になります。
そこで、今回の記事ではADコンバータ機能を利用する際の設定について詳しく説明します。
最初に設定の概要を確認しておきますね。
ADコンバータ機能を利用する場合、次の2種類の設定が必要になります。
- ADコンバータを利用するピンを「アナログ」の「入力」設定にする
- ADコンバータ動作の設定をする
❶の「アナログ入力」設定は第7回ですでに説明しましたが、ここでもう一度確認しておきます。
基礎編で作成したプログラムでは、main
関数のピンの機能設定部分で、ANSELA
レジスタでデジタル設定にして、TRISA
レジスタで入力ピンか出力ピンかの設定をしていました。
ADコンバータ機能を利用する場合、ピンの設定をANSELA
レジスタで「アナログ」に、TRISA
レジスタで「入力」に設定します。
この設定のあと、❷のADコンバータ動作の設定を行います。
設定内容はこのあと詳しく説明しますが、ここでは例えば、ADコンバータで読み取った電圧値をレジスタにどのように格納するのか、などの設定を行います。
実際の設定方法は、ADコンバータがどのように動いているか理解する必要がありますので、最初にADコンバータがどのような動作になっているか説明します。
ADコンバータの動作概要
❷の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 PicoなどのADコンバータも同じです。
ADコンバータは入力ピンのアナログ電圧値を「0.5V」などのように直接読み取るのではなく、下の図にあるように基準電圧を参照してアナログ値を読み取ります。といっても何のことやら?、って感じですよね。
もう少し具体的に説明します。
PIC12F1822のADコンバータは、「基準電圧」を1024等分した時、「入力ピンの電圧」がどのぐらいの値になるか、という形で読み取ります。
今回作成するADコンバータ機能では、基準電圧は電源電圧の5Vを使用します。つまり、次の図の左側のように基準電圧の5Vを1024等分しておきます。
このとき、入力ピンの電圧が例えば2Vの場合、ADコンバータの機能で読み取ると、次のように「409」という結果になります。
2 ÷ 5 × 1024 = 409(小数切り捨て)
このように、ADコンバータは入力電圧の具体的な値を直接読み取るのではなく、基準電圧を参考に、その基準電圧と比較してどのぐらいの割合かを読み取るわけです。
プログラムでピンの電圧読み取ると「409」という結果がら得られますので、実際の電圧を知りたい場合はプログラムで計算すれば何Vかわかるわけです。(5 ÷ 1024 × 409 = 2V)
ところで「1024等分」という数字ですが、これはPICマイコンごとに異なります。
実際にPIC12F1822のデータシートを確認してみます。

このページはPIC12F1822の仕様概要が書かれていますが、枠で囲んだ部分にADコンバータの仕様が記載されています。
ここに「10-bit resolution」(分解能10ビットという意味)と書かれています。
これは、基準電圧を10ビット等分、つまり1024等分して読み取ることができますよ、という意味です。
ところで、この「基準電圧」ですが、ちょっと厄介なところがあります。
例えば第7回の記事で説明した次の温度センサーを例に説明します。
この温度センサーは、出力端子に温度に応じた電圧が出力されます。例えば0度の時は0V、18度の時は0.18V、というように、温度が1度上がることに0.01V電圧が増えます。
つまり、この温度センサーを使う場合、出力電圧が何Vか、正確に読み取る必要があります。
このセンサーを使用するとき、基準電圧として「電源電圧」を使用した場合を考えてみます。(今回作成したブレッドボード回路では「電源電圧」は「電池ボックスの電圧」になります)
「電源電圧」を「電池ボックスの電圧」にすると、ちょっと厄介なことが起こるんです。
一般的な使い捨て電池の電圧は1.5Vと表示されていますが、実際には1.5Vではありません。新品の電池の電圧は1.6V程度、電池切れ状態のときは1.2V〜1.3Vといったところです。
ADコンバータの「基準電圧」として「電源電圧」、つまり「電池ボックスの電圧」を利用した場合、基準電圧は時間の経過とともに変わってきてしまいます。
このとき、例えばADコンバータで読み取った値が「100」といっても、基準電圧の1024分の100、ということがわかるだけで、正確な電圧はわかりません。
もし電源電圧(=基準電圧)が5Vだったら、5 ÷ 1024 × 100 = 約0.49Vですが、電池の消耗により電源電圧が低下してきて、4.5Vになると、4.5 ÷ 1024 × 100 = 約0.44Vと計算されてしまいます。
つまり、ピンの電圧を正確に読み取りたい場合、「基準電圧」として「変動する電源電圧」は使えない、ということになってしまいます。
そこで、ADコンバータ用に、正確な基準電圧を発生する回路がPICマイコンの内部に用意されています。
今回のスイッチの状態に応じた電圧を読み取りたい場合、電源電圧の半分ぐらいかどうかがわかればいいので、ADコンバータの基準電圧は変動する電源電圧で構いません。
でも、温度センサーのように正確な電圧値を読み取りたい場合は、基準電圧は変動しない正確な電圧にする必要があります。
このようなとき、ADコンバータの基準電圧としてPICマイコン内部の正確な基準電圧を選択することになります。(どの基準電圧を使用するかはプログラムで設定できます)
読み取った値
次に、読み取った値について説明します。
ピンの電圧を読み取った結果の数値はADRES
というレジスタに格納されます。(A/D converter RESultの意味)
PIC12F1822のADコンバータで読み取る数値範囲は「0〜1023」(10ビット)です。
一方、コンピュータの世界はデータは8ビット単位で扱いますので、10ビットの結果は16ビットで扱うことになります。
そのため、ADRES
レジスタは16ビットのレジスタになっています。
ADRES
レジスタは次のように16ビット(2バイト)で構成されていて、上位8ビットがADRESH
(ADRES High byte)、下位8ビットがADRESL
(ADRES Low byte)と別名がつけられています。

ところで、ADコンバータで読み取った10ビットの値を、この16ビットのレジスタに格納するわけですが、普通に考えると右詰で格納するのが自然ではないかと思います。
例えばADコンバータで読み取った値が10進数で「987」の場合、2進数では「1111011011」になります。これを右詰で格納すると、次のようになります。

このように格納された場合、ADRES
レジスタを単に16ビットの数値として読み取ればいいわけです。
ところで、10ビットの数値を16ビットのADRES
レジスタに格納する場合、もう一つの格納方法があります。
次のように左詰で格納する方法です。

このように格納した場合、ADRES
の数値を16ビットデータとしてそのまま読むと、10進数で「63168」になってしまいます。
このように左詰で格納すると数値が正しく読めないのであんまり意味がなさそうですよね。
でもこの格納方法はメリットがあるんです。
左詰で数値を格納した場合、上位8ビットのADRESH
のみをADコンバータの数値として扱う、ということができます。

ADRESH
の値のみを読み取った数値として扱うと、下位2ビットは捨てられてしまいます。
下位2ビットは細かい数字なので、上位8ビットだけを使おう、という意図になります。
といってもちょっとわかりづらいですよね。
そこで、10進数で別の例で説明します。
例えば何かの値段が「31,225円」「12,600円」「14,320円」のとき、大体の合計金額を知りたい場合、1円単位で足し算しないですよね。
この場合、カンマ以下の100円台の数字は一旦無視して、「31」と「12」と「14」を足せば大体の合計金額がわかります。31 + 12 + 14 = 57ですので、これらの金額の合計は大体「57,000円」ぐらいで、端数があるので、もうちょっと正確には「58,000円」ぐらいしょうかね。
このように10進数の場合は下位3桁を捨てても大体の計算ができます。
2進数でも同じで、10ビットの数値の下位2-bitを捨てて(2ビット分の正確性は捨てて)、上位8-bitを数値として扱う、ということができます。
この場合は、電源電圧を8ビット(=256分割)した、と考えて計算することになります。
このようにADコンバータで読み取った値を8ビットで扱うことにより、16ビット数値を扱うよりも計算が少なくなります。結果として計算時間が短くなり、消費電力も少なくなります。
このような背景があるので、ADコンバータ機能の設定では、10ビット数値を右詰にするか左詰にするかの設定があります。
以上がADコンバータの内部動作の概要です。
他にも設定項目がありますので、次に必要な設定項目について詳細を説明します。
ADコンバータ設定方法
ADコンバータ機能の設定は、ADCON0
とADCON1
レジスタで行います。(AD converter CONfigurationの意味)
それぞれのレジスタの設定項目を詳しく説明します。
ADCON0レジスタ
最初は「ADCON0」レジスタです。
次のように、CHS(5ビット)、GO/DONE(1ビット)、ADON(1ビット)から構成されています。

それぞれの設定項目について説明します。
CHS
ADCON0
レジスタの「CHS」では、どのピンの電圧値を読み取るかを設定します。(CHannel Selectの意味)
設定値は以下になります。
設定値(2進数) | 意味 |
---|---|
00000 | 7番ピン(AN0) |
00001 | 6番ピン(AN1) |
00010 | 5番ピン(AN2) |
00011 | 3番ピン(AN3) |
GO/DONE
次の「GO/DONE」は、ADコンバータに数値読み取り指示を行うビットです。
このビットは通常状態では「0」です。プログラムでこのビットを「1」にするとADコンバータモジュールが動き出して電圧値を数値としてADRES
に格納します。格納できたらこのビットは「0」になります。
ADコンバータの読み取り実行は、プログラムでGO/DONEを1にしたあと、GO/DONEの値を監視して0になったら読み取り完了、という流れになります。
ADコンバータでアナログ値を読み取る場合、ちょっと時間がかかるのでこのような動作になっています。
ADON
最後の「ADON」はADコンバータ機能を有効化するビットです。
このビットを1にするとADコンバータが有効になります。ADコンバータは有効化すると電力を消費するため、使わない場合はこのビットは「0」にして無効化しておきます。
ADCON1レジスタ
次に「ADCON1」レジスタです。
次のように、ADFM(1ビット)、ADCS(3ビット)、ADPREF(2ビット)から構成されています。

ADFM
最初の「ADFM」はADコンバータで読み取った数値を、ADRES
レジスタに左詰で格納するか右詰で格納するかの指定です。(AD Result Format Selectの意味です)
設定値は以下になります。
設定値 | 意味 |
---|---|
0 | 左詰め |
1 | 右詰め |
ADCS
次の「ADCS」は、ADコンバータでアナログ値をデジタル値に変換するときのクロックの設定(動作スピードの設定)です。
基本的に早い方がいいですが、選択できるスピードが限られていますので、その中から一番早い値を選ぶようにします。
ADコンバータの速度??という感じですが、この説明はさらに細かい説明が必要になりますので、設定概要のみ理解いただければと思います。
次の表は、PICマイコンのクロック周波数に対して、選択できるADCSの値の対応表です。

今回のPICマイコンプログラムはクロック周波数を1MHzにしていますので、選択できる一番早い動作スピードは枠で囲った部分になります。
このときのADCSの値は「000」ですので、この値に設定します。
ADPREF
最後の「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コンバータに電圧読み取りをしてもらいます。
ADコンバータに読み取りしてもらうタイミングで、次のように書きます。
GO = 1;
これでADコンバータの読み取り処理が開始されます。
数値読み取りにはちょっと時間がかかりますので、終わるまで待ちます。
読み取りが終わるとGOビットが0になりますので、次のコードで読み取りが終わるまで待ちます。
while( GO ) {
}
ADコンバータの読み取りか終わるとGOが0になりますので、このwhile文を抜けます。
抜けたあとは、結果はADRES
(数値右寄せ)で格納されていますので、ADRES
の値に応じた処理を行います。
以上がADコンバータの概略です。
他にも基準電圧を内部の正確な電圧を使用する場合、電圧値の設定などがありますが、今回は説明を省略いたします。
次回はこの内容を元に押されたスイッチに応じて、タイマー時間の増減を行うプログラムを作成します。
更新履歴
日付 | 内容 |
---|---|
2017.7.23 | 新規投稿 |
2018.12.2 | 電池電圧の説明補足 |
2025.5.14 | 説明順序変更、内容補足 |
こんにちは。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マイコンが使えるようになると、マイコンそのものは安いですし小さいので工作の自由度が上がると思います。
ただ、工作が一手間必要だったり、プログラムがちょっと複雑だったりしますが、これも楽しみの一つ、と思って色々なモノづくりに挑戦されてみてください。
記事を読み返すと、誤植があったり文章がおかしいところ、説明が??のところもありますので、わからないことがありましたらコメント欄、お問い合わせページからご連絡いただければと思います。