- 追加された行はこの色です。
- 削除された行はこの色です。
#analog
#norelated
#contents
* A/D変換サンプル [#a65c1d55]
A/D変換機能を使って、アナログな電圧を数値化する。~
今回はこれとフォトトランジスタを使って、照度計を作ってみよう。~
** 回路図 [#rac11946]
#ref(ADConvTest_old.png,left,nowrap,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Ωと切り替えて使えるかもね。~
#ref(ADConvTest.png,left,nowrap,A/D変換サンプル)
** ソースコード [#m3e37833]
ソースは今回は、前にやったLCD表示も使うので、この辺はライブラリ(というか別ソース)にした。
-util.h
#ifndef UTIL_H_
#define UTIL_H_
#include <util/delay.h>
void delay_ms( uint16_t time );
#endif /* UTIL_H_ */
-util.c
#include "util.h"
void delay_ms( uint16_t time )
{
// 指定ms分ループ
while( time-- ){
_delay_ms( 1 );
}
}
-lcdlib.h
#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_ */
-lcdlib.c
#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 );
}
*** 基本動作 [#af0c8e19]