目次
前
次
パルスモータチェック
MCRで用意されているコースを正確にトレースするには
位置決めを確実にできるパルスモータを利用するのが
最もよいはずです。
アマチュア無線の知人から、12V電源で動くパルスモータと
ギアボックスを8個貰ったので、動作チェックしてみます。
マイコン、CPLF/FPGAの出力できるパルス波高値は3Vから5V
くらいなので、そのままではパルスモータの励磁は不可能です。
パワートランジスタを利用したテスト用ドライバがあったので
接続してみました。
回路は単純で、以下のようになっています。
パルスモータの励磁には、1相、2相、1−2相があるので
簡単な1相励磁を実現する回路をVHDLで定義して作ります。
タイミングチャートは、以下。
タイミングチャートから、発振器、カウンタ、デコーダの3
ブロックがあればよいとして、VHDLコードを記述します。
デコーダ
1相励磁では、A、B、nA、nBの4ワイヤーに順次
パルスを出力して行けばよいので、カウンタ値を
入力としたデコーダで、パルスを出力します。
2→8→1→4と出力します。
タイミングチャートでは、8→4→2→1となって
いますが、配線の関係で2→8→1→4とします。
iPOUT <= X"8" when ( iPCNT = 1 ) else
X"1" when ( iPCNT = 2 ) else
X"4" when ( iPCNT = 3 ) else
X"2" ;
カウンタ
1相励磁では、4パターンのパルス出力となるので
4進カウンタを使います。バイナリカウンタにて
定義しています。
process (nRESET,iCLKX)
begin
if ( nRESET = '0' ) then
iPCNT <= 0 ;
elsif rising_edge(iCLKX) then
if ( iPCNT = CNTPMAX ) then
iPCNT <= 0 ;
else
iPCNT <= iPCNT + 1 ;
end if ;
end if ;
end process ;
ハザードが心配になる高周波数では、シフトレジスタに
よるジョンソンカウンタにします。
デコーダ値とカウント値の対応を再定義するだけなので
モータが動いてから、修正します。
カウンタクロックは、3種類用意し、DIPスイッチで
選べるようにします。
-- generate clock
process (iCLK)
begin
if rising_edge(iCLK) then
if ( iCNTX = CNTXMAX ) then
iCNTX <= 0 ;
else
iCNTX <= iCNTX + 1 ;
end if ;
end if ;
end process ;
iCLKHML(3) <= '1' when ( iCNTX < 2 ) else '0' ;
iCLKHML(2) <= '1' when ( iCNTX < 4 ) else '0' ;
iCLKHML(1) <= '1' when ( iCNTX = 0 ) else '0' ;
iCLKHML(0) <= '1' when ( iCNTX < 2 ) else '0' ;
-- select clock
iCLKX <= iCLKHML( conv_integer( SEL ) ) ;
発振器
パルスを生成するために、クロックが必要です。
このクロックは、マスタークロック4MHzを
分周して生成します。
process (CLOCK)
begin
if rising_edge(CLOCK) then
if ( iCNT = CNTMAX ) then
iCNT <= 0 ;
else
iCNT <= iCNT + 1 ;
end if ;
end if ;
end process ;
iCLK <= '1' when ( iCNT < CNTHALF ) else '0' ;
分周比はパルスモータが脱調しない範囲で
次の値を選択しました。
CONSTANT CNTMAX : integer := 3999 ;
CONSTANT CNTHALF : integer := 2000 ;
VHDLコードにまとめると、以下。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity pm is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 4MHz
-- enable
XENA :in std_logic ;
-- selector
SEL : in std_logic_vector(1 downto 0) ;
-- monitor
MCLK : out std_logic ;
-- pulse out
POUT : out std_logic_vector(7 downto 0) --;
);
end pm ;
architecture behavioral of pm is
--
CONSTANT CNTMAX : integer := 3999 ;
CONSTANT CNTHALF : integer := 2000 ;
CONSTANT CNTXMAX : integer := 7 ;
CONSTANT CNTPMAX : integer := 3 ;
-- divider
signal iCNT : integer range 0 to CNTMAX ;
signal iCLK : std_logic ;
signal iCNTX : integer range 0 to CNTXMAX ;
signal iCLKX : std_logic ;
signal iCLKHML : std_logic_vector(3 downto 0) ;
-- decode
signal iPOUT : std_logic_vector(3 downto 0) ;
signal iPCNT : integer range 0 to CNTPMAX ;
begin
-- output
POUT <= iPOUT & iPOUT when ( XENA = '1' ) else X"00" ;
MCLK <= iCLKX when ( nRESET = '1' ) else '0' ;
-- divider (generate 400Hz)
process (CLOCK)
begin
if rising_edge(CLOCK) then
if ( iCNT = CNTMAX ) then
iCNT <= 0 ;
else
iCNT <= iCNT + 1 ;
end if ;
end if ;
end process ;
iCLK <= '1' when ( iCNT < CNTHALF ) else '0' ;
-- generate clock
process (iCLK)
begin
if rising_edge(iCLK) then
if ( iCNTX = CNTXMAX ) then
iCNTX <= 0 ;
else
iCNTX <= iCNTX + 1 ;
end if ;
end if ;
end process ;
iCLKHML(3) <= '1' when ( iCNTX < 2 ) else '0' ;
iCLKHML(2) <= '1' when ( iCNTX < 4 ) else '0' ;
iCLKHML(1) <= '1' when ( iCNTX = 0 ) else '0' ;
iCLKHML(0) <= '1' when ( iCNTX < 2 ) else '0' ;
-- select clock
iCLKX <= iCLKHML( conv_integer( SEL ) ) ;
-- pulse counter
process (nRESET,iCLKX)
begin
if ( nRESET = '0' ) then
iPCNT <= 0 ;
elsif rising_edge(iCLKX) then
if ( iPCNT = CNTPMAX ) then
iPCNT <= 0 ;
else
iPCNT <= iPCNT + 1 ;
end if ;
end if ;
end process ;
-- decode
iPOUT <= X"8" when ( iPCNT = 1 ) else
X"1" when ( iPCNT = 2 ) else
X"4" when ( iPCNT = 3 ) else
X"2" ;
end behavioral;
パルス生成に利用しているクロックをモニタし、パルス
出力をするか否かの指定ができるようにしてあります。
XC9572の基板では、ピンアサインを以下としました。
# system
NET "CLOCK" LOC = "P5" ;
NET "nRESET" LOC = "P39" ;
# pulse out
NET "POUT<0>" LOC = "P1" ;
NET "POUT<1>" LOC = "P2" ;
NET "POUT<2>" LOC = "P3" ;
NET "POUT<3>" LOC = "P4" ;
# select , enable , monitor
NET "SEL<0>" LOC = "P11" ;
NET "SEL<1>" LOC = "P12" ;
NET "XENA" LOC = "P14" ;
NET "MCLK" LOC = "P22" ;
このVHDLコードでは、回転方向が固定なので
正転、逆転ができるようにします。
カウンタは、0から3を繰返しているので
デコードを2つ用意し、外部からの指示で
選択できるようにします。
デコーダは、次のように定義します。
iPOUT <= iPOUTY when ( XDIR = '1' ) else iPOUTX ;
iPOUTY <= X"8" when ( iPCNT = 1 ) else
X"1" when ( iPCNT = 2 ) else
X"4" when ( iPCNT = 3 ) else
X"2" ;
iPOUTX <= X"2" when ( iPCNT = 1 ) else
X"4" when ( iPCNT = 2 ) else
X"1" when ( iPCNT = 3 ) else
X"8" ;
VHDLコードにまとめると、以下。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity pm is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 4MHz
-- enable
XENA : in std_logic ;
-- direction
XDIR : in std_logic ;
-- selector
SEL : in std_logic_vector(1 downto 0) ;
-- monitor
MCLK : out std_logic ;
-- pulse out
POUT : out std_logic_vector(7 downto 0) --;
);
end pm ;
architecture behavioral of pm is
--
CONSTANT CNTMAX : integer := 3999 ;
CONSTANT CNTHALF : integer := 2000 ;
CONSTANT CNTXMAX : integer := 7 ;
CONSTANT CNTPMAX : integer := 3 ;
-- divider
signal iCNT : integer range 0 to CNTMAX ;
signal iCLK : std_logic ;
signal iCNTX : integer range 0 to CNTXMAX ;
signal iCLKX : std_logic ;
signal iCLKHML : std_logic_vector(3 downto 0) ;
-- decode
signal iPOUT : std_logic_vector(3 downto 0) ;
signal iPOUTX : std_logic_vector(3 downto 0) ;
signal iPOUTY : std_logic_vector(3 downto 0) ;
signal iPCNT : integer range 0 to CNTPMAX ;
begin
-- output
POUT <= iPOUT & iPOUT when ( XENA = '1' ) else X"00" ;
MCLK <= iCLKX when ( nRESET = '1' ) else '0' ;
-- divider (generate 400Hz)
process (CLOCK)
begin
if rising_edge(CLOCK) then
if ( iCNT = CNTMAX ) then
iCNT <= 0 ;
else
iCNT <= iCNT + 1 ;
end if ;
end if ;
end process ;
iCLK <= '1' when ( iCNT < CNTHALF ) else '0' ;
-- generate clock
process (iCLK)
begin
if rising_edge(iCLK) then
if ( iCNTX = CNTXMAX ) then
iCNTX <= 0 ;
else
iCNTX <= iCNTX + 1 ;
end if ;
end if ;
end process ;
iCLKHML(3) <= '1' when ( iCNTX < 2 ) else '0' ;
iCLKHML(2) <= '1' when ( iCNTX < 4 ) else '0' ;
iCLKHML(1) <= '1' when ( iCNTX = 0 ) else '0' ;
iCLKHML(0) <= '1' when ( iCNTX < 2 ) else '0' ;
-- select clock
iCLKX <= iCLKHML( conv_integer( SEL ) ) ;
-- pulse counter
process (nRESET,iCLKX)
begin
if ( nRESET = '0' ) then
iPCNT <= 0 ;
elsif rising_edge(iCLKX) then
if ( iPCNT = CNTPMAX ) then
iPCNT <= 0 ;
else
iPCNT <= iPCNT + 1 ;
end if ;
end if ;
end process ;
-- decode
iPOUT <= iPOUTY when ( XDIR = '1' ) else iPOUTX ;
iPOUTY <= X"8" when ( iPCNT = 1 ) else
X"1" when ( iPCNT = 2 ) else
X"4" when ( iPCNT = 3 ) else
X"2" ;
iPOUTX <= X"2" when ( iPCNT = 1 ) else
X"4" when ( iPCNT = 2 ) else
X"1" when ( iPCNT = 3 ) else
X"8" ;
end behavioral;
内部では、4ビットのレジスタを2個用意し
制御ピンを1ピン増えします。UCFの内容は
次のように単純です。
# system
NET "CLOCK" LOC = "P5" ;
NET "nRESET" LOC = "P39" ;
# pulse out
NET "POUT<0>" LOC = "P1" ;
NET "POUT<1>" LOC = "P2" ;
NET "POUT<2>" LOC = "P3" ;
NET "POUT<3>" LOC = "P4" ;
# select , enable , monitor
NET "SEL<0>" LOC = "P11" ;
NET "SEL<1>" LOC = "P12" ;
NET "XDIR" LOC = "P13" ;
NET "XENA" LOC = "P14" ;
NET "MCLK" LOC = "P22" ;
4進カウンタをジョンソンカウンタを使い
デコーダ出力との対応を変更します。
-- pulse counter
process (nRESET,iCLKX)
begin
if ( nRESET = '0' ) then
iPCNT <= 0 ;
elsif rising_edge(iCLKX) then
case iPCNT is
when 0 => iPCNT <= 1 ;
when 1 => iPCNT <= 3 ;
when 3 => iPCNT <= 2 ;
when 2 => iPCNT <= 0 ;
when others =>
iPCNT <= 0 ;
end case ;
end if ;
end process ;
-- decode
iPOUT <= iPOUTY when ( XDIR = '1' ) else iPOUTX ;
iPOUTY <= X"8" when ( iPCNT = 1 ) else
X"1" when ( iPCNT = 3 ) else
X"4" when ( iPCNT = 2 ) else
X"2" ;
iPOUTX <= X"2" when ( iPCNT = 1 ) else
X"4" when ( iPCNT = 3 ) else
X"1" when ( iPCNT = 2 ) else
X"8" ;
4進カウンタをジョンソンカウンタで実現すると
クロックの変化で値が変化するのが1ビットだけ
になります。
バイナリカウンタでは、ハザードによるノイズを
生成し、マイコンの誤動作を起こす可能性があり
ますが、ジョンソンカウンタではハザード生成が
ないので、クロックを高速から低速に可変する時
のノイズ発生を考えなくてよくなります。
パルスモータは、2相励磁も可能なので
デコーダの内容を変更で対応できるかを
確認します。
2相の場合、4ビット中2ビットが1に
なるようにデコードします。
iPOUT <= iPOUTY when ( XDIR = '1' ) else iPOUTX ;
iPOUTY <= X"A" when ( iPCNT = 1 ) else
X"9" when ( iPCNT = 2 ) else
X"5" when ( iPCNT = 3 ) else
X"6" ;
iPOUTX <= X"6" when ( iPCNT = 1 ) else
X"5" when ( iPCNT = 2 ) else
X"9" when ( iPCNT = 3 ) else
X"A" ;
パルスモータは、励磁方法の他に周波数を可変にして
スピード制御できないと移動メカには使えません。
NCO(Nemerically Controlled Oscillators)を利用して
パルスモータに与えるパルス周波数を可変にします。
NCOは、固定周波数クロックで、与える数値で任意の
周波数を生成します。周波数可変に利用する加算器が
必要で、これを位相アキュムレータと呼びます。
位相アキュムレータは、歩くときの歩幅を変えて
目的値に到達するまでの時間を変えるカラクリです。
最小歩幅は固定で、この歩幅の何倍という係数を指定
します。この倍数を、マイコンで計算してCPLD/FPGAに
与えます。
24ビットの位相アキュムレータと係数に相当する
加算値レジスタを用意し、24MHzのクロックで加算
していきます。
24MHzを選択したのは、手持ちの水晶があったためで
16MHzでも20MHzでも構いません。
希望周波数frを出力するための加算値は
次の計算で算出します。(^は、べき乗)
N = fr x (2^24) / (24x10^6)
VHDLコードは、以下。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity nco is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ; -- 24MHz
-- trigger
TRG : in std_logic ;
-- input data
ADR : in std_logic_vector(1 downto 0) ;
-- input data
Din : in std_logic_vector(7 downto 0) ;
--
COUT : out std_logic --;
);
end nco ;
architecture behavioral of nco is
-- phase accumlator
signal iPA : std_logic_vector(23 downto 0) ;
-- internal register
signal iDELTAX : std_logic_vector(23 downto 0) ;
signal iADR : std_logic_vector(1 downto 0) ;
-- synchronizer
signal iTRG_SFT : std_logic_vector(2 downto 0) ;
signal iTRG : std_logic ;
begin
-- output
COUT <= iPA(23) ;
-- input
iADR <= ADR ;
-- phase accumlator
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iPA <= (others => '0') ;
elsif rising_edge( CLOCK ) then
iPA <= iPA + iDELTAX ;
end if ;
end process ;
-- synchronizer
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iTRG_SFT <= "000" ;
elsif rising_edge(CLOCK) then
iTRG_SFT <= iTRG_SFT(1 downto 0) & TRG ;
end if ;
end process ;
iTRG <= '1' when ( iTRG_SFT = "011" ) else '0' ;
-- store internal register
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iDELTAX <= (others => '0') ;
elsif rising_edge( CLOCK ) then
if ( iTRG = '1' ) then
-- lower
if ( iADR = "00" ) then
iDELTAX( 7 downto 0) <= Din ;
end if ;
-- middle
if ( iADR = "01" ) then
iDELTAX(15 downto 8) <= Din ;
end if ;
-- upper
if ( iADR = "10" ) then
iDELTAX(23 downto 16) <= Din ;
end if ;
end if ;
end if ;
end process ;
end behavioral;
XilinxのXC95108でどのくらいのマクロセルを使うのか
調べると、80マクロセルくらいでした。この程度ならば
FPGAに楽々入ります。
UCFファイルは、以下。
# system
NET "CLOCK" LOC = "P9" ;
NET "nRESET" LOC = "P74" ;
# interface
NET "TRG" LOC = "P1" ;
# address
NET "ADR<0>" LOC = "P2" ;
NET "ADR<1>" LOC = "P3" ;
# data
NET "Din<0>" LOC = "P4" ;
NET "Din<1>" LOC = "P5" ;
NET "Din<2>" LOC = "P6" ;
NET "Din<3>" LOC = "P7" ;
NET "Din<4>" LOC = "P10" ;
NET "Din<5>" LOC = "P11" ;
NET "Din<6>" LOC = "P12" ;
NET "Din<7>" LOC = "P13" ;
# clock out
NET "COUT" LOC = "P14" ;
24ビットの加算値を更新するには、マイクロプロセッサを
利用します。
ArduinoやZ80を利用した場合、3バイトのデータを更新する
ことになり、8ビットデータバスに加えて、アドレス、制御
のピンが必要となり、ピン数の多いプロセッサが必要です。
ピン数の多いプロセッサ基板を利用するのは、テスト段階に
限定します。実際の移動には、FPGAの中にプロセッサを入れ
上位プロセッサの負担を減らします。
ピン数の多いプロセッサは、Z84C011を利用し、これを実装した
Z80基板を使います。
Z84C011には、パラレルポートが5ポートあるので、24ビットを
一度に更新できます。シリアルインタフェースを持たないので
SPIを使ってZ84C011に値を渡します。
SPIインタフェースで渡した24ビットを、そのままパラレルポート
に出力し、他にトリガーを1ピン使います。
24ビットをパラレルポートに出力する処理を
Z80アセンブリ言語で記述すると、以下。
; FHB high byte (2^23 - 2^16)
; FMB middle byte (2^15 - 2^8)
; FLB low byte ( 2^7 - 2^0)
SET_FV:
; stack
push bc
push af
; high byte
ld a,(FHB)
ld c,PADAT
out (c),a
; middle byte
ld a,(FMB)
ld c,PBDAT
out (c),a
; low byte
ld a,(FLB)
ld c,PCDAT
out (c),a
; stack
pop af
pop bc
;
ret
アセンブリ言語で、大まかな動作チェックをしたなら
RTOSを使い、雛形のファームウエアを作成してみます。
/* define my system I/O area */
__sfr __at 0x10 CTC_C0 ;
__sfr __at 0x11 CTC_C1 ;
__sfr __at 0x12 CTC_C2 ;
__sfr __at 0x13 CTC_C3 ;
__sfr __at 0x54 PACON ;
__sfr __at 0x55 PBCON ;
__sfr __at 0x56 PCCON ;
__sfr __at 0x34 PDCON ;
__sfr __at 0x44 PECON ;
__sfr __at 0x50 PADAT ;
__sfr __at 0x51 PBDAT ;
__sfr __at 0x53 PCDAT ;
__sfr __at 0x30 PDDAT ;
__sfr __at 0x40 PEDAT ;
/* define data types */
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
typedef unsigned long ULONG ;
typedef signed char SBYTE ;
typedef signed short SWORD ;
#define OFF 0
#define ON OFF+1
#define CLOSED 0
#define OPENED CLOSED+1
#define MASK0F 0x0f
#define MASKFF 0xff
#define SGATE_BIT 4
#define EDC_BIT 3
#define LM_BIT 1
#define RM_BIT 0
UBYTE sgate ;
UBYTE sensor ;
typedef union {
struct {
unsigned char B7:1;
unsigned char B6:1;
unsigned char B5:1;
unsigned char B4:1;
unsigned char B3:1;
unsigned char B2:1;
unsigned char B1:1;
unsigned char B0:1;
} BIT ;
unsigned char DR ;
} FLAGSP ;
FLAGSP x_flags ;
#define SFLAG x_flags.BIT.B0
#define UFLAG x_flags.BIT.B1
#define WFLAG x_flags.BIT.B2
#define TFLAG x_flags.BIT.B3
ULONG timcnt ;
UBYTE timeoutcnt ;
typedef struct {
void (*tsk)(void);
UWORD wcount ;
} TCBP ;
#define TSK_ID_MAX 8
#define TSK_ID0 0
#define TSK_ID1 1
#define TSK_ID2 2
#define TSK_ID3 3
#define TSK_ID4 4
#define TSK_ID5 5
#define TSK_ID6 6
#define TSK_ID7 7
/* define constant values */
#define LAST 10
#define TTS_SUSPEND 0
#define TTS_WAIT TTS_SUSPEND+1
#define TTS_READY TTS_SUSPEND+2
TCBP tcb[TSK_ID_MAX];
#define NORMAL TSK_ID1
#define CRANK TSK_ID2
#define ROTATE TSK_ID3
#define LANE TSK_ID4
#define CHANGE TSK_ID5
#define BLIND TSK_ID6
volatile UBYTE wflag ;
volatile UBYTE run_tsk ;
volatile UBYTE ready ;
volatile UBYTE suspend ;
volatile UBYTE waitq ;
UBYTE spat[13];
#define ALL_BLACK 0
#define ALL_WHITE 1
#define LEFT_WHITE 2
#define RIGHT_WHITE 3
#define CENTER 4
#define TINY_RIGHT 5
#define RIGHT 6
#define BIG_RIGHT 7
#define TINY_LEFT 8
#define LEFT 9
#define BIG_LEFT 10
#define BOTH_WHITE 11
#define ILLEAGAL 12
#define GEAR0 10
#define GEAR5 150
#define GEAR10 300
#define GEAR15 450
#define GEAR20 600
#define GEAR25 750
#define GEAR30 900
#define GEAR35 1050
#define GEAR40 1200
#define GEAR45 1350
#define GEAR50 1500
#define GEAR55 1650
#define GEAR60 1800
#define GEAR65 1950
#define GEAR70 2100
#define GEAR75 2250
#define GEAR80 2400
#define GEAR85 2550
#define GEAR90 2700
#define GEAR95 2850
#define GEAR100 2999
#define IDLE 0
#define RUN IDLE+1
#define DIR_CENTER 0
#define DIR_RIGHT 1
#define DIR_LEFT 2
UBYTE left ;
UBYTE right ;
UBYTE state ;
UBYTE mstate ;
UBYTE dir ;
/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void user_initialize(void);
void show_road_state(UBYTE x);
void init_spat(void);
UBYTE get_sensor(void);
UBYTE get_start(void);
void show_state(UBYTE x);
void send_dc(UBYTE lx,UBYTE rx);
void delay_ms(UWORD x);
/*-------------*/
/* system call */
/*-------------*/
void cre_tsk(UBYTE tid,void (*tsk)(void));
void sta_tsk(UBYTE tid,UBYTE sta);
void rsm_tsk(UBYTE tid);
void sus_tsk(UBYTE tid);
void slp_tsk(void);
void wai_tsk(UWORD x);
void init_os(void);
UBYTE is_tsk_ready(UBYTE tid);
/*------*/
/* task */
/*------*/
void tsk0_proc(void);
void tsk1_proc(void);
void tsk2_proc(void);
void tsk3_proc(void);
void tsk4_proc(void);
void tsk5_proc(void);
void tsk6_proc(void);
void tsk7_proc(void);
void main(void)
{
UBYTE tmp ;
UBYTE i ;
/* disable interrupt */
/* cli(); */
/* initialize port and variables */
user_initialize();
/* initialize monitor */
init_os();
/* create task */
cre_tsk(TSK_ID0,tsk0_proc);
cre_tsk(NORMAL ,tsk1_proc);
cre_tsk(CRANK ,tsk2_proc);
cre_tsk(ROTATE ,tsk3_proc);
cre_tsk(LANE ,tsk4_proc);
cre_tsk(CHANGE ,tsk5_proc);
cre_tsk(BLIND ,tsk6_proc);
cre_tsk(TSK_ID7,tsk7_proc);
/* start task task */
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(NORMAL ,TTS_SUSPEND);
sta_tsk(CRANK ,TTS_SUSPEND);
sta_tsk(ROTATE ,TTS_SUSPEND);
sta_tsk(LANE ,TTS_SUSPEND);
sta_tsk(CHANGE ,TTS_SUSPEND);
sta_tsk(BLIND ,TTS_SUSPEND);
sta_tsk(TSK_ID7,TTS_READY);
/* enable interrupt */
/* sei(); */
/* endless loop */
run_tsk = TSK_ID0 ;
/* endless loop */
while ( ON ) {
/* RTOS */
if ( is_tsk_ready( run_tsk ) == ON ) {
switch ( run_tsk ) {
case 1 : tsk1_proc(); break;
case 2 : tsk2_proc(); break;
case 3 : tsk3_proc(); break;
case 4 : tsk4_proc(); break;
case 5 : tsk5_proc(); break;
case 6 : tsk6_proc(); break;
case 7 : tsk7_proc(); break;
default : tsk0_proc(); break;
}
}
/* round robin */
run_tsk++;
if ( run_tsk == TSK_ID_MAX ) { run_tsk = TSK_ID0 ; }
/* cyclic handling */
if ( wflag ) {
wflag = OFF ;
tmp = waitq ;
for ( i = 0 ; i < TSK_ID_MAX ; i++ ) {
if ( tmp & ON ) {
tcb[i].wcount-- ;
if ( tcb[i].wcount == 0 ) { rsm_tsk(i); }
}
tmp >>= 1 ;
}
}
}
}
void user_initialize(void)
{
/* I/O values */
PADAT = 0x00 ;
PBDAT = 0x00 ;
PCDAT = 0x00 ;
PDDAT = 0xff ;
PEDAT = 0xff ;
/* I/O directions */
PACON = 0x00 ;
PBCON = 0xff ;
PCCON = 0xff ;
PDCON = 0xff ;
PECON = 0xff ;
/* clear flags */
x_flags.DR = 0 ;
/* initialize */
timcnt = 0 ;
/* others */
left = 0 ;
right = 0 ;
state = IDLE ;
mstate = 0 ;
init_spat();
}
/* system call */
void cre_tsk(UBYTE tid,void (*tsk)(void))
{
tcb[tid].tsk = tsk;
tcb[tid].wcount = 0;
}
void sta_tsk(UBYTE tid,UBYTE sta)
{
UWORD tmp ;
tmp = (1 << tid);
if ( sta == TTS_READY ) { ready |= tmp; }
if ( sta == TTS_SUSPEND ) { suspend |= tmp; }
if ( sta == TTS_WAIT ) { waitq |= tmp; }
}
void rsm_tsk(UBYTE tid)
{
UWORD tmp ;
tmp = (1 << tid);
ready |= tmp;
suspend &= ~tmp;
waitq &= ~tmp;
}
void sus_tsk(UBYTE tid)
{
UWORD tmp ;
tmp = (1 << tid);
ready &= ~tmp;
suspend |= tmp;
waitq &= ~tmp;
}
void slp_tsk(void)
{
sus_tsk(run_tsk);
}
void wai_tsk(UWORD x)
{
UWORD tmp ;
tmp = (1 << run_tsk);
ready &= ~tmp;
suspend &= ~tmp;
waitq |= tmp;
tcb[run_tsk].wcount = x ;
}
void init_os(void)
{
ready = 0;
suspend = 0;
waitq = 0;
}
UBYTE is_tsk_ready(UBYTE tid)
{
return( (ready >> tid) & 1 ) ;
}
void show_road_state(UBYTE x)
{
/* impress */
PDDAT = x ^ MASKFF ;
}
UBYTE get_sensor(void)
{
return sensor ;
}
UBYTE get_start(void)
{
return sgate;
}
void init_spat(void)
{
*(spat+ 0) = 0x00 ; /* ALL_BLACK */
*(spat+ 1) = 0xff ; /* ALL_WHITE */
*(spat+ 2) = 0xf0 ; /* LEFT_WHITE */
*(spat+ 3) = 0x0f ; /* RIGHT_WHITE */
*(spat+ 4) = 0x18 ; /* CENTER */
*(spat+ 5) = 0x0c ; /* TINY_RIGHT */
*(spat+ 6) = 0x06 ; /* RIGHT */
*(spat+ 7) = 0x03 ; /* BIG_RIGHT */
*(spat+ 8) = 0x30 ; /* TINY_LEFT */
*(spat+ 9) = 0x60 ; /* LEFT */
*(spat+10) = 0xc0 ; /* BIG_LEFT */
*(spat+11) = 0xc3 ; /* BOTH_WHITE */
*(spat+12) = 0x5a ; /* ILLEAGAL */
}
void show_state(UBYTE x)
{
/* impress */
PEDAT = x ^ MASKFF ;
}
void send_dc(UBYTE lx,UBYTE rx)
{
/* left motor */
PBDAT = lx ;
PCDAT |= (1 << LM_BIT) ;
PCDAT &= ~(1 << LM_BIT) ;
/* right motor */
PBDAT = rx ;
PCDAT |= (1 << RM_BIT) ;
PCDAT &= ~(1 << RM_BIT) ;
}
void delay_ms(UWORD x)
{
ULONG target ;
/* calculate last value */
target = timcnt + x ;
/* wait */
while ( timcnt < target ) ;
}
/* system control */
void tsk0_proc(void)
{
/* start trigger */
if ( state == IDLE ) {
if ( get_start() == OPENED ) {
state = RUN ;
rsm_tsk(NORMAL);
/* enable motor power */
PCDAT |= (1 << EDC_BIT);
/* timer trigger clear */
timeoutcnt = 0 ;
TFLAG = OFF ;
}
}
/* timeout */
if ( state == RUN ) {
if ( TFLAG == ON ) {
state = IDLE ;
/* suspend move handling task */
state = IDLE ;
sus_tsk(NORMAL);
sus_tsk(CRANK);
sus_tsk(ROTATE);
sus_tsk(LANE);
sus_tsk(CHANGE);
sus_tsk(BLIND);
/* machine state */
mstate = 0 ;
/* disable motor power */
PCDAT &= ~(1 << EDC_BIT);
}
}
/* show state */
show_state( mstate );
}
/* NORMAL */
void tsk1_proc(void)
{
/* machine state */
mstate = 0;
/* show */
show_state((1 << mstate));
/* default */
dir = DIR_CENTER ;
/* get sensor */
if ( sensor == CENTER ) {
left = 50 ; right = 50 ;
}
if ( sensor == TINY_RIGHT ) {
left = 50 ; right = 55 ;
}
if ( sensor == RIGHT ) {
left = 50 ; right = 60 ;
}
if ( sensor == BIG_RIGHT ) {
left = 50 ; right = 70 ;
}
if ( sensor == TINY_LEFT ) {
left = 55 ; right = 50 ;
}
if ( sensor == LEFT ) {
left = 60 ; right = 50 ;
}
if ( sensor == BIG_LEFT ) {
left = 70 ; right = 50 ;
}
if ( sensor == ALL_BLACK ) {
left = 10 ; right = 10 ;
}
if ( sensor == BOTH_WHITE ) {
left = 20 ; right = 20 ;
}
if ( sensor == ILLEAGAL ) {
left = 10 ; right = 10 ;
}
/* update */
send_dc(left,right);
/* judge */
if ( sensor == ALL_WHITE ) {
rsm_tsk(CRANK);
slp_tsk();
}
if ( sensor == LEFT_WHITE ) {
dir = DIR_LEFT ;
rsm_tsk(LANE);
slp_tsk();
}
if ( sensor == RIGHT_WHITE ) {
dir = DIR_RIGHT ;
rsm_tsk(LANE);
slp_tsk();
}
}
/* ROTATE */
void tsk2_proc(void)
{
/* machine state */
mstate = 1;
/* show */
show_state((1 << mstate));
/* get sensor */
if ( sensor == CENTER ) {
left = 30 ; right = 30 ;
}
if ( sensor == TINY_LEFT ) {
left = 35 ; right = 30 ;
}
if ( sensor == LEFT ) {
left = 40 ; right = 30 ;
}
if ( sensor == BIG_LEFT ) {
left = 45 ; right = 30 ;
}
if ( sensor == TINY_RIGHT ) {
left = 30 ; right = 35 ;
}
if ( sensor == RIGHT ) {
left = 30 ; right = 40 ;
}
if ( sensor == BIG_RIGHT ) {
left = 30 ; right = 45 ;
}
if ( sensor == BOTH_WHITE ) {
left = 20 ; right = 20 ;
}
if ( sensor == ALL_BLACK ) {
left = 10 ; right = 10 ;
}
if ( sensor == ILLEAGAL ) {
left = 10 ; right = 10 ;
}
/* update */
send_dc(left,right);
/* judge */
if ( sensor == LEFT_WHITE ) {
dir = DIR_LEFT ;
rsm_tsk(ROTATE);
slp_tsk();
}
if ( sensor == RIGHT_WHITE ) {
dir = DIR_RIGHT ;
rsm_tsk(ROTATE);
slp_tsk();
}
}
/* ROTATE */
void tsk3_proc(void)
{
/* machine state */
mstate = 2;
/* show */
show_state((1 << mstate));
/* judge */
if ( dir == DIR_RIGHT ) {
left = 10 ; right = 50 ;
}
if ( dir == DIR_LEFT ) {
left = 50 ; right = 10 ;
}
/* update */
send_dc(left,right);
/* judge */
if ( sensor == LEFT_WHITE ) {
dir = DIR_LEFT ;
rsm_tsk(ROTATE);
slp_tsk();
}
/* judge */
if ( sensor == CENTER ) {
send_dc(50,50);
dir = DIR_CENTER ;
rsm_tsk(NORMAL);
slp_tsk();
}
}
/* LANE */
void tsk4_proc(void)
{
UBYTE tdir ;
/* machine state */
mstate = 3 ;
/* show */
show_state((1 << mstate));
/* direction */
tdir = dir ;
/* get sensor */
if ( sensor == CENTER ) {
left = 30 ; right = 30 ;
}
if ( sensor == TINY_LEFT ) {
left = 35 ; right = 30 ;
}
if ( sensor == LEFT ) {
left = 40 ; right = 30 ;
}
if ( sensor == BIG_LEFT ) {
left = 45 ; right = 30 ;
}
if ( sensor == TINY_RIGHT ) {
left = 30 ; right = 35 ;
}
if ( sensor == RIGHT ) {
left = 30 ; right = 40 ;
}
if ( sensor == BIG_RIGHT ) {
left = 30 ; right = 45 ;
}
if ( sensor == BOTH_WHITE ) {
left = 20 ; right = 20 ;
}
if ( sensor == ALL_BLACK ) {
left = 10 ; right = 10 ;
}
if ( sensor == ILLEAGAL ) {
left = 10 ; right = 10 ;
}
/* update */
send_dc(left,right);
/* judge */
if ( sensor == ALL_BLACK ) {
dir <<= 2 ;
if ( tdir == DIR_LEFT ) {
dir = DIR_RIGHT ;
}
if ( tdir == DIR_RIGHT ) {
dir = DIR_LEFT ;
}
dir <<= 2 ;
dir |= tdir ;
rsm_tsk(CHANGE);
slp_tsk();
}
}
/* CHANGE */
void tsk5_proc(void)
{
UBYTE tdir ;
/* machine state */
mstate = 4 ;
/* show */
show_state((1 << mstate));
/* default */
tdir = dir & 3 ;
dir >>= 2 ;
/* rotate */
if ( tdir == DIR_LEFT ) {
left = 30 ; right = 50 ;
}
if ( tdir == DIR_RIGHT ) {
left = 50 ; right = 30 ;
}
/* update */
send_dc(left,right);
/* delay */
delay_ms(100);
/* update */
left = 30 ; right = 30 ;
/* exit */
rsm_tsk(BLIND);
slp_tsk();
}
/* BLIND */
void tsk6_proc(void)
{
/* machine state */
mstate = 5 ;
/* show */
show_state((1 << mstate));
/* rotate */
if ( dir == DIR_LEFT ) {
left = 10 ; right = 30 ;
}
if ( dir == DIR_RIGHT ) {
left = 30 ; right = 10 ;
}
/* update */
send_dc(left,right);
/* delay */
delay_ms(100);
/* */
left = 50 ; right = 50 ;
/* update */
send_dc(left,right);
/* delay */
delay_ms(50);
/* sensor */
if ( sensor == CENTER ) {
rsm_tsk(NORMAL);
slp_tsk();
}
}
void tsk7_proc(void)
{
/* get sensor data */
sensor = PADAT ;
/* start gate */
sgate = CLOSED ;
if ( !(sensor & (1 << SGATE_BIT)) ) { sgate = OPENED ; }
/* sensor */
sensor &= MASK0F ;
/* time out */
if ( timeoutcnt > 119 ) { TFLAG = ON ; }
/* show road state */
show_road_state( sensor );
/* wait 50ms */
wai_tsk(50);
}
目次
前
次