#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]