19 August 2012

PIC16F690でC言語(XC8)を使って2チャンネルのA/D変換とLCD表示

2チャンネルのA/D変換をして、LCDに表示するサンプルプログラム。 A/D変換後の16進数の値を、不動小数を用いずに10進数の電圧表示や、抵抗値の換算値にして表示している。

■ 検証環境
・LCD : Sure Electronics 2002 LCD Module (20x2 Character)
・PIC16F690
・半固定抵抗 10kオーム
・CDS (5mm) SEN9006

・Microchip MPLAB X IDE ver 1.30
・Microchip MPLAB XC8 ver 1.01

20120819-breadboard.jpg
ブレッドボード上に作成したテスト回路

20120819-circuit.png
回路図

■ main関数部分

浮動小数点を使わずに、long intを用いて値をオーバーフロー/アンダーフローさせずに小数点以下2桁の表示を作り出している。

電圧測定結果 0V ~ 5Vのレンジに変換するときに、一旦100倍して、テキスト表示時に100で割ったものと、100で割った余りを別々に表示している。(AN2の結果表示)

また、CDSの抵抗値を求めるために、

Vad = 26/(26+Rcds)*Vdd より、 Rcds = 26*(Vdd-Vad)/Vad と変形して、これを100倍して次のように計算する Rcds*100 = 26 * (Vdd-Vad) * 100 / Vad

#include <stdio.h> #include <stdlib.h> #include<xc.h> #include "lcd_lib.h" #include "rs232c.h" __CONFIG(CP_OFF & CPD_OFF & BOREN_OFF & MCLRE_OFF & WDTE_OFF & PWRTE_ON & FOSC_INTRCIO); /* * */ #ifndef _XTAL_FREQ /* 例:4MHzの場合、4000000 をセットする */ #define _XTAL_FREQ 4000000 #endif /* printf関数の出力先のスタブ関数を定義する */ void putch(unsigned char ch){ lcd_putch(ch); rs232c_putch(ch); return; } int main(int argc, char** argv) { OSCCON = 0b01100000; /* 内部オシレーター 4MHz */ TRISA = 0b00011100; /* IOポートRA2,RA4を入力モードに(RA3は仕様により入力専用) */ TRISB = 0b00100000; /* IOポートRB4,RB6,RB7(TX)を出力モード、RB5(RX)を入力モードに */ TRISC = 0; /* IOポートRC0...RC7を出力モードに */ INTCONbits.PEIE = 0; /* 割り込み機能をOFF */ PORTA = 0; /* RA0...RA5をV-low */ PORTB = 0; /* RB4...RA7をV-low */ PORTC = 0; /* RC0...RC7をV-low */ CM1CON0 = 0; /* コンパレーター1 無効化設定 */ CM2CON0 = 0; /* コンパレーター1 無効化設定 */ ANSEL = 0b00000110; /* AN2,AN3 A/Dコンバータを有効化 */ ANSELH = 0; /* 全てのA/Dコンバータを無効化 */ ADCON1 = 0b00010000; /* A/DクロックFosc/8 */ __delay_ms(1000); lcd_init(); lcd_goto(0); lcd_puts("Initialize LCD ..."); for(;;) { __delay_ms(1000); lcd_clear(); ADCON0 = 0b10001001; /* 1-0-0010-0-1: 右詰め,Vdd,AN2,AD有効化 */ __delay_us(10); /* 変換準備のための時間(Acquisition Requirement Time)待つ */ ADCON0bits.GO = 1; /* A/D変換開始 */ while(ADCON0bits.GO_DONE){} unsigned int value = ADRESH << 8 | ADRESL; unsigned long int value2 = value * 500L / 0x3ffL; printf("AD2 = %lu.%02luV (%04X)", value2/100, value2%100, value); ADCON0 = 0b10001101; /* 1-0-0011-0-1: 右詰め,AN3,AD有効化 */ __delay_us(10); /* 変換準備のための時間(Acquisition Requirement Time)待つ */ ADCON0bits.GO = 1; /* A/D変換開始 */ while(ADCON0bits.GO_DONE){} value = ADRESH << 8 | ADRESL; value2 = 26L * (0x3ffL - value) * 100 / value; lcd_goto(0x40); printf("AD3 = %lu.%02luk (%04X)", value2/100, value2%100, value); } }

変換準備のための時間(A/D Acquisition Requirements Time)は、PIC16F690のリファレンスマニュアル100ページ目に書かれている計算式で計算する。入力側インピーダンスが10kオームの場合は4.67マイクロ秒待てばよいとサンプル例として書かれている。 今回は、10マイクロ秒待ってみた。

仮に、このウエイトを入れないとどうなるか…

前回のA/D変換の結果が”低い値”だった場合、次のA/D変換の入力も”高い値”であっても、高い結果値が得られずに中途半端な値が返されるようだ。

A/D変換のレンジ : 0x0000 ~ 0x03ff

前回の値がたとえば 0x0010 だった場合、次の変換で本来0x03ffと結果が返らなければならないでも、0x255 など中途半端な値で結果が返されたりする。

■ LCD共通関数

先日の下記の記事に掲載したLCD制御共通関数を用いている

PIC16F690でC言語(XC8)を使ってLCDに文字表示


■ 参考になるWebページ

A/D変換サンプル
A/D変換を使う
簡易照度計(自動レンジ切り替え)

秋月電子で販売しているCDSセル(CDSセンサ)の特性測定

ちなみに、この記事で作成した回路のCDSをフォトトランジスタに差し替えて、抵抗を100k程度にして動作させることも出来る。(フォトトランジスタから照度値を得る場合は、抵抗値に変換するのではなく、電流値にへんかんする)