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程度にして動作させることも出来る。(フォトトランジスタから照度値を得る場合は、抵抗値に変換するのではなく、電流値にへんかんする)