| ホーム | 私の電子工作作品集 |
[ 初公開日:2019年7月25日 ] |
先に公開をしたページ "x09. TI社 TMS1121 デジタルタイマー" の "操作方法" の
項末 で、 『 この項を書き(作り)ながら ― PICを使用して CPU "TMS1121NLL" をシミュレートしてみたい ― という思いがすごく募ってきました 』
と書いたのですが、その思いが覚めてしまわない内に ―― と思ってそれを実現させてみました。 と言っても、それはそれ程容易なことではなく、このホームページの作成を含めて約3か月間もかかってしまい、このページの公開が、当初の自分の予定(希望)より1か月も遅れてしまいました。 良く言えば、このテーマだけで3か月間もの間、 私を楽しませてもらうことができたのですが、本音を言えば、やはり少々うんざりしてきたのも事実です。 TMS1121 については、40 年以上も昔に発売された IC ですからご存知の方も少ないと思いますが、当時としては、趣味仲間の間では結構トレンドな存在であったと思います。
幸い(?)なことにこれとは別に、もう1台 12、3 年ほど以前に作製をしたコンパクトなもの (このページと同時に公開をした "088. TI社 TMS1121 デジタルタイマー II" を参照) があったため、 当初の機能解析にはそちらの方を大いに活用をしました。 しかし、本機のシミュレーションプログラムのデバッグ作業に入ってからは、"TI社 TMS1121 デジタルタイマー II" の CPU 基板であるプリント基板 (2) を除いた、すべてのハードウエアを本機シミュレータで使用をするようになったために、 "TI社 TMS1121 デジタルタイマー II" としては使用ができなくなってしまいました。 そこで、その後の機能確認にはまた、初代の "TI社 TMS1121 デジタルタイマー" を使用せざるを得なくなったのですが、TMS1121 を稼働できる実機が2台あってつくづく良かったと思っています。 拡張機能の追加 II と回路変更 ( 2019/8/25 更新 ) 拡張機能の第2弾として3つばかり機能追加をしました。 そして、その機能追加をした新しい Ver. のプログラムを利用するためには、下に示す回路(図)を小改造( 参考: 改造後の回路図 ) することが必要となります。 詳細は プログラム の項の 拡張機能の追加 II と回路変更 を参照してください。 MEM CLR の機能改善に機能追加 ( 2019/8/28 更新 ) 前回の拡張機能の追加 II (プログラム Ver.1.10)で MEM CLR の機能改善を行ったのですが、その機能に新たな機能追加をしました。 詳細は MEM CLR の機能改善に機能追加、およびプログラムの 現在の最新バージョン: Ver. 1.20 を参照してください。 【 お知らせ 】 このページを公開してから早くも1年が経とうとしていますが、ときどき貴重なご意見なども頂いたりして、お陰様で地道ながらもご好評を頂いております。 しかしながら、ページをご覧になってせっかく興味を持たれても、 このページで公開をしているハードウエアのままでは、他の作品との合体であるためにその再現となると、今一難があって製作には至れないのではないでしょうか。 中途半端なものを公開してしまった、と少し反省をしています。 上述したように、シミュレータの第二弾 "185. TMS1121 デジタルタイマー・シミュレータ II" が完成し、この度、ホームページに公開する運びとなりました。 プログラムには新たな拡張機能として "カレンダー機能" を追加して、機能強化と、より利便性の向上を図っています。 |
上の回路図において、プリント基板 No1 とプリント基板 No3 の部分は "088. TI社 TMS1121 デジタルタイマー II" のものを再利用し、プリント基板 No2 の部分だけを新たに作製をしました。 回路図の左上に位置する PIC16F886 の信号名の内、内側の黒文字で示したものが PIC16F886 のオリジナルの信号名で、それぞれに対応した外側の赤文字で示したものが、シミュレートする TMS1121 の信号名を表しています。 また、ピン番号については PIC16F886 のものを表していますが、当然ながら TMS1121 とは異なっていますので、その点は注意が必要です。 PIC16F886 オリジナルの RA7 については、I/O ポートとして余っていたため、ジャンパースイッチ EXT を設けて追加した拡張機能を、有効にするかどうかの切り替え用としました。 PIC16F886 の直下にある 12H/24H タクトスイッチは、ジャンパースイッチ EXT が ON のときに有効となり、その拡張機能の内の1つである時計表示を、 12 時間表示にするか 24 時間表示にするかを押す度に切り替えるトグルスイッチとして機能します。 50Hz/60Hz の切り替え用ジャンパースイッチの ON/OFF 状態を、TMS1121 では電源を ON にした直後に一度だけ検出して、後は不要となるスイッチのようなため、その後は 12H/24H の切り替えスイッチとして使用することにしました。 12H/24H タクトスイッチを後から追加をしたために 50Hz/60Hz の切り替え用ジャンパースイッチと並列になってしまったため、運用上で注意をする点があります。 50Hz 地域ではこのままで何ら問題はないのですが、 60Hz 地域では 50Hz/60Hz の切り替え用ジャンパースイッチが常に ON となって、 12H/24H タクトスイッチで表示を切り替えることができません。 したがって、本機の電源を ON にした以降(CLK キーを押す前で [AM] と [PM] の LEDが交互に点滅をしている間)に 50Hz/60Hz の切り替え用ジャンパースイッチを OFF にするか、または、初めから常に 50Hz/60Hz の切り替え用ジャンパースイッチを OFF にしておいて、 12H/24H タクトスイッチを ON にしながら電源を ON にする( [AM] と [PM] の LEDが交互に点滅をし出したらタクトスイッチを OFF にする)必要があります。 後者の方が運用は楽だと思います。 また、図の上部中央に位置する NOR/BLK ジャンパースイッチについては、やはり拡張機能の内の1つである 時と分の区切りの [COL] LED を、通常の点灯しきり表示にするか、1秒間隔で 0.5 秒点 0.5 秒滅のブリンク表示をさせるかを切り替えるものです。 NOR/BLK ジャンパースイッチを NOR にした場合には、EXT ジャンパースイッチが ON/OFF のどちらでも点灯しきりで変化はありませんが、EXT ジャンパースイッチが ON で NOR/BLK ジャンパースイッチを BLK にした場合には、[COL] LED をブリンク表示します。 しかし、BLK のままで EXT ジャンパースイッチを OFF にすると、 [COL] LED は消灯してしまうので注意が必要です。 |
| 回路図 (TMS1121_Simulator.CE3) | ページトップ |
上述のように下の写真で3枚あるプリント基板の内、真ん中のプリント基板だけが本機シミュレータのために作製をしたもので、それ以外のすべてのハードウエアは "088. TI社 TMS1121 デジタルタイマー II" から拝借をしたものです。 |
本機を正面の斜め上から見たところ | 本機を背面の斜め上から見たところ |
本機を上面の真上から見たところ | 本機を裏面の真上から見たところ |
| ページトップ |
冒頭でも述べたように、オリジナルの TMS1121 の機能を PIC を使用して如何にシミュレートするか、TMS1121 の詳細な仕様書などが、もちろん私の手元にあるわけではないため、実現させるのは結構難しい問題でした。
実際の TMS1121 の動作振る舞い(機能)を一つ一つ観察 * しながら、PIC でも同等の振る舞いを行うようにプログラミングをして行きました。 ( * TMS1121 の操作方法については、"088. TI社 TMS1121 デジタルタイマー II" の "操作方法" を参照してください。) オリジナルの TMS1121 では、どのようなプログラミング(方法)を行っているのかはブラックボックスで、それを知る術はまったくありませんので、本機での PIC のプログラミング(方法)についてはすべてが私の想像による創造ですから、 オリジナルの方法とは異なっている点も多いだろうと思われます。 次に、オリジナルの TMS1121 の機能を PIC で実現(シミュレート)するために、私が行った方法をいくつか挙げて解説をして行きます。 実際の ソースファイル ( TMS1121_Simulator.asm ) も併せて参照しながら、 説明をご覧になることをお勧めします。 |
(項目数が多くなってしまったので、目次を付けました。)
|
|
なお、以下の説明で、タイマー用メモリへの書き込み、読み出し、内容消去、およびタイマー(設定時間)の監視とその処理、のそれぞれの項目内で引用しているプログラムを、更新をした最新のプログラム Ver.1.31 のものに差し替えをしました。 差し替え前のプログラムは間違いではないのですが、私の思い違いからバンクの扱いが(他の方が見て)、まわりくどくて分かり辛いものになっていたのを改めました。 以前からずっと気にはなっていたのですが、 今までなかなか修正をできないでいたものです。 ( 2021/5/19 更新 ) |
・ 入出力ポートとシミュレート用 PIC の選定 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
初めに、当然ですが TMS1121 と PIC の両者はハード的に異なっていて制約があるために、PIC なら何でも可というわけには行きません。 そこで、まず使用する PIC の選定から考慮する必要があります。 TMS1121 はシュリンク 28 ピンDIP IC のため 使用する PIC についても 28 ピンDIPに拘りました。 TMS1121 では下表に示すように、入力ポートが 4、出力ポートが 19 の計 23 個のポート数があります。 したがって、シミュレートするためには PIC にも同数以上のポート数の確保が必要です。
しかし、基本の使い方ではやはり 22 個となって足りないので、コンフィギュレーションでシステムクロックに内部発振器を使用するように設定して、ポート 2 個を新たに確保しました。 そして、内部クロック周波数には最大の 8 MHz を選択するように OSCCON レジスタの設定をしました。 具体的には以下のリストのように設定を行います。 : : __config _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _IESO_OFF & _FCMEN_OFF & _LVP_OFF : : movlw b'01110101' ;IRCF=111: 内部クロック周波数選択=8MHz ;OSTS=0: 内部クロック ;HTS=1: HFINTOSCステータスビット ;SCS=1: システムクロック選択=内部クロック movwf OSCCON : :なお、上表はオリジナル TMS1121 の各入出力ポートの信号名と、それらをシミュレートする PIC16F886 の入出力ポート名との対応を表しています。 表中の右端の PIC16F886 のポート RA7 については、新たに確保したポート 2 個の内の1つですが、 シミュレート目的にはもう必要がなくて余っていたため、回路図の下の説明 のように、ジャンパスイッチを設けて追加した拡張機能を有効にするかどうかの切り替え用としました。 (拡張機能については、本機での拡張機能の追加 を参照のこと。) | プログラムのトップに戻る | |
・ 各種LEDの表示とキー入力の検出 | |
この辺りの実現方法は上に示した 回路図 からも想像ができるので、オリジナル TMS1121 の方法とほとんど大差はないものと思います。 次の図は、シミュレーションプログラムのメインルーチンで行っている処理(動作)の概略を図に表したもので、 横軸に時間の推移とともに行っている処理(動作)の種類を割り当てています。 図に示すように、1周期を R0 〜 R6 の各信号の出力時間で7等分し、それぞれを各種のLEDの表示とキー入力の検出に割り当てています。 図で各 ピンク色の部分 がLEDの表示を行っている時間(約 300μS)で、各 水色の部分 がキー入力の検出に 割り当てられた時間ですが、図に示すLEDの表示時間に比べた時間比では、実際にはほんの一瞬で終ってしまいます。 また、この R1 信号の終了直前の部分(水色の部分)で、もし、数字"1"、数字"8"、OFF キーのどれかが押下されていた場合には、R1 信号がダイオードを通して K1、K2、K3 (PIC では RA4、RA5、RA6 が対応)のどれかの入力ポートに入力され、 PIC ではその信号の検出ができます。(ちなみに、もし複数のキーが押下されていた場合には、プログラムでは若番が優先される。) このように、7つに分割されたそれぞれのLEDの表示とキー入力の検出処理が終わった次には、すなわち、1周期の終了直前の部分では、まず、7回に分けたどれかで検出したキー入力があった場合には、黄色の部分 でそのキーの種類別の処理を行います。 そして、最後に 薄緑色の部分 では、予めメモリに記憶されているタイマー情報を検索 * して、その情報の週、時間が現在と一致した場合には、R7 〜 R10 (PIC では RA0 〜 RA3 が対応)の各出力ポートに ON/OFF 信号を出力して、 その先にあるリレーの制御をします。( * タイマー情報の検索は、毎周期ごとに行うのではなく、毎分ごとの 00 秒のときに1度だけ行う。) 以上で、メインルーチンの1周期が終了し、また次の周期の先頭の処理に戻って同様の処理が、電源が OFF になるまで繰り返してループ実行をし続けます。 このように、1周期全体の処理時間の内のその殆んどを7分割しながらLEDの表示で費やしています。 1分割あたり約 300μS、1周期では約 2.1 mS で各LEDのダイナミック点灯をしているわけです。 | プログラムのトップに戻る | |
・ キーマトリクスの各キーとキーコードの取得 | |||||||||||||||||||||||||||||||||||||||||||||||
下図は、既に上で示した 回路図 から、キースキャンのための PIC の入出力ポート回りと、キーマトリクスの部分を抜き出したものです。 そして、赤色に塗った経路が前項説明文中の、キー入力の検出のときの信号の流れを表していて、 PIC の出力ポート(RB1 〜 RB7)から ダイオード、押されているキースイッチを経由して、入力ポート(RA4 〜 RA6)の方向へ流れて行きます。 この2つのスイッチが並列になっているのは、回路図の下の説明 のように 12H/24H タクトスイッチを後から追加をしたためで、50Hz/60Hz の切り替え用ジャンパースイッチの取り扱いを運用上で対処をしてください。(すみません。) 12H/24H タクトスイッチは、拡張機能を使用するときに有効となるスイッチで、オリジナルの TMS1121 では存在しません。
また、ソフト的には各キーのそれぞれ(20 個 + 1 個)を区別するために、各キーにはコードを持たせてあります。 そのキーとコードの対応が上表で、右端のコード 21 は、プログラムがキースキャンをした結果、どのキーも押下していることを 検出できなかったときに、プログラムが返してくる値です。 このキースキャンを実際に行っているプログラムが次のサブルーチン( key_scan )で、前項の メインルーチンでの処理と時間の推移 の図に示したように、メインルーチン1周期ごとに各水色の部分の計7回ずつ CALL されます。 ;========================================================================== ; 押されたキーコードを取得 ;========================================================================== ; keycode: bit7: キー入力受付済み表示フラグ ... key_scan で設定、interrupt で解除 ; bit6: キーコード別処理済みフラグ ... key_proc で設定、interrupt で解除 ; bit4-0: (0-19): キーコード, (20): 拡張機能時12H/24Hキー, (21): キー入力なし表示 key_scan btfsc keycode,7 ;キー入力受付済み か? goto kscan04 ;Yes movf PORTA,W ;キー入力 andlw h'f0' movwf sw_stat ;スイッチの入力状態を一時保存 ; 拡張機能のチェック btfsc sw_stat,7 ;EXT JPスイッチ(このスイッチだけ逆論理)= OFF か? bcf ext_flg,7 ;Yes. bit7: 拡張機能フラグ = OFF btfss sw_stat,7 ;EXT JPスイッチ(このスイッチだけ逆論理)= ON か? bsf ext_flg,7 ;Yes. bit7: 拡張機能フラグ = ON ; K1(KB1) から K3(KB3) をチェック btfsc sw_stat,4 ;K1(KB1) = ON か? goto kscan03 ;Yes movlw 7 addwf keycode,F ;keycode + 7 btfsc sw_stat,5 ;K2(KB2) = ON か? goto kscan03 ;Yes movlw 7 addwf keycode,F ;keycode + 7 btfsc sw_stat,6 ;K3(KB3) = ON か? goto kscan02 ;Yes ; K1(KB1) から K3(KB3) のすべて OFF movlw h'1f' andwf keycode,W sublw 20 btfsc STATUS,Z ;keycode(bit4-0) = 20 か? goto kscan01 ;Yes movlw 13 subwf keycode,F ;keycode - 13 goto kscan04 kscan01 incf keycode,F ;keycode(bit4-0) = 21: キー入力なし表示 goto kscan04 ; K3(KB3) が ON kscan02 movlw h'1f' andwf keycode,W sublw 20 btfss STATUS,Z ;keycode(bit4-0) = 20 か? goto kscan03 ;No btfss ext_flg,7 ;拡張機能フラグ = ON か? goto kscan01 ;No. 無視 ; K1(KB1) から K3(KB3) のどれか ON kscan03 btfsc keycode,6 ;キーコード別処理済み か? goto kscan04 ;Yes bsf keycode,7 ;キー入力受付済み表示 ; チャタリング(ON)吸収の処理 movlw tm0_h_val ;ハードタイマー0カウント値を movwf TMR0 ;TMR0 に設定 movlw tm0_s_val ;ソフトタイマー0カウント値を movwf tm0_s_cnt ;ソフトタイマー0カウンタに設定 bsf INTCON,T0IE ;bit5(T0IE)=1: タイマー0割り込み許可 kscan04 returnこのキースキャン・サブルーチン( key_scan )の実行の結果、どれかのキー入力があった場合には、変数( keycode )の bit7(キー入力受付済み表示フラグ)を ON にし、bit4-0 には上に示した コード対応表 のように各キーに対応したキーコードを返します。 そして、CALL をしたメインルーチンでは、このフラグを見て次には、キーコード別の処理をするサブルーチンの CALL をします。 なお、変数( keycode )のキーコード( bit4-0 )部分は、メインルーチンの各周期が始まった直後、すなわち R0 信号に "H" が出力される直前に、各周期ごとに初期設定(0クリア)がされます。 | プログラムのトップに戻る | |
・ キースイッチのチャタリングとその対処 | |
上のサブルーチン( key_scan )で最後部分の緑色のところは、割り込みを使用して、キースイッチのチャタリングの影響を吸収する処理(の手続き)を行っています。 メカニカルなスイッチを使用している場合には、チャタリングの対処は必須です。 その方法にはいろいろあると思いますが、今回は PIC のタイマー0割り込みを利用してみました。 次のリストは、関連する定数の定義部分、OPTION_REG の設定、割り込み処理の、いずれも必要な部分だけを抜粋したものです。 ;========================================================================== ; 定数、変数の定義とレジスタ割付け ;========================================================================== : tm0_h_val equ 256 - 250 ;ハードタイマー0カウント値(割り込み周期)(4/8Mz*16*250=2msec) tm0_s_val equ 25 ;ソフトタイマー0カウント値 (2msec*25=50msec) : : ;========================================================================== ; 初期化処理 ;========================================================================== : : movlw b'11000011' ;PBPU=1: プルアップ抵抗を使用しない ;INTEDG=1: RB0/INTピンの立ち上がりで割り込み ;T0CS=0: 内部クロックを使用 ;T0SE=0: タイマーカウントアップエッジ指定 = 立ち上がり ;PSA=0: プリスケーラをタイマー0に使用 ;PS2-PS0=011: プリスケーラのスケール値 = 1:16 movwf OPTION_REG : : ;========================================================================== ; 割り込み処理 ;========================================================================== interrupt : : ; タイマー0割り込み処理(割り込み周期 = 4/8Mz*16*250=2msec) t0_int bcf INTCON,T0IF ;タイマー0割込みフラグをクリア movlw tm0_h_val ;ハードタイマー0カウント値を movwf TMR0 ;TMR0 に設定 decfsz tm0_s_cnt,F ;ソフトタイマー0カウンタ - 1 = 0 か? goto int_end ;No bcf INTCON,T0IE ;タイマー0割り込みを禁止 btfss keycode,7 ;キー入力受付済み表示フラグ = ON か? goto t0int01 ;No bcf keycode,7 ;Yes. キー入力受付済み表示解除 goto int_end t0int01 bcf keycode,6 ;キーコード別処理済み解除 ; goto int_end ; 割り込み処理の終了 int_end : : retfie ;割り込みからの復帰次の図は、メカニカルなスイッチを ON / OFF させたときの、入力ポートに加わる電圧の変位をイメージして、図に表したものです。 ON したときも OFF したときも、どちらにもその直後にはチャタリングが現れるので、 その影響をプログラムが受けないように対処をしなければなりません。 本機のプログラムでは下図に示すように、チャタリングがあっても初めの電圧変位でスイッチの ON を検出すると、すぐにそのキーコード別の処理を行います。 この処理は、既に メインルーチンでの処理と時間の推移 の図に示すように、検出と同一の周期の終了直前の黄色の部分で行われます。 そして、変数( keycode )の bit7(キー入力受付済み表示フラグ)= ON により、50 mS 間は新たなキー入力の受付をしません。 また、キーコード別の処理をするサブルーチンが実行されると、変数( keycode )の bit6(キーコード別処理済みフラグ)を ON にして、処理の重複実行を避けるようにしています。 次のリストは、メインルーチンの終わり間近の部分を抜粋したもので、メインルーチンでの処理と時間の推移 の図では、1周期の終了直前の黄色部分辺りのプログラムを表しています。 : : movf keycode,W andlw h'c0' btfsc STATUS,Z ;キー入力がある か? goto main06 ;No. ない btfsc keycode,6 ;コード別の処理 = 未 か? goto main05 ;No. 済み call key_processing ;押されたキーコード別の処理 goto main06 main05 btfsc keycode,7 ;bit7=0 解除された か? goto main06 ;No. 解除前 movf keycode,W andlw h'1f' sublw 21 ;21: キー入力なし表示 btfss STATUS,Z ;キー入力がない か? goto main06 ;No. ある movf tm0_s_cnt,W btfss STATUS,Z ;タイマー0割り込みを監視中 か? goto main06 ;Yes ; チャタリング(OFF)吸収の処理 movlw tm0_h_val ;ハードタイマー0カウント値を movwf TMR0 ;TMR0 に設定 movlw tm0_s_val ;ソフトタイマー0カウント値を movwf tm0_s_cnt ;ソフトタイマー0カウンタに設定 bsf INTCON,T0IE ;bit5(T0IE)=1: タイマー0割り込み許可 main06 : :メインルーチンでも最後部分の緑色のところで、割り込みを使用して、キースイッチのチャタリングの影響を吸収する処理(の手続き)を行っています。 先に示した キースキャン・サブルーチンでは、 スイッチを ON にしたときのチャタリングの対処 ですが、それに対してこの メインルーチンで行っているのは、スイッチを OFF にしたときのチャタリングの対処 です。 そして、やがてタイマー0割り込みによって 50 mS の経過を検知すると、変数( keycode )の bit6(キーコード別処理済みフラグ)を OFF にして、再び、新たなキー入力の受付を可能にします。 ただし、このスイッチを OFF にしたときのチャタリングの対処については、自分の意志に反した誤操作(スイッチを OFF にした直後に、また ON にしてしまうようなことが時々起こる)を考慮して、実際のプログラムリストでは、150 mS に調整がしてあります。 | プログラムのトップに戻る | |
・ 本機デジタルタイマーでの時計機能(計時方法) | |
本機(シミュレート)デジタルタイマーは、ここで改めていうのも変ですが、時計です。 各種のタイマー機能を持った高級時計です。 しかし、プログラムリストを見てもメインルーチン内には、時計として計時しているところは現れて来ません。 もちろん、その計時の結果である刻々と変化をして行く時刻の表示を、メインルーチンでの処理と時間の推移 の図に示すピンク色の部分で行っているわけですが、計時そのものはメインルーチン内では行っていないのです。 (本機は4桁表示で秒の表示がないため、刻々と変化をして行く様子を観察することはできない。) それでは、計時をどこでどのようにして行っているのでしょうか。 上表(入出力ポート) * の右端の K8 (RB0)入力ポートに計時用のパルス CLK が入力されてきます。(この様子を確認のために 回路図 の左上部分を見てください。) シミュレーションプログラムでは、PIC の入力ポート RB0 を外部割り込み (RB0/INT) 端子として使用をしているので、ここに計時用パルス CLK が入力されると割り込みが起こります。 ( * オリジナル TMS1121 の各入出力ポートの信号名と、それらをシミュレートする PIC16F886 の入出力ポート名との対応表) 計時用パルス CLK には 50 または 60 Hz のパルスを使用し、本デジタルタイマーでは商用電源を降圧したものを使用しています。 予め 50 または 60 という値を "1秒カウンタ" に設定 をしておき、パルス CLK によって割り込みが起こる度に、 割り込みルーチン内でその設定値をカウントダウンさせます。 そして、"1秒カウンタ" が 0 になる(1秒経過する)と、再び 50 または 60 という値を設定し直すとともに、"秒カウンタ" の更新(カウントアップ)をします。 そして、"秒カウンタ" が 60 カウントする度に "分カウンタ" の更新(カウントアップ)をし、次には同様に "時カウンタ" の更新(カウントアップ)をします。 そしてその次には、"時カウンタ" が 24 カウントする度に "曜日カウンタ" の更新(カウントアップ)をすることになります。 このようにして、"1秒"、"秒"、"分"、"時"、"曜日" が、割り込みルーチン内で順々に更新されて計時が行われます。 ちなみに、本機では時計の表示は 12 時間表示(拡張機能が OFF のとき)ですが、内部ではこのように 24 時間表示で計時をしています。 次のリストは、割り込みルーチン内から CALL されて、"秒"、"分"、"時"、"曜日" のそれぞれのカウンタを、更新するためのサブルーチン( bcd_ssmmhh_inc )です。 ;-------------------------------------------------------------------------- ; 秒、分、時_BCD カウンタと WEEK カウンタのインクリメント ;-------------------------------------------------------------------------- bcd_ssmmhh_inc ; 秒_BCD カウンタのインクリメント movlw bcd_ss ;秒_BCD カウンタの番地 movwf FSR ;間接アドレスに設定 call inc_bcd ;BCDインクリメント movlw h'60' subwf bcd_ss,W btfss STATUS,C ;秒_BCD カウンタ >= h'60' か? goto smhinc02 ;No clrf bcd_ss ;秒_BCD カウンタのクリア bsf tmon_flg,0 ;タイマー監視フラグ = ON ; 分_BCD カウンタのインクリメント movlw bcd_mm ;分_BCD カウンタの番地 movwf FSR ;間接アドレスに設定 call inc_bcd ;BCDインクリメント movlw h'60' subwf bcd_mm,W btfss STATUS,C ;分_BCD カウンタ >= h'60' か? goto smhinc02 ;No clrf bcd_mm ;分_BCD カウンタのクリア ; 時_BCD カウンタのインクリメント movlw bcd_hh ;時_BCD カウンタの番地 movwf FSR ;間接アドレスに設定 call inc_bcd ;BCDインクリメント movlw h'24' subwf bcd_hh,W btfss STATUS,C ;時_BCD カウンタ >= h'24' か? goto smhinc02 ;No clrf bcd_hh ;時_BCD カウンタのクリア ; WEEK カウンタのインクリメント incf week_cnt,F ;WEEK カウンタ + 1 movlw 7 + 1 subwf week_cnt,W btfss STATUS,C ;WEEK カウンタ < 7 + 1 か? goto smhinc01 ;Yes movlw 1 movwf week_cnt ;WEEK カウンタの初期設定 = 1:SUN smhinc01 btfsc mode_flg,0 ;キー入力モードフラグ = ON か? goto smhinc02 ;Yes bcf STATUS,C rlf ox_week,F ;WEEK セグメント出力を左に 1 ビットシフト btfsc STATUS,C ; rlf ox_week,F ;SUN (初期値) smhinc02 returnなお、上の説明文中に 予め 50 または 60 という値を "1秒カウンタ" に設定 というところがありますが、これは、本機の電源を ON にした直後に 50Hz/60Hz の切り替え用ジャンパースイッチの ON/OFF 状態を一度だけ検出をしてその状態を記憶し、 OFF のときには 50 カウント、ON のときには 60 カウントする判断に使用をしています。 | プログラムのトップに戻る | |
・ 時計機能に対する時刻の変更設定 | |
オリジナル TMS1121 では、電源を ON にした後 CLK キーを押すと、日曜 (SUN)、午後 (PM)、12 : 00 から時計のカウントが始まります。 当然ですが、現在時刻とは異なっていますので、時刻の変更設定が必要となります。 そこで、修正をするための各情報(曜日、AM/PM 別、時分)を入力後、CLK キーを押すことによって、今入力した新しい時刻に変更をすることができます。 次のリストは、押されたキーコード別の処理を行うサブルーチンの内で、CLK キー(19) の処理をしているところだけを抜粋したものと、入力された数字(時刻)の有効性チェックをしているサブルーチンです。 入力された数字が時間として妥当ではなかった場合には、有効性チェックサブルーチンが C (Carry bit) = 1 にしてその結果を知らせるため、それを受け取った CLK キー(19) の処理側では時刻の変更処理を中止して、4桁の7セグLEDに '9999' を表示して時間の入力エラーをユーザに知らせます。 ;========================================================================== ; 押されたキーコード別の処理 ;========================================================================== : : ;; CLK キー (19) の処理 ; ---------------------- btfsc fmmwwwss,7 ;f bit = 1 (有効) か? goto kproc19a ;Yes movf ox_week,W ;WEEK セグメント出力 btfsc STATUS,Z ;指定されている か? goto kproc19a ;No movf ox_mode,W ;MODE セグメント出力 andlw h'03' btfsc STATUS,Z ;AM/PM が指定されている か? goto kproc19a ;No ; 入力された数字(時刻)の有効性チェック clrf itv_flg ;インターバルフラグ = OFF call time_check ;有効性チェック btfsc STATUS,C ;エラー か? goto kproc99 ;Yes. ERROR'9999' btfsc STATUS,Z ;分桁 = h'a' か? goto kproc19a ;Yes ; 新しい曜日、時刻に設定変更 rrf fmmwwwss,F ;www bit を右に 2 ビットシフト rrf fmmwwwss,W andlw h'07' ;www bit を取り出す btfsc STATUS,Z ;www bit = 0: EDAY か? ;; goto kproc99 ;Yes. ERROR'9999' movlw 1 ;Yes. (TMS1121 original) movwf week_cnt ;新 WEEK を設定 call cnv_12to24 ;12時間表示を24時間表示に変換 movf temp_hh,W movwf bcd_hh ;時_BCD カウンタの更新 movf temp_mm,W movwf bcd_mm ;分_BCD カウンタの更新 clrf bcd_ss ;秒_BCD カウンタの更新 ;;(Ver.1.10にて追加) kproc19a clrf ox_week clrf mode_flg ;キー入力モードフラグ = OFF goto kproczz : : ;========================================================================== ; 入力された数字(時刻)の有効性チェック ;========================================================================== ; 出力フラグ: Z=0, C=0: OK ; Z=?, C=1: ERR ; Z=1, C=0: 分桁 temp_mm(bit3-0) = h'a' time_check movf temp_hh,W andlw h'f0' sublw h'a0' btfss STATUS,Z ;十時桁 = h'a' か? goto timck01 ;No movlw h'0f' andwf temp_hh,F ;十時桁 = 0 に置き換える movf temp_hh,W ; andlw h'0f' sublw h'0a' btfss STATUS,Z ;時桁 = h'a' か? goto timck01 ;No movlw h'12' btfsc itv_flg,0 ;インターバルフラグ = ON か? movlw h'00' ;Yes movwf temp_hh ;temp_hh = h'12' / h'00' に置き換える timck01 movlw h'13' btfsc itv_flg,0 ;インターバルフラグ = ON か? movlw h'12' ;Yes subwf temp_hh,W btfsc STATUS,C ;temp_hh < h'13' / h'12' か? goto timck03 ;No. ERROR'9999' movf temp_mm,W andlw h'f0' sublw h'a0' btfss STATUS,Z ;十分桁 = h'a' か? goto timck02 ;No movlw h'0f' andwf temp_mm,F ;十分桁 = 0 に置き換える movf temp_mm,W ; andlw h'0f' sublw h'0a' btfsc STATUS,Z ;分桁 = h'a' か? goto timck03 ;Yes timck02 movlw h'60' subwf temp_mm,W ; btfsc STATUS,C ;temp_mm < h'60' か? ; goto timck03 ;No. ERROR'9999' timck03 returnリスト中に現れる "十時"、"時"、"十分"、"分" の各桁は BCD データで表現をしていますが、BCD データは h'0' 〜 h'9' であって h'a' は BCD データではありません。 これらの各桁の BCD データを7セグLEDのパタンデータに変換をするときに、 ゼロサプレスを行うときには h'0' を便宜上 h'a' に置き換えて、そのパタン変換用に本プログラムでは使用をしています。 ちなみに、ゼロサプレスとは7セグLEDに "0" を表示すべきときに、"0" を表示するのではなく何も表示をしないスペース " " の状態にすることをいいます。 また、BCD データについては、次項の中の 時_BCD カウンタバイト の説明を参照してください。 | プログラムのトップに戻る | |
・ タイマーの設定とタイマー用メモリの構成 | |||||||
オリジナル TMS1121 では、現在時刻を表示する基本的な機能の他に、4個ある出力ポートを個別に(しかも自由に)制御(ON / OFF 信号を出力)する機能を持っています。 一般的には、出力ポートの先には 回路図 に示すように トランジスタ、リレーを接続して、このリレーを自由に ON / OFF させるのを(最終)目的としています。 4個あるリレー(スイッチ)を自由に ON / OFF 制御(設定)する方法には、次のように3つの方法があります。
どのようにするか ― とは、スイッチの動作モードを意味していて、TMS1121 では ON / OFF / SLP の3種類があります。 そして、これらの設定方法の内、ダイレクト・コントロール(直接制御) の ON / OFF の場合には、指定をしたスイッチを直ちに ON / OFF 制御するわけですから、その方法を記憶する必要はありません。 しかし、SLP の場合には、 指定をしたスイッチを直ちに ON 制御しますが、その1時間後には同スイッチを再び OFF 制御しなければならないため、その時間情報、曜日情報、スイッチ番号情報、動作モード( OFF )情報などを、一時的に記憶しておく必要があります。 そして、1時間後に OFF 制御をした後は記憶していた情報は不要となるため、自動的に消去されます。 インターバル・タイマーの設定(間隔プログラム) については、現在の時刻から何時間何分後は何時何分になる ― という時間計算がプログラムで必要になり、いずれにしろ、その時間情報、曜日情報、スイッチ番号情報、動作モード( ON / OFF / SLP ) 情報などを、ダイレクト・コントロールの SLP の場合と同様に一時的に記憶しておく必要がありますが、動作モードが ON / OFF の場合には、記憶時間になって ON / OFF 制御を実行した後は、記憶情報は不要となるため、やはり自動的に消去されます。 これに対して、インターバル・タイマーの SLP の場合には、何時間何分後にスイッチの ON 制御をした後、次には、その1時間後に同スイッチを再び OFF 制御しなければならないため、その時間を計算し直した後に、それらの変更情報をもう一度 記憶し直す必要があります。 そして、これ以降はダイレクト・コントロールの SLP の場合と同様に、OFF 時間になって OFF 制御をした後は記憶していた情報は不要となるため、自動的に消去されます。 通常の タイマーの設定(定時プログラム) については、ユーザが指定をした各情報をそのまま素直に記憶をします。 ただし、時間情報について本シミュレーションプログラムでは、12 時間表示形式で入力されたものを 24 時間表示に変換をしてから記憶をしています。 (この辺りは TMS1121 ではどのように記憶をしているのかは私には不明です。) また、この変換することについては、ダイレクト・コントロールの SLP、インターバル・タイマー、およびタイマーの ON / OFF / SLP のすべてにおいて、 時間情報を記憶する場合には共通です。 また、タイマーの設定(定時プログラム)の場合には、一旦記憶された各情報はユーザが故意に消去をしない限りそのままいつまでも残り続けます。 したがって、毎週同一の曜日、時刻になると同じ制御( ON / OFF / SLP )を実行します。 なお、このタイマー設定の SLP の場合には、インターバル・タイマーの場合と異なって、スイッチの ON 制御をした後、次の1時間後の OFF 制御のため変更情報を記憶し直すまでは同様ですが、OFF 制御をした後は、再び ON 制御をするための 元の SLP 情報に戻して記憶をし直すことになります。 オリジナル TMS1121 では、この記憶をするために使用するメモリを 20 ワード用意をしています。( 1 ワードが何バイト構成なのかは分かりません。) また、上述した各記憶情報(時間情報、曜日情報、スイッチ番号情報、動作モード情報など)を 1 ワードの中でどのように構成をさせているのかも私には分かりません。 上図は、本シミュレーションプログラムのために私が独自の考えの元で創造をしたもので、1 ワード(1メモリ単位)を3バイト構成として、各バイトの詳細を表しています。 まず初めに、3 バイト x 20 メモリ = 60 バイトの全体を タイマー用メモリ と名付け、1 ワード(1メモリ単位)3バイトの各バイトを、先頭から、タイマー制御バイト、時_BCD カウンタバイト、分_BCD カウンタバイトと 名付けています。 先頭バイトである タイマー制御バイト の各ビットの意味、構成は図に示す通りですが、先頭の f ビット (メモリ有効フラグ) = 1 の場合には、その1メモリ単位(3バイト)に書かれているデータ内容すべてが 有効(使用中)であることを示しています。 逆に、f ビット (メモリ有効フラグ) = 0 の場合には、例え他のビットやバイトにデータ内容が残っていても、その1メモリ単位(3バイト)は無効(空き)であることを示し、 他のデータ内容で上書をしても構わないことを意味しています。 次のバイトの 時_BCD カウンタバイト は、タイマー時間(時分)の内の "時" を記憶しておくバイトであり、最後のバイトの 分_BCD カウンタバイト は、"分" を記憶しておくバイトです。 これらのバイト名の BCD とは、Binary Coded Decimal の略であり 2進化 10進数 を意味し、"時分" 4 桁の各桁を 4 ビットずつの 2進数にして記憶をしています。 なお、ここで特筆すべきことは、時_BCD カウンタバイトの先頭の c ビット (メモリ自動消去フラグ) についてであり、本来はタイマー制御バイト内に収納すべきですが、上図をご覧のようにタイマー制御バイトには空きビットがありませんので、 この時_BCD カウンタバイトに収納をしました。 そして、上述したように一時的に記憶しておくタイマー情報の場合に、c ビット = 1 にします。 上述したように、TMS1121 では 20 ワード分のメモリを有していますが、PIC16F886 では図に示すように、Bank 1 以外にも自由に使用できる(未使用の)汎用レジスタ(GPR)がたくさんありますので、もし必要であれば、 20 ワード以上のメモリの確保も可能となります。 ただし、プログラムを作成するという観点からはメモリが連続していた方が有利となるため、複数の Bank に分散をさせた場合には、プログラムの作成が多少面倒にはなると思います。 | プログラムのトップに戻る | |
・ タイマー用メモリへの書き込み | |
前項 で述べた3つのタイマーの設定方法の内、ダイレクト・コントロール(直接制御)の ON / OFF の場合を除いたすべてで、タイマー情報の記憶のためにタイマー用メモリへの書き込みが行われます。 そして、事前にそれらの各方法で必要とするそれぞれのタイマー情報の入力の後、最後に ON / OFF / SLP のどれかのキー入力の検出を契機に、書き込みが行われるのです。 次のリストは、そのために必要な変数の定義と、タイマー用メモリへの書き込みを行うサブルーチン( write_memory )です。 * なお、リストはプログラム Ver.1.31 で更新をしたものに差し替えをしました。 ( 2021/5/19 更新 ) ;========================================================================== ; 定数、変数の定義とレジスタ割付け ;========================================================================== : cblock h'20' : bcd_hh ;時_BCD カウンタ bcd_mm ;分_BCD カウンタ : fmmwwwss ;タイマー制御バイト : endc : cblock h'a0' ;(バンク 1) timer_mem ;タイマー用メモリの先頭 ; (3 * 20 = 60 byte 使用) endc : ;========================================================================== ; タイマー用メモリへの書き込み ;========================================================================== write_memory movlw timer_mem ;タイマー用メモリの先頭アドレス movwf FSR movlw 20 movwf lp_cnt ;ループカウンタ wrmem01 btfss INDF,7 ;fmmwwwss の f bit = 1 (有効) か? goto wrmem02 ;No. 空き movlw 3 addwf FSR,F ;間接アドレス +3 更新 decfsz lp_cnt,F ;ループカウンタ - 1 = 0 か? goto wrmem01 ;No bsf STATUS,C ;C フラグ = 1、メモリオーバーエラー表示 goto wrmem03 ;ERROR'8888' wrmem02 bsf fmmwwwss,7 ;f bit = 1 (有効) にする movf fmmwwwss,W movwf INDF ;(タイマー制御バイト) へ書き込み incf FSR,F ;間接アドレス +1 更新 movf temp_hh,W movwf INDF ;(時_BCD カウンタバイト) へ書き込み incf FSR,F ;間接アドレス +1 更新 movf temp_mm,W movwf INDF ;(分_BCD カウンタバイト) へ書き込み bcf STATUS,C ;C フラグ = 0、書き込み済み wrmem03 returnタイマー用メモリは 20 ワードだけしか用意がされていないため、それを超えると書き込みができません。 このサブルーチンを CALL した親側では、実際に書き込みが できた/できなかった を把握する必要があるため、 このサブルーチンでは できたとき C (Carry bit) = 0 /できなかったとき C (Carry bit) = 1 にして、その結果を親側に知らせます。 親側では C (Carry bit) = 1 を受け取ると、4桁の7セグLEDに '8888' を表示してユーザにメモリオーバーエラーを知らせます。 | プログラムのトップに戻る | |
・ タイマー用メモリから読み出し | |||||
TMS1121 では、メモリに書き込まれているタイマー情報を読み出して、その内容を各LEDに表示する機能があり、その指示方法には次のように2つの方法があります。
指示方法としての具体的なキーの操作は、次の図のように行います。 図中の ・・・ は、表示内容の確認のための時間の経過を表し、・・・・・・・・・ は、その間省略を表しています。 曜日別表示の場合は曜日をキーに、スイッチ別表示の場合はスイッチをキーにして、タイマー用メモリの初めに検索一致したものを表示し、WEEK, WEEK、または SW, SW を繰り返す度に、次に一致したものを順次表示をして行きます。 そして、一致するものが無くなると、一旦、各LEDは全消灯になりますが、なおも WEEK, WEEK、または SW, SW を繰り返すと、再びタイマー用メモリの初めに戻って検索一致したものを表示します。 * なお、リストはプログラム Ver.1.31 で更新をしたものに差し替えをしました。 ( 2021/5/19 更新 ) ;========================================================================== ; (X),WEEK,WEEK / (X),SW,SW 時のタイマー用メモリの読み出し ;========================================================================== read_memory movf dsp_cnt,W ;表示カウンタ andlw h'1f' movwf work2 movlw h'00' btfsc ext_flg,5 ;9,SW 表示フラグ = ON か? movlw h'80' ;Yes iorwf wkcode,W ;キーコードワーク movwf work1 ;ワーク ; タイマー用メモリの検索 movlw timer_mem ;タイマー用メモリの先頭アドレス movwf FSR movlw 20 movwf lp_cnt ;ループカウンタ rdmem01 btfss INDF,7 ;fmmwwwss の f bit = 1 (有効) か? goto rdmem05 ;No btfsc work1,7 ;9,SW 表示フラグ = ON か? goto rdmem04 ;Yes movf fmmwwwss,W btfsc work1,0 ;WEEK キー(12) か? goto rdmem02 ;No. SW キー(13) andlw h'1c' ;www bit を取り出し xorwf INDF,W ;比較する andlw h'1c' goto rdmem03 rdmem02 andlw h'03' ;ss bit を取り出し xorwf INDF,W ;比較する andlw h'03' rdmem03 btfss STATUS,Z ;www / ss bit は等しい か? goto rdmem05 ;No rdmem04 movf work2,W btfsc STATUS,Z ;表示カウンタ = 0 か? goto rdmem06 ;Yes decf work2,F ;表示カウンタ - 1 rdmem05 movlw 3 addwf FSR,F ;間接アドレス +3 更新 decfsz lp_cnt,F ;ループカウンタ - 1 = 0 か? goto rdmem01 ;No bsf dsp_cnt,5 ;タイマー用メモリ検索終了フラグ = ON call clr_all_disp ;LED をすべて(コロンを除く)消灯 goto rdmem07 ; タイマー用メモリの読み出しと内容表示 rdmem06 movf INDF,W ; movwf fmmwwwss ;タイマー制御バイト incf FSR,F ;間接アドレス +1 更新 movf INDF,W ; andlw h'7f' ;メモリ自動消去フラグ = OFF movwf temp_hh ;時_BCD カウンタ incf FSR,F ;間接アドレス +1 更新 movf INDF,W ; movwf temp_mm ;分_BCD カウンタ incf dsp_cnt,F ;表示カウンタの更新 : rdmem07 return上のサブルーチン( read_memory )で、WEEK, WEEK、または SW, SW を繰り返す度に表示するメモリ内容を、次々に変更して行くために実際に活躍しているのが変数 dsp_cnt、および work2 で、 各ビットを次のような目的に割り付けています。 (ただし、work2 では bit4-0: 表示カウンタのみを使用。) ; dsp_cnt: ;bit7: X,WEEK / X,SW 処理済みフラグ ;bit6: LED 表示消灯フラグ、0: 表示 / 1: 消灯 ;bit5: タイマー用メモリ検索終了フラグ ;bit4-0: 表示カウンタ | プログラムのトップに戻る | |
・ タイマー用メモリの内容消去 | |||||||
TMS1121 では、メモリに書き込まれているタイマー情報の内、不要となったものをユーザの意志で消去することができ、その指示方法には次のように3つの方法があります。
曜日別消去、およびスイッチ別消去は、その名の通り指定をした曜日、またはスイッチ番号が、タイマー用メモリに書き込まれている最大 20 メモリの内、その内容(曜日、またはスイッチ番号)と一致したメモリだけを消去します。 これに対して、全消去は、書き込まれているすべてのメモリを無条件に消去します。 なお、消去されて空きとなったメモリは、再び新しいタイマー情報を書き込むことが可能となります。 次のリストは、押されたキーコード別の処理を行うサブルーチンの内で、MCLR キー(18) の処理をしているところだけを抜粋したものです。 * なお、リストはプログラム Ver.1.31 で更新をしたものに差し替えをしました。 ( 2021/5/19 更新 ) ;========================================================================== ; 押されたキーコード別の処理 ;========================================================================== : : ;; MCLR キー (18) の処理 ; ----------------------- btfss ext_flg,5 ;9,SW 表示フラグ = ON か? goto kproc18a ;No bcf ext_flg,5 ;9,SW 表示フラグ = OFF goto kproc18i kproc18a movlw h'03' movf ox_sw,F btfss STATUS,Z ;X,SW の処理済み か? goto kproc18b ;Yes movlw h'1c' movf ox_week,F btfsc STATUS,Z ;X,WEEK の処理済み か? goto kproc18e ;No ; X,SW,MCLR / X,WEEK,MCLR の処理 kproc18b movwf work1 ;ワーク movlw timer_mem ;タイマー用メモリの先頭アドレス movwf FSR movlw 20 movwf lp_cnt ;ループカウンタ kproc18c btfss INDF,7 ;(タイマー制御バイト) の f bit = 1 (有効) か? goto kproc18d ;No movf fmmwwwss,W andwf work1,W ;ss / www bit を取り出し xorwf INDF,W ;比較する andwf work1,W btfsc STATUS,Z ;ss / www bit は等しい か? clrf INDF ;Yes. 消去 kproc18d movlw 3 addwf FSR,F ;間接アドレス +3 更新 decfsz lp_cnt,F ;ループカウンタ - 1 = 0 か? goto kproc18c ;No goto kproc18h ; MCLR の処理(全消去) kproc18e btfss ext_flg,7 ;拡張機能フラグ = ON か? goto kproc18f ;No incf ext_flg,F ;bit2-0: MCLR カウンタ +1 更新 movf ext_flg,W andlw h'07' sublw 5 ;連続で 5 回 btfss STATUS,Z ;MCLR カウンタ = 5 か? goto kproc18j ;No movlw h'f8' andwf ext_flg,F ;MCLR カウンタをクリア kproc18f movlw timer_mem ;タイマー用メモリの先頭アドレス movwf FSR movlw 3 * 20 movwf lp_cnt ;ループカウンタ kproc18g clrf INDF ;消去 incf FSR,F ;間接アドレス +1 更新 decfsz lp_cnt,F ;ループカウンタ - 1 = 0 か? goto kproc18g ;No btfss ext_flg,7 ;拡張機能フラグ = ON か? clrf PORTA ;No. (リレー)制御用出力ポートの初期設定 kproc18h btfss ext_flg,7 ;拡張機能フラグ = ON か? goto kproc18i ;No bsf ext_flg,6 ;MCLR 表示フラグ = ON movlw 5 iorwf ext_flg,F ;MCLR カウンタの初期設定 clrf mcl_cnt ;MCLR 表示カウンタの初期設定 goto kproc18j kproc18i clrf mode_flg ;キー入力モードフラグ = OFF kproc18j goto kproczz : :オリジナル TMS1121 のプログラムでは、どのようにしてこれらのメモリ消去の処理を行っているのかは分かりませんが、本シミュレーションプログラムでは、曜日別消去、およびスイッチ別消去の場合には、該当の タイマー制御バイト の 8 ビットすべてを 0 クリアしています。 本来は先頭の f ビット (メモリ有効フラグ)だけをクリアすればよいのですが、プログラムの手間は同じですから、タイマー制御バイト 8 ビットをすべて 0 クリアをしました。 したがって、いずれにしろこれらの場合には、時_BCD カウンタバイト、および 分_BCD カウンタバイト にはゴミが残ります。 (ちなみに、これらのゴミが残っていても、新しいタイマー情報を書き込むときには 上書きをするので、一向に構わないのですが。) また、全消去の場合には、まったく無条件にタイマー用メモリ( 3 バイト x 20 メモリ = 60 バイト)全体のすべてを 0 クリアするようにしました。 したがって、私の勝手な拘りですが、これによってタイマー用メモリ全体がクリーンな状態になります。 なお、上のリストの ; MCLR の処理 以下の中のコメントに、;連続で 5 回 というくだりが出てきますが、これについては 本機での拡張機能の追加 の MEM CLR の機能改善 を参照してください。 | プログラムのトップに戻る | |
・ タイマー(設定時間)の監視とその処理 | |
いよいよ、オリジナル TMS1121 にとっての最終的な目的でもあり、最も重要なタイマーの監視とその処理を実行させる機能についてですが、TMS1121 ではどのようにしてそれらを実現しているのかは、知る由もなく私にはまったく分かりません。 しかし、私なりにそれらを想像することはできます。 本シミュレーションプログラムでは、既に 各種LEDの表示とキー入力の検出 の項で、タイマーの監視やその処理の実行についてどのようにするかの概要を述べました。 メインルーチンでの処理と時間の推移 の図に示すように、メインルーチンの1周期の終了直前の薄緑色の部分で行っているのですが、実際のプログラムでは次に示すように、ループ動作をしているメインルーチンの最終のところで行っています。 : ;-------------- タイマーの監視とその処理 ; tmon_flg: bit0: タイマー監視フラグ ... interrupt(bcd_ssmmhh_inc) で設定、time_monitor で解除 main06 btfsc tmon_flg,0 ;タイマー監視フラグ = ON か? call time_monitor ;Yes goto mainたったこれだけです。 もちろん、実際に処理を行うのは CALL をされた タイマーの監視とその処理サブルーチン( time_monitor ) ですが、ループ動作をしているメインルーチンで毎回(毎周期ごとに)行うわけではありません。 リストに示すように、 タイマー監視フラグ( tmon_flg )が ON のときにだけこのサブルーチンが CALL されます。 このタイマー監視フラグ( tmon_flg )を ON に設定しているのは、本機デジタルタイマーでの時計機能(計時方法) の項で示した サブルーチン( bcd_ssmmhh_inc ) で、 親元である割り込み処理ルーチン内の外部(RB0/INT)割り込みの処理から、毎秒ごとに1度ずつ CALL をされています。 そして、このサブルーチン( bcd_ssmmhh_inc )の初めの部分で行っている、秒_BCD カウンタのインクリメントの処理で、 秒_BCD カウンタが 60 カウントしたとき、すなわち、毎分ごとの 00 秒のとき に1度だけ、タイマー監視フラグ( tmon_flg )を ON に設定をしています。 したがって、約 2.1 mS のループ周期のメインルーチンですが、毎分ごとの 00 秒のときに1度だけ、タイマーの監視とその処理サブルーチン( time_monitor ) が実行されることになります。 (言い方を変えると、 メインルーチンのループ回数が、60 秒 ÷ 2.1 m秒 ≒ 28,571、すなわち 約 28,570 回に1度の頻度で、実行がされることになります。) 次のリストが、タイマーの監視とその処理サブルーチン( time_monitor ) です。 このサブルーチンはご覧のように少々長めなため、ここに示すべきかどうか迷いましたが、本シミュレーションプログラムの本命でもあるので、 やはり示しておくことにしました。 サブルーチン内で行っていることを要約すると、次のようになります。
* なお、リストはプログラム Ver.1.31 で更新をしたものに差し替えをしました。 ( 2021/5/19 更新 ) ;========================================================================== ; タイマーの監視とその処理 ;========================================================================== time_monitor movf week_cnt,W ;WEEK カウンタ btfsc ext_flg,7 ;拡張機能フラグ = ON か? movf week_cnt2,W ;Yes. WEEK カウンタ(拡張時) movwf cur_week ;ワーク bcf STATUS,C rlf cur_week,F ;WEEK カウンタ rlf cur_week,F ;左に 2 ビットシフト movlw timer_mem ;タイマー用メモリの先頭アドレス movwf FSR movlw 20 movwf lp_cnt ;ループカウンタ tmon01 btfss INDF,7 ;fmmwwwss の f bit = 1 (メモリ有効) か? goto tmon11 ;No movf INDF,W andlw h'1c' ;www bit を取り出す btfsc STATUS,Z ;www bit = 0: EDAY か? goto tmon02 ;Yes xorwf cur_week,W btfss STATUS,Z ;WEEK カウンタは等しいか? goto tmon11 ;No tmon02 movf INDF,W movwf fmmwwwss ;fmmwwwss を一旦退避 incf FSR,F ;間接アドレス +1 更新、(時_BCD カウンタバイト) movf INDF,W andlw h'7f' ;メモリ自動消去フラグを削除 subwf bcd_hh,W btfss STATUS,Z ;時_BCD カウンタは等しいか? goto tmon12 ;No incf FSR,F ;間接アドレス +1 更新、(分_BCD カウンタバイト) movf INDF,W subwf bcd_mm,W btfss STATUS,Z ;分_BCD カウンタは等しいか? goto tmon13 ;No ; WEEK, 時, 分が等しいときの出力(リレー)制御 movf fmmwwwss,W ;fmmwwwss を復帰 andlw h'03' ;ss bit を取り出す movwf work2 ;ループカウンタ btfsc STATUS,Z bsf work2,2 ;0 のときは 4 にする movlw h'01' movwf rx_sw ;RX(SW) 出力の初期値 tmon03 decf work2,F btfsc STATUS,Z ;ループカウンタ work2 - 1 = 0 か? goto tmon04 ;Yes bcf STATUS,C rlf rx_sw,F ;RX(SW) 出力を左に 1 ビットシフト goto tmon03 tmon04 movf rx_sw,W btfsc fmmwwwss,5 ;mm bit = x1: ON 出力 か? iorwf PORTA,F ;Yes. リレーを ON xorlw h'ff' btfss fmmwwwss,5 ;mm bit = x0: OFF 出力 か? andwf PORTA,F ;Yes. リレーを OFF ; アラームフラグの設定 btfss ext_flg,7 ;拡張機能フラグ = ON か? goto tmon05 ;No btfsc fmmwwwss,5 ;mm bit = x1: ON 出力 か? bsf alm_flg,0 ;Yes. リレー ON フラグのセット btfss fmmwwwss,5 ;mm bit = x0: OFF 出力 か? bsf alm_flg,1 ;Yes. リレー OFF フラグのセット ; メモリの自動消去 tmon05 decf FSR,F ;間接アドレス -1 更新、(時_BCD カウンタバイト) btfss INDF,7 ;メモリ自動消去フラグ = 1 か? goto tmon07 ;No btfss fmmwwwss,6 ;mm bit = 1x: SLP か? goto tmon06 ;No btfsc fmmwwwss,5 ;mm bit = 10: OFF か? goto tmon08 ;No tmon06 decf FSR,F ;間接アドレス -1 更新、(タイマー制御バイト) ;; bcf INDF,7 ;f bit = 0 clrf INDF ;メモリの消去 goto tmon11 ;No ; SLP のときのメモリ内容変更 tmon07 btfss fmmwwwss,6 ;mm bit = 1x: SLP か? goto tmon12 ;No tmon08 decf FSR,F ;間接アドレス -1 更新、(タイマー制御バイト) movlw h'20' xorwf INDF,F ;ON/OFF ビットを反転 incf FSR,F ;間接アドレス +1 更新、(時_BCD カウンタバイト) btfsc fmmwwwss,5 ;変更前の mm bit = 10: OFF 出力 か? goto tmon09 ;No. ON 出力 ; ON 出力時間に変更(メモリ自動消去フラグ = 1 のものはない) call dec_bcd ;時_BCD カウンタバイト - 1 movlw h'f9' ;h'00' - h'01' - h'06' subwf INDF,W btfss STATUS,Z ;時_BCD カウンタ = h'f9' か? goto tmon12 ;No movlw h'23' movwf INDF ;時_BCD カウンタ = h'23' 設定 movlw h'1c' andwf fmmwwwss,F btfsc STATUS,Z ;www bit = 0: EDAY か? goto tmon12 ;Yes movlw h'04' subwf fmmwwwss,F ;www bit を - 1 btfss STATUS,Z ;www bit = 0 か? goto tmon10 ;No movlw h'1c' movwf fmmwwwss ;www bit = 7 にする goto tmon10 ; OFF 出力時間に変更 tmon09 movf INDF,W movwf work1 ;時_BCD カウンタバイトを一旦退避 bcf INDF,7 ;メモリ自動消去フラグ = 0 call inc_bcd ;時_BCD カウンタバイト + 1 movlw h'24' subwf INDF,W btfsc STATUS,C ;時 BCD カウンタ < h'24' か? clrf INDF ;No. 時 BCD カウンタのクリア btfsc work1,7 ;メモリ自動消去フラグ = 1 か? bsf INDF,7 ;Yes. メモリ自動消去フラグ = 1 movlw h'7f' andwf INDF,W btfss STATUS,Z ;時_BCD カウンタ = 0 か? goto tmon12 ;No movlw h'1c' andwf fmmwwwss,F btfsc STATUS,Z ;www bit = 0: EDAY か? goto tmon12 ;Yes movlw h'04' addwf fmmwwwss,F ;www bit を + 1 btfss fmmwwwss,5 ;www bit = 8 か? goto tmon10 ;No movlw h'04' movwf fmmwwwss ;www bit = 1 にする ; WEEK を変更 tmon10 decf FSR,F ;間接アドレス -1 更新、(タイマー制御バイト) movlw h'e3' andwf INDF,F ;www bit をクリア movf fmmwwwss,W ;www bit iorwf INDF,F ;新しい www bit に更新 tmon11 incf FSR,F ;(時_BCD カウンタバイト) tmon12 incf FSR,F ;(分_BCD カウンタバイト) tmon13 incf FSR,F ;(次の タイマー制御バイト) decfsz lp_cnt,F ;ループカウンタ - 1 = 0 か? goto tmon01 ;No bcf tmon_flg,0 ;タイマー監視フラグ = OFF returnなお、言うまでもないことかもしれないですが、タイマー情報の比較対象となるのは "曜日"、"時"、"分" だけで "秒" データは持っていません。 したがって、現在のリアル時間の "秒" は毎秒ごとに更新されて行くのですが、 それが 00 秒になった瞬間、すなわち "分" が更新をされた瞬間の時点で、このサブルーチン( time_monitor )が CALL されるのです。 | プログラムのトップに戻る | |
・ 本機での拡張機能の追加 | |
本シミュレーションプログラムを作って行く過程で、オリジナル TMS1121 にはないのですが、こんな機能があるといいのにとか、こうしたらもっと便利になるのにとか、私が感じた機能をプログラムに盛り込むことにしました。 しかし、それはオリジナル TMS1121 から逸脱をすることにもなるので、これらの追加や変更の機能は拡張機能として、スイッチ操作によって有効/無効を自由に切り替えることが可能なように考慮をしました。 その切り替えスイッチが、回路図 に示す EXT ジャンパースイッチで、PIC16F886 のポート(RA7)が1つ余っていたのでそれに割り当ててみました。 そのことについては既に 入出力ポートとシミュレート用 PIC の選定 の項でも述べていることです。 拡張機能としては、現在、次の3つの機能があります。 本機の時計の表示は "時"、"分" の4桁表示のため、刻々と変化をして行く "秒" の様子が、見ている者に伝わってきません。 ― というわけでもないのですが、私の遊び心的な気持ちも手伝って、"時"、"分" の区切りの [COL] LED の表示を 動かしてみたのですが、頭で考えていたのとは異なって、どうやらこの試みは失敗のようでした。 0.5 秒間を "点"、次の 0.5 秒間を "滅" の1秒周期で、ブリンク表示をさせたのですが、人それぞれ感じ方は違うとは思いますが、私には、 [COL] LED の表示が動くのを見ていて、何か煩わしく感じられるのです。 そこで、EXT ジャンパースイッチで拡張機能を選択したときに、必ずブリンク表示をしてしまうのも困るので、回路図 に示すように新たに NOR/BLK ジャンパースイッチ を設けて、好みによって ノーマル表示(点灯し切り) / ブリンク表示(点滅) が選択できるようにしました。 ただし、EXT ジャンパースイッチが OFF のときに NOR/BLK ジャンパースイッチを BLK にすると、 [COL] LED は消灯してしまうので注意が必要です。 私はこれまでに、他のページで紹介をしている "014. 7セグメントLED表示時計" を、初めて PIC によって作製をして以来、他にも PIC を使用した数多くのデジタル時計を作製してきました。 そして、それらの数多くのデジタル時計で共通していることは、どれもすべてが 12 時間表示/24 時間表示 のどちらにも対応をさせていることです。 したがって、オリジナルの TMS1121 は 12 時間表示専用のデジタル時計ですが、本機ではシミュレーションプログラムを作成する当初から、24 時間表示にも対応をさせることを考えていました。 そして、それを拡張機能という形で盛り込むことにしたのです。 これならば、決して TMS1121 から逸脱をすることにはならないと思うのです。 時計としての計時(カウント)方法を、TMS1121 ではどのように行っているのか分かりませんが、本シミュレーションプログラムでは、LED表示が 12 時間表示でも、内部的には 24 時間表示で計時(カウント)をさせています。 その方がタイマー機能(タイマーの設定とタイマー用メモリの構成 を参照)を実現させるためにも都合が良いからです。 そして、LEDに表示をさせるときに 24 時間表示から、12 時間表示に変換をしているだけなのです。 言うまでもなく 24 時間表示をさせるときには、変換の必要はないので単純にそのままをLEDに表示をさせています。 したがって、LED表示を 24 時間表示に変更をするためには、まず EXT ジャンパースイッチを ON にした上で、12H/24H タクトスイッチを操作します。 このタクトスイッチについての詳細は、 キーマトリクスの各キーとキーコードの取得、および 回路図の下の説明 で述べた通りで、12 時間表示にするか 24 時間表示にするかを、押す度に切り替えます。 ここで、話は少し変わりますが 12 時間表示専用の TMS1121 では、時計機能に対する時刻の変更設定 や タイマーの設定とタイマー用メモリの構成 で述べたタイマーの設定 (定時プログラム)では、 当然ですが、そこで扱っている入力情報の指定には、AM/PM 別 を含めた曜日、時分を 12 時間表示形式で行っています。 これに対して本機では、拡張機能が OFF のときと 同機能が ON でも 12 時間表示のときには、同様に 12 時間表示形式で入力情報の指定を行いますが、拡張機能が ON で 24 時間表示のときには、AM/PM 別 を含めない 24 時間表示形式で 入力情報の指定を行うのが正道とは思いますが、ユーザの紛らわしさを避けるために、現在は、すべての場合において 12 時間表示形式を採用しています。 (将来的には変更をするかもしれません。) ここで、入力情報の指定を行うときの、12 時間表示形式と 24 時間表示形式の違いを再確認しておくと、前者では AM/PM 別、時間指定は 1 〜 12 であり、後者では時間指定は 0 〜 23 であって AM/PM 別の指定はありません。 オリジナルの TMS1121 では、タイマー用メモリの内容消去 をするのに3つの方法があることを述べましたが、その内の 曜日別消去、スイッチ別消去 については、 自分の意志でもってメモリ内容を消去するわけだから問題はないと思います。 これに対して 全消去 の場合には、自分の意志のときはもちろん問題はないが、誤操作のときには取り返しがつかなくなります。 ここで一番問題なのは、MCLR キーの単操作(たった1回の押下)でメモリ内容のすべてが消去されてしまう、ということにあります。 誤操作というのは往々にしてあり得ることで、できればこれを避けたいものです。 そこで、これに対処をしたのがここで述べる改善策です。 自分の意志で全消去を行うという場面はそうそうあるわけではないですから、単操作ではなく MCLR キーを5回の連続操作(5回連続で押下)で全消去を行う ように改めました。 2、3回というのはやはり誤操作であり得ると思うので、 5回にしました。 それほどの手間にはならないものと思います。 なお、5回未満の場合には無視をします。 また、メモリ内容の全消去の操作をすることによって、私がもう一つ困ると思うことは、現在、どれかのスイッチ(複数個を含む)が ON 動作をしていた場合に、それらのすべてのスイッチが OFF 状態にされてしまう、ということです。 TMS1121 の仕様なのかもしれませんが私に言わせれば、いらぬお節介で余計なことをするな ― と言いたいです。 しかし、これを逆手にとって(そうなることを意識して)、ON 動作のスイッチを OFF 状態にすることを目的として用いることはできなくなりますが、MCLR キーを5回の連続操作で 全消去を行っても、現在 ON 動作をしているスイッチとは無関係になる ように改めました。 ON 動作のスイッチを OFF 状態にするには、タイマーの設定とタイマー用メモリの構成 で説明をした、ダイレクト・コントロール (直接制御) で行えば良いと思います。 ただし、複数個の ON 動作をしているすべてのスイッチを、一気に OFF 状態にすることはできません。 MCLR キーを5回の連続操作で 全消去を行っても、見かけ上、何の反応も現れないため、本当にタイマー用メモリの内容が全消去できたのかどうか、心配になることがあります。 そのために タイマー用メモリから読み出し、 若しくは タイマー用メモリから読み出し方法の追加 で述べた方法で、実際にそれを確認する必要があります。 そこで、間違いなく全消去ができたことを示すために、MCLR キーを操作した直後に 見かけ上の反応を現す ことにしました。 その反応方法として、4桁の7セグ LED を点滅して見せます。 初めの 2.1 mS x 128 = 268.8 mS 間は4桁すべてに数字 "0" を点灯、 次の 268.8 mS 間は4桁すべてを滅灯、の点滅表示を5回繰り返す、というものです。 ここで 2.1 mS というのは メインルーチンでの処理と時間の推移 の図で示した1周期全体の処理時間であり、128 は1周期の繰り返し回数でプログラムを簡単にするためにとった値です。 なお、この7セグ LED の点滅表示は、メモリ内容の全消去のときだけでなく、スイッチ別消去、および曜日別消去のときにも、同様に表示をするようにしました。 | プログラムのトップに戻る | |
( プリント基板 (3) の改造と LED 基板の作製 を参照 ) |
||
本機プログラムの開発中(デバッグ中)の様子 | デバッグ用に作製した LED 基板 |
このプログラムの項を終えるに当たって | |
本ページのこのプログラムの項を書き始めた頃には、この項がこんなにも長文になってしまうとは、私自身思ってもいませんでした。 プログラムを作って行くという作業も結構大変ですが、趣味としてやっているわけですから、 その過程をある意味楽しみながらの作業であって、私にとっては至福の一時でもあります。 それに比べて、そのプログラムを自分のホームページで紹介するためとは言え、文章で説明をするという作業は、文章を書くことがあまり得意ではない私にとっては、プログラムを作ることよりも数倍以上も大変なことでした。 私が言わんとした内容が、 このプログラムの項をお読みになった皆様に伝わったでしょうか。 もし、この TMS1121 のシミュレーションプログラムに少しでも関心、興味を持たれたなら、長文ですがぜひ、初めからじっくりと読んでみてください。 不明点、ご感想など、なんでも結構ですので私までメールをいただけると、 作者としてすごく励みになります。 (なおメールアドレスは、トップページ の 【 ご挨拶とお願い 】 をクリックしていただけると、開いたページに表示がされています。) 本ページの冒頭でも述べたように、約3か月間も同一テーマに取り掛かっていて、私自身が少々うんざり気味になっていますので、取り敢えずプログラム、およびこのページを閉めてこれまでのまとめとして公開をすることにしますが、 もう少し細部に亘っても突っ込んでみたい(現在、TMS1121 とは異なった振る舞いをする部分も残っている)、という気持ちもあります。 また、拡張機能の追加についても、現在、実現をさせてみたいと考えている機能が数点あり、私の気力をもう少し充電し直した後に改めて追加発表をしたいと考えています。 |
― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ―
拡張機能の追加 II と回路変更 ( 2019/8/25 追加 ) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
その後の拡張機能として、次の3つの機能を追加しました。 TMS1121 では上記の タイマー用メモリから読み出し の項で、メモリに書き込まれているタイマー情報を読み出して、その内容を各LEDに表示する機能があり、その指示方法には、曜日別表示とスイッチ別表示の2つの方法がある ことを述べました。 私がこれまで TMS1121、および本シミュレータをいろいろ操作をしていて、この2つの方法以外に無条件で、記憶されている すべての内容を表示 する機能があると、より便利になるとずっと思っていました。 そこで、それを拡張機能として実現させてみることにしました。 その場合の指示方法には、スイッチ別表示の形式 (X), SW, SW で行い、次図のようにスイッチ番号 X の指定には、スイッチとして有り得ない番号 9 を割り当てました。 前回の拡張機能の内で3つ目の MEM CLR の機能改善 の項末で述べた、"ダイレクト・コントロール(直接制御)では、複数個の ON 動作をしているすべてのスイッチを、一気に OFF 状態にすることはできない" 件についてですが、 やはり、そのような機能があれば便利になると思い直し、メモリ内容の全消去の機能とは切り離した、全スイッチを OFF 状態にする 機能を設けました。 その場合の指示方法には、ダイレクト・コントロール (直接制御)形式で行い、次図のようにスイッチ番号 X の指定には、前項と同様にスイッチとして有り得ない番号 9 を割り当てました。 ここまで機能を拡張してくると、逆に全スイッチをすべて ON 状態にする機能も考えられ、実際に実現することは容易ですが、そこまで機能を拡張することは止めにしておきました。 オリジナルの TMS1121 のままでアラーム音を出力させることの不都合さは、"088. TI TMS1121 デジタルタイマー II" の 回路図の下の説明 で述べた通りですが、その不都合な点を改善するための方法を拡張機能に盛り込むことにしました。 また、各リレーの ON / OFF 時のアラーム音だけでなく、毎時の時報も併せて出力するように拡張をしました。 したがって、本シミュレータでは次図の左下に示すジャンパースイッチの CHANNEL SELECT は一切使用せずに、どこにもジャンパーをしないことが前提です。 アラーム音を出力する拡張機能を実現するためには、次図に示すように簡単なハードウエアの改造が必要で、赤色の線で示したように 1 ピン、および 9 ピンに与えられたそれぞれの役目の変更を行います。 まず、 1 ピンについては、 従来 MCLR 端子として使用していたのを入力ポートに変更をして、ここに 9 ピンで使用をしていた EXT ジャンパスイッチの引っ越しをします。 次に、入力ポートであった 9 ピンを出力ポートに変更をして、ここにアラーム音を出力するための圧電スピーカ(ブザー)を接続します。 このように回りくどいことを行っているのは、 MCLR 端子であった 1 ピンをポートに機能変更をするのに、 1 ピンは入力ポート専用で出力ポートには変更をすることができないためです。 上図のように、まずハードウエアの改造を行った上で、次にソフトウエアの変更を行います。 次のリストは、入出力ポートの設定に関した部分だけを抜粋したものです。 : : ;; __config _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _IESO_OFF & _FCMEN_OFF & _LVP_OFF ;; __config _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _IESO_OFF & _FCMEN_OFF & _LVP_OFF ;;(Ver.1.10にて変更) : : ; movlw b'11110000' ;RA7-4: 入力, RA3-0: 出力 ;; movlw b'01110000' ;RA6-4: 入力, RA7,RA3-0: 出力 ;;(Ver.1.10にて変更) movwf TRISA movlw b'00000001' ;RB7-1: 出力, RB0: 入力 movwf TRISB clrf TRISC ;RC7-0: 出力 movlw b'00001000' ;RE3: 入力 ;;(Ver.1.10にて追加) movwf TRISE ;; : :上記の 入出力ポートとシミュレート用 PIC の選定 の項で述べた、オリジナル TMS1121 と PIC16F886 の入出力ポートの対応表を、変更後に書き改めると次表のようになります。
* 表中の右2つが拡張機能のために必要な追加と変更をしたポート(薄黄色の部分)。 そして、ハードウエアを変更したことにより、EXT ジャンパスイッチの ON / OFF を検出している部分の変更も必要です。 key_scan : : ; 拡張機能のチェック ; btfsc sw_stat,7 ;EXT JPスイッチ(このスイッチだけ逆論理)= OFF か? ;; btfsc PORTE,3 ;EXT JPスイッチ(このスイッチだけ逆論理)= OFF か? ;;(Ver.1.10にて変更) bcf ext_flg,7 ;Yes. bit7: 拡張機能フラグ = OFF ; btfss sw_stat,7 ;EXT JPスイッチ(このスイッチだけ逆論理)= ON か? ;; btfss PORTE,3 ;EXT JPスイッチ(このスイッチだけ逆論理)= ON か? ;;(Ver.1.10にて変更) bsf ext_flg,7 ;Yes. bit7: 拡張機能フラグ = ON : :アラーム音の種類として、現在、次のリストの上部コメントのように4種類を用意しています。 これらのアラーム音の出力ルーチンは、先に示した メインルーチンでの処理と時間の推移 の図で表すと、1周期の終了直前の部分、 すなわち、 薄緑色の部分 の直後に実行をするように次のリストを追加挿入しました。 このアラーム音の内、リレー ON 時の "ピポッピポッ" 音と、リレー OFF 時の "ブッブー" 音は、タイマーの監視とその処理サブルーチン( time_monitor )内でフラグが設定されます。 また、時報予報音の "プッ" 音と、 時報正報音の "ポッ〜〜ン" 音は、割り込み処理ルーチン( interrupt )内から呼ばれる 秒、分、時_BCD カウンタと WEEK カウンタのインクリメントサブルーチン( bcd_ssmmhh_inc )内でフラグが設定されます。 もし、これらのフラグが複数 ON になった場合には、若番ビットのフラグのみが優先され他のフラグは無視をされます。 ;-------------- アラームフラグの監視とアラーム音出力 ;; ;; ; alm_flg: アラームフラグ ;; ; ┌────── bcd_ssmmhh_inc で設定 ;; ; │ ┌──── time_monitor で設定 ;; ; ┌┐┌┐ ;; ; 0000.xxxx ;; ; ││││ ;; ; │││└─── リレー ON フラグ ;"ピポッピポッ" ;; ; ││└──── リレー OFF フラグ ;"ブッブー" ;; ; │└───── 時報予報音フラグ ;"プッ" ;; ; └────── 時報正報音フラグ ;"ポッ〜〜ン" ;; ;; movf alm_flg,W ;; btfsc STATUS,Z ;アラームフラグのどれか = ON か? ;; goto main11 ;No ;; ;; btfss alm_flg,0 ;リレー ON フラグ = ON か? ;; goto main07 ;No ;; ;; call buzzer_pipo2 ;"ピポッピポッ" ブザー音出力 (490 mS) :: goto main10 ;; ;;(Ver.1.10にて追加) main07 btfss alm_flg,1 ;リレー OFF フラグ = ON か? ;; goto main08 ;No ;; ;; call buzzer_bubuu ;"ブッブー" ブザー音出力 (450 mS) ;; goto main10 ;; ;; main08 btfss alm_flg,2 ;時報予報音フラグ = ON か? ;; goto main09 ;No ;; ;; call buzzer_pu ;"プッ" ブザー音出力 (100 mS) ;; goto main10 ;; ;; main09 btfss alm_flg,3 ;時報正報音フラグ = ON か? ;; goto main10 ;No ;; ;; call buzzer_poon ;"ポッ〜〜ン" ブザー音出力 (1000 mS) ;; ; goto main10 ;; ;; main10 clrf alm_flg ;アラームフラグをクリア ;; ;; main11 goto main ;;なお、これらのアラーム音が出力されている時間(上のリストのように、最小 100 mS 〜 最大 1000 mS の間)中は、それぞれのアラーム音出力サブルーチン内の各ウェイトルーチンのために、設定されたウェイト時間が経過するまで、 プログラムの進行が次には進まなくなるので、したがって、LED のダイナミック点灯が途切れてその間は(短時間ですが)表示がされなくなります。 次に、上記のように4種類のアラーム音をどのようにして作成しているかを、時報予報音の "プッ" 音を例にしてリストに示します。 音階はタイマー1割り込みを使用して作り出しています。 ;========================================================================== ; 定数、変数の定義とレジスタ割付け ;========================================================================== : buzzer equ 7 ;RA7: アラーム音 出力 ;; : f_ra equ 65536 - 2273 ;ラ (440.00 Hz) ;; : ;========================================================================== ;; ; マクロ命令定義 ;; ;========================================================================== ;; ;; ; Sound_M マクロ ;; ;; Sound_M macro @scale,@time ;;(Ver.1.10にて追加) movlw high @scale ;出力音階 ;; movwf const_high ;; movlw low @scale ;; movwf const_low ;; movlw @time ;出力時間(ms) ;; call buzzer_control ;ブザー音出力コントロール ;; endm ;; ;========================================================================== ;; ; アラーム(ブザー)音出力ルーチン ;; ;========================================================================== ;; ;; ; 時報予報音 "プッ" ブザー音出力 (100 mS) ;; buzzer_pu ;; Sound_M f_ra, d'100' ;; ; movlw high f_ra ;出力音階 ; ; movwf const_high ; ; movlw low f_ra ;マクロ展開 ; movwf const_low ; ; movlw d'100' ;出力時間(ms) ; ; call buzzer_control ;ブザー音出力コントロール ; return ;; : ; ブザー音出力コントロール ;; buzzer_control ;; bsf STATUS,RP0 ;バンク 1 ;; bsf PIE1,TMR1IE ;タイマー1割り込みを許可 ;; bcf STATUS,RP0 ;バンク 0 ;; ;; call wait_xxms ;入力 W: 音長(ループカウンタ) ;; ;; bsf STATUS,RP0 ;バンク 1 ;; bcf PIE1,TMR1IE ;タイマー1割り込みを禁止 ;; bcf STATUS,RP0 ;バンク 0 ;; return ;; ;========================================================================== ;; ; ウェイト・ルーチン (at 8 MHz) ;; ;========================================================================== ;; ;; ; 100μ秒 ウェイトルーチン ;; wait_100us ;; movlw 39 ;1 ;; movwf wt1cnt ;1 ;; wait01 goto $+1 ;2 ;; decfsz wt1cnt,F ;1,2 ;; goto wait01 ;2,0 1+1+(2+1+2)*38+(2+2+0)=196 ;; ; goto $+1 ;2 ;; return ;2 196+2=198 ;; ; 198/8MHz*4 = 99 μS = 100 μS = 0.1 mS ;; ;; ; 1m 秒 ウェイトルーチン ;; wait_1ms ;; movlw 10 ;1 ;; wait02 movwf wt2cnt ;1 ;; wait03 call wait_100us ;2 2+198=200 ;; decfsz wt2cnt,F ;1,2 ;; goto wait03 ;2,0 1+1+(200+1+2)*9+(200+2+0)=2031 ;;(Ver.1.10にて追加) return ;2 2031+2=2033 ;; ; 2033/8MHz*4 = 1.0165 mS = 1 mS ;; ;; ; xx m 秒 ウェイトルーチン ;; wait_xxms ;; movwf wt3cnt ;1 ;; wait04 call wait_1ms ;2 2+2033=2035 ;; decfsz wt3cnt,F ;1,2 ;; goto wait04 ;2,0 1+(2035+1+2)*(xx-1)+(2035+2+0)=2038*(xx) ;; return ;2 2038*(xx)+2 ;; ; ;; ;========================================================================== ; 割り込み処理 ;========================================================================== interrupt : : ; タイマー1割り込み処理 ;; ;; t1_int bcf PIR1,TMR1IF ;タイマー1割込みフラグをクリア ;; ;; movf const_high,W ;音階用分周定数 high を ;;(Ver.1.10にて追加) movwf TMR1H ;TMR1H に設定 ;; movf const_low,W ;音階用分周定数 low を ;; movwf TMR1L ;TMR1L に設定 ;; ;; movlw 1 << buzzer ;対象ビットをセット ;; xorwf PORTA,F ;出力をビット反転 ;; ; goto int_end :なお、この時報予報音の "プッ" 音というのは、毎時の時報、すなわち "プッ"、"プッ"、"プッ"、"ポッ〜〜ン" の内の1つ、"プッ" を取り上げたもので、本来は 440 Hz の正弦波を 100 m 秒間 出力するものですが、ここではプログラムを簡単にするために 疑似的に矩形波で代用をしています。(というよりも、正弦波を作り出すなんてことは私にはできません。) |
ウェイト・ルーチンを変更( 2020/5/3 更新 ) | |
上に示した xx m 秒 ウェイトルーチン( wait_xxms ) を、次のリストに示すように変更をしました。 上のリストではサブルーチン内で他のサブルーチンを、2回ネストを重ねてコールをしていますが、PICにはスタックが8ワードしか存在しないため、 注意を払わないとすぐにオーバーフローを起こしてしまいます。 そして、その結果は即暴走につながるわけです。 ; xx m 秒( 1m 秒 x W )ウェイトルーチン ; wait_xxms ; movwf wt2cnt ;1 ; ; w1m01 movlw 221 ;1 ;(Ver.1.21にて変更) movwf wt1cnt ;1 ; ;スタックの消費量を改善 w1m02 goto $ + 1 ;2 ; goto $ + 1 ;2 ; goto $ + 1 ;2 ; decfsz wt1cnt,F ;1,2 ; goto w1m02 ;2,0 (2+2+2+1+2)*220+(2+2+2+2+0)=1988 ; ; nop ;1 ; goto $ + 1 ;2 ; goto $ + 1 ;2 ; decfsz wt2cnt,F ;1,2 ; goto w1m01 ;2,0 (1+1+1988+1+2+2+1+2)*(W-1)+(1+1+1988+1+2+2+2+0)=1998*W-1; ; return ;2 1+(1998*W-1)+2=1998*W+2 ; ;Total (1998*W+2)/8MHz*4 = (999*W + 1) μS ;本機のように、アラーム(ブザー)音出力ルーチン内でウェイトルーチンを組み合わせる場合には、サブルーチンコールのネストが多重になりがちですので、注意が必要です。 実は最近、他の同類のプログラムを作成していてやらかしてしまいました。 サブルーチンコールのネストということを常に意識をしていないと、いざ、プログラムが暴走したときにその原因の発見までに多くの時間を要し、無駄に頭を悩ませることになります。 その暴走をさせたプログラムでは、例として "ピポッピポッ" ブザー音の場合のネスト状態を検証してみると、 (main) → (buzzer_pipo2) → (buzzer_pipo) → (buzzer_pi) → (buzzer_control) → (wait_xxms) → (wait_1ms) → (wait_100us) → (interrupt) → (bcd_ssmmhh_inc) → (inc_bcd) (1) (2) (3) (4) (5) (6) (7) (8) (9) (10)となり、(7) までの状態のときに "ピポッピポッ" ブザー音とは非同期で (8) 以降のタイマー1割り込みが起こり、結果、スタックのオーバーフローで暴走をしていました。 これに対して本機では (8) までは同様(ただし、割り込み種別は 外部 (RB0/INT) 割り込み)ですが、(9) 以降が実行されるまでに "ピポッピポッ" ブザー音 (490 mS) が終了してしまうのでスタックのオーバーフローは起こりません。 しかし、ブザー音の出力中はスタックが (8) まで使用されて満タンの状態になります。 このように現状では暴走は起こりませんが、将来的にプログラムの更新を行ったときに余計なことで悩まなくてもいいように、 今のうちに悪の種は絶っておくことにしました。 具体的には、xx m 秒 ウェイトルーチン( wait_xxms ) 内では (6), (7) のネストコールを行わなくてもいいように、上リストのようなコードに書き換えました。 | プログラムのトップに戻る | |
現在の最新バージョン: Ver. 1.31 ( 2021/5/19 更新 )
この最新のプログラムは、"185. TMS1121 デジタルタイマー・シミュレータ II" に収録のプログラムと同じものです。 |
|||
ソースファイル (TMS1121_SimulatorII.asm) | この新 Ver. のプログラムを利用するためには、必ず アラーム音(ブザー音)の出力 で述べた回路変更が必要です。 | ||
HEX ファイル (TMS1121_SimulatorII.hex) | |||
参考: 旧 Ver. 1.10 |
|||
ソースファイル (TMS1121_Simulator_110.asm) | この Ver. のプログラムを利用するためには、必ず アラーム音(ブザー音)の出力 で述べた回路変更が必要です。 | ||
HEX ファイル (TMS1121_Simulator_110.hex) | |||
参考: 旧 Ver. 1.00 |
|||
ソースファイル (TMS1121_Simulator_100.asm) | |||
HEX ファイル (TMS1121_Simulator_100.hex) |
| ページトップ |
下のパターン図をご覧になってどこかおかしいことに気が付かれましたか? 実は、大変お恥ずかしいことをここに暴露しなければなりません。 質問の答えは右側の写真にあるように、PIC16F886 の DIPパッケージは 300 mil
の横幅サイズであって、パターン図のような 600 mil ではありません。 それを何の躊躇することもなく 600 mil サイズで作製をしてしまったのです。 そのことに気が付いたのは、PICにプログラムを書き込もうと思って、部品箱から PICを取り出したときでした。 愕然となりました。 今更何ともなりません。 プリント基板の作製は既に数週間も前に済ませてあったので、変更をすることなどはできません。 私にとって PIC16F886 を使用することは、本機が3作目で初めてのことではなかったのですが、このような失態を犯してしまいました。 今更、もう一度作り直すなんて気分には到底なりませんので、仕方なく右下の写真に示すように 600 mil → 300 mil への変換基板を作って対応をしました。 図らずも "088. TI社 TMS1121 デジタルタイマー II" では "足ピッチ変換 (シュリンクDIP 400mil → DIP 600mil) 用 ICソケットの作製" のように足ピッチ変換を行い、本機では横幅サイズ変換を行うことになってしまいました。 |
| プリント基板パターン図 (部品面) (TMS1121_SimulatorPC.CE3) | ページトップ |
| プリント基板パターン図 (ハンダ面) (TMS1121_SimulatorPC1.CE3) | ページトップ |
拡張機能の第2弾として追加をした機能(アラーム音の出力)のために、少々の回路変更が必要となります。 その変更をしたパターン図を次に示します。 なお、改造後の写真は、以下のパターン図 (部品面) と (ハンダ面) の2枚を追加しただけで、本機外観の写真等の差し替えは行っていませんので、その点は注意をしてください。 |
| プリント基板パターン図 (部品面) (TMS1121_Simulator2PC.CE3) | ページトップ |
| プリント基板パターン図 (ハンダ面) (TMS1121_Simulator2PC1.CE3) | ページトップ |
本機は、上に挙げたプリント基板だけではデジタルタイマーとしての機能はしません。 次に挙げるプリント基板(1) *、プリント基板(3) ** と一緒に構成させることが必要で、これら2枚のプリント基板は "088. TI TMS1121 デジタルタイマー II" で作製をしたものをここでも再利用します。 そのため、これらを次に再掲しました。 |
| プリント基板(1)パターン図 (部品面) (TI_TMS1121_DegitalTimerII1PC.CE3) | ページトップ |
| プリント基板(1)パターン図 (ハンダ面) (TI_TMS1121_DegitalTimerII1PC1.CE3) | ページトップ |
| プリント基板(3)パターン図 (部品面) (TI_TMS1121_DegitalTimerII3PC.CE3) | ページトップ |
| プリント基板(3)パターン図 (ハンダ面) (TI_TMS1121_DegitalTimerII3PC1.CE3) | ページトップ |
シミュレーションプログラムのデバッグ作業の効率を上げるために、次に示すように、プリント基板 (3) の改造を行いました。 改造といっても大したことではなく、下写真にも示す(黄色の長丸で囲んだ)ように
電源端子( +9V、GND )を設けただけで、左のパターン図の上部に示すような小さなプリント基板への、電源供給用です。 そして、この小さなプリント基板(LED 基板)の裏面側には、プリント基板 (3) 側の電源、4つの各リレー接点とを、ワンタッチで接続ができるように、コネクタ(メス側)を取り付けています。 LED 基板は、リレーが動作したときのメーク接点で LED を点灯させ、4つのリレー(スイッチ)の内で動作したのはどれかを、目視で簡単に識別をさせるのが目的です。 |
( 拡大 ) デバッグ用に作製した LED 基板 |
| プリント基板(3) の改造図 (部品面) (TI_TMS1121_DegitalTimerII3PCb.CE3) | ページトップ |
(主要部品: IC, トランジスタ等) | (データシート) | ||
PICマイコン | .................... | PIC16F886 | |
三端子レギュレータ | .................... | NJM78M05FA | |
トランジスタアレイ | .................... | TD62003AP | |
トランジスタ | .................... | 2SC509 | |
トランジスタ | .................... | 2SC1815 | |
ダイオード | .................... | 1N4148 |
| 部品表
| Excel ファイル (TMS1121_Simulator_parts.xls)
| ページトップ |
雑誌 「初歩のラジオ」 19??年 8月号 P.83-89
「週間メモリ・タイマーの作り方」 大河忠一氏
雑誌 「初歩のラジオ」 19??年 9月号 P.125-129
「週間メモリ・タイマーの作り方」 大河忠一氏
| ページトップ | ホーム |