' ' ********************************************** ' * * ' * AVR & BASCOM-AVR トレーニング・ボード * ' * 初期設定 Ver.1.01 * ' * * ' * AVR is using ATmega88P * ' * Basic Compiler is BASCOM-AVR * ' * Copyright By O-Family 2010.10.29 * ' ********************************************** ' $regfile = "m88pdef.dat" '使用するAVRを設定。 $crystal = 16000000 'AVRクロックを設定。 ' $hwstack = 64 'ハードウェア・スタックの容量を設定。 $swstack = 10 'ソフトウェア・スタックの容量を設定。 $framesize = 24 'フレーム領域の容量を設定。 ' ' * ポート名の定義 * ' Sw_1 Alias Pind.0 'スイッチ[1]の接続ポート。 Sw_2 Alias Pind.1 'スイッチ[2]の接続ポート。 Sw_3 Alias Pind.2 'スイッチ[3]の接続ポート。 Sw_4 Alias Pind.3 'スイッチ[4]の接続ポート。 ' Led_1r Alias Portd.4 'LED1[赤]の接続ポート。 Led_2g Alias Portd.5 'LED2[緑]の接続ポート。 Led_3y Alias Portd.6 'LED3[黄]の接続ポート。 Led_4b Alias Portd.7 'LED4[青]の接続ポート。 Sp_out Alias Portc.5 'スピーカーの接続ポート。 ' Ad_photo Alias 0 '光センサーのA/Dコンバータ・チャネル番号。 Ad_thermo Alias 1 '温度センサーのA/Dコンバータ・チャネル番号。 Ad_ain1 Alias 2 '測定入力[1]のA/Dコンバータ・チャネル番号。 Ad_ain2 Alias 3 '測定入力[2]のA/Dコンバータ・チャネル番号。 Ad_ain3 Alias 4 '測定入力[3]のA/Dコンバータ・チャネル番号。 Ad_spin Alias 5 'スピーカー入力のA/Dコンバータ・チャネル番号。 ' ' * ポートの初期設定 * ' Config Led_1r = Output 'LED1の接続ポートを出力に設定する。 ' Config Led_2g = Output 'LED2の接続ポートを出力に設定する。 Config Led_3y = Output 'LED3の接続ポートを出力に設定する。 ' Config Led_4b = Output 'LED4の接続ポートを出力に設定する。 Config Sp_out = Output 'スピーカーの接続ポートを出力に設定する。 ' Set Portd.0 'スイッチ[1]の接続ポートをプルアップする。 Set Portd.1 'スイッチ[2]の接続ポートをプルアップする。 ' Set Portd.2 'スイッチ[3]の接続ポートをプルアップする。 ' Set Portd.3 'スイッチ[4]の接続ポートをプルアップする。 Didr0 = &B00111111 'デジタル入力禁止レジスタの設定。 ' ' * LCDの初期設定 * ' Config Lcdmode = Port 'LCDを4ビットのポートモードに設定。 Config Lcdbus = 4 'LCDデータバスを4bitに設定。 Config Lcdpin = Pin , Db4 = Portb.3 , Db5 = Portb.2 'LCDのポート割り当て。 Config Lcdpin = Pin , Db6 = Portb.1 , Db7 = Portb.0 Config Lcdpin = Pin , E = Portb.4 , Rs = Portb.5 Config Lcd = 16 * 2 'LCD表示を16文字2行に設定。 Cls 'LCD表示をすべて消去。 ' ' * A/Dコンバータの初期設定 * ' Config Adc = Single , Prescaler = Auto , Reference = Avcc 'A/Dコンバータの設定。 Start Adc 'A/Dコンバータに電源を供給。 ' ' ↓********************************************↓ ' ↓ これ以降に各自のプログラムを作成して下さい ↓ ' ↓********************************************↓ ' '-------------------------------------------------------------------------------------------------- ' ' ****************************************** ' * CCS811 CO2 , VOC センサー (+ SHT31) * ' * テスト・プログラム * ' * Copyright O-Family 2017.10.29 * ' ****************************************** ' ' Ver. 3.01 新規作成バージョン。 2017.10.29 ' ' ' * ポート名の定義 * ' Ccs811_nwake Alias Portd.7 'CCS811の[nWAKE]ピン接続ポート。 Ccs811_nint Alias Pind.5 'CCS811の[nINT]ピン接続ポート。 ' Const Ccs811_adr = &HB4 'CCS811のI2Cアドレス。 Const Sht31_adr = &H88 'SHT31のI2Cアドレス。 ' ' * 変数の宣言 * ' Dim Eco2 As Word 'eCO2(等価CO2)値 ppm。 Dim Tvoc As Word 'TVOC(総VOC)値 ppb。 Dim I2cbuff(30) As Byte 'I2C送受信バッファー。 Dim I2clength As Byte 'I2C受信データのバイト数。 Dim Ccs811status As Byte 'CCS811のステータス。 Dim Ccs811mode As Byte 'CCS811の測定モード(サンプリング間隔)。 (0:1秒, 1:10秒, 2:60秒) Dim Dispmode As Byte 'LCDの表示モード。(0:VOC値表示, 1:気圧値表示, 2:CCS811のサンプリング間隔表示) 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 'エラーフラグ。 ' Dim Temp1 As Byte '汎用テンポラリ変数 Byte型 No.1 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 Eepccs811mode As Eram Byte 'EEPROM CCS811の測定モード(サンプリング間隔)。 (0:1秒, 1:10秒, 2:60秒) Dim Eepdispmode As Eram Byte 'EEPROM LCDの表示モード。(0:VOC値表示, 1:気圧値表示, 2:CCS811のサンプリング間隔表示) ' ' * ポートの初期設定 * ' Config Ccs811_nwake = Output 'CCS811の[nWAKE]ピン接続ポートを出力に設定する。 Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 ' ' * I2Cの初期設定 * ' ' $lib "i2cV2.LBX" '修正版のI2Cライブラリを組み込む。 Config Scl = Portd.2 'I2CバスのSCLラインを接続するポートピンを設定。 Config Sda = Portd.3 'I2CバスのSDAラインを接続するポートピンを設定。 I2cinit 'I2CバスのSCL,SDAラインを初期化する。 ' ' * カスタム文字をLCDに設定する * ' Deflcdchar 0 , &H06 , &H05 , &H06 , &H04 , &H00 , &H00 , &H00 , &H00 'カスタム文字[p]をLCDへ書き込む。 Deflcdchar 1 , &H18 , &H14 , &H18 , &H10 , &H00 , &H1A , &H15 , &H15 'カスタム文字[pm]をLCDへ書き込む。 Deflcdchar 2 , &H18 , &H14 , &H18 , &H10 , &H04 , &H06 , &H05 , &H06 'カスタム文字[pb]をLCDへ書き込む。 Deflcdchar 3 , &H00 , &H00 , &H0C , &H12 , &H04 , &H08 , &H1E , &H00 'カスタム文字[下付2]をLCDへ書き込む。 Deflcdchar 4 , &H08 , &H14 , &H08 , &H06 , &H09 , &H08 , &H09 , &H06 'カスタム文字[℃]をLCDへ書き込む。 Deflcdchar 5 , &H10 , &H18 , &H14 , &H14 , &H02 , &H05 , &H06 , &H04 'カスタム文字[hp]をLCDへ書き込む。 Cursor Off 'LCDのカーソルをオフにする。 ' ' * EEPROMのデータを確認する * ' If Eepccs811mode > 2 Then 'If EEPROMが初期値か? Then Eepccs811mode = 0 'EEPROM CCS811の測定モード(サンプリング間隔)。 (0:1秒, 1:10秒, 2:60秒) Eepdispmode = 0 'EEPROM LCDの表示モード。(0:VOC値表示, 1:気圧値表示, 2:CCS811のサンプリング間隔表示) End If Ccs811mode = Eepccs811mode Dispmode = Eepdispmode ' ' * Timerの設定 * ' Config Timer1 = Timer , Prescale = 1024 , Clear Timer = 1 'Timer1の設定。16,000,000Hz / 1024 = 15,625Hz Compare1a = 15625 - 1 '15,625Hz / 1Hz(1秒) = 15625カウント ' ' * CCS811の接続を確認する * ' Waitms 30 'CCS811の起動時間(tSTART)を待つ。 Reset Ccs811_nwake 'CCS811の[nWAKE]ピンを[L]にする。 Waitus 60 'CCS811の[nWAKE]=[L]からの起動時間(tAWAKE)を待つ。 ' I2cbuff(1) = &H20 '[HW_ID]レジスター。 I2clength = 1 'I2Cの受信バイト数。 Gosub Ccs811read 'CCS811からI2Cでデータを読み出す If I2cbuff(1) <> &H81 Then 'If CCS811のチップ識別番号が返ってこないか? Then Tempstr = " Not found!" Else '[HW_ID]が正常(81h)の場合。 I2cbuff(1) = &H00 '[STATUS]レジスター。 I2clength = 1 'I2Cの受信バイト数。 Gosub Ccs811read 'CCS811からI2Cでデータを読み出す 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]レジスター。 I2clength = 1 'I2Cの受信バイト数。 Gosub Ccs811read 'CCS811からI2Cでデータを読み出す 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]レジスター。 I2clength = 1 'I2Cの受信バイト数。 Gosub Ccs811read 'CCS811からI2Cでデータを読み出す 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 , 6666 '200Hzの音を500mS鳴らす。 Stop End If ' Gosub Ccs811init '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 Else 'SHT31が確認できない場合。 Locate 1 , 1 Lcd "# SHT31" Locate 2 , 1 Lcd " Not found!" Sound Sp_out , 100 , 6666 '200Hzの音を500mS鳴らす。 Wait 3 Sht31status = 0 Cls End If ' ' * 初期画面の表示 * ' Gosub Line2disp 'LCDの下段右側を表示する。 ' ' ******************* ' * メイン ルーチン * ' ******************* ' Main: ' ' * 1秒間隔の処理 * ' If Tifr1.ocf1a = 1 Then 'If 1秒経過したか? Then Set Tifr1.ocf1a 'Timer1の比較A一致フラグをリセット。 ' ' * SHT31から温度・湿度値を読み出す * ' If Sht31status <> 0 Then 'If SHT31が接続されているか? Then Gosub Sht31measure 'SHT31の温度・湿度を測定する。 Temperature = Temperature / 10 '温度値の小数点以下を1桁にする。 Humidity = Humidity / 10 '湿度値の小数点以下を1桁にする。 ' ' * 温度と湿度をLCDに表示する * ' Tempstr = Str(temperature) '温度値を表示する。 Locate 1 , 1 If Temperature < 0 Then 'If マイナスの値か? Then Lcd Format(tempstr , " 0.0") Else 'プラス値の場合。 Lcd Format(tempstr , " 0.0") End If Locate 1 , 6 Lcd Chr(4) '温度の単位[℃] ' Tempstr = Str(humidity) '湿度値を表示する。 Locate 2 , 1 Lcd Format(tempstr , " 0.0") ; "%" End If End If ' ' * CCS811からの測定完了[nINT]信号の処理 * ' If Ccs811_nint = 0 Then 'If CCS811の[nINT] = [L] 測定完了か? Then ' ' * CCS811からCO2とVOCを読み出す * ' Reset Ccs811_nwake 'CCS811の[nWAKE]ピンを[L]にする。 Waitus 60 'CCS811の[nWAKE]=[L]からの起動時間(tAWAKE)を待つ。 ' I2cbuff(1) = &H02 '[ALG_RESULT_DATA]レジスター。 I2clength = 5 'I2Cの受信バイト数。 Gosub Ccs811read 'CCS811からI2Cでデータを読み出す ' Eco2 = Makeint(i2cbuff(2) , I2cbuff(1)) '2バイトのECO2データをワード変数に変換する。 Tvoc = Makeint(i2cbuff(4) , I2cbuff(3)) '2バイトのTVOCデータをワード変数に変換する。 Ccs811status = I2cbuff(5) 'CCS811のステータスを一時保管する。 ' ' * CCS811に温度と湿度の環境パラメータを書き込む * ' If Sht31status <> 0 Then 'If SHT31が接続されているか? 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 ' Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 ' ' * CO2とVOCをLCDに表示する * ' If Ccs811status.0 = 0 Then 'If [ERROR](STATUSのbit0)は無いか? Then Locate 1 , 8 'CO2の値をLCDに表示する。 Tempstr = Str(eco2) '数値変数を文字変数に変換する。 Lcd "CO{003}" ; Format(tempstr , " 0") ; Chr(0) ; Chr(1) ' Gosub Line2disp 'LCDの下段右側を表示する。 ' Else 'エラーが発生している場合。 Locate 1 , 8 Lcd "# Error! " Reset Ccs811_nwake 'CCS811の[nWAKE]ピンを[L]にする。 Waitus 60 'CCS811の[nWAKE]=[L]からの起動時間(tAWAKE)を待つ。 I2cbuff(1) = &HE0 '[ERROR_ID]レジスター。 I2clength = 1 'I2Cの受信バイト数。 Gosub Ccs811read 'CCS811からI2Cでデータを読み出す Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 Locate 2 , 8 Lcd " " ; Bin(i2cbuff(1)) 'エラーのビットを表示する。 Sound Sp_out , 100 , 6666 '200Hzの音を500mS鳴らす。 End If End If ' ' * スイッチの入力処理 * ' Debounce Sw_1 , 0 , Sw1on '[SW1]が押されたか? Debounce Sw_2 , 0 , Sw2on '[SW2]が押されたか? ' Goto Main '-------------------------------------------------------------------------------------------------- ' ' * スイッチ[1]が押された場合 * ' Sw1on: If Ccs811status.0 = 0 Then 'If CCS811のエラーは無いか? Then Dispmode = Dispmode + 1 If Dispmode > 1 Then 'If 表示モードの上限か? Then Dispmode = 0 End If Eepdispmode = Dispmode 'EEPROMに保管する。 Gosub Line2disp 'LCDの下段右側を表示する。 Sound Sp_out , 200 , 333 '4000Hzの音を50mS鳴らす。 End If Goto Main ' ' * スイッチ[2]が押された場合 * ' Sw2on: If Ccs811status.0 = 0 Then 'If CCS811のエラーは無いか? Then If Dispmode = 1 Then 'If 表示モードがサンプリング間隔表示か? Then Ccs811mode = Ccs811mode + 1 If Ccs811mode > 2 Then 'If サンプリング間隔の上限か? Then Ccs811mode = 0 End If Eepccs811mode = Ccs811mode 'EEPROMに保管する。 ' Reset Ccs811_nwake 'CCS811の[nWAKE]ピンを[L]にする。 Waitus 60 'CCS811の[nWAKE]=[L]からの起動時間(tAWAKE)を待つ。 Gosub Ccs811init 'CCS811の初期設定。 Set Ccs811_nwake 'CCS811の[nWAKE]ピンを[H]にする。 ' Gosub Line2disp 'LCDの下段右側を表示する。 Sound Sp_out , 200 , 333 '4000Hzの音を50mS鳴らす。 End If End If Goto Main ' ' --------------------------------------- ' * LCDの下段右側を表示するサブルーチン * ' --------------------------------------- ' Line2disp: Locate 2 , 8 'LCDの下段右側の表示を選択する。 Select Case Dispmode Case 0 : 'VOC値の表示モードの場合。 Tempstr = Str(tvoc) '数値変数を文字変数に変換する。 Lcd "Voc" ; Format(tempstr , " 0") ; Chr(0) ; Chr(2) ' Case 1 : 'CCS811のサンプリング間隔表示の場合。 Lcd "Smp: sec" Locate 2 , 12 Select Case Ccs811mode Case 0 : Lcd " 1" Case 1 : Lcd "10" Case 2 : Lcd "60" End Select End Select Return '################################################################################################## ' ' * CCS811用の変数を宣言 * ' Dim Ccs811temp1 As Byte ' ' *********************************** ' * CCS811からI2Cでデータを読み出す * (I2cbuff(1) = CCS811のレジスターアドレス。 , I2clength = 読み出すデータのバイト数) ' *********************************** (I2cbuff(1)〜 読み出したレジスターのデータ) ' Ccs811read: I2csend Ccs811_adr , I2cbuff(1) , 1 'I2Cバスで、1バイトのアドレスを送信する。 ' Waitus 100 'CCS811のクロック・ストレッチ(内部処理の待ち時間)が終わるのを待つ。 ### BASCOMのバグにより ### ' I2cstart 'I2Cバスを、[スタート・コンディション]にする。 Ccs811temp1 = Ccs811_adr + &H01 I2cwbyte Ccs811temp1 'スレーブ・アドレス + 読み出しを送信。 Ccs811temp1 = 1 While I2clength <> 1 'If 受信データのバイト数が1以上か? Then I2crbyte I2cbuff(ccs811temp1) , Ack Ccs811temp1 = Ccs811temp1 + 1 I2clength = I2clength - 1 Wend I2crbyte I2cbuff(ccs811temp1) , Nack I2cstop '[ストップ・コンディション]の状態にする。 ' Waitus 100 'CCS811のクロック・ストレッチ(内部処理の待ち時間)が終わるのを待つ。 ### BASCOMのバグにより ### ' Return ' ' ******************** ' * CCS811の初期設定 * ' ******************** ' Ccs811init: 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 = 0 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