A/D変換サンプル

A/D変換機能を使って、アナログな電圧を数値化する。
今回はこれとフォトトランジスタを使って、照度計を作ってみよう。

回路図

A/D変換サンプル

LCDの回路に、フォトトランジスタ(NJL7502L)と、レンジ切り替え用の抵抗を4種類追加。
データシートによれば、光が当たることにより流れる電流は、0.3μA〜10mA。
てことは、オームの法則でこんな感じ?

最小レンジ:E=0.0000003(0.3μ)A*100000(100k)Ω=0.03V
最大レンジ:E=0.01(10m)A*100Ω=1V

A/D変換の精度は10bit(1024段階)なので、基準の5Vを1024で割って、最小0.005Vくらいか。
最小レンジの方が微妙かと思ったけど、変化を捕まえられないことはないかね。
まぁ、このあたりだと、データシートでは10ルクスにも届かないような範囲なので、ロウソク未満、月明かりくらい……?


で、書いて試してみてから気づいたんだけどね?
普段、AVRをいじってるのは夜の時間帯で、せいぜいが蛍光灯の明かりなわけですよ。
昼間だったとしても、窓と反対側の壁際にいるので、下手したら蛍光灯より暗いわけですよ。

で、蛍光灯の下で、100kΩの抵抗をつけて、最小レンジで計ってみても、最大値はぎりぎり5V*(1000/1024)いくかどうかってとこで、あとちょっとなんだけど、オーバーフローするところまでいかないのね。
安い懐中電灯を買ってきて、直接当ててみても、同じくらいだった。

てことは、流れてる電流は、I=(5V*1000/1024)/100000(100k)Ω=0.000049(49μ)Aなんで、これをルクスに換算すると、100〜200ルクス。
レンジ切り替えをする意味がないようです。
というわけで、今回は、100kΩの抵抗のみでいきます。
1MΩの抵抗とかがあれば、100kΩと切り替えて使えるかもね。

A/D変換サンプル

ソースコード

ソースは今回は、前にやったLCD表示も使うので、この辺はライブラリ(というか別ソース)にした。

#ifndef UTIL_H_
#define UTIL_H_

#include <util/delay.h>

void delay_ms( uint16_t time );

#endif /* UTIL_H_ */
#include "util.h"

void delay_ms( uint16_t time )
{
	// 指定ms分ループ
	while( time-- ){
		_delay_ms( 1 );
	}
}
#ifndef LCDLIB_H_
#define LCDLIB_H_

#include <avr/io.h>

#define LCD_PORT	PORTD	// LCD表示に使用する出力レジスタ
#define LCD_PIN	PIND	// LCD表示に使用する入力レジスタ
#define LCD_DDR	DDRD	// LCD表示に使用する方向レジスタ

#define LCD_D4	(1<<0)	// D4ビット位置
#define LCD_D5	(1<<1)	// D5ビット位置
#define LCD_D6	(1<<2)	// D6ビット位置
#define LCD_D7	(1<<3)	// D7ビット位置
#define LCD_E	(1<<5)	// Eビット位置
#define LCD_RW	(1<<6)	// R/Wビット位置
#define LCD_RS	(1<<7)	// RSビット位置
#define LCD_DATMASK	0x0F	// データビットマスク

// RSモード列挙型定義
typedef enum _tagRSMode{
	RS_MODE_COMMAND,	// コマンドモード
	RS_MODE_DATA,		// データモード
} RSMode;

void Lcd_init( void );
void Lcd_setpos( int8_t x, int8_t y );
void Lcd_setchar( char data );
void Lcd_setstr( char *str );
void Lcd_setcmd( uint8_t cmd );


#endif /* LCDLIB_H_ */
#include "LcdLib.h"
#include "util.h"

void lcd_busywait( void )
{
	uint8_t	bf = LCD_D7;	// Busyフラグが立った状態の変数を初期化

	// RSビットはクリアでコマンドモード
	LCD_PORT &= ~( LCD_RS );

	// ReadモードはRWビットを立てる
	LCD_PORT |= ( LCD_RW );

	// データビットを入力に設定
	LCD_DDR	&= ~( LCD_DATMASK );

	// Busyフラグが落ちるまでループ
	while( bf ){
		// イネーブルパルス出力(上位4bit)
		LCD_PORT |= LCD_E;
			// 高速駆動時はここでイネーブルパルス幅以上待つ必要があるはず
		bf = LCD_PIN & ( 1 << LCD_D7 );	// Busyフラグの読み出し
		LCD_PORT &= ~LCD_E;
			// 高速駆動時はここでイネーブルサイクル時間を稼ぐ必要があるはず

		// イネーブルパルス出力(下位4bit/4bitモードなので2回送る必要があるがここでは特にすることはない)
		LCD_PORT |= LCD_E;
			// 高速駆動時はここでイネーブルパルス幅以上待つ必要があるはず
		LCD_PORT &= ~LCD_E;
			// 高速駆動時はここでイネーブルサイクル時間を稼ぐ必要があるはず
	}

	// データビットを出力に戻す
	LCD_DDR	|= ( LCD_DATMASK );
}

