目次PIC回路集電波時計


電波時計 処理説明

プログラムアドレス処理

PIC16Fシリーズで使用される命令体系で直接扱える最大アドレス範囲は 0〜2047 です。今回使用しているPIC16F873はプログラムメモリを4Kワード実装しています。ですから、2048番地以降のプログラムメモリにアクセスするためには PCLATHレジスタの内容をその都度設定する必要があります。

PIC16Fシリーズではプログラムメモリは2Kワード単位で管理されます。この単位はページと呼ばれます。PCLATHレジスタの3ビット目および4ビット目でプログラムメモリのページが指定されます。

各ページを超えてGOTOまたはCALLでジャンプするためには LGOTO または LCALL を使います。
これらは擬似命令と呼ばれるもので、PIC16F自体の命令ではありません。これらの命令が書かれたソースコードをアセンブルすると以下のようにアセンブラで自動的に変換されます。
この例ではページ0からページ1のINIT(ページ内アドレス:h'3FE")にジャンプする例です。

ソースコード
    LGOTO    INIT
アセンブル結果
158A   BSF    PCLATH,3
120A   BCF    PCLATH,4
2BFE   GOTO   h'3FE'
PCLATHの3ビット目に"1"を設定し、プログラムページを1に切り替えています。
GOTOのようにジャンプするだけならば上記のようにLGOTOで自動的に処理されます。

問題は LCALL の場合です。
この場合はサブルーチンから戻ったあとでPCLATHを設定する処理が必要になります。
LCALLもサブルーチンにジャンプする際にはPCLATHを自動的に書き換えてくれます。
以下の例はページ1からページ0にあるサブルーチン(ページ内アドレス:h'ED")を呼び出す場合です。

ソースコード
   LCALL   GET_TBL_DATA
アセンブル結果
118A   BCF    PCLATH,3
120A   BCF    PCLATH,4
20ED   CALL   h'ED"
PCLATHの3ビット、4ビットをクリアしてページ0に切り替えています。

サブルーチンから元の処理に戻るためにサブルーチンの最後で RETURN が実行されます。
RETURN ではスタックメモリに書かれた戻り番地を参照して元の場所にジャンプしますが、この際PCLATHの値は書き換えません。
ですから、戻った先で自分のいるページをPCLATHに設定しないといけません。そうしないとサブルーチンのあるページの同じ番地のプログラムが実行されてしまいます。 LRETURN という擬似命令はありません。
今回のプログラムでは setpchマクロ でPCLATHの設定を行っています。
setpch  macro
        if      ($ >= 0) && ($ < 0x800)
        bcf     PCLATH,3
        else
        bsf     PCLATH,3
        endif
        endm
"$"は自分のアドレスを示します。PIC16F873の場合プログラムメモリは4Kワード、すなわち2ページしか使用していないのでPCLATHの3ビット目のみを設定しています。

LCALLでサブルーチンを呼んだあとに必ず setpchマクロが入れられています。
2290行目のLCALLに関するアセンブルリストは以下のようになっています。



M-ADRは16進表示の機械語アドレス、M-CODEは機械語コード、S-ADRはソースコードアドレスです。
この場合、プログラム位置はh'800'以降なので、ページ1になります。ですから、SETPCHマクロではPCLATHの3ビット目を1に設定するコードのみが展開されています。



プログラムの配置

基本的にはサブルーチン関係はページ0に格納し、メインルーチン、割り込み処理ルーチンはページ1に格納しています。



初期化ルーチン
03433 ;####################################################################
03434 ;
03435 ;                          Initial routine
03436 ;
03437 ;####################################################################
電源を投入して 0 番地からスタートすると、この処理にジャンプします。初期化ルーチンではPICの動作環境の設定を行います。
以下にSFRの設定内容を一覧にします。

レジスタ名初期値説  明
PORTA
PORTB
PORTC
00000000各ポートレジスタの内容をクリア。
RCSTA10010000シリアルポートを使用。連続受信を設定。
ADCON000000000A/D変換機能は使用しない。
ADCON100000110全てのポートをデジタルポートとして使用。
TRISA00010000RA4を入力モード。他は出力モード。
TRISB00001100RB2,RB3を入力モード。他は出力モード。
TRISC11000000RC6,RC7を入力モード。他は出力モード。
OPTION_REG00000000 内部プルアップは使用しない。タイマーは内部クロックを使用。
プリスケーラーはTMR0タイマーに使用。プリスケーラー値は1:2
SPBRG010100109,600bpsを指定 ( 値82 )
TXSTA00100110 8ビット通信。送信機能を使用。非同期モード。高速モード。
TRMTビットを"1"に設定しているが読出専用なので不要。
T2CON00000110タイマー2のポストスケーラーは1:1。タイマー2を使用。プリスケーラーは1:16。
TMR200000000タイマー2の値をクリア
PR200010011 タイマー2周期を設定。カウント値(20)より1つ少なく設定する。
(1/12.8MHz)*(4サイクル/クロック)*(20カウント(PR2))*(16(プリスケーラ))
0,078125uS*4*20*16=100uS
PIE100100010USART受信割込機能を使用。TMR2とPR2との一致機能を使用。
INTCON11000000全割込禁止を解除。周辺機器割込可。

上記以外に以下のような初期化処理が行われます。
    各種ワークエリアの初期化
    時刻エリアの初期化
    EEPROMから時刻表示モードの読込
    液晶ディスプレーの初期化


メインルーチン
03536 ;####################################################################
03537 ;
03538 ;                           Main routine
03539 ;
03540 ;####################################################################
メインルーチンでは以下の処理が行われます。

初期メッセージの表示 ソフトフローチャート(1/11)
初期メッセージは電源投入直後2秒間だけ表示されます。
時刻表示の更新 ソフトフローチャート(1/11)

clock_main というサブルーチンを使用して1秒毎にディスプレーの表示を更新します。サブルーチンの呼び出しは1秒に関係なくサイクリックに行われていますが、サブルーチンの中で1秒経過しかがどうかを判断しています。サブルーチンにしている理由は時刻設定が行われているときでも時刻表示更新をするためです。キー入力待ちの際には常に clock_main サブルーチンが呼び出され、時刻表示更新が行われます。
時刻設定キーの処理 ソフトフローチャート(2/11)ソフトフローチャート(3/11)

メインルーチンの中でステップが多いのがこの処理です。
最初はメニューキーが押されたかどうかがチェックされます。最初に押されるべきキーはメニューキーで、その他のキー押下は無効です。
キー操作の待ち時間は10秒に設定されます。その後の操作をせずに10秒経過するとそれまでのキー操作は無効になります。
03672         CALL    DISP_MAIN               ;Time display
上記の処理はソフトフローチャート(1/11)の右下に書かれているサブルーチン呼び出しです。 disp_main サブルーチンは clock_main サブルーチンの中でも使用されているので、一見、必要無いように思えます。しかし、 clock_main サブルーチンでは1秒毎にしか表示を更新しないので、キー処理を抜けるときに disp_main サブルーチンを呼び出して時刻をすぐ表示するようにしています。キー操作時にはディスプレーにはキー操作のメッセージが表示されていて時刻は表示されていません。
最初のメニューキー以降は操作するキーによって「時刻表示モード切替」または「タイマーの設定」が行われます。
clock_main サブルーチン ソフトフローチャート(4/11)

このサブルーチンではディスプレーの表示タイミングとデコードのタイミングが判断されます。ディスプレー表示は0秒毎に disp_main サブルーチンを呼び出して更新されます。また、400ミリ秒毎に dec_time サブルーチンを呼び出して電波に乗せられてきた情報を分析し時刻情報にする処理を行います。
clock_main サブルーチン ソフトフローチャート(5/11)

この処理はLCDに時刻またはメッセージを表示する処理です。



割り込み処理
03973 ;####################################################################
03974 ;
03975 ;                      Interruption processing
03976 ;
03977 ;####################################################################
今回の装置では「タイマー2による周期的割り込み」と「シリアル回線の受信割り込み」の2種類の割り込みが使われています。割り込み種類の判断処理では前記の2種類以外は発生しないとしてそれ以外の割り込み種別の場合でもタイマー2割り込みと同じ処理を行っています。異常動作を想定すると割り込み終了処理 ( int_vec1 ) にジャンプさせた方が無難です。
03979         BTFSC   PIR1,TMR2IF     ;timer2(100uS) int ?
03980         GOTO    TIMER_INT       ;Yes.
03981         BTFSC   PIR1,RCIF       ;RX int ?
03982         GOTO    SIO_INT         ;Yes.
              GOTO    INT_VEC1        ;No. Jump to return
タイマー2による100μ秒毎の周期的割り込み
    100μ秒割り込み処理では以下のような処理が行われます。

    ・ 100uSカウンターの減算 ( 100uS毎 ) ソフトフローチャート(6/11)
      今回のソフトウェアではLCDの制御等を100uS単位で制御しています。 ( delay100u サブルーチン )
      この時間を作るために100uSの割り込み処理でカウンター値を減算します。

    ・ 10mSカウンターの減算 ( 10mS毎 ) ソフトフローチャート(6/11)
      100uS割り込みを基に10mSに時間を計測し、10mS単位のカウンター減算を行います。 ( delay10m サブルーチン )

    ・LED点灯時間制御 ( 10mS毎 ) ソフトフローチャート(6/11)
      正秒、正分、正時、正日、正月の信号は100mSだけ出力します。点灯から100mS後に消灯する制御が行われます。

    ・ TCO( Time Code Output ) の情報分析 ( 10mS毎 ) ソフトフローチャート(7/11)ソフトフローチャート(8/11)
      TCOの信号には3種類あり、それぞれ以下のような規格および判定基準で動作しています。

        種類規格処理での判定基準
        マーカー200mS ± 5mS80mS 〜280mS
        1信号500mS ± 5mS400mS 〜 600mS
        0信号800mS ± 5mS700mS 〜 880mS

      TCO信号は信号の立ち下がり時点から立ち上がるまでの時間を計測し、信号の種類を判定しています。
      検出されたTCOコードは tco_code に格納され、時間デコード処理 ( dec_time ) で年、月、日、曜日、時、分、秒の分析に使用されます。
      TCO情報には月、日の情報は無く、1月1日からの通算日および閏年情報が送られます。それを基に月、日の情報に変換しています。

    ・ キー押下検出 ( 10mS毎 ) ソフトフローチャート(9/11)ソフトフローチャート(10/11)
      キーのなかで F2, F3, F4 および タイマーOFF はLCDの制御ポートと共用で使用されています。そのためキー状態を検出するときだけ RB4, RB5, RB6, RB7 を出力モードから入力モードに切り替えてキー状態の確認をしています。キー状態検出動作が終了すると再び出力モードに戻します。
      キーの押下を検出すると押されたキーの番号を検出します。検出したキーの番号は key_dat に格納されます。
      キーが連続的に押された場合、押されている時間によりキー処理状態 ( key_sts ) を設定するタイミングを変え、キー操作を簡便にしています。キー状態管理メモリ( key_sts )はキー押下検出で "1" に設定し、キー処理要求があることを表示します。フローチャートでは key_sts の設定/解除の箇所を全て描いてはいません。詳しくはソースコードを参照してください。
      この機能はタイマー時刻設定の場合にキーを連続的に押していることにより設定値の更新を高速で行い、設定を容易にするために設けられています。
      連続押下時間により更新速度は以下のように変化します。連続的に押している時間が長いほど速く更新するようになります。

        連続押下時間更新時間
        〜500mS自動更新しない
        500mS 〜 2400mS300mS毎に更新
        2400mS 〜100mS毎に更新

      この機能は時刻設定だけではなく、全てのキー操作で働きます。ですから、時刻表示の状態から Menu キーを連続的に押した場合、時刻設定 → タイマー選択 → 12H/24Hモード切替 → 時刻表示 ・・・ というように設定画面が高速で切り替わることになってしまいます。この防止処理はしていません。
シリアル通信回線でのキャラクタ受信割り込み ソフトフローチャート(11/11)
    シリアル回線で1文字のデータが受信される都度、割り込みが発生します。
    受信処理自体はPICのハードウェアで実行されます。受信割り込みが発生したからといっても、正常に受信されたとは限りません。そのため、受信割り込みの最初では正常に受信出来たかどうかの確認を行います。受信動作異常には以下の2種類があります。

    ・ フレーミングエラー : シリアル回線の信号でストップビットが検出できない場合
    ・ オーバーランエラー : 受信バッファーの内容を読み取らないうちに次のデータが回線から送られてきた場合

    上記のエラーが発生した場合には受信データは破棄され、受信回路の初期化が行われます。
    フレーミングエラーは雑音により発生しましが、それ以外に送信側の速度指定と受信側の速度指定が正しくない場合にも発生します。オーバーランエラーはソフトウェアの受信処理が正常に動作せず、ハードウェアで受信したデータをソフトウェアで引き取れない場合に発生します。ソフトウェアにバグが無ければ発生しません。
    上記のエラーチェックの結果正常に受信できている場合には rx_ptr が示すメモリアドレスに受信データが格納されます。受信データとしてCR( Carriage Return : 印字ヘッドを元に戻す)情報を受信するとコマンドの受信が終了したことになります。ちなみにLF( Line Feed ) は1行分紙を進めるという信号です。昔のタイプライター式のプリンターが使われていたころからのデータ形式です。


サブルーチン

今回のソフトウェアで使用しているサブルーチンの一覧を以下に示します。
名 称機  能
lcd_initLCDモジュールを初期化する
lcd_e_pulseLCDモジュールのE端子にパルスを出力する
print1 文字を表示する
messout文字列を表示する
cls表示をクリアする
locateカーソルを移動する
csr_onカーソルを表示する
csr_offカーソルを消去する
lcd_iwrLCDインストラクションレジスタにデータを書き込む
lcd_dwrLCDデータレジスタにデータを書き込む
disp_second秒を表示する
disp_minute分を表示する
disp_hour時を表示する
disp_day日を表示する
disp_month月を表示する
disp_year年を表示する
disp_wday曜日を表示する
disp_nday通算日を表示する
disp_time時刻を表示する
disp_date日付を表示する
disp_ok電波受信OKを表示する
disp_ng電波受信NGを表示する
disp_timerタイマー設定画面1行目を表示する
disp_tm_setタイマー設定値を表示する
sio_jobシリアル通信処理
cmd_checkシリアル受信コマンドをチェックする
sout_time時刻をシリアルポートに出力する
sout_date日付をシリアルポートに出力する
sout_stime秒に同期した時刻出力フラグをセットする
sout_status時刻修正ステータスをシリアルポートに出力する
cmd_set_timerタイマーを設定する
sout_timerタイマー設定値をシリアルポートに出力する
sout_decode時刻デコード状況をシリアルポートに出力する
cmd_timer_onタイマー出力オンする
cmd_timer_offタイマー出力オフする
sout_errエラーメッセージをシリアルポートに出力する
sout1文字をシリアルポートに出力する
ssout文字列をシリアルポートに出力する
sout_second秒をシリアルポートに出力する
sout_minute分をシリアルポートに出力する
sout_hour時をシリアルポートに出力する
sout_day日をシリアルポートに出力する
sout_month月をシリアルポートに出力する
sout_year年をシリアルポートに出力する
sout_wday曜日をシリアルポートに出力する
cv_str2num2桁の文字列をバイナリ値に変換する
sout_num2d2桁の数値をシリアルポートに出力する

名 称機  能
get_tbl_dataデータテーブルからデータを取得する
inc_time時間を1秒進める
inc_date日付を1日進める
inc_dec_timeデコードバックアップ時間を1分進める
cvt_nd2day通算日を月日に変換する
addw加算 [ BCCH,BCCL = BCCH,BCCL + ACCH,ACCL ]
mulib積和演算 [ BCCH,BCCL = BCCH,BCCL + (ACCL * W) ]
divb除算 [ ACCH = ACCL / W ]
us_subw減算 [ ACCH,ACCL = ACCH,ACCL - BCCH,BCCL (桁下がり無し) ]
init_time時刻、日付レジスタを初期化する
init_dec_timeデコード時刻、日付レジスタを初期化する
init_bk_dec_timeデコードバックアップ時刻、日付レジスタを初期化する
init_tm_chk_cntデコードチェックカウンタをクリアする
chk_tm_chk_cntデコードチェックカウンタをチェックする
adjust_0s時刻の0秒修正を行う
copy_timeデコード時刻を表示時刻にコピーする
dec_time時刻をデコードする
search_mark0秒マーカーを検出する
dec_time_m分をデコードする
dec_time_h時をデコードする
dec_time_nd通算日をデコードする
dec_time_y年をデコードする
dec_time_wd曜日をデコードする
delay100u100μS単位の遅延処理
delay10m10mS単位の遅延処理
chk_eep_datEEPROMのデータをチェックし、無効なら初期化する
eeprom_initEEPROMを初期化する
rd_eepromEEPROMからデータを読む
wr_eepromEEPROMにデータを書き込む
chk_timerタイマー時刻のチェックし、設定時刻であれば出力する
timer_offタイマー出力を停止する
comp_timerタイマー設定と現在時刻を比較する
set_tmbf_dataタイマー時刻をEEPROMに書き込む
get_tmbf_dataタイマー時刻をEEPROMから読み出す
get_tm_dataタイマー設定時刻をEEPROMから読み出す
set_tm_dataタイマー設定時刻をEEPROMに書き込む
set_tm_limタイマー設定上限値をセットする