目次
前
次
インタフェース構成
マイクロコンピュータとFPGAを接続し、必要な
処理をFPGAの中に作りこんだデジタル回路で
処理します。
マイクロコンピュータには、Arduinoを使います。
FPGAを接続する場合、汎用シリアルインタフェースを
採用する方が楽です。
Arduinoで使える汎用シリアルインタフェースには
IIC、SPI、Firmataがあります。
IICは、2ピンで処理できますが、データ線を
入力と出力の入替えが必要になるので、FPGAの
VHDLコードを記述するのが面倒。
SPIでArduinoとFPGAを接続します。
Arduinoは、SPIのマスターで利用し、DCモータの
DUTY比を転送します。1バイト構成は、以下。
DUTY比は0から99で、7ビットでまとめられること
から、1バイト中のMSB(最上位ビット)を、チャネル
指定に利用します。0で右、1で左とします。
センサーデータは8ビットとし、ニブルで2回
入力します。
Arduinoは、利用できるピン数が少ないのでニブル
で扱います。
BarCodeScanner処理は、FPGA内部のデジタル回路に
一任し、Arduinoはセンサーデータを使うことに専念
します。
スタートゲートの開閉で、動作を開始するので
超音波センサーを利用します。
ゲートの開閉状態を、論理値でFPGAから出力して
マイコンが使えるようにします。
センサーデータ処理
Arduinoからみてセンサーデータは8ビットですが
ピン数制約から、ニブルで2回入力します。
ArduinoのポートCを使います。
ポートCはアナログで使うのが基本ですが、今回は
デジタルで利用します。
信号割当ては、次のようにします。
- PC5(input) start gate status
- PC4(output) nibble selector
- PC3(input) sensor data
- PC2(input) sensor data
- PC1(input) sensor data
- PC0(input) sensor data
センサーデータを入力する関数を定義します。
#define SEL_BIT 4
byte get_sensor(void)
{
byte result ;
byte dh ;
byte dl ;
/* defalt */
result = 0 ;
/* get upper nibble */
PORTC |= (1 << SEL_BIT);
dh = PINC & 15 ;
dh <<= 4 ;
/* get lower nibble */
PORTC &= ~(1 << SEL_BIT);
dl = PINC & 15 ;
/* concatenate */
result = dh | dl ;
return result ;
}
ポートCの初期化は、以下。
PORTC = 0xff ;
DDRC = 0x10 ;
BCSとFPGAのインタフェースでは、CLKAとCLKBを
論理和でまとめて、入力します。
VHDLコードは、以下。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity bcs is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- BCS
START : in std_logic ;
CLKAB : in std_logic ;
DOS : in std_logic ;
-- monitor
MLOUT : out std_logic_vector(7 downto 0) ;
-- selector
SEL : in std_logic ;
-- register output
REGOUT : out std_logic_vector(3 downto 0) --;
);
end bcs ;
architecture behavioral of bcs is
-- values
CONSTANT SCNTMAX : integer := 2081 ;
CONSTANT SCNTXMAX : integer := 2048 ;
CONSTANT SCNTFINE : integer := 2080 ;
CONSTANT SCNTFIRST : integer := 33 ;
-- latch location
CONSTANT SCNTLATCH0 : integer := 128 ;
CONSTANT SCNTLATCH1 : integer := 384 ;
CONSTANT SCNTLATCH2 : integer := 640 ;
CONSTANT SCNTLATCH3 : integer := 896 ;
CONSTANT SCNTLATCH4 : integer := 1152 ;
CONSTANT SCNTLATCH5 : integer := 1408 ;
CONSTANT SCNTLATCH6 : integer := 1664 ;
CONSTANT SCNTLATCH7 : integer := 1920 ;
-- BUS interface
signal iSTART : std_logic ;
signal iDOS : std_logic ;
-- internal counter
signal iSCNT : integer range 0 to SCNTMAX ;
signal iSCNTX : integer range 0 to SCNTXMAX ;
signal iSTRG : std_logic ;
-- synchronizer
signal iSTART_SFT : std_logic_vector(2 downto 0) ;
signal iSTART_TRG : std_logic ;
signal iCLKAB_SFT : std_logic_vector(2 downto 0) ;
signal iCLKAB_TRG : std_logic ;
-- internal shift register
signal iREG_SFT : std_logic_vector(7 downto 0) ;
-- internal register
signal iSEL : std_logic ;
signal iREG : std_logic_vector(7 downto 0) ;
-- sequencer
signal iSTATE : integer range 0 to 3 ;
begin
-- input
iSTART <= START ;
iDOS <= DOS ;
iSEL <= SEL ;
-- output
REGOUT <= iREG(7 downto 4) when ( iSEL = '1' ) else iREG(3 downto 0) ;
MLOUT <= not iREG ;
-- internal counter
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSCNT <= 0 ;
iSCNTX <= 0 ;
elsif rising_edge(CLOCK) then
if ( iCLKAB_TRG = '1' ) then
-- total counter
if ( iSTART = '1' or iSCNT = SCNTMAX ) then
iSCNT <= 0 ;
else
iSCNT <= iSCNT + 1 ;
end if ;
-- sensing counter
if ( iSTART = '1' or iSCNT < SCNTFIRST ) then
iSCNTX <= 0 ;
else
iSCNTX <= iSCNTX + 1 ;
end if ;
end if ;
end if ;
end process ;
iSTRG <= '1' when ( iSCNTX = SCNTLATCH0 ) else
'1' when ( iSCNTX = SCNTLATCH1 ) else
'1' when ( iSCNTX = SCNTLATCH2 ) else
'1' when ( iSCNTX = SCNTLATCH3 ) else
'1' when ( iSCNTX = SCNTLATCH4 ) else
'1' when ( iSCNTX = SCNTLATCH5 ) else
'1' when ( iSCNTX = SCNTLATCH6 ) else
'1' when ( iSCNTX = SCNTLATCH7 ) else
'0' ;
-- synchronizer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTART_SFT <= "000" ;
iCLKAB_SFT <= "000" ;
elsif rising_edge(CLOCK) then
iSTART_SFT <= iSTART_SFT(1 downto 0) & iSTART ;
iCLKAB_SFT <= iCLKAB_SFT(1 downto 0) & CLKAB ;
end if ;
end process ;
iSTART_TRG <= '1' when ( iSTART_SFT = "011" ) else '0' ;
iCLKAB_TRG <= '1' when ( iCLKAB_SFT = "011" ) else '0' ;
-- internal register
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iREG_SFT <= X"00" ;
elsif rising_edge(CLOCK) then
if ( iSTRG = '1' ) then
iREG_SFT <= iREG_SFT(6 downto 0) & iDOS ;
end if ;
end if ;
end process ;
-- sequencer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= 0 ;
iREG <= X"00" ;
elsif rising_edge(CLOCK) then
case iSTATE is
-- wait trigger
when 0 => if ( iSTART_TRG = '1' ) then
iSTATE <= 1 ;
else
iSTATE <= 0 ;
end if ;
-- complete data store
when 1 => if ( iSCNT = SCNTFINE ) then
iSTATE <= 3 ;
else
iSTATE <= 1 ;
end if ;
-- copy
when 3 => iSTATE <= 2 ;
iREG <= iREG_SFT ;
-- return first state
when 2 => iSTATE <= 0 ;
-- default
when others =>
iSTATE <= 0 ;
end case ;
end if ;
end process ;
end behavioral;
FPGAには、Lattice SemiconductorのMachXO2を利用します。
スタートゲート処理
スタートゲートの開閉状態は、超音波センサーで
取得します。
FPGAが1ビットで状態値を出力するとして
Arudinoで利用する関数を定義します。
#define OPENED 0
#define CLOSED 1
#define START_BIT 5
byte get_start_status(void)
{
byte result ;
/* defalt */
result = OPENED ;
/* judge */
if ( PIND & (1 << START_BIT) ) { result = CLOSED ; }
return result ;
}
FPGA内に格納する回路のVHDLコードは、以下。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity usensor is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- sensor handler signal
UTRG : out std_logic ; -- TRG
URES : in std_logic ; -- ECHO
-- gate status (0:close 1:open)
GSTATE : out std_logic --;
);
end usensor ;
architecture behavioral of usensor is
-- values
CONSTANT SCNTMAX : integer := 65999 ; -- 60000us
CONSTANT TRGMAX : integer := 110 ; -- 10us
CONSTANT BFMAX : integer := 7000 ; -- > 200mm
CONSTANT DIFFMAX : integer := 6468 ; -- 200mm
CONSTANT DISMAX : integer := 12936 ; -- 400mm
-- 60ms generator
signal iSCNT : integer range 0 to SCNTMAX ;
-- trigger
signal iUTRG : std_logic ;
-- echo
signal iURES_SFT : std_logic_vector(2 downto 0) ;
signal iURES_HL : std_logic ;
signal iURES_LH : std_logic ;
-- sequencer
signal iSTATE : std_logic_vector(2 downto 0) ;
signal iBEGINX : integer range 0 to BFMAX ;
signal iFINEX : integer range 0 to BFMAX ;
signal iDIFF : integer range 0 to BFMAX ;
signal iGSTATE : std_logic ;
begin
-- output
UTRG <= iUTRG ;
GSTATE <= iGSTATE ;
-- counter
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSCNT <= 0 ;
elsif rising_edge(CLOCK) then
if ( iSCNT = SCNTMAX ) then
iSCNT <= 0 ;
else
iSCNT <= iSCNT + 1 ;
end if ;
end if ;
end process ;
iUTRG <= '1' when ( iSCNT < TRGMAX ) else '0' ;
-- synchronizer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iURES_SFT <= "000" ;
elsif rising_edge(CLOCK) then
iURES_SFT <= iURES_SFT(1 downto 0) & URES ;
end if ;
end process ;
iURES_LH <= '1' when ( iURES_SFT = "011" ) else '0' ;
iURES_HL <= '1' when ( iURES_SFT = "110" ) else '0' ;
-- sequencer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= "000" ;
iBEGINX <= 0 ;
iFINEX <= 0 ;
iDIFF <= 0 ;
elsif falling_edge(CLOCK) then
case conv_integer(iSTATE) is
-- clear counter
when 0 => if ( iSCNT = TRGMAX ) then
iSTATE <= "001" ;
iBEGINX <= 0 ;
iFINEX <= 0 ;
iDIFF <= 0 ;
else
iSTATE <= "000" ;
end if ;
-- wait trigger and store begin count
when 1 => if ( iURES_LH = '1' ) then
iSTATE <= "011" ;
iBEGINX <= iSCNT ;
elsif ( iSCNT > DISMAX ) then
iSTATE <= "100" ;
else
iSTATE <= "001" ;
end if ;
-- skip
when 3 => iSTATE <= "111" ;
-- wait trigger and store fine count
when 7 => if ( iURES_HL = '1' ) then
iSTATE <= "110" ;
iFINEX <= iSCNT ;
else
iSTATE <= "111" ;
end if ;
-- calculate
when 6 => iSTATE <= "100" ;
iDIFF <= (iFINEX - iBEGINX) ;
-- return first state
when 4 => iSTATE <= "000" ;
-- default
when others =>
iSTATE <= "000" ;
end case ;
end if ;
end process ;
iGSTATE <= '1' when ( iDIFF < DIFFMAX ) else '0' ;
end behavioral;
モータ制御
モータ制御はMOSFETを2つ接続し、パルスを
与えます。
MOSFETのゲートには、4V以上の電圧が必要なので
フォトカプラを入れます。CPLD/FPGAは3.3V動作
なので、波高値を大きくします。
FPGAは、SPIでDUTY比を受取り、LEFT、RIGHTに
パルスを出力します。
VHDLコードは、以下。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity xspi is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- SPI
SCK : in std_logic ;
nSS : in std_logic ;
MOSI : in std_logic ;
MISO : out std_logic ;
-- pulse out
LPVAL : out std_logic_vector(6 downto 0) ;
RPVAL : out std_logic_vector(6 downto 0) --;
);
end xspi ;
architecture behavioral of xspi is
-- BUS interface
signal iSS : std_logic ;
signal iMOSI : std_logic ;
-- synchronizer
signal iSS_SFT : std_logic_vector(2 downto 0) ;
signal iSCK_SFT : std_logic_vector(1 downto 0) ;
signal iSTRG : std_logic ;
signal iSCK_TRG : std_logic ;
-- internal shift register
signal iMOSI_SFT : std_logic_vector(7 downto 0) ;
-- internal register
signal iREG : std_logic_vector(7 downto 0) ;
signal iLEFT : std_logic_vector(6 downto 0) ;
signal iRIGHT : std_logic_vector(6 downto 0) ;
-- sequencer
signal iSTATE : std_logic_vector(1 downto 0) ;
begin
-- input
iSS <= not nSS ;
iMOSI <= MOSI ;
-- output
MISO <= iMOSI_SFT(7) ;
LPVAL <= iLEFT ;
RPVAL <= iRIGHT ;
-- synchronizer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSS_SFT <= "000" ;
iSCK_SFT <= "00" ;
elsif rising_edge(CLOCK) then
iSS_SFT <= iSS_SFT(1 downto 0) & iSS ;
iSCK_SFT <= iSCK_SFT(0) & SCK ;
end if ;
end process ;
iSTRG <= '1' when ( iSS_SFT = "110" ) else '0' ;
iSCK_TRG <= '1' when ( iSCK_SFT = "01" ) else '0' ;
-- internal register
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iMOSI_SFT <= X"00" ;
elsif rising_edge(CLOCK) then
if ( iSS = '1' and iSCK_TRG = '1' ) then
iMOSI_SFT <= iMOSI_SFT(6 downto 0) & iMOSI ;
end if ;
end if ;
end process ;
-- sequencer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= "00" ;
iREG <= X"00" ;
iLEFT <= "0000000" ;
iRIGHT <= "0000000" ;
elsif rising_edge(CLOCK) then
case conv_integer(iSTATE) is
-- wait trigger
when 0 => if ( iSTRG = '1' ) then
iSTATE <= "01" ;
iREG <= iMOSI_SFT ;
else
iSTATE <= "00" ;
end if ;
-- deliver
when 1 => iSTATE <= "11" ;
if ( iREG(7) = '1' ) then -- left register
iLEFT <= iREG(6 downto 0) ;
else
iRIGHT <= iREG(6 downto 0) ;
end if ;
-- clear register
when 3 => iSTATE <= "10" ;
iREG <= X"00" ;
-- return first state
when 2 => iSTATE <= "10" ;
-- default
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
end behavioral;
Arduinoは、SPIマスターとして左右のDUTY比を
FPGAに与えます。
SPIマスターにするとともに、シリアルデータを
MSBから出力するかLSBからにするかを決めます。
また、SCKの周波数を設定します。
MSBからデータ出力し、SCKをシステムクロックの
1/2に設定します。setup中で、パラメータ設定。
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE1);
SPI.setClockDivider(SPI_CLOCK_DIV2);
左右のDUTY比を一度に転送するため
ラッパー関数を用意します。
#define MASK7F 0x7f
void set_duty(byte lx,byte rx)
{
byte xleft ;
byte xright ;
/* generate code */
xleft = 0x80 | (lx & MASK7F) ;
xright = (rx & MASK7F) ;
/* left motor duty ratio */
SPI.transfer( xleft );
/* right motor duty ratio */
SPI.transfer( xright );
}
Arduinoスケッチ
移動処理を除いたArduinoスケッチを作成します。
シリアルインタフェースで、FPGAのテストが
できるようにしておきます。
コマンドインタプリタで利用する1文字コマンド
を定義します。
- ? help
- L led check
- P set both duty ratios
- S show system status
- G stat gate state
- D show sensor data
これらのコマンドは、Arduinoと接続するPersonalComputerで
pythonを利用したGUIで操作します。
スケッチは、以下。
#include <MsTimer2.h>
#include <SPI.h>
#define OFF 0
#define ON OFF+1
#define LED_BIT 1
#define SEL_BIT 4
#define START_BIT 5
#define OPENED 0
#define CLOSED 1
#define SYSMODE_IDLE 0
#define SYSMODE_RUN 1
#define NORMAL 0
#define CRANK NORMAL+1
#define LANE NORMAL+2
#define NONE 0
#define RIGHT_WHITE NONE+1
#define LEFT_WHITE NONE+2
#define ALL_WHITE NONE+3
#define MASK7F 0x7f
/* variables */
byte strg ;
byte strg_sft ;
byte sysmode ;
byte mode ;
byte wcondition ;
byte sensor ;
byte sindex ;
byte sbuf[16] ;
byte sflag ;
byte lduty ;
byte rduty ;
byte sensorx ;
byte msg[8] ;
/* function prototype */
void update_trigger();
void show_help();
void set_led(byte x);
void set_duty(byte lx,byte rx);
byte get_hex(byte x);
byte get_sensor();
byte get_start_status();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void setup() {
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
sflag = OFF ;
/* initialize port values */
PORTB = 0xfd ;
PORTC = 0xff ;
PORTD = 0xff ;
/* initialize port direction */
DDRB = 0xfe ;
DDRC = 0x10 ;
DDRD = 0xce ;
/* set system mode */
sysmode = SYSMODE_IDLE ;
/* set mode */
mode = NORMAL ;
wcondition = NONE ;
/* clear shift register */
strg_sft = 0 ;
/* 100ms period */
MsTimer2::set(100,update_trigger);
/* enable */
MsTimer2::start();
/* initialize SPI interface */
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE1);
SPI.setClockDivider(SPI_CLOCK_DIV2);
set_duty(0,0);
}
void loop()
{
byte cmd ;
byte i ;
/* get sensor data */
sensor = get_sensor();
/* start trigger handling */
if ( strg == ON ) {
/* clear flag */
strg = OFF ;
/* change system mode */
if ( sysmode == SYSMODE_IDLE ) {
sysmode = SYSMODE_RUN ;
/* disable timer2 */
MsTimer2::stop();
}
}
/* start gate handling */
if ( get_start_status() == OPENED ) {
/* change system mode */
if ( sysmode == SYSMODE_IDLE ) {
sysmode = SYSMODE_RUN ;
/* disable timer2 */
MsTimer2::stop();
}
}
/* serial handling */
if ( sflag == ON ) {
/* clear flag */
sflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* check LED */
if ( cmd == 'L' ) {
if ( *(sbuf+1) == '1' ) { set_led(ON) ; }
else { set_led(OFF); }
}
/* check motor */
if ( cmd == 'P' ) {
/* get left duty */
lduty = get_hex( *(sbuf+1) ) ;
lduty *= 10 ;
lduty += get_hex( *(sbuf+2) ) ;
/* get right duty */
rduty = get_hex( *(sbuf+3) ) ;
rduty *= 10 ;
rduty += get_hex( *(sbuf+4) ) ;
/* impress */
set_duty(lduty,rduty);
}
/* system status */
if ( cmd == 'S' ) {
if ( sysmode == SYSMODE_RUN ) { rs_puts("RUN"); }
else { rs_puts("IDLE"); }
crlf();
}
/* start gate state */
if ( cmd == 'G' ) {
if ( get_start_status() == OPENED ) { rs_puts("OPNED"); }
else { rs_puts("CLOSED"); }
crlf();
}
/* show sensor data */
if ( cmd == 'D' ) {
sensorx = sensor ;
/* separate */
for ( i = 0 ; i < 8 ; i++ ) {
/* get bit datum */
*(msg+7-i) = (sensorx & ON) + '0' ;
/* shift */
sensorx >>= 1 ;
}
/* show */
for ( i = 0 ; i < 8 ; i++ ) {
rs_putchar( *(msg+i) );
}
crlf();
}
}
/* run */
if ( sysmode == SYSMODE_RUN ) {
}
}
void update_trigger()
{
/* shift */
strg_sft <<= 1 ;
/* mask */
strg_sft &= 0x03 ;
/* judge trigger */
if ( (PINB & ON) == OFF ) { strg = ON ; }
}
void show_help()
{
rs_puts("? help"); crlf();
rs_puts("L led check"); crlf();
rs_puts("P set motor duty ratio"); crlf();
rs_puts("S show system status"); crlf();
rs_puts("G stat gate state"); crlf();
rs_puts("D show sensor data"); crlf();
}
byte get_sensor()
{
byte result ;
byte dh ;
byte dl ;
/* defalt */
result = 0 ;
/* get upper nibble */
PORTC |= (1 << SEL_BIT);
dh = PINC & 0x0f ;
dh <<= 4 ;
dh &= 0xf0 ;
/* get lower nibble */
PORTC &= ~(1 << SEL_BIT);
dl = PINC & 0x0f ;
/* concatenate */
result = dh | dl ;
return result ;
}
byte get_start_status()
{
byte result ;
/* defalt */
result = OPENED ;
/* judge */
if ( PIND & (1 << START_BIT) ) { result = CLOSED ; }
return result ;
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
sflag = ON ;
}
}
}
void set_led(byte x)
{
/* turn off */
PORTB &= ~(1 << LED_BIT) ;
/* turn on */
if ( x ) { PORTB |= (1 << LED_BIT) ; }
}
void set_duty(byte lx,byte rx)
{
byte xleft ;
byte xright ;
/* generate code */
xleft = 0x80 | (lx & MASK7F) ;
xright = (rx & MASK7F) ;
/* left motor duty ratio */
SPI.transfer( xleft );
/* right motor duty ratio */
SPI.transfer( xright );
}
byte get_hex(byte x)
{
byte result ;
/* default */
result = 0 ;
/* judge */
if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }
return result ;
}
void rs_putchar(char x)
{
Serial.write( x ) ;
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++ ;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
VHDLコード
FPGAであるLattice Mach XO2のVHDLコードを
まとめます。
最上位のVHDLコードを、mcr2015としておき
他をコンポーネントで取り込んで使います。
コンポーネントにしたVHDLコードは、以下。
- usensor.vhd
- bcs.vhd
- pout.vhd
- xspi.vhd
順番にリストを書いてみます。
mcr2015.vhdソースコード
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity mcr2015 is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- ultrasonic sensor
TRG : out std_logic ;
ECHO : in std_logic ;
SGATE : out std_logic ;
-- motor drive
LEFTP : out std_logic ;
RIGHTP : out std_logic ;
-- SPI interface
XSCK : in std_logic ;
XMOSI : in std_logic ;
XMISO : out std_logic ;
nXSS : in std_logic ;
-- Bar Code Scanner
START : in std_logic ;
DOS : in std_logic ;
CLKAB : in std_logic ;
LOUT : out std_logic_vector(7 downto 0) ;
-- Sensor data output (Arduino)
SEL : in std_logic ;
SDAT : out std_logic_vector(3 downto 0) --;
);
end mcr2015 ;
architecture behavior of mcr2015 is
--
component usensor
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- sensor handler signal
UTRG : out std_logic ; -- TRG
URES : in std_logic ; -- ECHO
-- gate status (0:close 1:open)
GSTATE : out std_logic --;
);
end component ;
-- motor drive
signal iLEFT : std_logic_vector(6 downto 0) ;
signal iRIGHT : std_logic_vector(6 downto 0) ;
component pout
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- sensor handler signal
LEFTV : in std_logic_vector(6 downto 0) ;
RIGHTV : in std_logic_vector(6 downto 0) ;
-- pulse out
LPOUT : out std_logic ;
RPOUT : out std_logic --;
);
end component ;
-- SPI interface
component xspi is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- SPI
SCK : in std_logic ;
nSS : in std_logic ;
MOSI : in std_logic ;
MISO : out std_logic ;
-- pulse out
LPVAL : out std_logic_vector(6 downto 0) ;
RPVAL : out std_logic_vector(6 downto 0) --;
);
end component ;
component bcs
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- BCS
START : in std_logic ;
CLKAB : in std_logic ;
DOS : in std_logic ;
-- monitor
MLOUT : out std_logic_vector(7 downto 0) ;
-- selector
SEL : in std_logic ;
-- register output
REGOUT : out std_logic_vector(3 downto 0) --;
);
end component ;
begin
-- ultrasonic
USENSORInst : usensor
port map (
-- system
nRESET => nRESET ,
CLOCK => CLOCK ,
-- sensor handler signal
UTRG => TRG ,
URES => ECHO ,
-- gate status (1:close 0:open)
GSTATE => SGATE
);
-- pulse out
PULSEOUTInst : pout
port map (
-- system
nRESET => nRESET ,
CLOCK => CLOCK ,
-- sensor handler signal
LEFTV => iLEFT ,
RIGHTV => iRIGHT ,
-- pulse out
LPOUT => LEFTP ,
RPOUT => RIGHTP -- ;
);
-- SPI interface
XSPIinst : xspi
port map (
-- system
nRESET => nRESET ,
CLOCK => CLOCK ,
-- SPI
SCK => XSCK ,
nSS => nXSS ,
MOSI => XMOSI ,
MISO => XMISO ,
-- pulse out
LPVAL => iLEFT ,
RPVAL => iRIGHT
);
BCSInst : bcs
port map (
-- system
nRESET => nRESET ,
CLOCK => CLOCK ,
-- BCS
START => START ,
CLKAB => CLKAB ,
DOS => DOS ,
-- monitor
MLOUT => LOUT ,
-- selector
SEL => SEL ,
-- register output
REGOUT => SDAT
);
end behavior;
超音波センサをスタートゲートの開閉に利用します。
usensor.vhdソースコード
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity usensor is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- sensor handler signal
UTRG : out std_logic ; -- TRG
URES : in std_logic ; -- ECHO
-- gate status (0:close 1:open)
GSTATE : out std_logic --;
);
end usensor ;
architecture behavioral of usensor is
-- values
CONSTANT SCNTMAX : integer := 65999 ; -- 60000us
CONSTANT TRGMAX : integer := 110 ; -- 10us
CONSTANT BFMAX : integer := 7000 ; -- > 200mm
CONSTANT DIFFMAX : integer := 6468 ; -- 200mm
CONSTANT DISMAX : integer := 12936 ; -- 400mm
-- 60ms generator
signal iSCNT : integer range 0 to SCNTMAX ;
-- trigger
signal iUTRG : std_logic ;
-- echo
signal iURES_SFT : std_logic_vector(2 downto 0) ;
signal iURES_HL : std_logic ;
signal iURES_LH : std_logic ;
-- sequencer
signal iSTATE : std_logic_vector(2 downto 0) ;
signal iBEGINX : integer range 0 to BFMAX ;
signal iFINEX : integer range 0 to BFMAX ;
signal iDIFF : integer range 0 to BFMAX ;
signal iGSTATE : std_logic ;
begin
-- output
UTRG <= iUTRG ;
GSTATE <= iGSTATE ;
-- counter
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSCNT <= 0 ;
elsif rising_edge(CLOCK) then
if ( iSCNT = SCNTMAX ) then
iSCNT <= 0 ;
else
iSCNT <= iSCNT + 1 ;
end if ;
end if ;
end process ;
iUTRG <= '1' when ( iSCNT < TRGMAX ) else '0' ;
-- synchronizer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iURES_SFT <= "000" ;
elsif rising_edge(CLOCK) then
iURES_SFT <= iURES_SFT(1 downto 0) & URES ;
end if ;
end process ;
iURES_LH <= '1' when ( iURES_SFT = "011" ) else '0' ;
iURES_HL <= '1' when ( iURES_SFT = "110" ) else '0' ;
-- sequencer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= "000" ;
iBEGINX <= 0 ;
iFINEX <= 0 ;
iDIFF <= 0 ;
elsif falling_edge(CLOCK) then
case conv_integer(iSTATE) is
-- clear counter
when 0 => if ( iSCNT = TRGMAX ) then
iSTATE <= "001" ;
iBEGINX <= 0 ;
iFINEX <= 0 ;
iDIFF <= 0 ;
else
iSTATE <= "000" ;
end if ;
-- wait trigger and store begin count
when 1 => if ( iURES_LH = '1' ) then
iSTATE <= "011" ;
iBEGINX <= iSCNT ;
elsif ( iSCNT > DISMAX ) then
iSTATE <= "100" ;
else
iSTATE <= "001" ;
end if ;
-- skip
when 3 => iSTATE <= "111" ;
-- wait trigger and store fine count
when 7 => if ( iURES_HL = '1' ) then
iSTATE <= "110" ;
iFINEX <= iSCNT ;
else
iSTATE <= "111" ;
end if ;
-- calculate
when 6 => iSTATE <= "100" ;
iDIFF <= (iFINEX - iBEGINX) ;
-- return first state
when 4 => iSTATE <= "000" ;
-- default
when others =>
iSTATE <= "000" ;
end case ;
end if ;
end process ;
iGSTATE <= '1' when ( iDIFF < DIFFMAX ) else '0' ;
end behavioral;
モータ制御のパルス出力を、SPIインタフェースと別に
定義して利用します。
pout.vhdソースコード
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity pout is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- sensor handler
LEFTV : in std_logic_vector(6 downto 0) ;
RIGHTV : in std_logic_vector(6 downto 0) ;
-- pulse out
LPOUT : out std_logic ;
RPOUT : out std_logic --;
);
end pout ;
architecture behavioral of pout is
-- counter
signal iPCNT : integer range 0 to 99 ;
signal iLEFTX : integer range 0 to 99 ;
signal iRIGHTX : integer range 0 to 99 ;
-- clock divider
signal iCNT : integer range 0 to 1099 ;
begin
-- output
LPOUT <= '1' when ( iPCNT < iLEFTX ) else '0' ;
RPOUT <= '1' when ( iPCNT < iRIGHTX ) else '0' ;
-- clock divider (generate 10kHz)
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iCNT <= 0 ;
elsif rising_edge(CLOCK) then
if ( iCNT = 1099 ) then
iCNT <= 0 ;
else
iCNT <= iCNT + 1 ;
end if ;
end if ;
end process ;
-- counter
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iPCNT <= 0 ;
iLEFTX <= 0 ;
iRIGHTX <= 0 ;
elsif rising_edge(CLOCK) then
if ( iCNT = 0 ) then
if ( iPCNT = 99 ) then
iPCNT <= 0 ;
iLEFTX <= conv_integer(LEFTV) ;
iRIGHTX <= conv_integer(RIGHTV) ;
else
iPCNT <= iPCNT + 1 ;
end if ;
end if ;
end if ;
end process ;
end behavioral;
SPIインタフェースで、2つのDCモータのDUTY比を取得します。
xspi.vhdソースコード
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity xspi is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 11MHz
-- SPI
SCK : in std_logic ;
nSS : in std_logic ;
MOSI : in std_logic ;
MISO : out std_logic ;
-- pulse out
LPVAL : out std_logic_vector(6 downto 0) ;
RPVAL : out std_logic_vector(6 downto 0) --;
);
end xspi ;
architecture behavioral of xspi is
-- BUS interface
signal iSS : std_logic ;
signal iMOSI : std_logic ;
-- synchronizer
signal iSS_SFT : std_logic_vector(2 downto 0) ;
signal iSCK_SFT : std_logic_vector(1 downto 0) ;
signal iSTRG : std_logic ;
signal iSCK_TRG : std_logic ;
-- internal shift register
signal iMOSI_SFT : std_logic_vector(7 downto 0) ;
-- internal register
signal iREG : std_logic_vector(7 downto 0) ;
signal iLEFT : std_logic_vector(6 downto 0) ;
signal iRIGHT : std_logic_vector(6 downto 0) ;
-- sequencer
signal iSTATE : std_logic_vector(1 downto 0) ;
begin
-- input
iSS <= not nSS ;
iMOSI <= MOSI ;
-- output
MISO <= iMOSI_SFT(7) ;
LPVAL <= iLEFT ;
RPVAL <= iRIGHT ;
-- synchronizer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSS_SFT <= "000" ;
iSCK_SFT <= "00" ;
elsif rising_edge(CLOCK) then
iSS_SFT <= iSS_SFT(1 downto 0) & iSS ;
iSCK_SFT <= iSCK_SFT(0) & SCK ;
end if ;
end process ;
iSTRG <= '1' when ( iSS_SFT = "110" ) else '0' ;
iSCK_TRG <= '1' when ( iSCK_SFT = "01" ) else '0' ;
-- internal register
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iMOSI_SFT <= X"00" ;
elsif rising_edge(CLOCK) then
if ( iSS = '1' and iSCK_TRG = '1' ) then
iMOSI_SFT <= iMOSI_SFT(6 downto 0) & iMOSI ;
end if ;
end if ;
end process ;
-- sequencer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= "00" ;
iREG <= X"00" ;
iLEFT <= "0000000" ;
iRIGHT <= "0000000" ;
elsif rising_edge(CLOCK) then
case conv_integer(iSTATE) is
-- wait trigger
when 0 => if ( iSTRG = '1' ) then
iSTATE <= "01" ;
iREG <= iMOSI_SFT ;
else
iSTATE <= "00" ;
end if ;
-- deliver
when 1 => iSTATE <= "11" ;
if ( iREG(7) = '1' ) then -- left register
iLEFT <= iREG(6 downto 0) ;
else
iRIGHT <= iREG(6 downto 0) ;
end if ;
-- clear register
when 3 => iSTATE <= "10" ;
iREG <= X"00" ;
-- return first state
when 2 => iSTATE <= "10" ;
-- default
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
end behavioral;
信号の接続は、LPFファイルで指定します。
BLOCK RESETPATHS;
BLOCK ASYNCPATHS;
BANK 0 VCCIO 3.3 V;
BANK 1 VCCIO 3.3 V;
BANK 2 VCCIO 3.3 V;
BANK 3 VCCIO 3.3 V;
IOBUF ALLPORTS IO_TYPE=LVCMOS33 ;
LOCATE COMP "nRESET" SITE "69" ;
LOCATE COMP "CLOCK" SITE "55" ;
# sensor monitor
LOCATE COMP "LOUT[0]" SITE "97" ;
LOCATE COMP "LOUT[1]" SITE "98" ;
LOCATE COMP "LOUT[2]" SITE "99" ;
LOCATE COMP "LOUT[3]" SITE "100" ;
LOCATE COMP "LOUT[4]" SITE "104" ;
LOCATE COMP "LOUT[5]" SITE "105" ;
LOCATE COMP "LOUT[6]" SITE "106" ;
LOCATE COMP "LOUT[7]" SITE "107" ;
# ultrasonic sensor
LOCATE COMP "TRG" SITE "142" ;
LOCATE COMP "ECHO" SITE "143" ;
# start gate status
LOCATE COMP "SGATE" SITE "67" ;
# Bar Code Scanner
LOCATE COMP "START" SITE "121" ;
LOCATE COMP "DOS" SITE "122" ;
LOCATE COMP "CLKAB" SITE "128" ;
# Sensor data output (Arduino)
LOCATE COMP "SEL" SITE "65" ;
LOCATE COMP "SDAT[3]" SITE "62" ;
LOCATE COMP "SDAT[2]" SITE "61" ;
LOCATE COMP "SDAT[1]" SITE "60" ;
LOCATE COMP "SDAT[0]" SITE "58" ;
# motor drive
LOCATE COMP "LEFTP" SITE "111" ;
LOCATE COMP "RIGHTP" SITE "112" ;
LOCATE COMP "XSCK" SITE "71" ;
LOCATE COMP "XMISO" SITE "70" ;
LOCATE COMP "XMOSI" SITE "69" ;
LOCATE COMP "nXSS" SITE "68" ;
目次
前
次