$prog &HFF , &H62 , &HDF , &HFF 'ヒューズ設定。(工場出荷状態) ' ' ****************************************** ' * CCS811 IAQ モニター プログラム * ' * * ' * Basic Compiler is BASCOM-AVR * ' * Copyright O-Family 2018. 3.20 * ' ****************************************** ' ' Ver 0.01 [試用版] 初回公開バージョン。 2018. 3.20 ' ' Const Prgver = " 0.01" 'プログラム・バージョン。 Const Demo_version = 0 '試用版の選択。(0:試用版 , 1:完全版) ' ' $regfile = "ATtiny861.DAT" '使用するAVRを設定。 $crystal = 1000000 'AVRクロックを設定。 ' $hwstack = 64 'ハードウェア・スタックの容量を設定。 $swstack = 10 'ソフトウェア・スタックの容量を設定。 $framesize = 24 'フレーム領域の容量を設定。 ' ' * ポート名の定義 * ' Sw_1 Alias Pinb.4 'スイッチ[1]の接続ポート。 Sw_1pu Alias Portb.4 'スイッチ[1]のプルアップポート。 Sw_2 Alias Pinb.5 'スイッチ[2]の接続ポート。 Sw_2pu Alias Portb.5 'スイッチ[2]のプルアップポート。 Sp_out Alias Porta.7 'スピーカー出力の接続ポート。 Led_r Alias Porta.6 'LED[赤]の接続ポート。 Ccs811_nwake Alias Porta.3 'CCS811の[nWAKE]ピン接続ポート。 Ccs811_nint Alias Pina.2 'CCS811の[nINT]ピン接続ポート。 Ad_battery_gnd Alias Porta.4 '電池電圧測定用の抵抗接地ポート。 ' Const Ccs811_adr = &HB4 'CCS811のI2Cアドレス。 Const Sht31_adr = &H88 'SHT31のI2Cアドレス。 ' ' * 変数の宣言 * ' Dim Dispmode As Byte 'LCDの表示モード。(0:VOC値, 1:グラフ表示, 2:バッテリー電圧) Dim Alarmmode As Byte 'アラームの動作モード。(0:オフ, 1:上位のみ, 2:上位と下位) Dim Time8sec As Byte '8.192秒経過フラグ。 Dim Time10min As Byte '10分カウンター。 Dim Time1hour As Word '1時間カウンター。 Dim Intchkcun As Byte '割り込み起動後のループ回数。 Dim Voltage As Byte '電池電圧値。 Dim Dbuff(24) As Word '24時間のデータバッファー。 Dim Dbuffpoi As Byte '24時間のデータバッファー用ポインター。 Dim Graphsel As Byte 'グラフ表示の時間選択フラグ。 Dim Cursorposi As Byte 'LCDカーソルのX位置。 Dim Alarmupth As Word 'アラーム上位値。(2500, 3000, 3500, 4000 ppm) Dim Alarmloth As Word 'アラーム下位値。(1000, 1500, 2000 ppm) Dim Alarmupintv As Byte 'アラーム上位のブザー音の間隔。(8.192秒のタイマー値) Dim Alarmlointv As Byte 'アラーム下位のブザー音の間隔。(8.192秒のタイマー値) Dim Buzzerflag As Byte 'ブザーのON/OFFフラグ。 Dim Buzzerwait As Byte 'ブザー音の待機中タイマー。 Dim Thdatawrf As Byte '温度と湿度データの書き込み選択フラグ。(0:未使用, 1:CCS811へ書き込む) ' Dim Eco2 As Word 'eCO2(等価CO2)値 ppm。 Dim Tvoc As Word 'TVOC(総VOC)値 ppb。 Dim I2cbuff(30) As Byte 'I2C送受信バッファー。 Dim Ccs811status As Byte 'CCS811のステータス。 Dim Ccs811mode As Byte 'CCS811の測定モード(サンプリング間隔)。 (0:1秒, 1:10秒, 2:60秒) Dim Sht31status As Byte 'SHT31のステータス。(0:接続なし, 1:接続あり) Dim Temperature As Integer 'SHT31温度値。(x0.01) Dim Humidity As Integer 'SHT31湿度値。(x0.01) Dim Errorflag As Byte 'SHT31のエラーフラグ。 ' Dim Temp1 As Byte '汎用テンポラリ変数 Byte型 No.1 Dim Temp2 As Byte '汎用テンポラリ変数 Byte型 No.2 Dim Temp3 As Byte '汎用テンポラリ変数 Byte型 No.3 Dim Temp4 As Byte '汎用テンポラリ変数 Byte型 No.4 Dim Tempw1 As Word '汎用テンポラリ変数 Word型 No.1 Dim Tempi1 As Integer '汎用テンポラリ変数 Integer型 No.1 Dim Templ1 As Long '汎用テンポラリ変数 Long型 No.1 Dim Tempstr As String * 20 '汎用テンポラリ変数 String型 ' Dim Dummy As Eram Long 'EEPROM 4バイトのダミーエリア。 Dim Eeplcdcontrast As Eram Byte 'EEPROM LCDのコントラスト設定。(0〜63) Dim Eepccs811mode As Eram Byte 'EEPROM CCS811の測定モード(サンプリング間隔)。 (0:1秒, 1:10秒, 2:60秒) Dim Eepalarmmode As Eram Byte 'EEPROM アラームの動作モード。(0:オフ, 1:上位のみ, 2:上位と下位) Dim Eepalarmupper As Eram Byte 'EEPROM アラームの上位設定値。(0:2500, 1:3000, 2:3500, 3:4000) Dim Eepalarmlower As Eram Byte 'EEPROM アラームの下位設定値。(0:1000, 1:1500, 2:2000) Dim Eepalarmupintv As Eram Byte 'EEPROM アラーム上位のブザー音の間隔。(0:1min, 1:3min, 2:5min, 3:10min, 4:15min) Dim Eepalarmlointv As Eram Byte 'EEPROM アラーム下位のブザー音の間隔。(0:1min, 1:3min, 2:5min, 3:10min, 4:15min) Dim Eepthdatawrf As Eram Byte 'EEPROM 温度と湿度データの書き込み選択フラグ。(0:未使用, 1:CCS811へ書き込む) ' ' * ポートの初期設定 * ' Set Sw_1pu 'スイッチ[1]の接続ポートをプルアップする。 Set Sw_2pu 'スイッチ[2]の接続ポートをプルアップする。 Config Sp_out = Output 'スピーカー出力の接続ポートを出力に設定する。 Config Led_r = Output 'LED[赤]の接続ポートを出力に設定する。 Config Ccs811_nwake = Output 'CCS811の[nWAKE]ピン接続ポートを出力に設定する。 Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 ' Set Portb.0 '未使用ポートをプルアップする。 Set Portb.1 '未使用ポートをプルアップする。 Set Portb.2 '未使用ポートをプルアップする。 Set Portb.3 '未使用ポートをプルアップする。 Set Portb.6 '未使用ポートをプルアップする。 ' ' * EEPROMのデータを確認する * ' If Eepccs811mode > 2 Then 'If EEPROMが初期値か? Then Eeplcdcontrast = 32 'EEPROM LCDのコントラスト設定。(0〜63) Eepccs811mode = 1 'EEPROM CCS811の測定モード(サンプリング間隔)。 (0:1秒, 1:10秒, 2:60秒) Eepalarmmode = 1 'EEPROM アラームの動作モード。(0:オフ, 1:上位のみ, 2:上位と下位) Eepalarmupper = 0 'アラームの上位設定値。(0:2500, 1:3000, 2:3500, 3:4000) Eepalarmlower = 1 'アラームの下位設定値。(0:1000, 1:1500, 2:2000) Eepalarmupintv = 2 'EEPROM アラーム上位のブザー音の間隔。(0:1min, 1:3min, 2:5min, 3:10min, 4:15min) Eepalarmlointv = 3 'EEPROM アラーム下位のブザー音の間隔。(0:1min, 1:3min, 2:5min, 3:10min, 4:15min) Eepthdatawrf = 1 'EEPROM 温度と湿度データの書き込み選択フラグ。(0:未使用, 1:CCS811へ書き込む) End If Ccs811mode = Eepccs811mode Alarmmode = Eepalarmmode Thdatawrf = Eepthdatawrf Gosub Alarmthset 'アラームのしきい値を設定する。 Gosub Alarmbzset 'アラームのブザー音の間隔を設定する。 ' ' * A/D変換器の設定 * ' Set Prr.prusi '電力削減 USI回路の停止。 Config Aci = Off 'アナログ比較器の電源を切る。 Ad_battery Alias 4 '電池電圧測定用のA/Dチャネル番号。 Set Didr0.adc4d 'デジタル入力禁止レジスタの設定。 Config Adc = Single , Prescaler = Auto , Reference = Internal_2.56_nocap 'A/Dコンバータの設定。 Stop Adc 'A/Dコンバータの電源を切る。 ' ' * I2Cの初期設定 * ' $lib "i2cV2.LBX" '修正版のI2Cライブラリを組み込む。 Config Scl = Porta.0 'I2CバスのSCLラインを接続するポートピンを設定。 Config Sda = Porta.1 'I2CバスのSDAラインを接続するポートピンを設定。 I2cinit 'I2Cバスを初期化する。 ' ' * LCDの初期設定 * ' Const Vmode = 3 '[AQM1602XA]の電源電圧。(3.5V以下 = 3 , 3.5V以上 = 5) Config Lcd = 16x2 , Chipset = St7032 'LCD表示を16文字2行に設定。 $lib "Lcd_RX1602A5.LBX" 'I2C LCD用のライブラリを組み込む。(i2cV2.LBXの場合はこのライブラリを使用する) Initlcd 'LCDを初期化する。 Temp1 = Eeplcdcontrast 'EEPROM LCDのコントラスト設定。(0〜63) Lcdcontrast Temp1 'LCDのコントラストを設定する。(0〜63) ' ' * カスタム文字をLCDに設定する * ' Deflcdchar 0 , &H08 , &H14 , &H08 , &H06 , &H09 , &H08 , &H09 , &H06 'カスタム文字[℃]をLCDへ書き込む。 Deflcdchar 1 , &H00 , &H00 , &H0C , &H12 , &H04 , &H08 , &H1E , &H00 'カスタム文字[下付2]をLCDへ書き込む。 Deflcdchar 2 , &H18 , &H14 , &H18 , &H10 , &H00 , &H1A , &H15 , &H15 'カスタム文字[pm]をLCDへ書き込む。 Deflcdchar 3 , &H00 , &H1F , &H00 , &H00 , &H00 , &H00 , &H00 , &H00 'カスタム文字[バー1bit]をLCDへ書き込む。 Deflcdchar 4 , &H00 , &H00 , &H1F , &H00 , &H00 , &H00 , &H00 , &H00 'カスタム文字[バー2bit]をLCDへ書き込む。 Deflcdchar 5 , &H00 , &H00 , &H00 , &H00 , &H1F , &H00 , &H00 , &H00 'カスタム文字[バー4bit]をLCDへ書き込む。 Deflcdchar 6 , &H00 , &H00 , &H00 , &H00 , &H00 , &H1F , &H00 , &H00 'カスタム文字[バー5bit]をLCDへ書き込む。 Gosub Lcdcustom 'カスタム文字をLCDに設定する。 Cursor Off 'LCDのカーソルをオフにする。 Cls 'LCD表示をすべて消去。 ' ' * プログラム・バージョンの表示 * ' If Sw_1 = 0 Then 'If [SW1]が押されているか? Then Lcd "CCS811 IAQ Moni" 'プログラム・バージョンを表示。 Locate 2 , 7 Lcd "Ver." ; Prgver Gosub Swoffcheck 'スイッチが離されるまで待つ。 Cls 'LCD表示をすべて消去。 End If ' ' * CCS811への温度と湿度データの書き込みを選択 * ' If Sw_2 = 0 Then 'If [SW2]が押されているか? Then Toggle Thdatawrf.0 'フラグを反転する。 Eepthdatawrf = Thdatawrf 'EEPROMへ保存する。 Lcd "Temp & Humi data" 'CCS811への温度と湿度データの書き込み選択を表示。 Locate 2 , 1 Lcd "Env_Reg: [" ; If Thdatawrf = 0 Then Lcd "Unuse]" Else Lcd "Write]" End If Gosub Buz2000 '2,000Hzの音を50mS鳴らす。 Gosub Swoffcheck 'スイッチが離されるまで待つ。 Cls 'LCD表示をすべて消去。 End If ' ' * CCS811の接続を確認する * ' Waitms 30 'CCS811の起動時間(tSTART)を待つ。 Reset Ccs811_nwake 'CCS811の[nWAKE]ピンを[L]にする。 Waitus 60 'CCS811の[nWAKE]=[L]からの起動時間(tAWAKE)を待つ。 ' Tempstr = "" I2cbuff(1) = &H20 '[HW_ID]レジスター。 I2creceive Ccs811_adr , I2cbuff(1) , 1 , 1 'I2Cバスで1バイトのアドレスを送信し1バイトのデータを受信する。 If I2cbuff(1) <> &H81 Then 'If CCS811のチップ識別番号が返ってこないか? Then Tempstr = " Not found!" Else '[HW_ID]が正常(81h)の場合。 I2cbuff(1) = &H00 '[STATUS]レジスター。 I2creceive Ccs811_adr , I2cbuff(1) , 1 , 1 'I2Cバスで1バイトのアドレスを送信し1バイトのデータを受信する。 If I2cbuff(1).4 = 0 Then 'If CCS811に有効なアプリケーションがロードされていないか? Then Tempstr = " Appli invalid!" Else '有効なアプリケーションがロードされている場合。 I2cbuff(1) = &HF4 '[APP_START]レジスター。 I2csend Ccs811_adr , I2cbuff(1) , 1 'I2Cバスで1バイトのアドレスを送信する。 ' I2cbuff(1) = &H00 '[STATUS]レジスター。 I2creceive Ccs811_adr , I2cbuff(1) , 1 , 1 'I2Cバスで1バイトのアドレスを送信し1バイトのデータを受信する。 If I2cbuff(1).7 = 0 Then 'If CCS811がアプリケーション・モードになっていないか? Then Tempstr = " Not appli mode!" Else 'CCS811がアプリケーション・モードになっている場合。 If I2cbuff(1).0 = 1 Then 'If CCS811のエラーがあるか? Then I2cbuff(1) = &HE0 '[ERROR_ID]レジスター。 I2creceive Ccs811_adr , I2cbuff(1) , 1 , 1 'I2Cバスで1バイトのアドレスを送信し1バイトのデータを受信する。 Tempstr = " Error! " + Bin(i2cbuff(1)) 'エラーのビットを表示する。 Else 'エラーがない場合。 Tempstr = "" End If End If End If End If ' If Tempstr <> "" Then 'If CCS811の起動にエラーがあるか? Then Locate 1 , 1 Lcd "# CCS811" Locate 2 , 1 Lcd Tempstr Sound Sp_out , 100 , 417 '200Hzの音を500mS鳴らす。 Stop End If ' Gosub Ccs811modeset 'CCS811の動作モードを設定する。 Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 ' ' * SHT31の接続を確認する * ' Waitms 30 'CCS811のI2C通信の内部処理時間を待つ。 I2cbuff(1) = &HF3 '[ステータス]レジスター。 I2cbuff(2) = &H2D I2creceive Sht31_adr , I2cbuff(1) , 2 , 3 'I2Cバスで、2バイトのコマンドを送信し3バイトのデータを受信する。 If Err = 0 Then 'If [SHT31]の応答があるか? Then Sht31status = 1 Gosub Sht31measure 'SHT31の温度・湿度を測定する。 Temperature = Temperature / 10 '温度値の小数点以下を1桁にする。 Humidity = Humidity / 10 '湿度値の小数点以下を1桁にする。 Else 'SHT31が確認できない場合。 Locate 1 , 1 Lcd "# SHT31" Locate 2 , 1 Lcd " Not found!" Sound Sp_out , 100 , 417 '200Hzの音を500mS鳴らす。 Wait 3 Sht31status = 0 'SHT31を未接続にする。 Thdatawrf = 0 '温度と湿度データの書き込み選択フラグを未使用にする。 Errorflag = 1 'SHT31のエラーフラグを立てる。 Cls End If ' ' * INT1 CCS811の[nINT]割り込みの設定 * ' Config Int1 = Low Level 'CCS811の[nINT]ピンの割り込み。(PA2) (※パワーダウンからの起動はレベル割り込みのみ) On Int1 Exint1 Nosave '外部割り込み[INT1]ルーチンのラベルを設定。 Enable Int1 '外部割り込み[INT1]を許可する。 ' ' * ウォッチドッグ・タイマーの設定 * ' Config Watchdog = 8192 'ウォッチドッグ・タイマーを8.192秒に設定する。 Set Wdtcr.wdie 'ウォッチドッグ・タイマーを割り込み発生に設定する。 On Wdt Wdtint Nosave 'ウォッチドッグ・タイマーの割り込みルーチンを設定する。 ' ' * ピン変化割り込みの設定 * ' Pcmsk0 = &B0000_0000 'PCINT割り込みマスクを初期化する。 Pcmsk1 = &B0000_0000 Set Pcmsk1.pcint12 '[SW1] PB4[PCINT12]を許可する。 Set Pcmsk1.pcint13 '[SW2] PB5[PCINT13]を許可する。 On Pcint Pinchgint Nosave 'ピン変化割り込みルーチンのラベルを設定。 Enable Pcint 'ピン変化割り込みを許可する。 ' ' * 初期画面の表示 * ' Gosub Batmeasure '電池電圧を測定する。 Gosub Thdisp '温度と湿度をLCDに表示する。 Gosub Eco2disp 'eCO2値をLCDに表示する。 Gosub Line2disp 'LCDの下段右側を表示する。 Dbuffpoi = 1 '24時間のデータバッファーポインターを初期化する。 Gosub Buz4000 '4,000Hzの音を50mS鳴らす。 '-------------------------------------------------------------------------------------------------- ' ' ******************* ' * メイン ルーチン * ' ******************* ' Main: If Intchkcun = 0 Then '起動後のループ回数を処理したか? Then Enable Interrupts 'すべての割り込みを許可する。 Config Powermode = Powerdown 'スリープモードへ移行する。(ピン変化割り込みで再起動する) Disable Interrupts 'すべての割り込みを禁止する。 Intchkcun = 3 Else Intchkcun = Intchkcun - 1 End If ' ' * 8秒間隔の処理 * (ウォッチドッグ・タイマーの8.192秒間隔) ' If Time8sec <> 0 Then 'If 8.192秒経過したか? Then Time8sec = 0 Cursor Off 'LCDのカーソルをOFFにする。 ' ' * SHT31から温度・湿度値を読み出す * ' If Sht31status <> 0 Then 'If SHT31が接続されているか? Then Gosub Sht31measure 'SHT31の温度・湿度を測定する。 Temperature = Temperature / 10 '温度値の小数点以下を1桁にする。 Humidity = Humidity / 10 '湿度値の小数点以下を1桁にする。 Gosub Thdisp '温度と湿度をLCDに表示する。 End If ' ' * 電池電圧の測定 * ' Time10min = Time10min + 1 If Time10min > 72 Then 'If 10分(8.192秒*73)経過したか? Then Time10min = 0 Gosub Batmeasure '電池電圧を測定する。 Gosub Line2disp 'LCDの下段右側を表示する。 ' If Voltage < 39 Then 'If 電池電圧が3.9V以下になったか? Then If Alarmmode <> 0 Then 'If アラームの動作モードが消音以外か? Then Sound Sp_out , 20 , 417 '200Hzの音を100mS鳴らす。 End If End If End If ' If Voltage < 39 Then 'If 電池電圧が3.9V以下になったか? Then Cursor Off 'LCDのカーソルをOFFにする。 Dispmode = 2 '表示モードを[2]:バッテリー電圧にする。 Gosub Lcdcustom 'カスタム文字をLCDに設定する。 Graphsel = 0 'グラフ表示を8Hに初期設定する。 Gosub Line2disp 'LCDの下段右側を表示する。 ' Set Led_r 'LED[赤]を点灯する。 Waitms 20 '50mSの待ち時間。 Reset Led_r 'LED[赤]を消灯する。 End If ' ' * 1時間毎の処理 * ' Time1hour = Time1hour + 1 If Time1hour > 439 Then 'If 1時間(8.192秒*440)経過したか? Then Time1hour = 0 ' Dbuffpoi = Dbuffpoi + 1 '24時間のデータバッファーポインターを更新する。 If Dbuffpoi > 24 Then 'If ポインターが上限か? Then Dbuffpoi = 1 End If Dbuff(dbuffpoi) = 0 'eCO2の最大値をリセットする。 Gosub Line2disp 'LCDの下段右側を表示する。 End If Gosub Line2cursor 'LCDのカーソルを表示する。 ' ' * アラームの処理 * ' If Buzzerwait <> 0 Then 'If ブザー音の待機中か? Then Buzzerwait = Buzzerwait - 1 End If ' If Eco2 >= Alarmupth Then 'If アラームの上位値を超えたか? Then。 If Alarmmode > 0 Then 'If アラームの動作モードが[1:上位のみ or 2:上位と下位]か? Then If Buzzerwait = 0 Then 'If ブザー音の待機中ではないか? Then Buzzerflag = 1 'ブザーをONにする。 Buzzerwait = Alarmupintv 'アラーム上位のブザー音の間隔をブザーの待ち時間に設定する。 End If Else Buzzerflag = 0 'ブザーをOFFにする。 Buzzerwait = 0 'アラームのブザー音の間隔を初期化する。 End If Gosub Alarmcont 'アラームLEDとブザーを制御する。 Waitms 100 Gosub Alarmcont 'アラームLEDとブザーを制御する。 Buzzerflag = 0 ' Else 'アラームの上位値を超えていない場合。 If Eco2 >= Alarmloth Then 'If アラームの下位値を超えたか? Then。 If Alarmmode > 1 Then 'If アラームの動作モードが[2:上位と下位]か? Then If Buzzerwait = 0 Then 'If ブザー音の待機中ではないか? Then Buzzerflag = 1 'ブザーをONにする。 Buzzerwait = Alarmlointv 'アラーム下位のブザー音の間隔をブザーの待ち時間に設定する。 End If Else Buzzerflag = 0 'ブザーをOFFにする。 End If Gosub Alarmcont 'アラームLEDとブザーを制御する。 Buzzerflag = 0 End If End If End If ' ' * CCS811からの測定完了[nINT]信号の処理 * ' If Ccs811_nint = 0 Then 'If CCS811の[nINT] = [L] 測定完了か? Then Cursor Off 'LCDのカーソルをOFFにする。 ' ' * CCS811からCO2とVOCを読み出す * ' Reset Ccs811_nwake 'CCS811の[nWAKE]ピンを[L]にする。 Waitus 60 'CCS811の[nWAKE]=[L]からの起動時間(tAWAKE)を待つ。 ' I2cbuff(1) = &H02 '[ALG_RESULT_DATA]レジスター。 I2creceive Ccs811_adr , I2cbuff(1) , 1 , 5 'I2Cバスで1バイトのアドレスを送信し5バイトのデータを受信する。 ' Eco2 = Makeint(i2cbuff(2) , I2cbuff(1)) '2バイトのECO2データをワード変数に変換する。 Tvoc = Makeint(i2cbuff(4) , I2cbuff(3)) '2バイトのTVOCデータをワード変数に変換する。 Ccs811status = I2cbuff(5) 'CCS811のステータスを一時保管する。 ' ' * CCS811に温度と湿度の環境パラメータを書き込む * ' If Thdatawrf <> 0 Then 'If 温度と湿度データをCCS811へ書き込むか? Then If Sht31status <> 0 Then 'If SHT31が接続されているか? Then If Errorflag = 0 Then 'If センサーのエラーがないか? Then Tempi1 = Humidity / 5 '[湿度 x 512]の上位バイトのみ使用。(48.5% x 512 = 24832 = 6100h -> 485 / 5 = 97 = 61h) I2cbuff(2) = Low(tempi1) 'インテジャー型変数の下位バイトを取り出す。 I2cbuff(3) = &H00 ' Tempi1 = Temperature + 250 '温度値に25℃のオフセットを加算する。 If Tempi1 < 0 Then 'If 温度+25℃のオフセットがマイナスか? Then Tempi1 = 0 End If Tempi1 = Tempi1 / 5 '[温度+25℃ x 512]の上位バイトのみ使用。(23.5℃ + 25℃[ofs] x 512 = 24832 = 6100h -> (23.5 + 25) / 5 = 97 = 61h) I2cbuff(4) = Low(tempi1) 'インテジャー型変数の下位バイトを取り出す。 I2cbuff(5) = &H00 ' I2cbuff(1) = &H05 '[ENV_DATA]レジスター。 I2csend Ccs811_adr , I2cbuff(1) , 5 'I2Cバスで1バイトのアドレスと4バイトのデータを送信する。 End If End If End If Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 ' ' * 1時間内のeCO2の最高値を確認する * ' If Dbuff(dbuffpoi) < Eco2 Then 'If eCO2の最高値を超えたか? Then Dbuff(dbuffpoi) = Eco2 End If ' ' * 測定値を表示する * ' Gosub Eco2disp 'eCO2値をLCDに表示する。 Gosub Line2disp 'LCDの下段右側を表示する。 End If ' ' * スイッチの入力処理 * ' Debounce Sw_1 , 0 , Sw1on '[SW1]が押されたか? Debounce Sw_2 , 0 , Sw2on '[SW2]が押されたか? ' Goto Main '################################################################################################## ' ' * スイッチ[1]が押された場合 * ' Sw1on: If Sw_2 = 0 Then Goto Setmode 'If [SW1]と[SW2]が同時に押されたか? Then Dispmode = Dispmode + 1 If Dispmode > 2 Then 'If 表示モードの上限か? Then Dispmode = 0 End If ' #if Demo_version = 0 'If 試用版か? Then If Dispmode = 1 Then 'If 表示モードが[1]:グラフ表示か? Then Dispmode = 2 End If #endif ' Sw1on1: Gosub Lcdcustom 'カスタム文字をLCDに設定する。 Graphsel = 0 'グラフ表示を8Hに初期設定する。 Cls 'LCD表示をすべて消去。 Cursor Off 'LCDのカーソルをOFFにする。 Gosub Batmeasure '電池電圧を測定する。 Gosub Thdisp '温度と湿度をLCDに表示する。 Gosub Eco2disp 'eCO2値をLCDに表示する。 Gosub Line2disp 'LCDの下段右側を表示する。 Gosub Buz2000 '2,000Hzの音を50mS鳴らす。 Goto Main ' ' * スイッチ[2]が押された場合 * ' Sw2on: If Sw_1 = 0 Then Goto Setmode 'If [SW1]と[SW2]が同時に押されたか? Then If Dispmode = 0 Then 'If 表示モードが[0]:VOC値表示か? Then Alarmmode = Alarmmode + 1 If Alarmmode > 2 Then 'If アラームモードの上限か? Then Alarmmode = 0 End If Eepalarmmode = Alarmmode 'EEPROMに保管する。 Buzzerwait = 0 'アラームのブザー音の間隔を初期化する。 Gosub Batmeasure '電池電圧を測定する。 Else '表示モードが[1]:グラフ表示か[2]:バッテリー電圧の場合。 Graphsel = Graphsel + 1 If Graphsel > 1 Then 'If グラフ表示が16H(温度と湿度データの書き込み状態)か? Then Graphsel = 0 End If End If ' Cursor Off 'LCDのカーソルをOFFにする。 Gosub Thdisp '温度と湿度をLCDに表示する。 Gosub Line2disp 'LCDの下段右側を表示する。 Gosub Buz4000 '4,000Hzの音を50mS鳴らす。 Goto Main '-------------------------------------------------------------------------------------------------- ' ' ----------------------------------------- ' * 温度と湿度をLCDに表示するサブルーチン * ' ----------------------------------------- ' Thdisp: Locate 1 , 1 If Dispmode = 1 Then Goto Thdisp2 'If 表示モードが[1]:グラフ表示か? Then Thdisp1: If Sht31status <> 0 Then 'If SHT31が接続されているか? Then If Errorflag = 0 Then 'If センサーのエラーがないか? Then Tempstr = Str(temperature) '温度値を表示する。 If Temperature < 0 Then 'If マイナスの値か? Then Lcd Format(tempstr , " 0.0") Else 'プラス値の場合。 Lcd Format(tempstr , " 0.0") End If Locate 1 , 6 Lcd Chr(0) '温度の単位[℃] ' Tempstr = Str(humidity) '湿度値を表示する。 Locate 2 , 1 Lcd Format(tempstr , " 0.0") ; "%" Else 'センサーのエラーがある場合。 Lcd "SHT31 " Locate 2 , 1 Lcd "Error!" End If Else 'SHT31が接続されていない場合。 Lcd "* " Locate 2 , 1 Lcd "* " End If Return ' Thdisp2: '表示モードが[1]:グラフ表示の場合。 If Graphsel = 0 Then Goto Thdisp1 'If グラフ表示が8Hか? Then Tempw1 = 0 'eCO2の24時間最大値を調べる。 For Temp1 = 1 To 24 If Tempw1 < Dbuff(temp1) Then 'If eCO2の24時間最大値か? Then Tempw1 = Dbuff(temp1) End If Next Temp1 ' Tempstr = Str(tempw1) '数値変数を文字変数に変換する。 Lcd "Mx" ; Format(tempstr , " 0") 'eCO2の24時間最大値をLCDに表示する。 Return ' ' ------------------------------------- ' * eCO2値をLCDに表示するサブルーチン * ' ------------------------------------- ' Eco2disp: Locate 1 , 8 If Ccs811status.0 = 0 Then 'If [ERROR](STATUSのbit0)は無いか? Then Tempstr = Str(eco2) '数値変数を文字変数に変換する。 Lcd "CO{001}" ; Format(tempstr , " 0") ; " {002}" 'eCO2の値をLCDに表示する。 ' Else 'エラーが発生している場合。 Reset Ccs811_nwake 'CCS811の[nWAKE]ピンを[L]にする。 Waitus 60 'CCS811の[nWAKE]=[L]からの起動時間(tAWAKE)を待つ。 I2cbuff(1) = &HE0 '[ERROR_ID]レジスター。 I2creceive Ccs811_adr , I2cbuff(1) , 1 , 1 'I2Cバスで1バイトのアドレスを送信し1バイトのデータを受信する。 Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 Lcd "E" ; Bin(i2cbuff(1)) 'エラーのビットを表示する。 Sound Sp_out , 100 , 417 '200Hzの音を500mS鳴らす。 End If Return ' ' --------------------------------------- ' * LCDの下段右側を表示するサブルーチン * ' --------------------------------------- ' Line2disp: Locate 2 , 7 'LCDの下段右側の表示を選択する。 Lcd " " ; Select Case Dispmode Case 0 : '[0]:VOC値の場合。 Tempstr = Str(tvoc) '数値変数を文字変数に変換する。 Lcd "Voc" ; Format(tempstr , " 0") ; Chr(7) 'VOC値をLCDに表示する。 Cursorposi = 0 'LCDのカーソルをOFFにする。 ' '$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ '$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ' Case 2 : '[2]:バッテリー電圧の場合。 If Graphsel = 0 Then 'If バッテリー電圧表示モードか? Then If Voltage > 38 Then 'If 電池電圧が3.8V以上あるか? Then Lcd "Bat:" ; Else Lcd "Low:" ; End If Tempstr = Str(voltage) '数値変数を文字変数に変換する。 Lcd Format(tempstr , "0.0") ; "v " '電池電圧値をLCDに表示する。 Else '温度と湿度データの書き込み選択状態を表示の場合。 Lcd "THdt:" ; If Thdatawrf <> 0 Then 'If 温度と湿度データをCCS811へ書き込むか? Then Lcd "Wrt" Else Lcd "Non" End If End If Cursorposi = 0 'LCDのカーソルをOFFにする。 ' End Select ' If Dispmode <> 1 Then Goto Line2disp1 'If 表示モードが[1]:グラフ表示以外か? Then If Graphsel <> 0 Then Goto Line2cursor 'If グラフ表示が16Hか? Then Line2disp1: Select Case Alarmmode Case 0 : Temp1 = &HF7 'アラームがオフの場合。 Case 1 : Temp1 = &H3C 'アラームが上位のみの場合。 Case 2 : Temp1 = &HFB 'アラームが上位と下位の場合。 End Select Locate 2 , 16 Lcd Chr(temp1) 'アラームの動作モードをLCDに表示する。 ' Line2cursor: 'LCDのカーソルを表示する。 If Cursorposi <> 0 Then 'If LCDのカーソルがONか? Then Locate 2 , Cursorposi 'グラフ用の最下位レベルカーソルを表示する。 Cursor On 'LCDのカーソルをONにする。 End If Return '$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ '$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ' ' ---------------------------------- ' * 電池電圧を測定するサブルーチン * ' ---------------------------------- ' Batmeasure: Start Adc 'A/Dコンバータに電源を供給する。 Config Ad_battery_gnd = Output '電池電圧測定用の抵抗接地ポートをGNDに接続する。 Waitus 100 'AVR内部基準電源の安定待ち時間。 Tempw1 = Getadc(ad_battery) '電池電圧値をA/D変換する。 Config Ad_battery_gnd = Input '電池電圧測定用の抵抗接地ポートを開放にする。 Stop Adc 'A/Dコンバータの電源を切る。 Templ1 = 256 * Tempw1 '2.56V÷1024×10000×[A/D値]÷1000 Templ1 = Templ1 / 5120 '式を変えて 2.56V×100×[A/D値]÷1024×2倍(抵抗分割分)÷10(小数点以下1桁) Voltage = Templ1 'Byte変数に代入する。 Return ' ' -------------------------------------------- ' * アラームのしきい値を設定するサブルーチン * ' -------------------------------------------- ' Alarmthset: Temp1 = Eepalarmupper Select Case Temp1 Case 0 : Alarmupth = 2500 'アラーム上位。(0:2500) Case 1 : Alarmupth = 3000 'アラーム上位。(1:3000) Case 2 : Alarmupth = 3500 'アラーム上位。(2:3500) Case 3 : Alarmupth = 4000 'アラーム上位。(3:4000) End Select ' Temp1 = Eepalarmlower Select Case Temp1 Case 0 : Alarmloth = 1000 'アラーム下位。(0:1000) Case 1 : Alarmloth = 1500 'アラーム下位。(1:1500) Case 2 : Alarmloth = 2000 'アラーム下位。(2:2000) End Select Return ' ' -------------------------------------------------- ' * アラームのブザー音の間隔を設定するサブルーチン * ' -------------------------------------------------- ' Alarmbzset: Temp1 = Eepalarmupintv 'アラーム上位のブザー間隔。 Gosub Alarmbzsets Alarmupintv = Temp3 Temp4 = Temp2 ' Temp1 = Eepalarmlointv 'アラーム下位のブザー間隔。 Gosub Alarmbzsets Alarmlointv = Temp3 Return ' Alarmbzsets: Select Case Temp1 Case 0 : Temp2 = 1 : Temp3 = 7 'アラーム上位。(0: 1分, 57.3秒) Case 1 : Temp2 = 3 : Temp3 = 21 'アラーム上位。(1: 3分, 172.0秒) Case 2 : Temp2 = 5 : Temp3 = 36 'アラーム上位。(2: 5分, 294.9秒) Case 3 : Temp2 = 10 : Temp3 = 72 'アラーム上位。(3:10分, 589.8秒) Case 4 : Temp2 = 15 : Temp3 = 109 'アラーム上位。(4:15分, 892.9秒) End Select Return ' ' --------------------------------------------- ' * アラームLEDとブザーを制御するサブルーチン * ' --------------------------------------------- ' Alarmcont: Set Led_r 'LED[赤]を点灯する。 If Buzzerflag <> 0 Then 'If ブザーがONか? Then Gosub Buz4000 '4,000Hzの音を50mS鳴らす。 Else Waitms 50 '50mSの待ち時間。 End If Waitms 50 '50mSの待ち時間。 Reset Led_r 'LED[赤]を消灯する。 Return ' ' ------------------------------------------- ' * カスタム文字をLCDに設定するサブルーチン * ' ------------------------------------------- ' Lcdcustom: If Dispmode = 0 Then 'If 表示モード[0] Voc表示か? Then Deflcdchar 7 , &H18 , &H14 , &H18 , &H10 , &H04 , &H06 , &H05 , &H06 'カスタム文字[pb]をLCDへ書き込む。 Else Deflcdchar 7 , &H00 , &H00 , &H00 , &H00 , &H00 , &H00 , &H00 , &H1F 'カスタム文字[バー7bit]をLCDへ書き込む。 End If Return ' ' --------------------------------------- ' * 4,000Hzの音を50mS鳴らすサブルーチン * ' --------------------------------------- ' Buz4000: Sound Sp_out , 200 , 21 '4,000Hzの音を50mS鳴らす。 Return ' ' --------------------------------------- ' * 2,000Hzの音を50mS鳴らすサブルーチン * ' --------------------------------------- ' Buz2000: Sound Sp_out , 100 , 42 '2,000Hzの音を50mS鳴らす。 Return '================================================================================================== ' ' ************** ' * 設定モード * ' ************** ' Setmode: '$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ '$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ' ' * 設定モードの3ページ目 * ' Setmode50: Locate 1 , 1 Lcd "Samp Time: sec" Temp2 = Eeplcdcontrast Locate 2 , 1 Lcd "Contrast Set: " ; Temp2 Gosub Buz2000 '2,000Hzの音を50mS鳴らす。 ' #if Demo_version = 0 'If 試用版か? Then Gosub Swoffcheck 'スイッチが離されるまで待つ。 Cursor Off Blink 'LCDのカーソルをオン・点滅にする。 #endif ' ' * CCS811のサンプリング間隔の設定 * ' Setmode51: Locate 1 , 12 Select Case Ccs811mode Case 0 : Lcd " 1" Case 1 : Lcd "10" Case 2 : Lcd "60" End Select Locate 1 , 10 Setmode52: Debounce Sw_1 , 0 , Setmode54 '[SW1]が押されたか? Debounce Sw_2 , 0 , Setmode53 '[SW2]が押されたか? Goto Setmode52 ' Setmode53: '[SW2]が押された場合。 Ccs811mode = Ccs811mode + 1 If Ccs811mode > 2 Then 'If サンプリング間隔の上限か? Then Ccs811mode = 0 End If Gosub Buz4000 '4,000Hzの音を50mS鳴らす。 Goto Setmode51 ' Setmode54: '[SW1]が押された場合。 Temp1 = Eepccs811mode If Temp1 <> Ccs811mode Then 'If サンプリング間隔に変更があったか? Then Eepccs811mode = Ccs811mode 'EEPROMに保管する。 Reset Ccs811_nwake 'CCS811の[nWAKE]ピンを[L]にする。 Waitus 60 'CCS811の[nWAKE]=[L]からの起動時間(tAWAKE)を待つ。 Gosub Ccs811modeset 'CCS811の動作モードを設定する。 Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 End If Gosub Buz2000 '2,000Hzの音を50mS鳴らす。 ' ' * LCDのコントラスト設定 * ' Setmode61: Locate 2 , 15 Lcd Temp2 Locate 2 , 13 Setmode62: Debounce Sw_1 , 0 , Setmode64 '[SW1]が押されたか? Debounce Sw_2 , 0 , Setmode63 '[SW2]が押されたか? Goto Setmode62 ' Setmode63: '[SW2]が押された場合。 Temp2 = Temp2 + 1 If Temp2 > 50 Then 'If コントラスト値の上限か? Then Temp2 = 20 End If _temp1 = Temp2 'LCDのコントラスト値。(0〜63) !CALL _LCD_CONTRAST 'コントラスト設定ルーチン。 Gosub Buz4000 '4,000Hzの音を50mS鳴らす。 Goto Setmode61 ' Setmode64: '[SW1]が押された場合。 Eeplcdcontrast = Temp2 'EEPROMに保管する。 Gosub Buz2000 '2,000Hzの音を50mS鳴らす。 Gosub Swoffcheck 'スイッチが離されるまで待つ。 Goto Sw1on1 '-------------------------------------------------------------------------------------------------- ' ' * アラームの設定値を表示する * ' Alarmthdisp: Gosub Alarmthset 'アラームのしきい値を設定する。 Locate 1 , 11 Lcd Alarmupth 'アラーム上位値をLCDに表示する。 Locate 2 , 11 Lcd Alarmloth 'アラーム下位値をLCDに表示する。 Return ' ' * アラーム・ブザー音の間隔の設定値を表示する * ' Alarmbzdisp: Gosub Alarmbzset 'アラームのブザー音の間隔を設定する。 Locate 1 , 12 If Temp4 < 10 Then 'If ゼロサプレスか? Then Lcd " " ; End If Lcd Temp4 ' Locate 2 , 12 If Temp2 < 10 Then 'If ゼロサプレスか? Then Lcd " " ; End If Lcd Temp2 Return ' ' * スイッチが離されるまで待つサブルーチン * ' Swoffcheck: Waitms 30 'チャタリングの待ち時間。 Bitwait Sw_1 , Set '[SW1]が離されるまで待つ。 Bitwait Sw_2 , Set '[SW2]が離されるまで待つ。 Waitms 30 'チャタリングの待ち時間。 Return '================================================================================================== ' ' ************************************************ ' * ウォッチドッグ・タイマー割り込み処理ルーチン * ' ************************************************ ' $asm Wdtint: PUSH R24 LDI R24,$FF STS {time8sec},R24 '8秒経過フラグを立てる。 POP R24 RETI $end Asm ' ' ****************************** ' * 外部割り込み[INT1]ルーチン * CCS811の[nINT]ピンの割り込み ' ****************************** ' Exint1: '何も処理しない。 Return ' ' **************************** ' * ピン変化割り込みルーチン * [SW1],[SW2] ' **************************** ' Pinchgint: '何も処理しない。 Return '################################################################################################## ' ' ************************************** ' * CCS811の動作モード設定サブルーチン * ' ************************************** ' Ccs811modeset: I2cbuff(1) = &H01 '[MEAS_MODE]レジスター。 Select Case Ccs811mode 'CCS811の測定モード(サンプリング間隔)。 (0:1秒, 1:10秒, 2:60秒) Case 0 : I2cbuff(2) = &B0001_1000 '001:モード1 毎秒測定。 測定完了で[nINT]へ割り込みを発生。 Case 1 : I2cbuff(2) = &B0010_1000 '010:モード2 10秒毎に測定。 測定完了で[nINT]へ割り込みを発生。 Case 2 : I2cbuff(2) = &B0011_1000 '011:モード3 60秒毎に測定。 測定完了で[nINT]へ割り込みを発生。 End Select I2csend Ccs811_adr , I2cbuff(1) , 2 'I2Cバスで1バイトのアドレスと1バイトのデータを送信する。 Return '================================================================================================== ' ' ********************************************** ' * SHT31 温度・湿度 センサー 測定サブルーチン * ' ********************************************** ' Sht31measure: I2cbuff(1) = &H24 '単発測定コマンド、精度:高、クロックストレッチ:無し。 I2cbuff(2) = &H00 I2csend Sht31_adr , I2cbuff(1) , 2 'I2Cバスで2バイトのコマンドを送信する。 Waitms 20 '測定の待ち時間。 ' I2cstart '[スタート・コンディション] の状態にする。 Temp1 = Sht31_adr + 1 I2cwbyte Temp1 'スレーブ・アドレス(読み出し)を送信する。 I2crbyte I2cbuff(1) , Ack '温度の上位バイトを読み出す。 I2crbyte I2cbuff(2) , Ack '温度の下位バイトを読み出す。 I2crbyte I2cbuff(3) , Ack '温度のチェックサムを読み出す。 I2crbyte I2cbuff(5) , Ack '湿度の上位バイトを読み出す。 I2crbyte I2cbuff(6) , Ack '湿度の下位バイトを読み出す。 I2crbyte I2cbuff(7) , Nack '湿度のチェックサムを読み出す。 I2cstop '[ストップ・コンディション] の状態にする。 Errorflag = Err ' ' * 温度の計算 * ' Gosub Sht31crc 'SHT31用のCRC8を計算する。 If I2cbuff(3) <> I2cbuff(4) Then 'If CRCエラーか? Then Errorflag = 1 'チェックサム・エラーが発生した。 End If Tempw1 = Makeint(i2cbuff(2) , I2cbuff(1)) '2バイトを1ワードに変換する。 Templ1 = 17500 * Tempw1 'T = -45 + 175 * St / (2^16-1) (小数点演算を使わないために100倍した値) Templ1 = Templ1 / 65535 Temperature = Templ1 Temperature = Temperature - 4500 ' ' * 湿度の計算 * ' I2cbuff(1) = I2cbuff(5) I2cbuff(2) = I2cbuff(6) I2cbuff(3) = I2cbuff(7) Gosub Sht31crc 'SHT31用のCRC8を計算する。 If I2cbuff(3) <> I2cbuff(4) Then 'If CRCエラーか? Then Errorflag = 1 'チェックサム・エラーが発生した。 End If Tempw1 = Makeint(i2cbuff(2) , I2cbuff(1)) '2バイトを1ワードに変換する。 Templ1 = 10000 * Tempw1 'RH = 100 * Srh / (2^16-1) (小数点演算を使わないために100倍した値) Templ1 = Templ1 / 65535 Humidity = Templ1 Return ' ' * SHT31用のCRC8を計算する * ' Sht31crc: Loadadr I2cbuff(1) , Z $asm LDI R16,$02 '計算するバイト数。 SER R24 'CRC値。(FFh) LDI R22,$31 'POLYNOMIAL = 0x131 P(x)=x^8+x^5+x^4+1 = 1_0011_0001 Sht31crc1: LD R25,Z+ '計算するバイトを読み出す。 EOR R24,R25 'CRC = CRC EXOR I2cbuff(x) LDI R17,$08 Sht31crc2: LSL R24 'CRCを左へ1bitシフトする。 BRcc Sht31crc3 'If シフト前のCRCのbit7が[1]か? Else EOR R24,R22 'CRC = CRC EXOR &H31 Sht31crc3: DEC R17 BRNE Sht31crc2 'If 8ビット終了か? Else DEC R16 BRNE Sht31crc1 'If 計算するバイト数が終わったか? Else ADIW R30,1 '受信CRCバイトの次に計算CRC値を格納する。 ST Z,R24 $end Asm Return ' ' End