FPGAにおけるRS232Cの設計

FPGAにおけるRS232Cの設計

 RS232Cの回路を実装するにあたり、FPGAではUARTのIPを各社が用意しているので、それを使うのが簡単です。しかし、IPだけではUARTの全機能を実装できないことが多く、その場合には周辺の回路を自作しなくてはなりません。

 UARTとRS232Cの違いを質問されることが多いのですが、UARTは機能の名前でRS232Cは規格の名前です。本来規格の方が厳しい仕様になっていますが、RS232Cの場合にはあまりにも普及しすぎて、本当の規格を満足して「RS232C」と呼んでいることは稀になってしまいました。したがって、現場ではRS232Cは信号レベルが5V/-5Vよりも大きい物。UARTはFPGA等のIOピン信号レベルの物。というように解釈することが多いようです。

 まず、UARTの仕様を決めます。ここでは、一般的と思われる仕様で設計を考えます。

  • ボーレート 115200bps
  • ハンドシェイク 無
  • データ 8bit
  • パリティ 無
  • ストップビット 1bit
  • 通信方式 ASCII

 この仕様でIPにパラメータを設定すればIPが作成されるので、その入出力に従った信号で接続すれば良いのですが、注意する点があります。

 ボーレートはFPGAの動作を決める必要があるので、コマンドの文字数と合わせて確認する必要があります。

 たとえば、ボーレートが115200bpsという事は1秒間に115200bit送信・受信するという事ですから、start+8bit+stopで10bitが1byteになりますので、1秒間に1152バイト送受信する計算になります。

 つまり、UARTのIPから1秒間に1152バイト出力される。という事になりますので、その速度を処理できるようにしておく必要があります。

 まず、FPGAに信号を入れる前に信号レベルの変換が必要になります。しかも、最近はパソコンにRS232Cポート(COMポート)が無いので、USBでCOMポートの拡張をする必要があります。レベル変換の様子を以下に示します。

 パソコンにRS232Cを増設するにはUSBのアダプタが市販されています。USBに接続するタイプが多いのですが、RS232C側はコネクタがいろいろとあるので、自分が使うタイプに合わせて選定してください。一般的なのはDサブ9ピンというタイプのコネクタです。

 次にレベル変換を行います。レベル変換は普通、FPGAの基板にICを実装します。MAXIM社やアナログデバイセズ社等多くの会社からリリースしていますので、基板の電源電圧やFPGAのIO電圧に合わせて選びようにします。

 FPGAの中ではIPの後にバイナリ<->ASCIIの変換回路を配置します。

ここで、ASCII通信の場合のコマンド通信がどのようになっているのかを説明しておきます。

 上の図はCPUからATDP 117[CR][LF]が送られた時の通信を示しています。このようにASCII通信の場合、ASCIIコード表の16進数が通信上に流れています。それゆえ、ASCIIコードを元に戻す必要があります。

 コマンドがATDPだけしか無い。というのであれば、41(hex),54(hex),
44(hex),50(hex)だけを監視していれば良いので、わざわざASCIIにコードをバイナリにする必要が無いかも知れませんが、数値である1,1,7は数値としてFPGAの中で処理したくなりますので、31(hex)が来ても困ります。なので、31(ASCII:hex)を01(binary:hex)に直す。という回路が必要になります。

 このように全部のASCII文字をバイナリに変換しても良いのですが、数値だけを変換する。という方法もあります。

 数値だけを変換したい。という事であれば、ASCIIコード表を見ていただくとわかりますが、数値は30~39(hex)です。30(hex)を減算するという方法もありますが、このような場合にはテーブルで変換してしまった方が簡単です。

	Hexout	<=	"0000" when ( ASCIIin = X"30" ) else		-- "0"
				"0001" when ( ASCIIin = X"31" ) else		-- "1"
				"0010" when ( ASCIIin = X"32" ) else		-- "2"
				"0011" when ( ASCIIin = X"33" ) else		-- "3"
				"0100" when ( ASCIIin = X"34" ) else		-- "4"
				"0101" when ( ASCIIin = X"35" ) else		-- "5"
				"0110" when ( ASCIIin = X"36" ) else		-- "6"
				"0111" when ( ASCIIin = X"37" ) else		-- "7"
				"1000" when ( ASCIIin = X"38" ) else		-- "8"
				"1001" when ( ASCIIin = X"39" ) else		-- "9"
				"1010" when ( ASCIIin = X"41" ) else		-- "A"
				"1011" when ( ASCIIin = X"42" ) else		-- "B"
				"1100" when ( ASCIIin = X"43" ) else		-- "C"
				"1101" when ( ASCIIin = X"44" ) else		-- "D"
				"1110" when ( ASCIIin = X"45" ) else		-- "E"
				"1111" when ( ASCIIin = X"46" ) else		-- "F"
				"1010" when ( ASCIIin = X"61" ) else		-- "a"
				"1011" when ( ASCIIin = X"62" ) else		-- "b"
				"1100" when ( ASCIIin = X"63" ) else		-- "c"
				"1101" when ( ASCIIin = X"64" ) else		-- "d"
				"1110" when ( ASCIIin = X"65" ) else		-- "e"
				"1111" when ( ASCIIin = X"66" ) else		-- "f"
				"1111" ;

	ASCIIOutL				<=	X"30" when ( HexIn(3 downto 0) = X"0" ) else	--	0
                                X"31" when ( HexIn(3 downto 0) = X"1" ) else	--	1
                                X"32" when ( HexIn(3 downto 0) = X"2" ) else	--	2
                                X"33" when ( HexIn(3 downto 0) = X"3" ) else	--	3
                                X"34" when ( HexIn(3 downto 0) = X"4" ) else	--	4
                                X"35" when ( HexIn(3 downto 0) = X"5" ) else	--	5
                                X"36" when ( HexIn(3 downto 0) = X"6" ) else	--	6
                                X"37" when ( HexIn(3 downto 0) = X"7" ) else	--	7
                                X"38" when ( HexIn(3 downto 0) = X"8" ) else	--	8
                                X"39" when ( HexIn(3 downto 0) = X"9" ) else	--	9
                                X"41" when ( HexIn(3 downto 0) = X"A" ) else	--	A
                                X"42" when ( HexIn(3 downto 0) = X"B" ) else	--	B
                                X"43" when ( HexIn(3 downto 0) = X"C" ) else	--	C
                                X"44" when ( HexIn(3 downto 0) = X"D" ) else	--	D
                                X"45" when ( HexIn(3 downto 0) = X"E" ) else	--	E
			                    X"46" when ( HexIn(3 downto 0) = X"F" ) else	--	F
								X"30" ;

 このようなテーブルで変換してしまえば、16進数で使う数値だけを変換することができます。もちろん、コマンドの中身をキチンと解釈する必要がありますので、上記の回路の他にコマンド部分を分解したり、解釈する回路が必要になりますので、工夫してみてください。