| ホーム |     私の電子工作作品集

181. TMS1121 デジタルタイマー・シミュレータ

[ 初公開日:2019年7月25日 ]

 先に公開をしたページ "x09. TI社 TMS1121 デジタルタイマー" の "操作方法" の 項末 で、 『 この項を書き(作り)ながら ― PICを使用して CPU "TMS1121NLL" をシミュレートしてみたい ― という思いがすごく募ってきました 』 と書いたのですが、その思いが覚めてしまわない内に ―― と思ってそれを実現させてみました。

 と言っても、それはそれ程容易なことではなく、このホームページの作成を含めて約3か月間もかかってしまい、このページの公開が、当初の自分の予定(希望)より1か月も遅れてしまいました。 良く言えば、このテーマだけで3か月間もの間、 私を楽しませてもらうことができたのですが、本音を言えば、やはり少々うんざりしてきたのも事実です。

 TMS1121 については、40 年以上も昔に発売された IC ですからご存知の方も少ないと思いますが、当時としては、趣味仲間の間では結構トレンドな存在であったと思います。  "TMS1121" をシミュレートすると言っても、その詳細な仕様書がもちろん手元にあるわけではありませんので、実際の "TMS1121"(を使用したタイマー)をあれこれといじくりまわして、まず機能(振る舞い)を調べる必要があります。 しかし、"x09. TI社 TMS1121 デジタルタイマー" では、かなり大きな部類のケースに組み込んでありますので、あまり手軽に とは言えません。

 幸い(?)なことにこれとは別に、もう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 を参照してください。


【 お知らせ 】  ( 2020/7/8 ) ( 2020/7/24 )

 このページを公開してから早くも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 ) も併せて参照しながら、 説明をご覧になることをお勧めします。

(項目数が多くなってしまったので、目次を付けました。)

入出力ポートとシミュレート用 PIC の選定
各種LEDの表示とキー入力の検出
キーマトリクスの各キーとキーコードの取得
キースイッチのチャタリングとその対処
本機デジタルタイマーでの時計機能(計時方法)
時計機能に対する時刻の変更設定
タイマーの設定とタイマー用メモリの構成
タイマー用メモリへの書き込み
タイマー用メモリから読み出し
タイマー用メモリの内容消去
タイマー(設定時間)の監視とその処理
本機での拡張機能の追加
拡張機能の追加 II と回路変更 ( 2019/8/25 追加 )
現在の最新バージョン


左: TMS1121、右: PIC16F886 ( 拡大

 なお、以下の説明で、タイマー用メモリへの書き込み、読み出し、内容消去、およびタイマー(設定時間)の監視とその処理、のそれぞれの項目内で引用しているプログラムを、更新をした最新のプログラム Ver.1.31 のものに差し替えをしました。 差し替え前のプログラムは間違いではないのですが、私の思い違いからバンクの扱いが(他の方が見て)、まわりくどくて分かり辛いものになっていたのを改めました。 以前からずっと気にはなっていたのですが、 今までなかなか修正をできないでいたものです。 ( 2021/5/19 更新 )

・ 入出力ポートとシミュレート用 PIC の選定

初めに、当然ですが TMS1121 と PIC の両者はハード的に異なっていて制約があるために、PIC なら何でも可というわけには行きません。 そこで、まず使用する PIC の選定から考慮する必要があります。 TMS1121 はシュリンク 28 ピンDIP IC のため 使用する PIC についても 28 ピンDIPに拘りました。

TMS1121 では下表に示すように、入力ポートが 4、出力ポートが 19 の計 23 個のポート数があります。 したがって、シミュレートするためには PIC にも同数以上のポート数の確保が必要です。
    ポートの用途 LED 各桁点灯/キースキャン 信号出力 LED 各セグメント信号出力 リレー制御信号出力 キースキャン信号入力 計時用パルス入力 拡張 JPSW 入力
    TMS1121 R0 R1 R2 R3 R4 R5 R6 O0 O1 O2 O3 O4 O5 O6 O7 R7 R8 R9 R10 K1 K2 K3 K8 -
    PIC16F886 RB1 RB2 RB3 RB4 RB5 RB6 RB7 RC0 RC1 RC2 RC3 RC4 RC5 RC6 RC7 RA0 RA1 RA2 RA3 RA4 RA5 RA6 RB0 RA7
例えば PIC16F873A や PIC16F876A は 28 ピンの PIC ですが、入出力ポートの数が 22 個と、僅か1個ですが足りないために使用することができません。 これに対して、これらの PIC の改良品である PIC16F883 または PIC16F886 では、 最大 25 個 * までの入出力ポート数を確保することが可能となっています。 私の手持ちの関係で今回は PIC16F886 を使用することにしました。

    * 英文データシート(DS41291F)の page 2 の表には、I/O 数が 24 と記されているが、実際には RA0 〜 RA7, RB0 〜 RB7, RC0 〜 RC7, RE3 の計 25 である。 ただし、RE3 は入力ポート専用。

しかし、基本の使い方ではやはり 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の表示時間に比べた時間比では、実際にはほんの一瞬で終ってしまいます。
R0 〜 R6 というのはオリジナル TMS1121 の信号名で、PIC では図中の左端に記してあるように、それぞれ RB1 〜 RB7 の各出力ポートが対応をしてシミュレートをしています。 そして、"H" 信号を出力することで R0 〜 R6 信号のそれぞれが有効となり、 例えば R1 を有効とした場合には、O0 〜 O7 (PIC では RC0 〜 RC7 が対応)の各出力ポートに数字パタンのセグメント信号を出力すれば、1st LED すなわち、"分"桁の7セグ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)の方向へ流れて行きます。
オリジナルの TMS1121 では、使用するキースイッチの数を全部で 20 個を想定(下表の黄色の部分)していて、これらの各キーを 回路図上では、上図右側のキーマトリクスのように配置をしています。 横1行に7個ずつ3行にして並べます。 最後の3行7列目は、右側のマトリクス上にはありません。 ところが、図中の PIC の直下に位置する、並列接続をした 12H/24H タクトスイッチと 50Hz/60Hz の切り替え用ジャンパースイッチが、論理的には3行7列目に位置するのと同等になります。

