PIC 12F629
によるエレKey製作
自作工程のメモとして
2006.Jun.18
以下はその記録ですが、6年ぶりのアセンブラ言語と、以前使った PIC 16F84 とは違う8ピンのPICを使うために、大変な頭の体操になりました。
また、最初に書きましたパドル操作の感覚も、完成してみると「思い違い」だった気がしています。
せっかく覚え直したアセンブラですので、これ以外にも作ってみたい気がしています。続編 cwbufkey を掲載しました。(2006.07.29)
1.テスト基板の用意 |
いきなりアセンブラでプログラムし、動作させる腕も知識もありませんので、テスト基板を組み立てて実験することにしました。
手持ちのPCBや秋月から取り寄せた「ソケット」類と、関ハム2006 で入手したLED点滅キット(パーツを一つひとつそろえるのが面倒だったため)を200円で購入して、やっとの思いでくみ上げました。(最近、面倒くさく感じるようになってきており、ブレッドボードに組めばもっと早く取り掛かれたかも知れません。)
2.ゼロプレッシャーソケットを使う |
久しぶり(6年)のアセンブラなので、100回ぐらいはPICを抜き差しする必要があると考え、PICライターに使っている物と同種のソケットを用意し、丸ピンのICソケット上にゼロプレッシャーソケットを載せます。
どうせならと、28ピンをつけて、上で 16F84 の回路も組めるように余裕を残したつもりです。
↓ これは、LEDの点滅が動き出した頃の画像で、これ以降も少しずつ構成を変えています。
3.スピード調節の実験 |
CWのスピード調節は、PICのポートひとつを使って実現しているため、コンデンサへの充電か放電で時間を割り出していることは想定できましたが、どんなコードを書けばその動きが実現するのかで迷いました。
残念ながら、オシロスコープは持っていません。
また、周波数カウンタも無いため、簡易的な周波数カウンタが付いた秋月のP−16というマルチメーターも購入してしまいました。
その後、パソコン(PC)上で、オシロスコープの動きをするフリーソフトが見つかりましたのでそれを使い、やっと納得がいった次第です。最初は、GP2ポートをHighにする時間を、数十mSのデレイループを記述しプログラム上で確保しましたが、GP2ポートをHigh(出力)にした直後に入力に切り替えても、充分Highの状態が確保されていることが確認できました。
↓ 以下は、GP2ポートの波形です。
音源ボードを使っているため、交流専用となり、周波数が低くなると短形波では、信号がゼロ点に戻り測定できませんが、0.2mS ぐらいでは、Highレベルを保持しています。
ボリュームの操作でHighの時間が変わります。
↓ これは、パドルを接続して時定数を調節しているころの画像です。
LED出力部や、アース用の端子を追加しています。
4.開発環境 |
開発用には、サクラエディタ、MPLAB V7.40、秋月PICプログラマーVer4を使いました。
エディタで修正して、MPLABでアセンブル(ビルド)し、書き込みを繰り返し、本当に100回くらいやったかもしれません。
6.ソースファイル |
プログラムは実験の域を出ないため、単機能に徹しています。
信号比は 1:3 の固定で、バグキーモードも付いていません。
昔、自分で作ったプログラムが解読できなかったため、コメントはしっかり入れたつもりです。
他の方も、ボリュームでのスピードコントロールを、色々な方法で実現させているようで、これと同じ方法や、コンデンサ充電用のHighレベル信号を別ポートから入れる方法や、AD変換ポート(同種の PIC 12F675 に有り)を使う方法もWeb上で公開されていました。
そのどちらかの方法を真似れば早かったと思いますが、その他の部分を自分流に変更する時には、かえって手間取ってしまうと考え、一から(6年ぶり)書いてみました。
その意味では参考にならず、間違いがあるかも知れませんが、掲載しておきます。シンプルなPICキーが必要になりこのバージョンを使おうとしましたが、パドルがもつれる??(ドットレバーを放すタイミング遅れ)ためドットの処理を調整しました。(2008/12/31)
(エディタ上でTAB間隔を8文字に設定し直しましたが、これでもコメント等の位置がずれています。)
--------------------------------------------------------------------
; ;PICマイコンによる、CWキーヤーの製作 ; ;2006/06/18:(C)JE3JLB (最下部へコード埋め込みあり) ;2006/07/06:タブ位置を8文字に設定し直した。 ;V1.01:2008/12/31:ダッシュの先頭から約1/3間は、パドルのドットを記憶しない。 ; ファイル名は、key628a.asm と変更する。 ;CWコードの定数を確認 ;PARISコードの1ワード(W)は、14+8+10+6+8+(4)=50ドット ;1分間に 5W( 25文字)送出するには、 250ドット/分 ; (4.2dot/Sec 240ms/dot)短点連続周波数= 2.1Hz ;1分間に10W( 50文字)送出するには、 500ドット/分 ; (8.3dot/Sec 120ms/dot)短点連続周波数= 4.2Hz ;1分間に20W(100文字)送出するには、1,000ドット/分 ; (16.7dot/Sec 60ms/dot)短点連続周波数= 8.3Hz ;1分間に30W(150文字)送出するには、1,500ドット/分 ; (25.0dot/Sec 40ms/dot)短点連続周波数=12.5Hz ;1分間に40W(200文字)送出するには、2,000ドット/分 ; (33.3dot/Sec 30ms/dot)短点連続周波数=16.7Hz ; ; ; ;◆回路構成の概略(全半角等幅文字) ; ; ; PIC 12F629 3V 3V ; 3V ┌──V──┐ │ │ ; │ │ │ 100K│ │100K ; └─│1 8│─┐ (R) (R) ; │ │ E │ │ ; CW出力 ──│2 7│────┴────(パドル:ドット) ; │ │ │ ; NC:<--│3 6│──────┴──(パドル:ダッシュ) ; │ │ 10K ; PU:-->│4 5│───┬──(R)-┐ ; │ │ (C) (VR) 50K ; └─────┘ │103 │ ; E E ; ; Eはアース、NC:は未接続、PU:はプルアップ。 ; ;◆動作について ;SQUEEZE Key 二枚羽のパドルを持つキーヤー の、 ;アイアンビック mode B の動作になるとは思いますが、明確には不明です。 ; ;◆参考書籍 ;中尾真治氏著 オーム社 (平成17年3月20日 第1版) ;おもしろいPICマイコン −PIC12F675 を使いこなす− ; ;固有定数のメモ:3474 ;固有定数のメモ:3468 ; ; LIST P=12F629 INCLUDE P12F629.INC CB = _CPD_OFF ;データEEPROMもプログラム・メモリも CB &= _CP_OFF ;コード・プロテクトしない。 CB &= _BODEN_ON ;ブラウンアウト・リセットを使う。 CB &= _MCLRE_OFF ;MCLRリセットを使わない。 CB &= _PWRTE_ON ;パワーアップ・タイマを使う。 CB &= _WDT_OFF ;ウォッチドッグ・タイマを使わない。 CB &= _INTRC_OSC_NOCLKOUT ;内部発振器を使う。 __CONFIG CB __IDLOCS H'0100' ;IDロケーションへ、バージョン1.00 CBLOCK H'20' GPIO_OUT ;GPIOポートの出力データ W_TEMP ;割込み処理用 S_TEMP ;割込み処理用 CCC ;カウンター CCC256 ;カウンター上位 PAD_MEMO ;パドルの状態をメモしておく(Hi アクティブ) ; ;bit1 1=ダッシュ が押された。 ; ;bit0 1=ドット が押された。 RUN_MEMO ;実行中の状態をメモ(Hi アクティブ) ;bit3 1=ダッシュ信号 0=ダッシュ後の間隔(1ドット分) ;bit2 1=ドット信号 0=ドット後の間隔(1ドット分) ;bit1 1=ダッシュ送出中 ;bit0 1=ドット送出中 ; ENDC ; GPIO_MODE EQU B'00001011' ; ;GP0-1.3 をインプット GP2.4-5 をアウトプット DEF_INTCON equ B'10010000' ;割り込みの設定。 ;bit7 GIE 割り込みを使用する=1 ;bit6 PIE1 レジスタの割り込みを使わないので=0 ;bit5 タイマ0オーバーフロー割り込みを使わないので=0 ;bit4 INTピン入力エッジ割り込みを使うので=1 ;bit3 ◆◆GPIO入力変化割り込みを使わないので=0 ;bit2 タイマ0オーバーフロー時=0 ;bit1 GP2/INTピン入力信号のエッジがあったときだけ=1 ;bit0 ◆◆GPIOポートで入力データが変化したときだけ=1 ; PAD_DOT EQU O'0' ;パドル:ドットのビット位置。 PAD_DAS EQU O'1' ;パドル:ダッシュのビット位置。 CR_BIT EQU O'2' ;外部でボリュームをつけている端子 ST_BIT EQU O'4' ;ボリュームにHを出力して、 ; ;その後Lowにする。 LED_BIT EQU O'5' ;LEDのビット DOT_STA EQU O'2' DAS_STA EQU O'3' ; TIME_1 EQU D'20' ;ドットの長さ TIME_3 EQU D'60' ;ダッシュの長さ(ドット×3倍) ; ;(例)1:3.4 にするなら、20 X 3.4 = 68 ; ; を記述する。 CCC_DEF equ D'240' ;タイミングカウンター下位の初期値 ; ;GP0=1 入力 パドル ドット ;GP1=1 入力 パドル ダッシュ ;GP2=1 出力 初期は出力で、1を出力指定しておく。 ; 外部のボリュームを読み込む時は、瞬間入力に設定して ; 放電時間経過後のLowエッジ割り込みを待つ。 ;GP3=1 入力 未使用 ;GP4=0 出力 未使用 ;GP5=0 出力 キー出力(ハイアクティブ) ;________________________ org H'0' ;電源onスタート goto START_DEF org 4 ;割り込み発生時の処理 ;////////////////////////////////////////////////////////////// movwf W_TEMP ;以下の4行は割込み処理前の定型処理。 movf STATUS,W ; clrf STATUS ; movwf S_TEMP ; ; ISR_INT: btfss INTCON,INTF ;INT入力エッジ割り込みか? goto ISR_INT_END ;それ以外なら、割り込みの終了へ行く。 ; bsf STATUS,RP0 ;バンク 1 にする。 bcf TRISIO,CR_BIT ;GP2ビットを出力に戻す。 ; (High=コンデンサへ充電) bcf STATUS,RP0 ;バンク 0 にする。 ;----------------------------充電のための時間稼ぎ(実質不要) nop ; goto $+1 ; goto $+1 nop ;----------------------------割込み発生の再スタート処理 bsf STATUS,RP0 ;バンク 1 にする。 bsf TRISIO,CR_BIT ;GP2ビットを一時的に入力にする。 bcf STATUS,RP0 ;バンク 0 にする。 ;----------------------------◆◆ここに割込み処理を書く incfsz CCC,F ;カウンター +1 goto ISR_INT_END incf CCC256,F movlw CCC_DEF movwf CCC ;◆タイミングカウンター下位を初期セット ; ;----------------------------◆◆ここまで割込み処理を書く ISR_INT_END: bcf INTCON,INTF ;INTF 入力エッジ割込みフラグを、 ;プログラムでクリヤーして復帰する。 movf S_TEMP,W ;以下の4行は割込み復帰の定型処理。 movwf STATUS ; swapf W_TEMP,F ; swapf W_TEMP,W ; retfie ;//////////////////////////////////////////////////////////// MAIN_LOOP0: bcf INTCON,INTF ;エッジ割込みフラグをクリヤー ;---------------------------- 割込み発生のスタート処理 bsf STATUS,RP0 ;バンク 1 にする。 bsf TRISIO,CR_BIT ;GP2ビットを一時的に入力にする。 bcf STATUS,RP0 ;バンク 0 にする。 MAIN_LOOP: ;-------------------------- パドル入力の確認 PAD_CHECK0: btfsc RUN_MEMO,PAD_DOT ;ドット送出中以外ならドットを確認 goto PAD_CHECK1 ;ダッシュチェックへ btfsc GPIO,PAD_DOT ;ドット側が押されたか? goto PAD_CHECK1 ;ダッシュチェックへ bsf PAD_MEMO,PAD_DOT ;パドルメモへドットをセット PAD_CHECK1: btfsc RUN_MEMO,PAD_DAS ;ダッシュ送出中以外ならダッシュを確認 goto PAD_CHECK_E ;パドル確認終了へ btfsc GPIO,PAD_DAS ;ダッシュ側が押されたか? goto PAD_CHECK_E bsf PAD_MEMO,PAD_DAS ;パドルメモへドットをセット PAD_CHECK_E: goto RUN_CHECK0 ;------------------------- 実行中の処理を確認 RUN_CHECK0: btfss RUN_MEMO,PAD_DOT ;ドット送出中か? goto RUN_CHECK1 ;次のチェックへ goto RUN_DOT_0 ;ドット送出処理へ行く。 RUN_CHECK1: btfss RUN_MEMO,PAD_DAS ;ダッシュ送出中か? goto RUN_CHECK_E ;次のチェックへ goto RUN_DAS_0 ;ダッシュ送出処理へ RUN_CHECK_E: call MEMO_DOT_CHECK ;サブルーチンでドット押下を確認 call MEMO_DAS_CHECK ;サブルーチンでダッシュ押下を確認 goto MAIN_LOOP_E ;ループ最終処理へ ; ;------------------------- RUN_DOT_0: ;ドット送出処理 btfss RUN_MEMO,DOT_STA ;信号部分か判定する。 goto RUN_DOT_1 movlw TIME_1 ;単位時間1を W へ呼び出す。 subwf CCC256,W ;CCC256カウンタからTIME_1(ドット時間)を引く。 ; ;結果を W レジスタに入れる。 btfss STATUS,C ;C=1 で、時間が経過した。 goto RUN_DOT_E call CNT_CLR ;タイミングカウンタークリヤー(初期化) call CW_OFF ;信号送出を止める。 bcf RUN_MEMO,DOT_STA ;状態を信号送出後の間隔時間にする。 goto RUN_DOT_E ;ループ最終処理へ RUN_DOT_1: movlw TIME_1 ;単位時間1を W へ呼び出す。 subwf CCC256,W ;CCC256カウンタからTIME_1(ドット時間)を引く。 ; ;結果を W レジスタに入れる。 btfss STATUS,C ;C=1 で、時間が経過した。 goto RUN_DOT_E call CNT_CLR ;タイミングカウンタークリヤー(初期化) bcf RUN_MEMO,PAD_DOT ;ドット送出の終了 bcf PAD_MEMO,PAD_DOT ;パドルのドットメモをクリヤー ; bcf PAD_MEMO,PAD_DAS ;◆これを有効にすると、アイアンビックの ; ; mode A になるはず。(もう一箇所有り) ; call MEMO_DAS_CHECK ;ドット処理直後は、 ; ;ダッシュの状態を優先確認する。 ; ;(アイアンピック動作) RUN_DOT_E: goto MAIN_LOOP_E ;------------------------- RUN_DAS_0: ;ダッシュ送出処理 btfss RUN_MEMO,DAS_STA ;信号部分か判定する。 goto RUN_DAS_1 ; ; ;---------------------------------- movlw TIME_1 ;単位時間 1 を W へ呼び出す。 subwf CCC256,W ;CCC256カウンタからTIME_1(ドット時間相当)を引く。 ; ;結果を W レジスタに入れる。 btfss STATUS,C ;C=1 で、時間が経過した。 bcf PAD_MEMO,PAD_DOT ;パドルのドットメモをクリヤ ;◆◆◆ダッシュの先頭から約1/3間は、 ;パドルのドットを記憶しない。 ; ;---------------------------------- ; movlw TIME_3 ;単位時間3を W へ呼び出す。 subwf CCC256,W ;CCC256カウンタからTIME_3(ダッシュ時間)を引く。 ; ;結果を W レジスタに入れる。 btfss STATUS,C ;C=1 で、時間が経過した。 goto RUN_DAS_E call CNT_CLR ;タイミングカウンタークリヤー(初期化) call CW_OFF ;信号送出を止める。 bcf RUN_MEMO,DAS_STA ;状態を信号送出後の間隔時間にする。 goto RUN_DAS_E ;ループ最終処理へ RUN_DAS_1: movlw TIME_1 ;単位時間1を W へ呼び出す。 subwf CCC256,W ;CCC256カウンタからTIME_1(ドット時間)を引く。 ; ;結果を W レジスタに入れる。 btfss STATUS,C ;C=1 で、時間が経過した。 goto RUN_DAS_E call CNT_CLR ;タイミングカウンタークリヤー(初期化) bcf RUN_MEMO,PAD_DAS ;ダッシュ送出の終了 bcf PAD_MEMO,PAD_DAS ;パドルのダッシュメモをクリヤー ; bcf PAD_MEMO,PAD_DOT ;◆これを有効にすると、アイアンビックの ; ; mode A になるはず。(もう一箇所有り) call MEMO_DOT_CHECK ;ダッシュ処理直後は、 ; ;ドットの状態を優先確認する。 ; ;(アイアンピック動作) RUN_DAS_E: goto MAIN_LOOP_E ;------------------------- nop ;無意味 nop ;無意味 nop ;無意味 MAIN_LOOP_E: sleep nop ;sleep のおまじない goto MAIN_LOOP START_DEF: ;------------------------------------------------------------------- bcf STATUS,RP0 ;バンク 0 にする。 call H'3FF' ;内部発振器をキャリブレーションする。 bsf STATUS,RP0 ;バンク 1 にする。 movwf OSCCAL bcf STATUS,RP0 ;バンク 0 にする。 movlw B'00000111' ;コンパレーターoffにする movwf CMCON bsf STATUS,RP0 ;バンク 1 にする。 ; clrf ANSEL ;GPIOポートをデジタルI/Oモードにする。 movlw GPIO_MODE ;先頭で定義 B'00001011';GP2 は出力指定。 movwf TRISIO movlw B'00000000' ;ウィーク・プルアップを使わない。 movwf WPU bcf OPTION_REG,INTEDG ;INTピン入力信号の ; ;立下りエッジ割込み(bank1) bcf STATUS,RP0 ;バンク 0 にする。 ;------------------------------------------------------------------- movlw B'00000100' ;GP2 だけはHighを出力指定。Sleep時に注意。 movwf GPIO_OUT ;GPIO_OUTの内容を初期化する (レジスター上のポート) call IOPORT ;初期値を出力ポートに書き込み(必須) ;------------------------------------------------------------------- ; call CNT_CLR ;タイミングカウンタークリヤー(初期化) clrf RUN_MEMO ;送出中信号メモをクリヤー clrf PAD_MEMO ;パドルの状態をクリヤー movlw DEF_INTCON ;割り込みのセット movwf INTCON goto MAIN_LOOP0 ;------------------------------------------------------------------- ; ;割込みタイミング用のカウンタクリヤー CNT_CLR: movlw CCC_DEF movwf CCC ;タイミングカウンターを初期セット clrf CCC256 ;タイミングカウンター上位を初期セット return ;------------------------------- ;何も実行中で無い時に実行する。 ; ;パドルメモの確認 MEMO_DOT_CHECK: btfss PAD_MEMO,PAD_DOT ;ドットが押されているか? goto MEMO_DOT_CHECK_E call CNT_CLR ;タイミングカウンタークリヤー(初期化) bsf RUN_MEMO,DOT_STA ;状態ビットの信号部分処理中にセット call CW_ON ;CW信号ON bsf RUN_MEMO,PAD_DOT ;ドット処理中をセット bcf PAD_MEMO,PAD_DOT ;ドットが押されていたメモをクリヤー MEMO_DOT_CHECK_E: return ;-------------------------------- MEMO_DAS_CHECK: btfss PAD_MEMO,PAD_DAS ;ダッシュが押されているか? goto MEMO_DAS_CHECK_E call CNT_CLR ;タイミングカウンタークリヤー(初期化) bsf RUN_MEMO,DAS_STA ;状態ビットの信号部分処理中にセット call CW_ON ;CW信号ON bsf RUN_MEMO,PAD_DAS ;ダッシュ処理中をセット bcf PAD_MEMO,PAD_DAS ;ダッシュが押されていたメモをクリヤー MEMO_DAS_CHECK_E: return ;------------------------------------------------------------------- CW_ON: ;CALL命令で呼ぶ:Key出力 ON bsf GPIO_OUT,LED_BIT goto IOPORT CW_OFF: ;CALL命令で呼ぶ:Key出力 OFF bcf GPIO_OUT,LED_BIT goto IOPORT IOPORT: ;I/Oピンの設定とデータ出力 movf GPIO_OUT,W ;GPIOに出力データを書く。 movwf GPIO return ;------------------------------------------------------------------- ;------------------------------------------------------------------- data '2','0','0','6','/','0','6','/','1','8',':' data '(','C',')','J','E','3','J','L','B',' ' ;------------------------------------------------------------------- org 3FFH ;念のため用意している、 dw 3468H ;チップ固有のキャリブレDATA。 end------------------------------------------------------------------------
ソースファイル key629.asm (2006/07/06)