void lcd_setcmd4( RSMode mode, uint8_t data )
{
	uint8_t	tmpval = LCD_PORT;	// ポート状態を一旦変数に入れる

	// 書き込むポートを一旦落とす
	tmpval &= ~( LCD_RW | LCD_RS | LCD_DATMASK );

	// RSビットコマンドモードであればクリアのまま
	if( RS_MODE_DATA == mode ){
		tmpval |= LCD_RS;	// RSビットはデータモードであれば立てる
	}

	// RWビットはクリアでWriteモードなのでそのまま

	// データビットセット(下位4bitのみセットするのマスク)
	tmpval |= ( LCD_DATMASK & data );

	// ポートに書き込み
	LCD_PORT = tmpval;

	// イネーブルパルス出力
	LCD_PORT |= LCD_E;
		// 高速駆動時はここでイネーブルパルス幅以上待つ必要があるはず
	LCD_PORT &= ~LCD_E;
		// 高速駆動時はここでイネーブルサイクル時間を稼ぐ必要があるはず
}

void Lcd_setcmd( uint8_t cmd )
{
	// 上位4bit送信
	lcd_setcmd4( RS_MODE_COMMAND, LCD_DATMASK & ( cmd >> 4 ));
	// 下位4bit送信
	lcd_setcmd4( RS_MODE_COMMAND, LCD_DATMASK & cmd );
	// Busyチェック
	lcd_busywait();
}

void Lcd_setchar( char data )
{
	// 上位4bit送信
	lcd_setcmd4( RS_MODE_DATA, LCD_DATMASK & ( data >> 4 ));
	// 下位4bit送信
	lcd_setcmd4( RS_MODE_DATA, LCD_DATMASK & data );
	// Busyチェック
	lcd_busywait();
}

void Lcd_setstr( char *str )
{
	// 終了(NULL文字までループ)
	while( *str ){
		// 1文字ずつ設定
		Lcd_setchar( *str );
		str++;
	}
}

void Lcd_init( void )
{
	// 最初は全てのポートを出力に設定
	LCD_DDR	|= ( LCD_E | LCD_RW | LCD_RS | LCD_DATMASK );
	// 信号もLowにしておく
	LCD_PORT &= ~( LCD_E | LCD_RW | LCD_RS | LCD_DATMASK );

	// 最初はBusyチェックが効かないので、時間待ちする
	delay_ms( 15 );		// 15ms待ち
	lcd_setcmd4( RS_MODE_COMMAND, 0x3 );	// 8bitモード設定
	delay_ms( 5 );		// 4.1ms以上待ちなので、5ms待ち
	lcd_setcmd4( RS_MODE_COMMAND, 0x3 );	// 8bitモード設定
	delay_ms( 1 );		// 100μs以上待ちなので、とりあえず1ms待っておく
	lcd_setcmd4( RS_MODE_COMMAND, 0x3 );	// 8bitモード設定
	delay_ms( 1 );		// ファンクションセットは39μs以上待ちなので、とりあえず1ms待っておく

	// 初期化シーケンスに書いてある通り、4bitの設定は4bitのみ渡せれば有効なので、1回のみ
	lcd_setcmd4( RS_MODE_COMMAND, 0x2 );	// 4bitモード設定
	delay_ms( 1 );		// ファンクションセットは39μs以上待ちなので、とりあえず1ms待っておく

	// ここからは4bitモード動作なので、8bitの命令を4bitずつ2回に分けて送ってやる
	lcd_setcmd4( RS_MODE_COMMAND, 0x2 );	// ファンクションセット、4bitモード
	lcd_setcmd4( RS_MODE_COMMAND, 0x8 );	// 2Line表示、5*8ドットフォント
	delay_ms( 1 );		// ファンクションセットは39μs以上待ちなので、とりあえず1ms待っておく
	lcd_setcmd4( RS_MODE_COMMAND, 0x0 );	// Display ON/OFF設定
	lcd_setcmd4( RS_MODE_COMMAND, 0xF );	// Display ON / Cursor ON / Blinking ON
	delay_ms( 1 );		// Display ON/OFF設定は39μs以上待ちなので、とりあえず1ms待っておく
	lcd_setcmd4( RS_MODE_COMMAND, 0x0 );	// Clear Displayコマンド
	lcd_setcmd4( RS_MODE_COMMAND, 0x1 );	// スペースでDDRAMが埋められ、アドレスカウンタがDDRAMの00H(先頭)に移動
	delay_ms( 2 );		// Clear Displayコマンドは1.53ms以上待ちなので、2ms待ち
	lcd_setcmd4( RS_MODE_COMMAND, 0x0 );	// Entry Mode
	lcd_setcmd4( RS_MODE_COMMAND, 0x6 );	// インクリメントモード / ディスプレイシフトなし
	delay_ms( 1 );		// Entry Modeコマンドは39μs以上待ちなので、とりあえず1ms待っておく
}

void Lcd_setpos( int8_t x, int8_t y )
{
	int8_t val = 0x80;	// コマンドコード設定

	if( x ) val |= 0x40;	// 行指定が0(1行目)じゃなかったら、2行目bitを立てる
	val |= ( y & 0x0f );	// 列指定の4bitを設定する

	// コマンド送信
	Lcd_setcmd( val );
}

基本動作


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS