今回は ADC (Analog to Digital Converter) の使い方を解説します。
PIC の IOピンは通常デジタルの入出力しか出来ませんが、ADC を使用することでアナログ入力(電圧測定)をすることが可能になります。
ADC の活用例としてサーミスタの抵抗値を測定することで温度を測定してみようと思います。
目次
使用する部品
今回使用する主な部品は以下の通りです。
- PIC16F1579
- サーミスタ 10kΩ(平行線タイプ)
- LCD 1602
PIC については ADC モジュールが搭載されているものであれば大丈夫です。
LCDの使い方についてはこちらの記事を参照してください。
ADCモジュール
まずは ADCモジュールについて簡単に解説します。
先程 ADC でアナログ入力が可能になると説明しましたが、実際には完全なアナログ値というのは扱うことが出来ないので、デジタル値で電圧を測定します。
デジタル入力が High / Low の2段階しかないのに対し、アナログ入力では 0 ~ 1023 (10bit ADC の場合)の 1024 段階あります。
この 1024 段階はある基準(基本は VDD と GND)に対する相対値であり、実際の電圧を表している訳ではないことに注意してください。
続いてPIC16F1579 のデータシートを見てみると、冒頭の部分に次の記述が見られます。
10bit というのは ADC の解像度であり、10bit = 1024 段階で電圧を測定することができます。
12ch というのは ADC に利用できる IOピンの数のことです。気を付けなければならないのは、すべての IOピンが利用可能という訳ではない点と、同時にどれか1つの IOピンしか測定できないという点です。
ADC 関連レジスタ
ADC に関連するレジスタとその初期化例を解説します。各ビットについての詳細はクリックすると見られます。
ADCON0
詳細
CHS (Analog Channel Select bits)
ADC の測定を行うチャンネルを指定します。
00000 = AN0, 00001 = AN1, … 01011 = AN11
01100 ~ 11100 = 予約済み
11101 = 内部温度計, 11110 = DAC, 11111 = FVR
GO/DONE (ADC Conversion Status bit)
このビットを1にすると ADC測定が開始され、測定中は1のままで測定が終了すると0になります。
ADON (ADC Enable bit)
このビットを1にすると ADCモジュールが有効化されます。ADCモジュールは消費電力が比較的大きいので、ADC を使用しない場合は0にしておいた方が良いようです。
チャンネルは AN0を使用し、ADCモジュールを有効化させます。
ADCON1
詳細
ADFM (ADC Result Format Select bit)
1:右詰め 0:左詰め(後述)
ADCS (ADC Conversion Clock Select bits)
000 = Fosc/2, 001 = Fosc/8, 010 = Fosc/32, 011 = FRC
100 = Fosc/4, 101 = Fosc/16, 110 = Fosc/64, 111 = FRC
ADC の測定のクロックを指定します。次の表(網掛けの部分は不適切)をもとに Fosc に対して適切な分周比を選択します。
ADPREF (ADC Positive Voltage Reference Configuration bit)
00 = VDD, 01 = 予約済み, 10 = 外部参照ピン, 11 = FVR
ADC の上限の基準を設定します。電圧を測定したい場合、電源電圧が正確に分かっていれば VDD での測定結果から求められますが、そうでない場合は FVR(Fixed Voltage Reference)モジュールを基準とすることで電圧が計算できます。
また、このデバイスにはありませんが、下限の基準を設定出来るものもあります。
測定結果は右詰めで、Fosc は4MHz で使用し、上限の基準はVDDとします。
ADCON2
詳細
TRIGSEL (Auto-Conversion Trigger Selection bits)
タイマーなどの特定のイベントで自動で ADC測定を行うように設定できます。
自動測定は行いません。
ADRESH, ADRESL
ADC の測定結果は 10bit なので、ADRESH と ADRESL の2つのレジスタにまたがって保存されます。
10bit すべて使用する場合は右詰め、上位8bit で十分な場合は左詰めにするとよいです。
サンプルプログラム
ADC の初期化後、実際に ADC の測定をするプログラムがこちらです。
1 2 3 4 5 6 |
int ADC_GetConversion(){ ADCON0bits.ADGO = 1; //測定開始 while(ADCON0bits.ADGO); //測定終了待ち return ((ADRESH << 8) | ADRESL) & 0x03FF; //測定結果 } |
今回は1ch しか使わないためチャンネルの選択は省略していますが、複数のチャンネルを切り替える場合は、測定開始の前に ADCON0 レジスタの CHS にチャンネルを指定しておく必要があります。
サーミスタ
サーミスタは温度によって抵抗値の変化する抵抗です。抵抗値を測定することによって温度を計算することが出来ます。
(写真の先端部の太い部分がサーミスタで大部分はリード線です)
サーミスタの抵抗値と温度の関係は次のようになっています。
(T0, R0 はある温度とその温度における抵抗値、Bはサーミスタ固有の定数)
これを T について解くと次のようになります。
T0, R0, B は定数ですので、T は R の関数として表されました。
そこで R を測定する必要がありますが、ADC で直接 R を測定することは出来ません。ではどうするかというと、基準となる抵抗を用意して、分圧から抵抗値の比を求めてサーミスタの抵抗値を計算します。
基準の抵抗の値を r 、ADC の測定結果を A とすると、次の式が成立します。この時、電源電圧には依存しないことに注意してください。
これを R について解くと次のようになり、R が A の関数として表されます。
これで晴れて ADC の測定結果から温度が計算できるようになりましたが、この計算をマイコンにやらせるのは大変なので、あらかじめ Excel で計算してデータを用意してしまいましょう。
データシートから、T0 = 25 (℃)、R0 = 10000(Ω)、B = 3435(K) が得られます。r についてはまだ適切な値が分からないので変更できるようにしておき、とりあえず 10000(Ω)とします。
Excelに先程の計算式を入力します。
A = 0, 1023 の時にゼロ除算のエラーが発生しますが、測定値がそのような値をとることは考えられないので気にする必要はありません。
計算結果をグラフにしてみるとこんな感じです。
実際に使用する環境を考えると、測定範囲は 0℃~100℃ で十分なのでその範囲で見てみます。
手持ちの抵抗の中から r の値を適当に選んでみた結果、r = 3200 (Ω)のグラフが一番よさそうでした。(3.3kΩの抵抗をテスターで測ったら3.2kΩでした)
ということで r の値と温度のテーブルが得られました。
動作テスト
ADC を使用してサーミスタで温度を測定し LCD に表示させてみました。
比較用として温度センサー BME280 の測定結果と並べてみました。どちらも 27.0℃ となっており、きちんと温度を測定できていることが分かります。
気温を測定するだけであれば温度センサーと変わりませんが、サーミスタであれば水温なども測定することが出来ます。
以下回路図とプログラムです。
温度のテーブルに関しては、10倍して小数第一位までを整数とした上で、見づらいので別ファイルに入れておきました。また、0番目と 1023 番目にはとりあえず0を入れておきました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator; I/O function on CLKIN pin) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled) #pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR) #pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled) #pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) #pragma config PPS1WAY = ON // PPSLOCK bit One-Way Set Enable bit (PPSLOCKED Bit Can Be Cleared & Set Once) #pragma config PLLEN = ON // PLL Enable (4x PLL enabled) #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LPBOREN = OFF // Low Power Brown-out Reset enable bit (LPBOR is disabled) #pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming) #include <xc.h> #include <stdio.h> #include "lcd.h" #include "tempTable.h" #define _XTAL_FREQ 4000000 #define DEGREE 0 const char degree[8] = { //℃ 0b10000, 0b00110, 0b01001, 0b01000, 0b01000, 0b01001, 0b00110, 0b00000 }; void putch(char c){ lcd_DATA(c); } void Init(){ TRISA = 0b00001001; ANSELA = 0b00000001; TRISB = 0b00000000; ANSELB = 0b00000000; TRISC = 0b00000000; ANSELC = 0b00000000; OSCCONbits.IRCF = 0b1101; //4MHz ADCON0 = 0b00000001; //AN0 /ADC Enable ADCON1 = 0b10010000; //Right justified /Fosc/8 (2us) /VDD ADCON2 = 0b00000000; //No auto conversion } int ADC_GetConversion(){ ADCON0bits.ADGO = 1; //測定開始 while(ADCON0bits.ADGO); //測定終了待ち return ((ADRESH << 8) | ADRESL) & 0x03FF; //測定結果 } void main(void) { Init(); lcd_Init(); lcd_DefineCharacter(DEGREE, degree); //アドレスDEGREE(0)に配列degreeのフォントデータ書込み int res; while(1){ res = ADC_GetConversion(); lcd_SetCursor(0,0); printf("Temp:%2d.%1d%c",tempTable[res]/10, tempTable[res]%10, DEGREE); __delay_ms(100); } } |
1 |
const int tempTable[] = {0,7204,5543,4805,4357,4043,3807,3620,3466,3336,3224,3126,3040,2962,2892,2828,2770,2716,2667,2620,2577,2537,2499,2463,2429,2396,2366,2337,2309,2282,2257,2233,2209,2187,2165,2144,2124,2105,2086,2068,2050,2033,2017,2001,1985,1970,1955,1941,1927,1913,1900,1887,1874,1862,1850,1838,1827,1815,1804,1793,1783,1772,1762,1752,1742,1733,1723,1714,1705,1696,1687,1679,1670,1662,1654,1645,1638,1630,1622,1614,1607,1600,1592,1585,1578,1571,1564,1557,1551,1544,1538,1531,1525,1519,1513,1506,1500,1494,1489,1483,1477,1471,1466,1460,1455,1449,1444,1439,1433,1428,1423,1418,1413,1408,1403,1398,1393,1388,1384,1379,1374,1370,1365,1361,1356,1352,1347,1343,1339,1334,1330,1326,1322,1318,1313,1309,1305,1301,1297,1293,1290,1286,1282,1278,1274,1270,1267,1263,1259,1256,1252,1249,1245,1241,1238,1234,1231,1227,1224,1221,1217,1214,1211,1207,1204,1201,1198,1194,1191,1188,1185,1182,1179,1175,1172,1169,1166,1163,1160,1157,1154,1151,1148,1145,1143,1140,1137,1134,1131,1128,1125,1123,1120,1117,1114,1112,1109,1106,1104,1101,1098,1096,1093,1090,1088,1085,1083,1080,1077,1075,1072,1070,1067,1065,1062,1060,1057,1055,1053,1050,1048,1045,1043,1041,1038,1036,1034,1031,1029,1027,1024,1022,1020,1017,1015,1013,1011,1008,1006,1004,1002,999,997,995,993,991,989,986,984,982,980,978,976,974,972,970,967,965,963,961,959,957,955,953,951,949,947,945,943,941,939,937,935,933,931,929,927,926,924,922,920,918,916,914,912,910,908,907,905,903,901,899,897,896,894,892,890,888,886,885,883,881,879,877,876,874,872,870,869,867,865,863,862,860,858,857,855,853,851,850,848,846,845,843,841,840,838,836,835,833,831,830,828,826,825,823,821,820,818,817,815,813,812,810,809,807,805,804,802,801,799,797,796,794,793,791,790,788,787,785,783,782,780,779,777,776,774,773,771,770,768,767,765,764,762,761,759,758,756,755,753,752,750,749,747,746,744,743,742,740,739,737,736,734,733,731,730,729,727,726,724,723,721,720,719,717,716,714,713,711,710,709,707,706,704,703,702,700,699,698,696,695,693,692,691,689,688,687,685,684,682,681,680,678,677,676,674,673,672,670,669,668,666,665,664,662,661,660,658,657,656,654,653,652,650,649,648,646,645,644,642,641,640,639,637,636,635,633,632,631,629,628,627,626,624,623,622,620,619,618,617,615,614,613,612,610,609,608,606,605,604,603,601,600,599,598,596,595,594,593,591,590,589,588,586,585,584,582,581,580,579,578,576,575,574,573,571,570,569,568,566,565,564,563,561,560,559,558,556,555,554,553,552,550,549,548,547,545,544,543,542,541,539,538,537,536,534,533,532,531,530,528,527,526,525,523,522,521,520,519,517,516,515,514,513,511,510,509,508,507,505,504,503,502,501,499,498,497,496,494,493,492,491,490,488,487,486,485,484,482,481,480,479,478,476,475,474,473,472,470,469,468,467,466,464,463,462,461,460,458,457,456,455,454,452,451,450,449,448,446,445,444,443,442,440,439,438,437,436,434,433,432,431,430,428,427,426,425,424,422,421,420,419,418,416,415,414,413,411,410,409,408,407,405,404,403,402,401,399,398,397,396,394,393,392,391,390,388,387,386,385,384,382,381,380,379,377,376,375,374,372,371,370,369,368,366,365,364,363,361,360,359,358,356,355,354,353,351,350,349,348,346,345,344,343,341,340,339,338,336,335,334,333,331,330,329,328,326,325,324,323,321,320,319,317,316,315,314,312,311,310,308,307,306,305,303,302,301,299,298,297,295,294,293,292,290,289,288,286,285,284,282,281,280,278,277,276,274,273,272,270,269,268,266,265,264,262,261,260,258,257,255,254,253,251,250,249,247,246,244,243,242,240,239,238,236,235,233,232,230,229,228,226,225,223,222,221,219,218,216,215,213,212,210,209,207,206,205,203,202,200,199,197,196,194,193,191,190,188,187,185,184,182,181,179,177,176,174,173,171,170,168,167,165,163,162,160,159,157,155,154,152,151,149,147,146,144,142,141,139,137,136,134,132,131,129,127,126,124,122,120,119,117,115,114,112,110,108,106,105,103,101,99,97,96,94,92,90,88,86,85,83,81,79,77,75,73,71,69,67,65,63,61,59,57,55,53,51,49,47,45,43,41,39,37,35,33,30,28,26,24,22,19,17,15,13,10,8,6,4,1,-1,-4,-6,-8,-11,-13,-16,-18,-21,-23,-26,-28,-31,-33,-36,-39,-41,-44,-47,-49,-52,-55,-58,-61,-63,-66,-69,-72,-75,-78,-81,-84,-87,-91,-94,-97,-100,-103,-107,-110,-114,-117,-120,-124,-128,-131,-135,-139,-142,-146,-150,-154,-158,-162,-166,-171,-175,-179,-184,-188,-193,-197,-202,-207,-212,-217,-222,-228,-233,-239,-245,-250,-256,-263,-269,-276,-282,-289,-297,-304,-312,-320,-328,-337,-346,-356,-366,-376,-388,-400,-412,-426,-441,-457,-475,-494,-517,-543,-573,-612,-664,-746,0}; |
LCDライブラリ
1 2 3 4 5 6 7 |
void lcd_Send4(char c); void lcd_Send8(char c); void lcd_INST(char command); void lcd_DATA(char data); void lcd_Init(); void lcd_SetCursor(char row,char column); void lcd_DefineCharacter(char adrs, char* pattern); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#define E RC4 #define RS RC5 #define PORT PORTC #define _XTAL_FREQ 4000000 #include <xc.h> void lcd_Send4(char c){ E = 1; PORT = (PORT & 0xF0) | (c & 0x0F); //RC0-3 に送信する4bitを設定 NOP(); //クロック周波数が高い場合に必要な遅延 E = 0; //E立ち下げで送信 } void lcd_Send8(char c){ lcd_Send4(c >> 4); //上位4bit ビットマスクはlcd_Send4() の中で lcd_Send4(c); //下位4bit } void lcd_INST(char command){ RS = 0; lcd_Send8(command); __delay_us(40); } void lcd_DATA(char data){ RS = 1; lcd_Send8(data); __delay_us(40); } void lcd_Init(){ RS = 0; E = 0; __delay_ms(45); lcd_Send4(0x03); __delay_ms(5); lcd_Send4(0x03); __delay_us(100); lcd_Send4(0x03); //ここまでで確実に8bitモードになる lcd_Send4(0x02); //4bitモードに設定 __delay_us(40); lcd_INST(0x28); //ファンクションセット : 4bit/ 2lines/ 5*7 lcd_INST(0x08); //表示OFF/ カーソルOFF/ ブリンクOFF lcd_INST(0x01); //ディスプレイクリア __delay_ms(2); lcd_INST(0x06); //エントリーモード : カーソル右移動/ 表示シフトなし lcd_INST(0x0C); //表示ON } void lcd_SetCursor(char row, char column){ //行、列は0番目から const char lcd_DDRAM[4] = {0x00, 0x40, 0x14, 0x54}; //各行の先頭アドレス char adrs = lcd_DDRAM[row] + column; //指定位置のアドレス lcd_INST(0x80 + adrs); } void lcd_DefineCharacter(char adrs, char* pattern){ lcd_INST(0x40 | (adrs << 3)); //CGRAMアドレス (adrs: 0-7) for(char i=0; i<8; i++) lcd_DATA(pattern[i]); lcd_INST(0x80); //書き込み先をDDRAMに戻す } |
今回の記事は以上となります。最後までご覧いただきありがとうございました。
サーミスタによる温度測定、サーミスタの抵抗値を読む方法についてあれこれ書いてみました。
ご一読いただければ。
A/Dコンバータでサーミスタの抵抗値を読む サーミスタをつなぐ場所は?
http://igarage.cocolog-nifty.com/blog/2023/03/post-ad45c1.html
Arduino サーミスタを使った温度測定で 【ゼロ除算問題】
http://igarage.cocolog-nifty.com/blog/2023/03/post-4bb96a.html
ミスが広まる 1/1023 vs 1/1024
http://igarage.cocolog-nifty.com/blog/2020/01/post-a02d3f.html