第10回 ADコンバータ(4)〜ADコンバータの機能設定〜

今回は、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コンバータモジュール部の概略を説明したものです。

Pic app 10 adc block diagram

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」などのように直接読み取るのではなく、下の図にあるように基準電圧を参照してアナログ値を読み取ります。といっても何のことやら?、って感じですよね。

Pic app 10 adc block diagram

もう少し具体的に説明します。

PIC12F1822のADコンバータは、「基準電圧」を1024等分した時、「入力ピンの電圧」がどのぐらいの値になるか、という形で読み取ります

今回作成するADコンバータ機能では、基準電圧は電源電圧の5Vを使用します。つまり、次の図の左側のように基準電圧の5Vを1024等分しておきます。

Pic app 10 ref voltage

このとき、入力ピンの電圧が例えば2Vの場合、ADコンバータの機能で読み取ると、次のように「409」という結果になります。

2 ÷ 5 × 1024 = 409(小数切り捨て)

このように、ADコンバータは入力電圧の具体的な値を直接読み取るのではなく、基準電圧を参考に、その基準電圧と比較してどのぐらいの割合かを読み取るわけです。

プログラムでピンの電圧読み取ると「409」という結果がら得られますので、実際の電圧を知りたい場合はプログラムで計算すれば何Vかわかるわけです。(5 ÷ 1024 × 409 = 2V)


ところで「1024等分」という数字ですが、これはPICマイコンごとに異なります。

実際にPIC12F1822のデータシートを確認してみます。

(Microchip Technology社「PIC12F1822データシート」より引用)

このページはPIC12F1822の仕様概要が書かれていますが、枠で囲んだ部分にADコンバータの仕様が記載されています。

ここに「10-bit resolution」(分解能10ビットという意味)と書かれています。

これは、基準電圧を10ビット等分、つまり1024等分して読み取ることができますよ、という意味です。


ところで、この「基準電圧」ですが、ちょっと厄介なところがあります。

例えば第7回の記事で説明した次の温度センサーを例に説明します。

Pic app 7 temp sensor

この温度センサーは、出力端子に温度に応じた電圧が出力されます。例えば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)と別名がつけられています。

Pic app 10 adres structure

ところで、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コンバータ機能の設定は、ADCON0ADCON1レジスタで行います。(AD converter CONfigurationの意味)

それぞれのレジスタの設定項目を詳しく説明します。

ADCON0レジスタ

最初は「ADCON0」レジスタです。

次のように、CHS(5ビット)、GO/DONE(1ビット)、ADON(1ビット)から構成されています。

(Microchip Technology社「PIC12F1822データシート」より引用)

それぞれの設定項目について説明します。

CHS

ADCON0レジスタの「CHS」では、どのピンの電圧値を読み取るかを設定します。(CHannel Selectの意味)

設定値は以下になります。

設定値(2進数)意味
000007番ピン(AN0)
000016番ピン(AN1)
000105番ピン(AN2)
000113番ピン(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ビット)から構成されています。

(Microchip Technology社「PIC12F1822データシート」より引用)

ADFM

最初の「ADFM」はADコンバータで読み取った数値を、ADRESレジスタに左詰で格納するか右詰で格納するかの指定です。(AD Result Format Selectの意味です)

設定値は以下になります。

設定値意味
0左詰め
1右詰め

ADCS

次の「ADCS」は、ADコンバータでアナログ値をデジタル値に変換するときのクロックの設定(動作スピードの設定)です。

基本的に早い方がいいですが、選択できるスピードが限られていますので、その中から一番早い値を選ぶようにします。

ADコンバータの速度??という感じですが、この説明はさらに細かい説明が必要になりますので、設定概要のみ理解いただければと思います。

次の表は、PICマイコンのクロック周波数に対して、選択できるADCSの値の対応表です。

(Microchip Technology社「PIC12F1822データシート」より引用)

今回の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説明順序変更、内容補足
通知の設定
通知タイミング
guest
8 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
CKW
CKW
1 年 前

こんにちは。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です。何が原因かわかりますでしょうか?

CKW
CKW
返信  CKW
1 年 前

すみません、解決しました

for (int i = 0; i<10; i++)
{
voltage = adconv();
voltage2 = voltage + voltage2;
}
これをmainの中で回すとvoltage2が足され続けてオーバーフローしてたみたいです。

for文の前で毎回voltageとvoltage2を初期化するようにしたらうまくいきました。
ただなぜ1136が表示されていたのかは謎です。

ogu
ogu
2 年 前

いつもこちらのサイトにはお世話になっております。
質問させてください。
基準電圧が5VでLM35DZの温度センサーが最大の1Vを出力してきたとしたら
ADRESにはいくつの数値が格納されるのでしょうか?
1024???それとも5Vに対して1Vなので5分の1ぐらいの204???
いつもレベルの低い質問ばかりですいません。
よろしくお願いいたします。

ogu
ogu
返信  管理者
2 年 前

さっそくの回答ありがとうございました。
少しずつ知識と経験を増やしていって上手にマイコンを制御できるよう
精進したいと思います。また不明点問い合わせさせていただくと思います
のでその時はよろしくお願いします。

だいちゃん
だいちゃん
7 年 前

こんにちは。還暦すぎで始めたPICですが、このサイトにきてやっと少しだけ理解することができました。全部理解できてはいませんが一通り試してからもう一度最初に戻り勉強します。とても分かりやすい説明で助かります。

管理者
管理者
返信  だいちゃん
7 年 前

だいちゃん様、
コメントいただきどうもありがとうございました。

電子工作はArduinoやRaspberryPiの方が簡単にできますが、PICマイコンが使えるようになると、マイコンそのものは安いですし小さいので工作の自由度が上がると思います。

ただ、工作が一手間必要だったり、プログラムがちょっと複雑だったりしますが、これも楽しみの一つ、と思って色々なモノづくりに挑戦されてみてください。

記事を読み返すと、誤植があったり文章がおかしいところ、説明が??のところもありますので、わからないことがありましたらコメント欄、お問い合わせページからご連絡いただければと思います。

目次