その9 外部供給電源の単一化と、A/D変換部の安定化の追求!
TempLanの外部電源部分の大型専用安定化電源(+5.00V、+12V)を単一電源化しようと思った事から、 様々な問題が発生した。(その9を参照) 1.DC12Vから3端子安定化ICでDC5Vに落として使用するには、 3端子ICでの発熱を考慮しなくてはならず、 使用する電流値にもよるが、 自作パソコンのCPU用のヒートシンクぐらいのものが必要になってしまう。 温度や、湿度を扱うような回路には熱源をいかに分離するかが問題となる。 電源部分はバッテリーにするか、 センサーや、センサーアンプ部分を分離するか、 AC-DC電源部分を分離するしか方法はない。 2.小型の簡易外部電源5Vアダプターは、安価に電源回路部分を分離でき、 DC+5V(+-5%)ぐらいまでを許容できる様ないわゆるデジタル回路には便利であるが、 今回のようにA/D変換部分を含むような精密なアナログ回路には、 そのままでは不向きである。 その結果生まれた最終的な回路はこうなった。
外部供給が5V単一電源となり12Vラインが不要となったTempLan基盤。 多少ギザギザが発生しているでものの、この程度であれば十分と思う・・・(サンプル間隔1回/1Sec.)
;********************************************************************** ; TempLan28V JLY 30th 2008 ; LM35DZ 用の ゼロ点補正(約20mV)を含む ; AD変換値から20(0x14)を単純に2バイト減算する ; short mode & long mode has ;********************************************************************** list p=16f873a #include __CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _WRT_OFF & _LVP_OFF & _CPD_OFF w_temp EQU 0x20 status_temp EQU 0x21 CNT1 EQU 0x22 CNT2 EQU 0x23 CNT3 EQU 0x24 TMP_DATA EQU 0x25 TMP_DATA_B EQU 0x26 ; busyチェック用 TMP_CNT EQU 0x27 CCNT EQU 0x28 ; 文字数カウンタ LCNT EQU 0x29 ; 行番号フラグ TMP_CHR EQU 0x2a TMP_ADD EQU 0x2b H_comp EQU 0x2c ;LM35DZ温度補正値(上位バイト) L_comp EQU 0x2d ;LM35DZ温度補正値(下位バイト) TMP_CNT2 EQU 0x2e ;バイナリ->BCD変換用 count EQU 0x30 temp EQU 0x31 H_byte EQU 0x32 L_byte EQU 0x33 R0 EQU 0x34 R1 EQU 0x35 R2 EQU 0x36 TXTEMP EQU 0x37 TXDT_1 EQU 0x38 TXDT_2 EQU 0x39 TXDT_3 EQU 0x3a ;Symbols For LCD #define LCD_DATA PORTB #define LCD_DDIR TRISB #define LCD_RS PORTB,0 #define LCD_RW PORTB,1 #define LCD_E PORTB,2 #define BUSY_BIT 7 #define ROWS d'16' ; 横方向は16文字 #define Hcomp 0 #define Lcomp d'15' ; 15mV ;********************************************************************** ORG 0x000 ; processor reset vector clrf PCLATH ; ensure page bits are cleared goto main ; go to beginning of program ORG 0x004 ; interrupt vector location movwf w_temp ; save off current W register contents movf STATUS,w ; move status register into W register bcf STATUS,RP0 ; ensure file register bank set to 0 movwf status_temp ; save off contents of STATUS register ; isr code can go here or be located as a call subroutine elsewhere bcf STATUS,RP0 ; ensure file register bank set to 0 movf status_temp,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf w_temp,f swapf w_temp,w ; restore pre-isr W register contents retfie ; return from interrupt main call dly_10m ; 周辺待ち bcf STATUS,RP0 bcf STATUS,RP1 ; バンク0に設定 clrf INTCON ; 割り込みを禁止 ;*********************************************************** ; 初期化 ;*********************************************************** bsf STATUS,RP0 ; バンク1に設定 movlw b'10111111' ; RC7/RX(入力),RC6/TX(出力) movwf TRISC ; PORTC の設定 clrf LCD_DDIR ; LCDデータポートを出力に clrf PORTA movlw b'11111111' movwf TRISA ; PORTAを入力に movlw b'00100100' ; 8BIT,送信許可,非同期,高速 movwf TXSTA ; TXSTA レジスタの設定 movlw 81H ; ボーレート 9600bps (20MHz:高速設定時) movwf SPBRG ; SPBRG レジスタの設定 bcf STATUS,RP0 ; Bank 0 へ戻す movlw b'10010000' ; シリアル,8BIT,継続受信許可 movwf RCSTA ; RCSTA レジスタの設定 ; A/Dコンバータ設定 movlw b'11000001' movwf ADCON0 bsf STATUS,RP0 ; bank1 movlw b'10000001' ; ADFM=0,VREF=AN3 movwf ADCON1 bcf STATUS,RP0 ; bank0 bcf LCD_RS ; bcf LCD_RW ; LCDの制御信号をゼロに bcf LCD_E ; clrf LCD_DATA ; movlw Lcomp movwf L_comp movlw Hcomp movwf H_comp main_init call dly_10m ; 念のため10mS待つ call lcd_init ; LCD初期化 call dly_10m movlw b'00001100' ;カーソルオフ、左から右へ表示 call cmd_send call dly_10m clrf CCNT ; キャラクタ数カウンタをリセット clrf LCNT ; 行フラグをリセット clrf TMP_CNT ; テンポラリカウンタをクリア str_out1 movf TMP_CNT,w ;文字列出力 call INIT_MSG addlw 0x00 ;ゼロだったなら終了 btfsc STATUS,Z goto msg_out call chr_out incf TMP_CNT,1 goto str_out1 msg_out movlw d'100' movwf TMP_CNT ;初期メッセージ1秒表示 msg_loop call dly_10m decfsz TMP_CNT,1;初期メッセージ表示遅延 goto msg_loop call disp_clr ;表示クリア adc_loop bsf ADCON0,GO adc_loop1 btfsc ADCON0,GO ; A/D変換待ち goto adc_loop1 bsf STATUS,RP0 ; Bank=1 movf ADRESL,w bcf STATUS,RP0 ; Bank=0 movwf L_byte ; 下位バイト取り込み rrf L_byte,f ; 右ローテートで1/2する bcf STATUS,C ; キャリー無視 movf ADRESH,w ; 上位バイト取り込み movwf H_byte rrf H_byte,f ; 右ローテートで1/2する btfss STATUS,C ; キャリーが立っているか? goto adc_do2 movlw b'10000000' iorwf L_byte,f ; キャリーがあれば1桁下げ adc_do2 movf L_comp,0 subwf L_byte,1 btfss 3h,0 decf H_byte,1 movf H_comp,0 subwf H_byte,1 call B2_BCD call disp_clr btfss PORTC,0 ;Check PORTC Bit 0 goto set_ch ;If PORTC Bit 0 is 0, then goto Set_ch. goto set_blank ;If PORTC Bit 0 is 1, then goto set_blank. set_ch movlw 0x0021 ;ASCII CODE "!" call chr_out goto label_1 set_blank movlw 0x0020 call chr_out label_1 movlw 0x0020 call chr_out swapf R1,w andlw 0x0f movlw 0x20 call chr_out movf R1,w andlw 0x0f call BINHEX movwf TXDT_1 call chr_out swapf R2,w andlw 0x0f call BINHEX movwf TXDT_2 call chr_out movlw 0x2e call chr_out movf R2,w andlw 0x0f call BINHEX movwf TXDT_3 call chr_out movlw 0xdf call chr_out movlw 0x43 call chr_out ;*********************************************************** ; Data 転送 ;*********************************************************** TEXT movf TXDT_1,w call TX movf TXDT_2,w call TX movlw 0x2e call TX movf TXDT_3,w call TX movlw 0x0d call TX btfss PORTC,0 GOTO label_10 GOTO label_11 label_10 MOVLW 0x0001 MOVWF TMP_CNT GOTO msg_loop2 label_11 MOVLW 0x000f MOVWF TMP_CNT msg_loop2 call label_29 decfsz TMP_CNT,f goto msg_loop2 goto adc_loop ;*********************************************************** ; 送信サブルーチン ; TRMT=1で送信可 ;*********************************************************** TX movwf TXTEMP ; 送信するデータを変数(TXTEMP)に格納 bsf STATUS,RP0 ; Bank 1 へ切替 LPTX ; btfss TXSTA,TRMT ; 送信可能であるかチェック(1:可能, 0:禁止) goto LPTX ; 禁止であれば LPTX のラベル間を繰り返す ; bcf STATUS,RP0 ; Bank 0 へ戻す movf TXTEMP,W ; 変数(TXTEMP)に格納していた送信データをWregにロード movwf TXREG ; 送信データはTXREGレジスタを通してシリアル出力される return ; メインルーチンへ戻る ;表示クリア disp_clr movlw b'00000001' call cmd_send ; 表示クリア clrf CCNT clrf LCNT return ; 1文字出力 ; wに表示したい文字をセット chr_out call data_send incf CCNT,f ;出力した文字数を+1 movlw ROWS subwf CCNT,w ;もしもROWSと同じになったら btfss STATUS,Z goto chr_done ; ROWSと同じではないので終了 line_chg clrf CCNT ;文字数カウンタをリセット btfss LCNT,0 ; 現在は1行目か? goto chr_ld1 movlw b'10000000';1行目にアドレスをセット call cmd_send bcf LCNT,0 goto chr_done chr_ld1 ; 2行目にアドレスをセット movlw b'11000000' call cmd_send bsf LCNT,0 chr_done return INIT_MSG addwf PCL,1 DT "TempLan V281 INIT.OK!",0x00 BINHEX addwf PCL,1 DT "0123456789ABCDEF",0x00 ; 液晶を初期化 ; 4bitモード lcd_init call dly_10m call dly_10m movlw b'00110000' ;初期化データ1 call cmd_send4 call dly_10m movlw b'00110000' ;初期化データ2 call cmd_send4 call dly_10m movlw b'00110000' ;初期化データ3 call cmd_send4 call dly_100u movlw b'00100000' ;4bitモードにセット call cmd_send4 call dly_100u call busy_check movlw b'00101000' ;初期モード設定 call cmd_send movlw b'00001000' ;ディスプレイオフ call cmd_send movlw b'00000001' ;液晶をクリア call cmd_send movlw b'00000110' ;エントリモードをセット call cmd_send return ; 液晶にコマンドを送出 ; wレジスタ=コマンド ; Fレジスタ: TMP_DATA cmd_send ; CMD=w movwf TMP_DATA andlw 0xf0 movwf LCD_DATA bcf LCD_RW bcf LCD_RS bsf LCD_E nop nop bcf LCD_E swapf TMP_DATA,w andlw 0xf0 movwf LCD_DATA bcf LCD_RW bcf LCD_RS bsf LCD_E nop nop bcf LCD_E call busy_check return ; 初期化用4bit幅コマンド送出 ; wレジスタ=コマンド(上位4bit) ; Fレジスタ : TMP_DATA ; ビジーチェックできない期間用 cmd_send4 movwf TMP_DATA andlw 0xf0 movwf LCD_DATA bcf LCD_RW bcf LCD_RS bsf LCD_E nop nop bcf LCD_E return ; 液晶にデータを送出 ; wレジスタ=データ ; Fレジスタ: TMP_DATA data_send movwf TMP_DATA andlw 0xf0 movwf LCD_DATA bcf LCD_RW bsf LCD_RS bsf LCD_E nop nop bcf LCD_E swapf TMP_DATA,w andlw 0xf0 movwf LCD_DATA bcf LCD_RW bsf LCD_RS bsf LCD_E nop nop bcf LCD_E call busy_check return ; 液晶ビジーチェック ; Fレジスタ:TMP_DATA_B busy_check bsf STATUS,RP0 ; bank=1 movlw 0xf0 movwf LCD_DDIR bcf STATUS,RP0 ; bank=0 busy_chk1 bcf LCD_RS bsf LCD_RW bsf LCD_E nop movf LCD_DATA,w andlw 0xf0 movwf TMP_DATA_B bcf LCD_E nop nop bsf LCD_E nop ; 下位4ビットは無視 nop bcf LCD_E btfsc TMP_DATA_B,BUSY_BIT goto busy_chk1 bcf LCD_RW bsf STATUS,RP0 ; bank=1 movlw 0x00 movwf LCD_DDIR bcf STATUS,RP0 ; bank=0 return ;時間遅延ルーチン クロック20MHz用 ; 100マイクロ秒遅延 ; Fレジスタ: CNT1 dly_100u ; 100uS movlw d'100' movwf CNT1 dly_100u1 nop nop ; 1uS@1path nop nop decfsz CNT1,f goto dly_100u1 return label_29 MOVLW 0x0064 MOVWF TMP_CNT2 label_30 CALL dly_10m DECFSZ TMP_CNT2,f GOTO label_30 RETURN ; 10ミリ秒遅延 ; Fレジスタ:CNT1 dly_10m ; 10mS movlw d'10' movwf CNT1 dly_10m1 call dly_1m decfsz CNT1,f goto dly_10m1 return ; 1ミリ秒遅延 ; Fレジスタ:CNT2,3 dly_1m ; 1ms movlw d'4' movwf CNT2 dly_1m1 movlw d'250' movwf CNT3 dly_1m2 ; 250us Loop nop nop nop nop decfsz CNT3,f goto dly_1m2 decfsz CNT2,f goto dly_1m1 return ; バイナリ->BCD変換ルーチン ; Microchip アプリケーションノート AN526より B2_BCD bcf STATUS,0 movlw .16 movwf count clrf R0 clrf R1 clrf R2 loop16 rlf L_byte,F rlf H_byte,F rlf R2,F rlf R1,F rlf R0,F decfsz count,F goto adjDEC retlw 0 adjDEC movlw R2 movwf FSR call adjBCD movlw R1 movwf FSR call adjBCD movlw R0 movwf FSR call adjBCD goto loop16 adjBCD movlw 3 addwf 0,W movwf temp btfsc temp,3 movwf 0 movlw 30 addwf 0,W movwf temp btfsc temp,7 movwf 0 retlw 0 END
今回のハード、プログラムで、今までと異なっているところを順に説明します。 1.実験用にリセット用の押しボタンスイッチSW1を追加しています。通常は不要と思われます。 2.外部電源として5V(+-5%)程度の単一外部電源仕様です。消費電流が350mA程ありました。 3.その5V外部電源供給口に、1000μ以上の電解コンデンサーを追加しています。A/D変換部分への電源変動を押さえます。 4.PICのRA3/AN3/Vref+端子に、4.00Vの基準電圧をTL431CPを使用して発生させて接続しています。 TLP431CPのカソード側の電圧は、R9,R10,VR3の値のみで決まってしまうので、 外部電源の5.0VがVR3を設定後に変動しても殆ど影響なく4.00Vを保つ事ができます。 PICアセンブラー上の、「A/Dコンバータ設定」部分のビット0が"1"になっています。 movlw b'10000001' ; ADFM=0,VREF=AN3 ソフトウエアでは基本的にはこの部分のみが今回の変更点です。 5.RC0にDIP-SW1を追加しています。 このスイッチがOFFの場合には、 10kΩで5Vにプルアップして"1"を、 ONの場合にはグランドに接続して"0"をPICのポートCのゼロビット目に入力しています。 これは、ON時に温度計測サイクルを1回/1秒に変更して通常よりも早くして、 VR1の調整を行いやすくし、 OFF時は計測サイクルをゆっくりにしておくものです。 6.温度センサーもOPアンプと同様に12Vの電圧で駆動しています。 OPアンプの出力を0から4.0V得る為には5Vでは足りません。 また、安定したOPアンプの動作を得る為にも12Vを供給しています。 7.DC-DCアップコンバーター回路のMC34063AのICも、 小パワーながら発熱も無く5Vから安定した12.0Vを発生してくれます。 この詳細はその8を参照してください。 こちらのアップコンバーター回路でも1000μの電解コンデンサーは重要です。 調整は、まずソフト上の#define Lcomp d'15' ; 15mVを #define Lcomp d'0' として補正ゼロにしてコンパイルして実行し、 LM35Aの出力をデジタルテスターで測定しながらLCDの表示を調整します。 その後、先ほどの補正分を修正して記述してください。
この後、イーサネット接続なしのLCDなし、 7セグメントLED-3桁表示式の、 温度-湿度計を紹介しますが、 TempLanに湿度センサーを組込む為の前実験にもなっています。 おかげさまで、この実験は既に完成していますので、 ハード回路図と、PICのソフトウエアを公開すれば良いだけです。 その11を待っててね! その実験をふまえて、ハード的にはどうすれば湿度計ユニットを組込めるかは簡単です。 一番問題なのは、LANを使ってどのように通信フォーマットを変更すれば、 温度情報、湿度情報の2つのデーターを間違える事無く相手側に伝える事ができるのか? これが最も重要な課題になってきます。 これについては、まだ解決していません。 またまたいつになる事やら!