electric keyer monitor
XilinxのXC9572が余っていたので、エレクトリックキーヤーの
動作をモニタするVHDLコードを作ってみた。
エレクトリックキーヤーは、アマチュア無線でモールス符号を
打ち出すとき、2つのスイッチで長点、短点に相当する時間分
接点を接続します。
単純に2つのスイッチで、接点の接続時間分を出すだけでは
確認できないので、サイドトーンと呼ぶ1kHz程度の周波数を
スピーカ接続し、モニタすることが多いです。
2入力1出力のデジタル回路とみなし、エレクトリックキーヤー
のモニタを設計する。
最初にブロック図を作成。
スイッチが押されたことを、シンクロナイザで判断し
シーケンサ(ステートマシン)で1kHzの矩形波を出力
するか否かを、時間長を含めて制御する。
出力側から回路を定義する。
buffer
外部クロックを分周して、1kHzクロックが
生成されていると仮定する。
イネーブル信号が与えられたなら、1kHzクロックを
外部に出力し、それ以外では'L'を出力。
組合わせ回路と考え、次のように定義する。
FOUT <= iCLK when ( iENABLE = '1' ) else '0' ;
sequencer+counter
シンクロナイザから、長点、短点のトリガーが与えられた
なら、カウンタに値を設定。カウンタが0でないときに
イネーブル信号を出力する。
状態は4つあればよいので、2ビットジョンソンカウンタで
ステートを回すシーケンサで対応する。
状態遷移図から、シーケンサを定義。
- トリガーを待つ。(トリガーがきたら、カウンタを設定し、1ステートへ)
- カウンタが0なら2ステートに、0より大きければデクリメント。
- カウンタをゼロクリア後、4ステートへ。
- 最初に戻る
上のステート処理を、VHDLで定義。
process (nRESET,iCLK)
begin
if ( nRESET = '0' ) then
iSTATE <= "00" ;
iSCNT <= 0 ;
elsif rising_edge(iCLK) then
case conv_integer(iSTATE) is
-- wait trigger
when 0 => if ( iSSW = '1' ) then
iSTATE <= "01" ;
iSCNT <= 100 ;
elsif ( iLSW = '1' ) then
iSTATE <= "01" ;
iSCNT <= 300 ;
else
iSTATE <= "00" ;
end if ;
-- judge or decrement
when 1 => if ( iSCNT = 0 ) then
iSTATE <= "11" ;
else
iSCNT <= iSCNT - 1 ;
iSTATE <= "01" ;
end if ;
-- clear counter
when 3 => iSCNT <= 0 ;
iSTATE <= "10" ;
-- return first state
when 2 => iSTATE <= "00" ;
-- default
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
iENABLE <= iSTATE(0) ;
無線機では、CWを出力の制御信号をモノラルタイプの
プラグで与えます。
CWの出力制御信号は、iSCNTの2ビット中の1ビットを
バッファを介して出力すれば充分。
2ビットジョンソンカウンタを使うと、バイナリーカウンタ
で必須のデコーダが不要になります。
ステートマシンでは、状態値が1、3で出力制御信号を印加。
synchronizer
スイッチはチャタリングを含んでいるので、シフトレジスタを
使い、3回スイッチ状態を読込み、011で押されたと判断。
シフトレジスタは、チャタリングを除去するとともに
信号のrising_edge、falling_edgeを検出する目的で
利用する。
250Hz(周期4ms)のクロックを利用し、スイッチが
押されたことを判断するVHDLコードは以下。
process (nRESET,iCLKX)
begin
if ( nRESET = '0' ) then
iLSW_SFT <= "000" ;
iSSW_SFT <= "000" ;
elsif rising_edge( iCLKX ) then
iLSW_SFT <= iLSW_SFT(1 downto 0) & (not LSW) ;
iSSW_SFT <= iSSW_SFT(1 downto 0) & (not SSW) ;
end if ;
end process ;
iLSW <= '1' when ( iLSW_SFT = "011" ) else '0' ;
iSSW <= '1' when ( iSSW_SFT = "011" ) else '0' ;
divider
外部クロックを、分周して1kHzと250Hzのクロックを
生成。外部クロックは4kHzとすると、カウンタを2
つ用意して、2つのクロック生成は、以下となる。
-- clock divider CLOCK = 4kHz
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iCNT <= 0 ;
elsif rising_edge( CLOCK ) then
if ( iCNT = 3 ) then
iCNT <= 0 ;
else
iCNT <= iCNT + 1 ;
end if ;
end if ;
end process ;
iCLK <= '1' when ( iCNT = 0 ) else '0' ; -- 1kHz
-- clock divider CLOCK = 1kHz
process (nRESET,iCLK)
begin
if ( nRESET = '0' ) then
iCNTX <= 0 ;
elsif rising_edge( iCLK ) then
if ( iCNT = 4 ) then
iCNTX <= 0 ;
else
iCNTX <= iCNTX + 1 ;
end if ;
end if ;
end process ;
iCLKX <= '1' when ( iCNTX = 0 ) else '0' ; -- 250Hz
まとめると、以下。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity ekey is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 4kHz
-- select switches
LSW : in std_logic ;
SSW : in std_logic ;
-- CW control
COUT : out std_logic ;
-- side monitor
FOUT : out std_logic -- ;
);
end ekey ;
architecture behavioral of ekey is
-- divider
signal iCLK : std_logic ;
signal iCNT : integer range 0 to 3 ;
signal iCLKX : std_logic ;
signal iCNTX : integer range 0 to 4 ;
-- trigger
signal iLSW : std_logic ;
signal iSSW : std_logic ;
signal iLSW_SFT : std_logic_vector(2 downto 0) ;
signal iSSW_SFT : std_logic_vector(2 downto 0) ;
-- sequencer
signal iSTATE : std_logic_vector(1 downto 0) ;
signal iSCNT : integer range 0 to 300 ;
signal iENABLE : std_logic ;
begin
-- outputs
FOUT <= iCLK when ( iENABLE = '1' ) else '0' ;
COUT <= iSTATE(0);
-- clock divider CLOCK = 4kHz
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iCNT <= 0 ;
elsif rising_edge( CLOCK ) then
if ( iCNT = 3 ) then
iCNT <= 0 ;
else
iCNT <= iCNT + 1 ;
end if ;
end if ;
end process ;
iCLK <= '1' when ( iCNT = 0 ) else '0' ; -- 1kHz
-- clock divider CLOCK = 1kHz
process (nRESET,iCLK)
begin
if ( nRESET = '0' ) then
iCNTX <= 0 ;
elsif rising_edge( iCLK ) then
if ( iCNT = 4 ) then
iCNTX <= 0 ;
else
iCNTX <= iCNTX + 1 ;
end if ;
end if ;
end process ;
iCLKX <= '1' when ( iCNTX = 0 ) else '0' ; -- 250Hz
-- synchronizer
process (nRESET,iCLKX)
begin
if ( nRESET = '0' ) then
iLSW_SFT <= "000" ;
iSSW_SFT <= "000" ;
elsif rising_edge( iCLKX ) then
iLSW_SFT <= iLSW_SFT(1 downto 0) & (not LSW) ;
iSSW_SFT <= iSSW_SFT(1 downto 0) & (not SSW) ;
end if ;
end process ;
iLSW <= '1' when ( iLSW_SFT = "011" ) else '0' ;
iSSW <= '1' when ( iSSW_SFT = "011" ) else '0' ;
-- sequencer+counter
process (nRESET,iCLK)
begin
if ( nRESET = '0' ) then
iSTATE <= "00" ;
iSCNT <= 0 ;
elsif rising_edge(iCLK) then
case conv_integer(iSTATE) is
-- wait trigger
when 0 => if ( iSSW = '1' ) then
iSTATE <= "01" ;
iSCNT <= 100 ;
elsif ( iLSW = '1' ) then
iSTATE <= "01" ;
iSCNT <= 300 ;
else
iSTATE <= "00" ;
end if ;
-- judge or decrement
when 1 => if ( iSCNT = 0 ) then
iSTATE <= "11" ;
else
iSCNT <= iSCNT - 1 ;
iSTATE <= "01" ;
end if ;
-- clear counter
when 3 => iSCNT <= 0 ;
iSTATE <= "10" ;
-- return first state
when 2 => iSTATE <= "00" ;
-- default
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
iENABLE <= iSTATE(0) ;
end behavioral;
接続を指定するためのUCFファイルを記述。
# system
NET "CLOCK" LOC = "P5" ;
NET "nRESET" LOC = "P39" ;
# control
NET "LSW" LOC = "P1" ;
NET "SSW" LOC = "P43" ;
# CW control
NET "COUT" LOC = "P2" ;
# frequency
NET "FOUT" LOC = "P26" ;
目次
前
次