#RC受信器の信号
RC受信器から出力される信号は図1のような波形になります。
これって正式な名称あるんですかね? 便宜的にRC信号と呼ぶことにします。
highレベルのパルス幅を1.5msを中心に-127~+127のバス信号に変換するのが今回の目的です。
ちなみに、RC受信器には電源として5Vを入れているのですが、使ってるRC受信器では出力信号のレベルは3.3Vでした。LDO的な電源レギュレータが入ってるんですかね。今回のZynqのように3.3Vを使うことが多いのでこれまた都合がよいです。
#ソースコード
ソースコードは以下のURLのどこかにあるTKRCV.vhdです。テストベンチはbench_TKRCV.vhdです。
https://github.com/x7700jp/x7700jp_codes
たいした分量じゃないので全文引用します。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity TKRCV is
generic (
CW : integer := 7 ; -- PERI_GEN cnt Width
CNT : integer := 110 -- 1.4ms/256/clk_peri
);
port (
iCLK : in std_logic;
iRST : in std_logic;
iRCV : in std_logic;
oRCV : out std_logic_vector(7 downto 0)
);
end TKRCV;
architecture RTL of TKRCV is
type tSM is (IDLE,CNT_A);
signal rSM : tSM := IDLE;
signal rCNT : std_logic_vector(CW-1 downto 0) := (others => '0');
signal rCNT2 : std_logic_vector( 8 downto 0) := (others => '0');
signal rRCV : std_logic_vector( 7 downto 0) := (others => '0');
signal gRCV : std_logic_vector( 8 downto 0) ;
signal rIN : std_logic := '0';
constant cRCV_ZERO : std_logic_vector(11 downto 0) := conv_std_logic_vector(273,9); -- 1500 us
constant cRCV_LOW : std_logic_vector(11 downto 0) := conv_std_logic_vector(146,9); -- 800 us
constant cRCV_HIGH : std_logic_vector(11 downto 0) := conv_std_logic_vector(400,9); -- 2200 us
begin
--
P_FF : process(iCLK) begin
if (iCLK'event and iCLK = '1') then
if (iRST = '1') then
rIN <= '0';
else
rIN <= iRCV;
end if;
end if;
end process;
P_CNT : process(iCLK) begin
if (iCLK'event and iCLK = '1') then
if (iRST = '1') then
rSM <= IDLE;
rCNT <= (others => '0');
rCNT2 <= (others => '0');
rRCV <= (others => '0');
else
case rSM is
when IDLE =>
if (iRCV = '1' and rIN = '0') then -- iRCV rising edge det
rSM <= CNT_A;
rCNT <= (rCNT'high downto 1 => '0') & '1';
rCNT2 <= (rCNT2'high downto 1 => '0') & '1';
else
rCNT <= (others => '0');
rCNT2 <= (others => '0');
end if;
when CNT_A =>
if (iRCV = '0' or cRCV_HIGH < rCNT ) then
rSM <= IDLE;
rRCV <= gRCV(7 downto 0);
else
if (rCNT = conv_std_logic_vector(CNT,CW)) then
rCNT <= (others => '0');
rCNT2 <= rCNT2 + '1';
else
rCNT <= rCNT + '1';
end if;
end if;
end case;
end if;
end if;
end process;
-- gate
gRCV <= "111111110" when(rCNT2 < cRCV_LOW ) else
"001111111" when(cRCV_HIGH < rCNT2 ) else
rCNT2 - cRCV_ZERO ;
-- output
oRCV <= rRCV;
end RTL;
####■generic値について
CNT:CNT=1.4ms/256/clk_periと設定します。(clk_periはクロックの周期)
CW:CNT値を表現できるビット数
####■ポートについて
iCLK : 入力クロック
iRST : リセット信号(1でリセット)
iRCV : RC信号
oRCV : RC信号のhighレベル幅を-127~+127に変換した値
####■動作について
#####1.立ち上がり検出
if (iRCV = '1' and rIN = '0') then -- iRCV rising edge det でRC信号の立ち上がり検出をしています。
立ち上がり検出は対象を1クロック遅らせた信号と比較することでできます。
RC信号の立ち上がりを検出したらステート(rSM をIDLEからCNT_A)を推移させます。
rCNT <= (rCNT'high downto 1 => '0') & '1';の記述は最下位以外を0、最下位を1とする記述です。
#####2.カウント動作
ステートがCNT_Aのときはカウント動作です。
if (iRCV = '0' or cRCV_HIGH < rCNT ) thenはRC信号が0になるか、タイムオーバーを見てます。
RC信号が0になった際に、計数している値をラッチして出力となります。
タイムオーバーはなんらかの不具合でRC信号が想定よりhigh時間が長い場合、タイムオーバーとします。RC信号の次の立ち上がりまではステートはIDLEになります。
if (rCNT = conv_std_logic_vector(CNT,CW)) thenではgeneric値のCNTで設定した間隔でrCNT2を加算しています。
#####3.ゲート処理
gRCV <= "111111110" when(rCNT2 < cRCV_LOW ) elseでrCNT2を正規化し、-127~+127に変換しています。
#####4.シミュレーション結果
図2 シミュレーション結果
※シミュレーションはパルス間隔をつめてます。
RC信号の立ち上がりで動作するため、パルス間隔が所定の長さである必要が無いためです。
#次回予告と雑記
###次回予告
次回はトップデザインの記述について書こうと思います。
VivadoのIP integraterで出力したラッパーと自前のVHDLをマージした部分を赤裸々に公開しようかと思います。
(すでにソースコードはあげていますが)
###雑記
RC信号を入出力するって結構難しいと思います。入力だけ/出力だけというのは簡単ですが、入出力をいっぺんに行う場合、FPGAのような各機能を独立して動かせるようなデバイスでないと大変です。
FPGAやZynqであれば、マイコンで言えばタイマーを使いまくるデザインについてはすごくすっきりします。
では次回。