この2つのスイッチが並列になっているのは、回路図の下の説明 のように 12H/24H タクトスイッチを後から追加をしたためで、50Hz/60Hz の切り替え用ジャンパースイッチの取り扱いを運用上で対処をしてください。(すみません。) 12H/24H タクトスイッチは、拡張機能を使用するときに有効となるスイッチで、オリジナルの TMS1121 では存在しません。

    キー名 0 (EDAY) 1
    (SUN)
    2
    (MON)
    3
    (TUE)
    4
    (WED)
    5
    (THU)
    6
    (FRI)
    7
    (SAT)
    8 9 AM PM WEEK SW ON OFF SLP CLR MCLR CLK 12H/24H -
    キーコード 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

また、ソフト的には各キーのそれぞれ(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 にして、処理の重複実行を避けるようにしています。
やがて、タイマー0割り込みによって 50 mS の経過を検知すると、変数( keycode )の bit7(キー入力受付済み表示フラグ)を OFF にして、再びキー入力の受付を可能にしますが、この時点では新たなキー入力の受付が目的ではなく、 先に ON となったキーが次に OFF に転じるのを、監視するのを目的としています。

次のリストは、メインルーチンの終わり間近の部分を抜粋したもので、メインルーチンでの処理と時間の推移 の図では、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 にします。
上図は、PIC16F886 のデータメモリの構成を図に表したものですが、薄黄色の部分が汎用レジスタ(GPR)でユーザが自由に使用できる RAM メモリです。 そして、図に示すように Bank 0 〜 Bank 3 の 4 つに分かれていますが、 この内の Bank 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 を繰り返すと、再びタイマー用メモリの初めに戻って検索一致したものを表示します。
次のリストは、(X), WEEK, WEEK、または (X), SW, SW のキー操作を行うと、タイマー用メモリの読み出しを行うサブルーチン( read_memory )です。 どちらの場合も (X) で指定をした番号が検索キーとなって、タイマー用メモリの タイマー制御バイト と比較されますが、両者では比較をするビットの位置が異なります。 前者では www ビット (WEEK)、後者では ss ビット (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 ) です。 このサブルーチンはご覧のように少々長めなため、ここに示すべきかどうか迷いましたが、本シミュレーションプログラムの本命でもあるので、 やはり示しておくことにしました。

サブルーチン内で行っていることを要約すると、次のようになります。

  1. まず初めに行っていることは、CALL されてこのサブルーチンにプログラムの実行が移った時点での、今現在のリアルな "曜日"、"時"、"分" の各データが、このサブルーチンに引き継がれます。

  2. 次に、20 ワードあるタイマー用メモリの先頭のワードから、そのデータ内容が有効( f ビット = 1 )であるかどうかを調べ、有効( f ビット = 1 )の場合には次の 3. に進みますが、無効( f ビット = 0 )の場合にはそのワードはパスをして、 次のワードの検査に移ります。 こうして、順次、最終のワードまで調べて行きます。

  3. 2. の検査の結果、有効ワードの場合には、ワード内に記憶をしているデータと、1. で引き継いだ3つのデータ "曜日"、"時"、"分" が、順に比較照合されますが、3つのデータのどれかが異なっていた場合にはそのワードはパスをして、 再び 2. に戻って次のワードの検査に移ります。

  4. 3. で3つのデータすべてが一致した場合には、ワードの先頭バイトである タイマー制御バイト の内容指示に従って、どのスイッチ( ss ビット (SW) )をどうするか( mm ビット (MODE) )の処理の実行を行います。 具体的には、R7 〜 R10 (PIC では RA0 〜 RA3 が対応)の出力ポートに ON / OFF 信号を出力して、一般的には、その先にあるリレーの制御をします。

  5. 4. で出力ポートの制御をした後に、ワードの第2バイトである 時_BCD カウンタバイト のメモリ自動消去フラグが、有効( c ビット = 1 )であるかどうかを調べ、無効( c ビット = 0 )の場合には次の 6. に進みますが、 有効( c ビット = 1 )の場合には次に、タイマー制御バイトの mm ビット (MODE) を調べて、もし SLP + ON の場合にはやはり次の 6. に進みます。 しかし、 mm ビット (MODE) が ON / OFF / SLP + OFF の場合には、そのワードを無効( f ビット = 0 ) にする消去処理を行います。 そして、その後は再び 2. に戻って次のワードの検査に移ります。

  6. ここで、mm ビット (MODE) が SLP でなかった場合には、再び 2. に戻って次のワードの検査に移りますが、SLP の場合にはそのワードの記憶データの変更を行います。 まず mm ビット (MODE) を、SLP + ON であったものは SLP + OFF に、 逆に SLP + OFF であったものは SLP + ON に変更を行います。

  7. そして、SLP + OFF に変更をしたものについては、現在記憶している "時分" データに1時間をプラスした OFF 時間に変更をします。 また逆に、SLP + ON に変更をしたものについては、1時間をマイナスした ON 時間に変更をします。 これらの変更は "曜日" データも含めて行います。

  8. 最後に、2. で最終のワードまで行き着くと、タイマー監視フラグ( tmon_flg )を OFF にしてこのサブルーチンを終了します。

* なお、リストはプログラム 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つの機能があります。

  • コロン [COL] LED のブリンク表示

    本機の時計の表示は "時"、"分" の4桁表示のため、刻々と変化をして行く "秒" の様子が、見ている者に伝わってきません。 ― というわけでもないのですが、私の遊び心的な気持ちも手伝って、"時"、"分" の区切りの [COL] LED の表示を 動かしてみたのですが、頭で考えていたのとは異なって、どうやらこの試みは失敗のようでした。

    0.5 秒間を "点"、次の 0.5 秒間を "滅" の1秒周期で、ブリンク表示をさせたのですが、人それぞれ感じ方は違うとは思いますが、私には、 [COL] LED の表示が動くのを見ていて、何か煩わしく感じられるのです。

    そこで、EXT ジャンパースイッチで拡張機能を選択したときに、必ずブリンク表示をしてしまうのも困るので、回路図 に示すように新たに NOR/BLK ジャンパースイッチ を設けて、好みによって ノーマル表示(点灯し切り)ブリンク表示(点滅) が選択できるようにしました。

    ただし、EXT ジャンパースイッチが OFF のときに NOR/BLK ジャンパースイッチを BLK にすると、 [COL] LED は消灯してしまうので注意が必要です。

  • 時計機能の 24 時間表示

    私はこれまでに、他のページで紹介をしている "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 別の指定はありません。

  • MEM CLR の機能改善

    オリジナルの TMS1121 では、タイマー用メモリの内容消去 をするのに3つの方法があることを述べましたが、その内の 曜日別消去スイッチ別消去 については、 自分の意志でもってメモリ内容を消去するわけだから問題はないと思います。 これに対して 全消去 の場合には、自分の意志のときはもちろん問題はないが、誤操作のときには取り返しがつかなくなります。

    ここで一番問題なのは、MCLR キーの単操作(たった1回の押下)でメモリ内容のすべてが消去されてしまう、ということにあります。 誤操作というのは往々にしてあり得ることで、できればこれを避けたいものです。 そこで、これに対処をしたのがここで述べる改善策です。

    自分の意志で全消去を行うという場面はそうそうあるわけではないですから、単操作ではなく MCLR キーを5回の連続操作(5回連続で押下)で全消去を行う ように改めました。 2、3回というのはやはり誤操作であり得ると思うので、 5回にしました。 それほどの手間にはならないものと思います。 なお、5回未満の場合には無視をします。

    また、メモリ内容の全消去の操作をすることによって、私がもう一つ困ると思うことは、現在、どれかのスイッチ(複数個を含む)が ON 動作をしていた場合に、それらのすべてのスイッチが OFF 状態にされてしまう、ということです。 TMS1121 の仕様なのかもしれませんが私に言わせれば、いらぬお節介で余計なことをするな ― と言いたいです。

    しかし、これを逆手にとって(そうなることを意識して)、ON 動作のスイッチを OFF 状態にすることを目的として用いることはできなくなりますが、MCLR キーを5回の連続操作で 全消去を行っても、現在 ON 動作をしているスイッチとは無関係になる ように改めました。

    ON 動作のスイッチを OFF 状態にするには、タイマーの設定とタイマー用メモリの構成 で説明をした、ダイレクト・コントロール (直接制御) で行えば良いと思います。 ただし、複数個の ON 動作をしているすべてのスイッチを、一気に OFF 状態にすることはできません。

  • MEM CLR の機能改善に機能追加  プログラム Ver.1.20  ( 2019/8/28 追加 )

    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 の点滅表示は、メモリ内容の全消去のときだけでなく、スイッチ別消去、および曜日別消去のときにも、同様に表示をするようにしました。

    | プログラムのトップに戻る |

  • このプログラムの項を終えるに当たって

    本ページのこのプログラムの項を書き始めた頃には、この項がこんなにも長文になってしまうとは、私自身思ってもいませんでした。 プログラムを作って行くという作業も結構大変ですが、趣味としてやっているわけですから、 その過程をある意味楽しみながらの作業であって、私にとっては至福の一時でもあります。

    それに比べて、そのプログラムを自分のホームページで紹介するためとは言え、文章で説明をするという作業は、文章を書くことがあまり得意ではない私にとっては、プログラムを作ることよりも数倍以上も大変なことでした。 私が言わんとした内容が、 このプログラムの項をお読みになった皆様に伝わったでしょうか。

    もし、この TMS1121 のシミュレーションプログラムに少しでも関心、興味を持たれたなら、長文ですがぜひ、初めからじっくりと読んでみてください。 不明点、ご感想など、なんでも結構ですので私までメールをいただけると、 作者としてすごく励みになります。 (なおメールアドレスは、トップページ の 【 ご挨拶とお願い 】 をクリックしていただけると、開いたページに表示がされています。)

    本ページの冒頭でも述べたように、約3か月間も同一テーマに取り掛かっていて、私自身が少々うんざり気味になっていますので、取り敢えずプログラム、およびこのページを閉めてこれまでのまとめとして公開をすることにしますが、 もう少し細部に亘っても突っ込んでみたい(現在、TMS1121 とは異なった振る舞いをする部分も残っている)、という気持ちもあります。

    また、拡張機能の追加についても、現在、実現をさせてみたいと考えている機能が数点あり、私の気力をもう少し充電し直した後に改めて追加発表をしたいと考えています。

    ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ― ―


    拡張機能の追加 II と回路変更 ( 2019/8/25 追加 )

    その後の拡張機能として、次の3つの機能を追加しました。

  • タイマー用メモリから読み出し方法の追加

    TMS1121 では上記の タイマー用メモリから読み出し の項で、メモリに書き込まれているタイマー情報を読み出して、その内容を各LEDに表示する機能があり、その指示方法には、曜日別表示とスイッチ別表示の2つの方法がある ことを述べました。 私がこれまで TMS1121、および本シミュレータをいろいろ操作をしていて、この2つの方法以外に無条件で、記憶されている すべての内容を表示 する機能があると、より便利になるとずっと思っていました。 そこで、それを拡張機能として実現させてみることにしました。

    その場合の指示方法には、スイッチ別表示の形式 (X), SW, SW で行い、次図のようにスイッチ番号 X の指定には、スイッチとして有り得ない番号 9 を割り当てました。
    キースイッチを 9, SW と押すと、通常のスイッチ別指定とは異なっていることを示すために、 SW1 〜 SW4 のすべての LED を点灯させます。 そして、それ以降のキー操作はスイッチ別指定の場合とまったく同様に行います。 また、メモリ内容の LED 表示の方法は、曜日別やスイッチ別はなく 記憶されている順(無差別)にすべての表示がされます。

  • ON 動作(複数個を含む)のスイッチをすべて OFF にする

    前回の拡張機能の内で3つ目の MEM CLR の機能改善 の項末で述べた、"ダイレクト・コントロール(直接制御)では、複数個の ON 動作をしているすべてのスイッチを、一気に OFF 状態にすることはできない" 件についてですが、 やはり、そのような機能があれば便利になると思い直し、メモリ内容の全消去の機能とは切り離した、全スイッチを OFF 状態にする 機能を設けました。

    その場合の指示方法には、ダイレクト・コントロール (直接制御)形式で行い、次図のようにスイッチ番号 X の指定には、前項と同様にスイッチとして有り得ない番号 9 を割り当てました。
    前項と同様に キースイッチを 9, SW と押すと、通常のスイッチ別指定とは異なっていることを示すために、 SW1 〜 SW4 のすべての LED を点灯させます。 その後、OFF キーを押すとそのときに動作をしているスイッチのすべてが OFF 状態になり、 LED表示は現在の時刻表示に戻ります。

    ここまで機能を拡張してくると、逆に全スイッチをすべて 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 の入出力ポートの対応表を、変更後に書き改めると次表のようになります。

      ポートの用途 LED 各桁点灯/キースキャン 信号出力 LED 各セグメント信号出力 リレー制御信号出力 キースキャン信号
      入力
      計時用パルス
      入力
      拡張 JPSW
      入力
      拡張ブザー
      出力
      TMS1121 R0 R1 R2 R3 R4 R5 R6 O0 O1 O2 O3 O4 O5 O6 O7 R7 R8 R9 R10 K1 K2 K3 K8 - -
      PIC16F886 RB1 RB2 RB3 RB4 RB5 RB6 RB7 RC0 RC1 RC2 RC3 RC4 RC5 RC6 RC7 RA0 RA1 RA2 RA3 RA4 RA5 RA6 RB0 RE3 RA7

      * 表中の右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) | ページトップ |

    ■ プリント基板パターン図 (部品面) ... (改造後) ■  ( 2019/8/25 追加 )

     拡張機能の第2弾として追加をした機能(アラーム音の出力)のために、少々の回路変更が必要となります。 その変更をしたパターン図を次に示します。

     なお、改造後の写真は、以下のパターン図 (部品面) と (ハンダ面) の2枚を追加しただけで、本機外観の写真等の差し替えは行っていませんので、その点は注意をしてください。

    | プリント基板パターン図 (部品面) (TMS1121_Simulator2PC.CE3) | ページトップ |

    ■ プリント基板パターン図 (ハンダ面) ... (改造後) ■  ( 2019/8/25 追加 )

    | プリント基板パターン図 (ハンダ面) (TMS1121_Simulator2PC1.CE3) | ページトップ |

     本機は、上に挙げたプリント基板だけではデジタルタイマーとしての機能はしません。 次に挙げるプリント基板(1) *、プリント基板(3) ** と一緒に構成させることが必要で、これら2枚のプリント基板は "088. TI TMS1121 デジタルタイマー II" で作製をしたものをここでも再利用します。 そのため、これらを次に再掲しました。

    ■ プリント基板(1)パターン図 (部品面) ■ *

    | プリント基板(1)パターン図 (部品面) (TI_TMS1121_DegitalTimerII1PC.CE3) | ページトップ |

    ■ プリント基板(1)パターン図 (ハンダ面) ■ *

    | プリント基板(1)パターン図 (ハンダ面) (TI_TMS1121_DegitalTimerII1PC1.CE3) | ページトップ |

    ■ プリント基板(3)パターン図 (部品面) ■ **

    | プリント基板(3)パターン図 (部品面) (TI_TMS1121_DegitalTimerII3PC.CE3) | ページトップ |

    ■ プリント基板(3)パターン図 (ハンダ面) ■ **

    | プリント基板(3)パターン図 (ハンダ面) (TI_TMS1121_DegitalTimerII3PC1.CE3) | ページトップ |

    ■ プリント基板 (3) の改造と LED 基板の作製 ■

     シミュレーションプログラムのデバッグ作業の効率を上げるために、次に示すように、プリント基板 (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 「週間メモリ・タイマーの作り方」 大河忠一氏

    | ページトップ | ホーム |


    Copyright (C) 2019-2023 やまもとみのる
    初版:2019年7月25日、初公開:2019年7月25日、最終更新:2023年10月31日