' ' *********************************************** ' * * ' * 周波数カウンター・プログラム * ' * * ' * AVR is using ATmega88 * ' * Basic Compiler is BASCOM-AVR * ' * Copyright By O-Family 2008.11.20 * ' *********************************************** ' ' Ver 1.01 初回公開バージョン ' ' Const Prgver = "01.01" 'プログラム・バージョン。 ' $regfile = "m88def.dat" '使用するAVRを設定。 $crystal = 12800000 'AVRクロックを設定。 ' Sw_o1 Alias Portd.2 'スイッチ1のポートピン指定。 Sw_1 Alias Pind.2 Sw_o2 Alias Portd.1 'スイッチ2のポートピン指定。 Sw_2 Alias Pind.1 Sw_o3 Alias Portd.0 'スイッチ3のポートピン指定。 Sw_3 Alias Pind.0 ' Msel_a Alias Portb.1 '計測モード選択Aのポートピン指定。 Msel_b Alias Portd.7 '計測モード選択Bのポートピン指定。 ' ' Dim Capmode As Byte 'キャプチャー割り込みの動作モード。 Dim Capendf As Byte 'キャプチャー計測完了フラグ。 Dim Capdat1 As Word 'キャプチャー・レジスター値 バッファー1。 Dim Capovf1 As Word 'オーバーフロー・カウント値 バッファー1。 Dim Capdat2 As Word 'キャプチャー・レジスター値 バッファー2。 Dim Capovf2 As Word 'オーバーフロー・カウント値 バッファー2。 Dim Capovfc As Word 'オーバーフロー・カウンター。 ' Dim Frqdat As Long 'キャプチャー・データを差分計算したカウント値。 Dim Measure As Byte '計測モード。(0 = ゲート・モード , 1 = レシプロカル・モード) Dim Prescale As Byte 'プリスケラ値。(0=1/1 , 1=1/10 , 2=1/100) Dim Average As Long 'レシプロカル・モード用の平均値。 Dim Avernum As Long 'レシプロカル・モード用の平均数。 ' Dim Temp1 As Byte '汎用テンポラリ変数 Byte型 No.1 Dim Templ1 As Long '汎用テンポラリ変数 Long型 No.1 Dim Temps1 As Single '汎用テンポラリ変数 Single型 No.1 Dim Tempstr As String * 15 '汎用テンポラリ変数 String型 ' ' ' ' * LCDの初期設定 * ' Config Lcdmode = Port 'LCDを4ビットのポートモードに設定。 Config Lcdbus = 4 'LCDデータバスを4bitに設定。 Config Lcdpin = Pin , Db4 = Portb.5 , Db5 = Portb.4 'LCDのポート割り当て。 Config Lcdpin = Pin , Db6 = Portb.3 , Db7 = Portb.2 Config Lcdpin = Pin , E = Portc.0 , Rs = Portc.1 Config Lcd = 16 * 2 'LCD表示を16文字2行に設定。 ' Cursor Off 'LCDのカーソルをオフ。 Cls 'LCD表示をすべて消去。 ' ' * ポートの初期設定 * ' Config Msel_a = Output 'モード選択Aポートを出力に設定。 Config Msel_b = Output 'モード選択Bポートを出力に設定。 ' Set Sw_o1 'スイッチ1 ポートをプルアップ。 Set Sw_o2 'スイッチ2 ポートをプルアップ。 Set Sw_o3 'スイッチ3 ポートをプルアップ。 Set Portb.7 '未使用ポートをプルアップ。 Set Portc.2 '未使用ポートをプルアップ。 Set Portc.3 '未使用ポートをプルアップ。 Set Portc.4 '未使用ポートをプルアップ。 Set Portc.5 '未使用ポートをプルアップ。 ' ' * タイマーの設定 * ' Config Timer2 = Timer , Prescale = 256 , Clear Timer = 1 , Compare B = Toggle '12,800,000Hz / 256 = 50,000Hz Ocr2a = 250 - 1 '50,000Hz / 250カウント = (200 / 2) = 100Hz Ocr2b = 250 - 1 Config Timer0 = Counter , Edge = Rising , Clear Timer = 1 , Compare A = Toggle '[OC0A]端子から1Hzパルスを出力。 Ocr0a = 50 - 1 '100Hz / 50カウント = (2 / 2) = 1Hz ' Config Timer1 = Counter , Edge = Falling , Capture Edge = Falling '[T1]の立ち下がりエッジでカウント。 On Capture1 Tintcap Nosave 'キャプチャー割り込みルーチンのラベルを設定。 Enable Capture1 'キャプチャー割り込みを許可。 On Timer1 Tintovf Nosave 'オーバーフロー割り込みルーチンのラベルを設定。 Enable Timer1 'オーバーフロー割り込みを許可。 ' ' * プログラム・バージョンの表示 * ' If Sw_1 = 0 Then 'If スイッチ1が押されているか? Then Locate 1 , 1 'プログラム・バージョンを表示。 Lcd "FrequencyCounter" Locate 2 , 4 Lcd "Ver. " ; Prgver Bitwait Sw_1 , Set 'スイッチ1が離されるまで待つ。 Waitms 30 'チャタリング・タイマー。 Cls End If ' ' * 変数と表示の初期設定 * ' Measure = 0 '0 = ゲート・モード。 Prescale = 0 'プリスケラ値 0 = 1/1 。 Average = 0 'レシプロカル・モード用の平均値。 Avernum = 0 'レシプロカル・モード用の平均数。 Gosub Prescdss 'プリスケラ値の表示と設定。 Enable Interrupts 'すべての割り込みを許可。 ' ' ******************* ' * メイン ルーチン * ' ******************* ' Main: Gosub Switchin 'スイッチの入力処理。 Gosub Frqcal 'カウント値の計算処理。 Goto Main ' ' ********************************** ' * スイッチの入力処理サブルーチン * ' ********************************** ' Switchin: If Sw_1 = 0 Then Goto Switchin1 'If スイッチ1が押されたか? Then If Sw_2 = 0 Then Goto Switchin2 'If スイッチ2が押されたか? Then Return ' Switchin1: 'スイッチ1 (モードの選択) If Measure = 0 Then 'If 計測モードがゲート・モード? Then Measure = 1 'レシプロカル・モードに設定。 Locate 1 , 1 Lcd "Rcp" Locate 2 , 1 Lcd Spc(4) Set Msel_a 'モードセレクターを設定。 Set Msel_b Prescale = 0 Config Timer1 = Timer , Prescale = 1 , Capture Edge = Falling 'AVR動作クロックによりカウント。 Else Measure = 0 'ゲート・モードに設定。 Gosub Prescdss Config Timer1 = Counter , Edge = Falling , Capture Edge = Falling '[T1]の立ち下がりエッジでカウント。 End If Bitwait Sw_1 , Set 'スイッチ1が離されるまで待つ。 Waitms 30 'チャタリング・タイマー。 Return ' ' Switchin2: 'スイッチ2 (プリスケラの選択) If Measure <> 0 Then Return 'If ゲート・モード? Else Prescale = Prescale + 1 Gosub Prescdss 'プリスケラ値の表示と設定。 Bitwait Sw_2 , Set 'スイッチ2が離されるまで待つ。 Waitms 30 'チャタリング・タイマー。 Return ' ' * プリスケラ値の表示と設定サブルーチン * ' Prescdss: Locate 1 , 1 Lcd "Gat" Locate 2 , 1 Select Case Prescale Case 1 : 'プリスケラ 1/10。 Lcd "P10" Set Msel_a 'モードセレクターを設定。 Reset Msel_b Case 2 : 'プリスケラ 1/100。 Lcd "100" Reset Msel_a 'モードセレクターを設定。 Set Msel_b Case Else : 'プリスケラ 1/1。 Lcd "P1 " Reset Msel_a 'モードセレクターを設定。 Reset Msel_b Prescale = 0 End Select Return ' ' ************************************ ' * カウント値の計算処理サブルーチン * ' ************************************ ' Frqcal: If Capendf = 0 Then Return 'If キャプチャー計測が完了したか? Else ' Gosub Capsubt 'キャプチャー・バッファーの値を差分計算する。=> (Frqdat) If Measure = 1 Then Goto Frqcal2 'If レシプロカル・モード? Then ' ' ****************** ' * ゲート・モード * ' ****************** ' Tempstr = Str(frqdat) '計測値を文字列に変換。 Templ1 = 1000000000 '周期測定用の被除数。(1/1 用) Select Case Prescale 'プリスケラにより、無効桁のマーク(x)を追加。 Case 1 : Tempstr = Tempstr + "x" '1/10 モード。 Templ1 = 100000000 '周期測定用の被除数。(1/10 用) Case 2 : Tempstr = Tempstr + "xx" '1/100 モード。 Templ1 = 10000000 '周期測定用の被除数。(1/100 用) End Select ' ' * LCDへ周波数を表示 * ' Temp1 = Len(tempstr) '計測値の桁数を調べる。 Locate 1 , 5 Select Case Temp1 '周波数により表示桁を整える。 Case 8 : Lcd Format(tempstr , " . ") ; "MHz"; '10MHz以上。 Case 7 : Lcd Format(tempstr , " . ") ; " MHz"; '1〜9MHz。 Case Is > 3: Lcd Spc(1) ; Format(tempstr , " . ") ; " KHz"; '1〜999KHz。 Case Else : Lcd Spc(5) ; Format(tempstr , " ") ; " Hz "; '1〜999Hz。 End Select ' ' * LCDへ周期を表示 * ' Templ1 = Templ1 / Frqdat '計測値を周期に変換。 Locate 2 , 5 Gosub Timdss '1ns〜2Secまでの周期を表示する。 ' Capendf = 0 'キャプチャー計測完了フラグをクリア。 Return ' ' ************************ ' * レシプロカル・モード * ' ************************ ' Frqcal2: Locate 1 , 5 If Frqdat < 64 Then Goto Frqcal21 'If 200KHzを超えたか? Then If Frqdat < 12800000 Then Goto Frqcal22 'If 1Hz以上か? Then ' ' * LCDへ周波数を表示 * ' Frqcal24: Temps1 = 12800000 / Frqdat '計測値を周波数に変換。 Frqcal25: Select Case Temps1 '周波数により表示桁を整える。 Case Is >= 100000 : Lcd Fusing(temps1 , "#.##") ; '100000〜999999Hz。 Case Is >= 10000 : Lcd Fusing(temps1 , "#.###") ; '10000〜99999Hz。 Case Is >= 1000 : Lcd Fusing(temps1 , "#.####") ; '1000〜9999Hz。 Case Is >= 100 : Lcd Fusing(temps1 , "#.#####") ; '100〜999Hz。 Case Is >= 10 : Lcd Fusing(temps1 , "#.######") ; '10〜99Hz。 Case Else : Lcd Fusing(temps1 , "#.#######") ; '0〜9Hz。 End Select Lcd " Hz" ' ' * LCDへ周期を表示 * ' Locate 2 , 5 If Frqdat < 25600000 Then 'If 周期が2秒未満か? Then (Long変数がオーバーフローするため) Temps1 = 78.125 * Frqdat '計測値を周期に変換。 Templ1 = Int(temps1) '整数化。 Gosub Timdss '1ns〜2Secまでの周期を表示する。 Else Temps1 = 0.78125 * Frqdat '計測値を周期に変換。 Templ1 = Int(temps1) '整数化。 Tempstr = Str(templ1) '周期値を文字列に変換。 If Templ1 < 100000000 Then Lcd Format(tempstr , " . ") ; " S "; '2S〜9S。 Else Tempstr = Left(tempstr , 8) Lcd Format(tempstr , " . ") ; " S "; '10S〜99S。 End If End If ' Frqcal26: Capendf = 0 'キャプチャー計測完了フラグをクリア。 Return ' ' Frqcal21: '200KHzを超えたオーバー表示。 Lcd "--- Over ---" Locate 2 , 5 Lcd Spc(12) Goto Frqcal26 ' Frqcal22: '1Hz以上の場合、0.5秒間隔で平均処理し表示を更新。 If Tifr0.ocf0a = 1 Then Goto Frqcal23 'If 0.5秒経過したか? Then Average = Average + Frqdat '平均値に加算。 Avernum = Avernum + 1 '平均数を加算。 Goto Frqcal26 ' Frqcal23: Set Tifr0.ocf0a 'タイマー0比較A割り込み要求フラグをリセット。 If Avernum = 0 Then Goto Frqcal24 'If 平均化処理が済んでいないか? Then Temps1 = Average / Avernum '平均値を算出。 Temps1 = 12800000 / Temps1 '計測値を周波数に変換。 Average = 0 Avernum = 0 Goto Frqcal25 ' ' ********************************************* ' * 1ns〜2Secまでの周期を表示するサブルーチン * ' ********************************************* ' Timdss: Tempstr = Str(templ1) '周期値を文字列に変換。 Temp1 = Len(tempstr) '周期値の桁数を調べる。 Select Case Temp1 '周期値により表示桁を整える。 Case 10 : Tempstr = Left(tempstr , 8) Lcd Format(tempstr , " . ") ; " S "; '1S以上。 Case 9 : Tempstr = Left(tempstr , 8) '100〜999mS。 Lcd Format(tempstr , " . ") ; " mS"; Case Is >= 7 : Lcd Format(tempstr , " . ") ; " mS"; '1〜99mS。 Case Is >= 4: Lcd Spc(2) ; Format(tempstr , " . ") ; " uS"; '1〜999uS。 Case Else : Lcd Spc(6) ; Format(tempstr , " ") ; " nS"; '1〜999nS。 End Select Return ' ' ********************************************************** ' * キャプチャー・バッファーの値を差分計算するサブルーチン * 差分カウント値 => Frqdat(ロング型) ' ********************************************************** ' Capsubt: $asm LDS R24,{Capendf} 'キャプチャー・バッファーの使用状況を調べる。 SBRC R24,0 'バッファー1が測定終了データか? Then RJMP Capsubt1 ; LDS R16,{capdat1} '32bit引き算(バッファー2)−(バッファー1)。 LDS R17,{capdat1+1} LDS R18,{capovf1} LDS R19,{capovf1+1} LDS R20,{capdat2} LDS R21,{capdat2+1} LDS R22,{capovf2} LDS R23,{capovf2+1} RJMP Capsubt2 ; Capsubt1: LDS R16,{capdat2} '32bit引き算(バッファー1)−(バッファー2)。 LDS R17,{capdat2+1} LDS R18,{capovf2} LDS R19,{capovf2+1} LDS R20,{capdat1} LDS R21,{capdat1+1} LDS R22,{capovf1} LDS R23,{capovf1+1} ; Capsubt2: Sub R20 , R16 '32bit引き算。 SBC R21,R17 SBC R22,R18 SBC R23,R19 STS {Frqdat},R20 '計算結果をロング型変数に格納。 STS {Frqdat+1},R21 STS {Frqdat+2},R22 STS {Frqdat+3},R23 $end Asm Return ' ' ******************************************** ' * Timer1 キャプチャー 割り込み処理ルーチン * ' ******************************************** ' Tintcap: $asm PUSH R1 IN R1,SREG 'ステータス・レジスタを待避。 PUSH R22 PUSH R23 'R23は、拡張I/Oレジスタへのアクセスに使用する。 PUSH R24 PUSH R25 ; LDS R22,{Capendf} 'メインルーチンの処理状態をチェック。 CPI R22,0 BREQ Tintcap1 'If メインルーチンでキャプチャー・データの処理が終了しているか? Else JMP Tintcap8 ; Tintcap1: IN R24,ICR1L 'キャプチャー・レジスターの下位バイトを取得。 IN R23,ICR1H 'キャプチャー・レジスターの上位バイトを取得。 ; LDS R22,{Capmode} 'キャプチャー割り込みのモードを調べる。 SBRC R22,0 'If データ・バッファー2 へ書き込み? Then RJMP Tintcap2 ; ; '---<データ・バッファー1 書き込み処理>--- STS {Capdat1},R24 'データ・バッファー1へ保存。 STS {Capdat1+1},R23 ; LDS R24,{Capovfc} 'オーバーフロー・カウント値を取得。 LDS R25,{Capovfc+1} CPI R23,0 BRNE Tintcap3 'If キャプチャー・データの上位バイトが00? Else ; IN R23,TIFR1 SBRS R23,TOV1 'オーバーフローが発生しているか? Else RJMP Tintcap3 ; ADIW R24,1 'オーバーフロー・カウント値を+1 ; Tintcap3: STS {Capovf1},R24 'データ・バッファー1へ保存。 STS {Capovf1+1},R25 ORI R22,&H01 'モードをデータ・バッファー2 書き込みに変更。 RJMP Tintcap5 ; ; Tintcap2: '---<データ・バッファー2 書き込み処理>--- STS {Capdat2},R24 'データ・バッファー2へ保存。 STS {Capdat2+1},R23 ; LDS R24,{Capovfc} 'オーバーフロー・カウント値を取得。 LDS R25,{Capovfc+1} CPI R23,0 BRNE Tintcap4 'If キャプチャー・データの上位バイトが00? Else ; IN R23,TIFR1 SBRS R23,TOV1 'オーバーフローが発生しているか? Else RJMP Tintcap4 ; ADIW R24,1 'オーバーフロー・カウント値を+1 ; Tintcap4: STS {Capovf2},R24 'データ・バッファー2へ保存。 STS {Capovf2+1},R25 ANDI R22,&HF0 'モードをデータ・バッファー1 書き込みに変更。 ; ; Tintcap5: CPI R22,&H10 BRCC Tintcap6 'If 1と2の両バッファーにデータの準備が完了か? Then ORI R22,&H10 'キャプチャー・モードを2つめのデータ待ちにセット。 RJMP Tintcap7 ; Tintcap6: STS {Capendf},R22 'キャプチャー計測完了フラグをセット。 Tintcap7: STS {Capmode},R22 'キャプチャー・モードを更新。 ; POP R25 POP R24 POP R23 POP R22 Out Sreg , R1 'ステータス・レジスタを復帰 POP R1 $end Asm Return ' ' $asm Tintcap8: 'メインルーチンでキャプチャー・データの処理が終わっていない。 LDI R22,0 'キャプチャー・モードを初期化する。 RJMP Tintcap7 $end Asm ' ' ********************************************** ' * Timer1 オーバーフロー 割り込み処理ルーチン * ' ********************************************** ' Tintovf: $asm PUSH R1 IN R1,SREG 'ステータス・レジスタを待避。 PUSH R24 PUSH R25 ; LDS R24,{Capovfc} 'オーバーフロー・カウント値を+1。 LDS R25,{Capovfc+1} ADIW R24,1 STS {Capovfc},R24 STS {Capovfc+1},R25 ; POP R25 POP R24 Out Sreg , R1 'ステータス・レジスタを復帰 POP R1 $end Asm Return ' End