Zynqでロボット制御2　RC受信器から信号取り込み

#RC受信器の信号
RC受信器から出力される信号は図１のような波形になります。
これって正式な名称あるんですかね？　便宜的にRC信号と呼ぶことにします。

![rc.jpg](https://qiita-image-store.s3.amazonaws.com/0/142745/f01c4373-3738-1c65-b321-949dadc1dc7d.jpeg)
図１　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

たいした分量じゃないので全文引用します。

```vhdl:TKRCV.vhd

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  : リセット信号（１でリセット）
iRCV  : RC信号
oRCV  : RC信号のhighレベル幅を-127～+127に変換した値

####■動作について
#####１．立ち上がり検出
if (iRCV = '1' and rIN = '0') then -- iRCV rising edge det　でRC信号の立ち上がり検出をしています。
立ち上がり検出は対象を１クロック遅らせた信号と比較することでできます。
RC信号の立ち上がりを検出したらステート（rSM をIDLEからCNT_A）を推移させます。
rCNT  <= (rCNT'high  downto 1 => '0') & '1';の記述は最下位以外を０、最下位を１とする記述です。

#####２．カウント動作
ステートが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を加算しています。

#####３．ゲート処理
gRCV <= "111111110"       when(rCNT2     < cRCV_LOW ) elseでrCNT2を正規化し、-127～+127に変換しています。

#####４．シミュレーション結果
![rc_robot_sim_rcrcv.jpg](https://qiita-image-store.s3.amazonaws.com/0/142745/f6c05e3c-0196-c322-6834-d38d12e7eef0.jpeg)
図２　シミュレーション結果

※シミュレーションはパルス間隔をつめてます。
　RC信号の立ち上がりで動作するため、パルス間隔が所定の長さである必要が無いためです。

#次回予告と雑記
###次回予告
次回はトップデザインの記述について書こうと思います。
VivadoのIP integraterで出力したラッパーと自前のVHDLをマージした部分を赤裸々に公開しようかと思います。
（すでにソースコードはあげていますが）

###雑記
RC信号を入出力するって結構難しいと思います。入力だけ／出力だけというのは簡単ですが、入出力をいっぺんに行う場合、FPGAのような各機能を独立して動かせるようなデバイスでないと大変です。
FPGAやZynqであれば、マイコンで言えばタイマーを使いまくるデザインについてはすごくすっきりします。

では次回。