その11 LCD表示式、Lan接続型 温度-湿度計 TempLanRH へ
LCD表示式、Lan接続型の 温度-湿度計です。 TempLan(Lan型温度計)に、湿度を組み合わせたものです。 7SEG-LED表示方式のものからLEDを取り去りLCD表示にして、Xportを追加したものといってもいいでしょう。 今回も2つのセンサーを搭載し、2つのA/D変換入力を切換えながら約10秒間隔でデーターをLANで転送します。 温度センサーは、LM35A 湿度センサーユニットは、マルツ電波で購入した、HSM-20G(写真の緑色の基盤)です。 電源投入後1〜2分ぐらいは初期変動があるようです。
;********************************************************************** ; TempLanRH.asm Aug. 19th 2009 ; V 0.1 ; ;********************************************************************** 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温度補正値(下位バイト) A_H EQU 0x30 A_L EQU 0x31 TMP_CNT2 EQU 0x32 TMP_CNT3 EQU 0x33 ;バイナリ->BCD変換用 count EQU 0x34 temp EQU 0x35 H_byte EQU 0x36 L_byte EQU 0x37 R0 EQU 0x38 R1 EQU 0x39 R2 EQU 0x3a TXTEMP EQU 0x40 TXDT_1 EQU 0x41 TXDT_2 EQU 0x42 TXDT_3 EQU 0x43 ;Symbols For LCD #define HcompTM 0 #define LcompTM d'15' ; 15mV #define HcompRH 0 #define LcompRH 0x4d ; 300mV(300*1024/4.0/1000=76.8->0x4d) #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文字 ;********************************************************************** 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 レジスタの設定 bcf LCD_RS ; bcf LCD_RW ; LCDの制御信号をゼロに bcf LCD_E ; clrf LCD_DATA ; 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 ; ************************************************************************** ; A/D Temp ADTM_Loop movlw LcompTM movwf L_comp movlw HcompTM movwf H_comp call disp_clr ;表示クリア movlw b'11000001' ; A/D入力=AN0 movwf ADCON0 bsf STATUS,RP0 ; bank1 movlw b'10000001' ; ADFM=0,VREF+=AN3 movwf ADCON1 bcf STATUS,RP0 ; bank0 ADTM_START 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 LCD_dispTM movlw 0x20 call chr_out call LCD_TRDATA movlw 0xdf ; call chr_out movlw 0x43 ; call chr_out TransTM movlw 0x54 ; "T" call TX movlw 0x4d ; "M" call TX movlw 0x0d call TX 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 ; ************************************************************************** ; A/D %RH ADRH movlw LcompRH movwf L_comp movlw HcompRH movwf H_comp movlw b'11001001' ; A/D入力=AN1 movwf ADCON0 bsf STATUS,RP0 ; bank1 movlw b'10000001' ; ADFM=0,VREF+=AN3 movwf ADCON1 bcf STATUS,RP0 ; bank0 ADRH_START bsf ADCON0,GO RHadc_RHloop1 btfsc ADCON0,GO ; A/D変換待ち goto RHadc_RHloop1 bsf STATUS,RP0 ; Bank=1 movf ADRESL,w bcf STATUS,RP0 ; Bank=0 movwf L_byte ; 下位バイト取り込み movf ADRESH,w ; 上位バイト取り込み movwf H_byte RHadc_do2 ;**************************************************************************** ;%RH=%RH-0.3 movf L_comp,w subwf L_byte,f btfss 3h,0 decf H_byte,f movf H_comp,w subwf H_byte,f movf L_byte,w movwf A_L movf H_byte,w movwf A_H ;*************************************************************************** ;%RH=%RH*1.13=%RH+%RH/8 bcf STATUS,C rrf A_H,f ; 上位から処理 rrf A_L,f ; A = A/2 bcf STATUS,C rrf A_H,f ; 上位から処理 rrf A_L,f ; A = A/2 bcf STATUS,C rrf A_H,f ; 上位から処理 rrf A_L,f ; A = A/2 movf A_L,w addwf L_byte,f btfsc 3h,0 incf H_byte,f movf A_H,w addwf H_byte,f call B2_BCD movlw 0x20 ; " "(space) call chr_out call LCD_TRDATA movlw 0x25 ;"%" call chr_out TransRH movlw 0x52 ; "R" call TX movlw 0x48 ; "H" call TX movlw 0x0d call TX 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 MOVLW 0x07 MOVWF TMP_CNT3 msg_loop2 call label_29 decfsz TMP_CNT3,f goto msg_loop2 goto ADTM_Loop ; Sub ;*********************************************************** ; 送信サブルーチン ; 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 "TempLanRH V0.1 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変換ルーチン The 16 bit binary number = FFFF ; After conversion the Decimal number in R0,R1,R2 = 06,55,35 ; 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 LCD_TRDATA 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 return END
import java.applet.*; import java.awt.*; import java.net.*; import java.io.*; import java.text.*;
public class TempLanRH01 extends Applet implements Runnable { Thread timer;
Graphics OffGC; Image OffScreenImage; InetAddress xport_ip = null; int port = 10001; int inum01 = 0 ; int inum02 = 0 ; int grx = 50 ; int gry01 = 550 ; int gry02 = 550 ; double num01 ; double num02 ; Socket xport_socket; BufferedReader socketin; String buff; String buff01; String buff02; String sTM = "TM"; String sRH = "RH"; public void initOffGC() { OffGC.setColor(Color.black); OffGC.setFont(new Font("Serif",Font.BOLD,18)); OffGC.drawLine( 45, 550, 750, 550); // 時間軸 OffGC.drawLine( 50, 555, 50, 50); // 温度軸 OffGC.drawLine( 45, 500, 750, 500); // 5℃
OffGC.drawLine( 45, 450, 750, 450); // 10℃
OffGC.drawLine( 45, 400, 750, 400); // 15℃
OffGC.drawLine( 45, 350, 750, 350); // 20℃
OffGC.drawLine( 45, 300, 750, 300); // 25℃
OffGC.drawLine( 45, 250, 750, 250); // 30℃
OffGC.drawLine( 45, 200, 750, 200); // 35℃
OffGC.drawLine( 45, 150, 750, 150); // 40℃
OffGC.drawLine( 45, 100, 750, 100); // 45℃
OffGC.drawLine( 45, 50, 750, 50); // 50℃
OffGC.setColor(Color.blue); OffGC.drawString("0",30,555);
OffGC.drawString("5",30,505);
OffGC.drawString("10",20,455);
OffGC.drawString("15",20,405);
OffGC.drawString("20",20,355);
OffGC.drawString("25",20,305);
OffGC.drawString("30",20,255);
OffGC.drawString("35",20,205);
OffGC.drawString("40",20,155);
OffGC.drawString("45",20,105);
OffGC.drawString("50",20,55);
OffGC.setColor(Color.green);
OffGC.drawString("0",770,555);
OffGC.drawString("10",760,505);
OffGC.drawString("20",760,455);
OffGC.drawString("30",760,405);
OffGC.drawString("40",760,355);
OffGC.drawString("50",760,305);
OffGC.drawString("60",760,255);
OffGC.drawString("70",760,205);
OffGC.drawString("80",760,155);
OffGC.drawString("90",760,105);
OffGC.drawString("100",750,55); }
public void init() { OffScreenImage = createImage(800,600); OffGC = OffScreenImage.getGraphics(); try { xport_ip = InetAddress.getByName(getCodeBase().getHost()); }
catch(UnknownHostException e){} try { xport_socket = new Socket(xport_ip, port); socketin = new BufferedReader( new InputStreamReader(xport_socket.getInputStream())); } catch(IOException e){} initOffGC();
} public void start() { timer = new Thread(this); timer.start(); } public void stop() { try { socketin.close(); xport_socket.close(); } catch(IOException e){} timer = null; } public void run() { Thread me = Thread.currentThread(); while(timer == me) { try { Thread.currentThread().sleep(100); } catch(InterruptedException e){} try { buff = socketin.readLine(); } catch(IOException e){}
if( buff.equals(sTM) ) {
try { buff01 = socketin.readLine(); // 温度データーの読み込み } catch(IOException e){} num01 = Double.parseDouble(buff01); num01 = 10.0 * num01; inum01 = (int)num01; gry01 = -inum01 + 550 ;
} if( buff.equals(sRH) ) {
try { buff02 = socketin.readLine(); // 湿度データーの読み込み } catch(IOException e){} num02 = Double.parseDouble(buff02); num02 = 10.0 * num02; inum02 = (int)num02; gry02 = -inum02/2 + 550 ; // 1/2する事によって、温度表示ようエリアをスケーリングして共用する。 }
String t01 = " " + buff01 + "℃ " + buff02 + "%"; Dimension d = getSize(); if(grx > 750){ grx = 50 ; OffGC.clearRect(0, 0, d.width, d.height); initOffGC(); }
Font CurrentFont = OffGC.getFont(); Font font = new Font("MS 明朝" , Font.BOLD , 36); OffGC.setFont(font); FontMetrics metrics = getFontMetrics(font);
OffGC.setColor(Color.black); OffGC.clearRect(0, 0, 400, 40); OffGC.drawString(t01, 30, 35); // 現在の湿度-湿度を数値で表示
OffGC.setColor(Color.blue); OffGC.fillOval(grx, gry01, 2, 2); OffGC.setColor(Color.green); OffGC.fillOval(grx, gry02, 2, 2); grx = grx + 2 ;
OffGC.setFont(CurrentFont); repaint(); } } public void paint(Graphics g) { g.drawImage(OffScreenImage,0,0,this); } public void update(Graphics g) { paint(g); }
public void destroy() { OffGC.dispose(); } }
Lan接続の温度-湿度計がついに90%完成いたしました!
TempLanを単一電源化し、ついでに湿度計を足してしまえ!っていう乗りでしたが、 これがどうして、なかなか一筋縄では行きませんでした。
Lan型温度計と、Lan型湿度計としては、そんなに苦労しないでできてしまったのですが、 2つを一つのシステムの中に混ぜてしまうというのはなかなか思ったよりも大変でした。
元となるPICのアセンブラーが大変で、 プログラムが大きくなると、 今までまともに動いていたはずのものが、 動作がおかしくなって暴走(何もしない)してしまいます。
仕方なしに、 無駄なルーチンをサブルーチンにまとめ直して、 全体を小型化してやっとまともに動きました。
そこまで行くのには、 部分的に動作が正しい事を検証しながら、 だんだん大きくしていって、 おかしくなったら、 サブルーチン化して小型化するを繰り返していきます。
バグで暴走しているのか、アドレスのアクセス範囲を超えてしまったかは、 部分的に実験していくしかありません。
アセンブラーと、Xportを利用する為のJava アプレットの開発を平行して行わなくてはなりません。 これもなかなかの作業です。
Javaがオブジェクト指向の言語であり、そのオブジェクト指向になれていない上に、 Socketというクラスを利用するのですが、初心者のわたしには何を扱っているのかが分かりにくく、 しかも、スレードという技術(平行処理)を利用しなくては今回のような処理はやりにくいので・・・ そのうえ、コンピューター上に文字列表示ではなくて、グラフを書く・・・オフスクリーンの手法も必要で・・・
D/Aコンバート部分ではハードウエアの知識も・・・
えーい、なかなか面白かったじゃねーか! でも、完璧じゃあ無い! 素人だからかんべんしてくだせぃ!
Java アプレットのままでは、データーの垂れ流しはできても、 外部にファイルとして記録する事が制限されている。
今一度、Java アプリケーションを作って、 外部ファイルとして記録を残せるようにしたい。
今回のおもしろかったところは、転送データーの識別方法だった! Javaの通信コードは非常に素直なのだ。 英文字はすべてASCIIコードで送り側も、 受け取り側も非常に素直にデーターをやり取りできた。
元々温度データーはバイナリーではなく、 小数点も含めて4文字のASCIIコードで送っている。
そこで、データーを送る前に、”TM"とか、"RH"とか文字列を送って、 受け取る側でいまから何が送られてくるのか判断してやれば良い訳だった!
ところが、Javaがオブジェクト指向であったが為に落居いる初心者のトラップにまんまとはまってしまった。 String クラスのオブジェクトを == で同一判断をやってしまった。何をやっても一致しない。当たり前だった。 こういう場合は、equals()を使用すべしと、様々なJavaの教科書に載っていました。 いやー、初心者、初心者!
本当にいろいろありましたが、趣味だからこの辺で!