目次
前
次
ゲート開閉
サーボモータを利用し、ゲートの開閉を実現してみます。
サーボモータで、バーの状態を、水平となす角度を
0度か90度のいずれかにして対応します。
サーボモータは、20ms周期で1msから2msの
パルスを与えると、パルス幅に応じた角度を生成する
アクチュエータと考えます。
20ms周期は、50Hzに相当。
周期で考えるとわかりにくいので、カウンタの値に
置き換えて、サーボモータに与えるパルス幅を表現
してみます。
10kHzでカウンタを動かすと、200カウントで20msになります。
1msから2msは、10カウントから20カウントに相当。
カウンタの値が、10、15、20でバーの状態は0度、90度、180度に
なります。
バーの配置によりますが、カウンタの値を0と15の組合わせに
するのか、20と15の組合わせにすると、開閉操作ができそう。
10と15の組合わせで開閉を制御することにします。
開と閉の2ボタンを用意し、ボタンを押すと
バーがその状態になる仕様で動かします。
2ボタンをポート2に接続し、押されたことを
状態変化(L→H)で検出。
状態は、開か閉かしかないので、論理値で表現できます。
フラグを用意して、フラグがセットされているとOPEN
クリアされているとCLOSEと状態を与えておきましょう。
ボタンが押されたことは、シフトレジスタを利用し
L→Hの変化を検出します。
タイマー割込み発生を、イベント通知して貰い
2ボタンの状態を取得。一つ前の状態と比較し
変化があれば、ボタンが押されたと判断。
擬似コーディングすると、次のようになります。
2変数(sftc、sfto)を1ビット左にシフト
2変数(sftc、sfto)の2ビットをマスク
ポートから2ボタンの状態を取得し、変数(tmp)に格納
変数tmpのCビットが1ならば、sftcに1を加える
変数tmpのOビットが1ならば、sftoに1を加える
sftcが1なら、状態をCLOSEにする
sftoが1なら、状態をOPENにする
擬似コーディングの内容を、アセンブリ言語の記述に変換。
; 2変数(sftc、sfto)を1ビット左にシフト
xch a,r2
rl a
xch a,r2
xch a,r3
rl a
xch a,r3
; 2変数(sftc、sfto)の2ビットをマスク
xch a,r2
anl a,#3
xch a,r2
xch a,r3
anl a,#3
xch a,r3
; ポートから2ボタンの状態を取得し、変数(tmp)に格納
in a,P2
anl a,#0f0h
swap a
rr a
rr a
anl a,#3
mov r4,a
; 変数tmpのCビットが1ならば、sftcに1を加える
rrc a
jnc next
inc r2
; 変数tmpのOビットが1ならば、sftoに1を加える
next:
rrc a
jnc judge
inc r3
; sftcが1なら、状態をCLOSEにする
judge:
mov a,r2
rrc a
jnc next1
mov r5,#CLOSE
; sftoが1なら、状態をOPENにする
next1:
mov a,r3
rrc a
jnc exit
mov r5,#OPEN
exit:
冗長部分を削減しておきます。
; shift sftc
xch a,r2
rl a
; masking sftc
anl a,#3
; resume
xch a,r2
; shift sfto
xch a,r3
rl a
; masking sfto
anl a,#3
; resume
xch a,r3
; get both button state
in a,P2
anl a,#0f0h
swap a
rr a
rr a
anl a,#3
; if C = 1 , then sftc increment
rrc a
jnc NEXT1
inc r2
; if O = 1 , then sfto increment
NEXT1:
rrc a
jnc NEXT2
inc r3
; if sftc = 1 , then state is CLOSE
NEXT2:
mov a,r2
rrc a
jnc NEXT3
mov r4,#CLOSE
; if sfto = 1 , then state is OPEN
NEXT3:
mov a,r3
rrc a
jnc NEXT4
mov r4,#OPEN
NEXT4:
サーボモータのパルスを生成するには、Dタイプの
フリップフロック4013を利用します。
セット、リセット端子に、トリガーを入力し
出力Qの論理値を制御します。
タイミングチャートでみると、以下。
0から199をカウントするカウンタの出力を
デコードして、0でセット。リセットするのは
11か16にすれば、10か15のパルス幅を
作り出せます。
カウンタにROMを接続し、ROMの出力データで
カウンタのリセット、フリップフロップの
セット、リセットを生成します。
回路は、以下とすればよいでしょう。
8048は、selectorに対し論理値を出力するだけ。
CLOSE、OPENの論理値は、0か1なので、この
論理値をselectorに与えます。
この方式でプログラムを作成すると、以下。
;
; TEST program for 8048 ( PROASM-II )
; Copyright (C) 2017 Kensuke Ooyu
;
INCLUDE 8048.LIB
;*******************
; value and address
;*******************
TZERO equ 6
TFLAG equ 18h
MCNT equ 19h
CLOSE equ 0
OPEN equ 1
;****************
; define symbols
;****************
ENTRY equ 0h
E_INT equ 3h
E_TIM equ 7h
;*******************
; interrupt vectors
;*******************
org ENTRY
jmp START
; external interrupt
org E_INT
retr
; timer interrupt
org E_TIM
jmp E_TIMX
org 10h
;**************
; sub routines
;**************
INIT:
; disable external interrupt
dis i
; disable timer interrupt
dis tcnti
; initialize I/O
mov a,#0
outl p1,a
outl p2,a
; clear flag
clr F0
; clear counter
mov r2,#0
mov r3,#0
mov r4,#CLOSE
call CON_HND
;
ret
INIT_TIM:
; stop counter
stop tcnt
; initialize
mov a,#TZERO
mov t,a
; start timer
strt t
;
ret
E_TIMX:
; set flag
cpl F0
; stop counter
stop tcnt
; initialize
mov a,#TZERO
mov t,a
; start timer
strt t
;
retr
SFT_HND:
; shift sftc
xch a,r2
rl a
anl a,#3 ; masking sftc
xch a,r2 ; resume
; shift sfto
xch a,r3
rl a
anl a,#3 ; masking sftc
xch a,r3 ; resume
; get both button state
in a,P2
anl a,#0f0h
swap a
rr a
rr a
anl a,#3
; if C = 1 , then sftc increment
rrc a
jnc SFT_HND1
inc r2
SFT_HND1:
; if O = 1 , then sfto increment
rrc a
jnc SFT_HND2
inc r3
SFT_HND2:
; if sftc = 1 , then state is CLOSE
mov a,r2
rrc a
jnc SFT_HND3
mov r4,#CLOSE
SFT_HND3:
; if sfto = 1 , then state is OPEN
mov a,r3
rrc a
jnc SFT_HND4
mov r4,#OPEN
SFT_HND4:
;
ret
CON_HND:
; get state
mov a,r4
; impress
outl P1,a
;
ret
;**************
; main routine
;**************
org 100h
START:
call INIT
call INIT_TIM
; enable
en TCNTI
MAIN:
; judge flag
jf0 MAINL
; always branch
jmp MAINE
MAINL:
; clear flag
clr F0
; judge states
call SFT_HND
; set control signal
call CON_HND
;
MAINE:
;
jmp MAIN
end
アセンブル結果は、次のようになります。
0000 ;
0000 ; TEST program for 8048 ( PROASM-II )
0000 ; Copyright (C) 2017 Kensuke Ooyu
0000 ;
0000 INCLUDE 8048.LIB
0000 list
0000
0000 ;*******************
0000 ; value and address
0000 ;*******************
0006 TZERO equ 6
0018 TFLAG equ 18h
0019 MCNT equ 19h
0000
0000 CLOSE equ 0
0001 OPEN equ 1
0000
0000 ;****************
0000 ; define symbols
0000 ;****************
0000 ENTRY equ 0h
0003 E_INT equ 3h
0007 E_TIM equ 7h
0000
0000 ;*******************
0000 ; interrupt vectors
0000 ;*******************
0000 org ENTRY
0000 2400 + jmp START
0002
0002 ; external interrupt
0002 org E_INT
0003 93 + retr
0004
0004 ; timer interrupt
0004 org E_TIM
0007 0426 + jmp E_TIMX
0009
0009 org 10h
0010 ;**************
0010 ; sub routines
0010 ;**************
0010 INIT:
0010 ; disable external interrupt
0010 15 + dis i
0011 ; disable timer interrupt
0011 35 + dis tcnti
0012 ; initialize I/O
0012 2300 + mov a,#0
0014 39 + outl p1,a
0015 3A + outl p2,a
0016 ; clear flag
0016 85 + clr F0
0017 ; clear counter
0017 BA00 + mov r2,#0
0019 BB00 + mov r3,#0
001B BC00 + mov r4,#CLOSE
001D 1454 + call CON_HND
001F ;
001F 83 + ret
0020
0020 INIT_TIM:
0020 ; stop counter
0020 65 + stop tcnt
0021 ; initialize
0021 2306 + mov a,#TZERO
0023 62 + mov t,a
0024 ; start timer
0024 55 + strt t
0025 ;
0025 83 + ret
0026
0026 E_TIMX:
0026 ; set flag
0026 95 + cpl F0
0027 ; stop counter
0027 65 + stop tcnt
0028 ; initialize
0028 2306 + mov a,#TZERO
002A 62 + mov t,a
002B ; start timer
002B 55 + strt t
002C ;
002C 93 + retr
002D
002D SFT_HND:
002D ; shift sftc
002D 2A + xch a,r2
002E E7 + rl a
002F 5303 + anl a,#3 ; masking sftc
0031 2A + xch a,r2 ; resume
0032
0032 ; shift sfto
0032 2B + xch a,r3
0033 E7 + rl a
0034 5303 + anl a,#3 ; masking sftc
0036 2B + xch a,r3 ; resume
0037
0037 ; get both button state
0037 0A + in a,P2
0038 53F0 + anl a,#0f0h
003A 47 + swap a
003B 77 + rr a
003C 77 + rr a
003D 5303 + anl a,#3
003F
003F ; if C = 1 , then sftc increment
003F 67 + rrc a
0040 E643 + jnc SFT_HND1
0042 1A + inc r2
0043 SFT_HND1:
0043 ; if O = 1 , then sfto increment
0043 67 + rrc a
0044 E647 + jnc SFT_HND2
0046 1B + inc r3
0047
0047 SFT_HND2:
0047 ; if sftc = 1 , then state is CLOSE
0047 FA + mov a,r2
0048 67 + rrc a
0049 E64D + jnc SFT_HND3
004B BC00 + mov r4,#CLOSE
004D
004D SFT_HND3:
004D ; if sfto = 1 , then state is OPEN
004D FB + mov a,r3
004E 67 + rrc a
004F E653 + jnc SFT_HND4
0051 BC01 + mov r4,#OPEN
0053
0053 SFT_HND4:
0053 ;
0053 83 + ret
0054
0054 CON_HND:
0054 ; get state
0054 FC + mov a,r4
0055 ; impress
0055 39 + outl P1,a
0056 ;
0056 83 + ret
0057
0057 ;**************
0057 ; main routine
0057 ;**************
0057 org 100h
0100 START:
0100 1410 + call INIT
0102 1420 + call INIT_TIM
0104 ; enable
0104 25 + en TCNTI
0105 MAIN:
0105 ; judge flag
0105 B609 + jf0 MAINL
0107 ; always branch
0107 240E + jmp MAINE
0109
0109 MAINL:
0109 ; clear flag
0109 85 + clr F0
010A ; judge states
010A 142D + call SFT_HND
010C ; set control signal
010C 1454 + call CON_HND
010E ;
010E MAINE:
010E ;
010E 2405 + jmp MAIN
0110
0110 end
ポートを入力で使うときには、論理値の'1'を
書き込んでおかないと、いけないのです。
パワーオンリセットでは、ポートに全ビットに
対し、論理値の'1'を設定するので省略しました。
ROMに入れるデータは、次のようにテキストファイルに
入れておきます。(ニブル単位で入れています。)
0 0 1
1 0 0
2 0 0
3 0 0
4 0 0
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
10 0 0
11 0 2
12 0 0
13 0 0
14 0 0
15 0 0
16 0 4
17 0 0
18 0 0
19 0 0
20 0 0
21 0 0
22 0 0
23 0 0
24 0 0
25 0 0
26 0 0
27 0 0
28 0 0
29 0 0
30 0 0
31 0 0
32 0 0
33 0 0
34 0 0
35 0 0
36 0 0
37 0 0
38 0 0
39 0 0
40 0 0
41 0 0
42 0 0
43 0 0
44 0 0
45 0 0
46 0 0
47 0 0
48 0 0
49 0 0
50 0 0
51 0 0
52 0 0
53 0 0
54 0 0
55 0 0
56 0 0
57 0 0
58 0 0
59 0 0
60 0 0
61 0 0
62 0 0
63 0 0
64 0 0
65 0 0
66 0 0
67 0 0
68 0 0
69 0 0
70 0 0
71 0 0
72 0 0
73 0 0
74 0 0
75 0 0
76 0 0
77 0 0
78 0 0
79 0 0
80 0 0
81 0 0
82 0 0
83 0 0
84 0 0
85 0 0
86 0 0
87 0 0
88 0 0
89 0 0
90 0 0
91 0 0
92 0 0
93 0 0
94 0 0
95 0 0
96 0 0
97 0 0
98 0 0
99 0 0
100 0 0
101 0 0
102 0 0
103 0 0
104 0 0
105 0 0
106 0 0
107 0 0
108 0 0
109 0 0
110 0 0
111 0 0
112 0 0
113 0 0
114 0 0
115 0 0
116 0 0
117 0 0
118 0 0
119 0 0
120 0 0
121 0 0
122 0 0
123 0 0
124 0 0
125 0 0
126 0 0
127 0 0
128 0 0
129 0 0
130 0 0
131 0 0
132 0 0
133 0 0
134 0 0
135 0 0
136 0 0
137 0 0
138 0 0
139 0 0
140 0 0
141 0 0
142 0 0
143 0 0
144 0 0
145 0 0
146 0 0
147 0 0
148 0 0
149 0 0
150 0 0
151 0 0
152 0 0
153 0 0
154 0 0
155 0 0
156 0 0
157 0 0
158 0 0
159 0 0
160 0 0
161 0 0
162 0 0
163 0 0
164 0 0
165 0 0
166 0 0
167 0 0
168 0 0
169 0 0
170 0 0
171 0 0
172 0 0
173 0 0
174 0 0
175 0 0
176 0 0
177 0 0
178 0 0
179 0 0
180 0 0
181 0 0
182 0 0
183 0 0
184 0 0
185 0 0
186 0 0
187 0 0
188 0 0
189 0 0
190 0 0
191 0 0
192 0 0
193 0 0
194 0 0
195 0 0
196 0 0
197 0 0
198 0 0
199 0 0
200 0 8
201 0 0
202 0 0
203 0 0
204 0 0
205 0 0
206 0 0
207 0 0
208 0 0
209 0 0
210 0 0
211 0 0
212 0 0
213 0 0
214 0 0
215 0 0
216 0 0
217 0 0
218 0 0
219 0 0
220 0 0
221 0 0
222 0 0
223 0 0
224 0 0
225 0 0
226 0 0
227 0 0
228 0 0
229 0 0
230 0 0
231 0 0
232 0 0
233 0 0
234 0 0
235 0 0
236 0 0
237 0 0
238 0 0
239 0 0
240 0 0
241 0 0
242 0 0
243 0 0
244 0 0
245 0 0
246 0 0
247 0 0
248 0 0
249 0 0
250 0 0
251 0 0
252 0 0
253 0 0
254 0 0
255 0 0
0から255をテキストに書き込んでおきます。
AWKスクリプトで0から255をアドレスとみて
4ビットのデータを生成。
AWKスクリプトは、以下。
{
d0 = 0
d1 = 0
d2 = 0
d3 = 0
if ( $1 == 0 ) { d0 = 1 }
if ( $1 == 11 ) { d1 = 1 }
if ( $1 == 16 ) { d2 = 1 }
if ( $1 == 200 ) { d3 = 1 }
res = ((d3 * 2 + d2) * 2 + d1) * 2 + d0
printf("%d 0 %d\n",$1,res)
}
テキストファイルからROMライターが受け付ける
HEXファイルフォーマットに変換するときにも
AWKスクリプトを使いました。その内容は以下。
# bit inverse
function xinverse(x) {
# default
result = 0
# judge
if ( x == 0 ) { result = 1 }
return result
}
# 1's compliment
function bnot(x) {
# copy
tmpx = x
# separate
x0 = int( tmpx % 2 ) ; tmpx = tmpx / 2 ;
x1 = int( tmpx % 2 ) ; tmpx = tmpx / 2 ;
x2 = int( tmpx % 2 ) ; tmpx = tmpx / 2 ;
x3 = int( tmpx % 2 ) ; tmpx = tmpx / 2 ;
x4 = int( tmpx % 2 ) ; tmpx = tmpx / 2 ;
x5 = int( tmpx % 2 ) ; tmpx = tmpx / 2 ;
x6 = int( tmpx % 2 ) ; tmpx = tmpx / 2 ;
x7 = int( tmpx % 2 ) ;
# inverse
x0 = xinverse(x0) ; x1 = xinverse(x1)
x2 = xinverse(x2) ; x3 = xinverse(x3)
x4 = xinverse(x4) ; x5 = xinverse(x5)
x6 = xinverse(x6) ; x7 = xinverse(x7)
# calculate
result = 0 ;
result = result * 2 + x7 ; result = result * 2 + x6
result = result * 2 + x5 ; result = result * 2 + x4
result = result * 2 + x3 ; result = result * 2 + x2
result = result * 2 + x1 ; result = result * 2 + x0
return result
}
BEGIN {
res = 0
}
{
adr = $1
ln = NR - 1
r = ln % 8
# show address
if ( r == 0 ) {
# data size
res = res + 8
# address
res = res + (adr / 256) + (adr % 256)
# tmp
tmp = $2 * 16 + $3
# 2 data
res = res + tmp
# show
printf(":08%04X00%02X",adr,tmp)
}
# add check sum
if ( r == 7 ) {
# tmp
tmp = $2 * 16 + $3
# add
res = res + tmp
# calculate check sum
result = bnot(res % 256) + 1
# show
printf("%02X%02X\n",tmp,result % 256)
# prepare next
res = 0
}
# others
if ( r != 0 && r != 7 ) {
# tmp
tmp = $2 * 16 + $3
# add
res = res + tmp
# show
printf("%02X",tmp)
}
}
END {
printf(":00000001FF\n")
}
このスクリプト(mkrom.awk)で生成したHEXファイル
の内容は、次のようになります。
:080000000100000000000000F7
:080008000000000200000000EE
:080010000400000000000000E4
:080018000000000000000000E0
:080020000000000000000000D8
:080028000000000000000000D0
:080030000000000000000000C8
:080038000000000000000000C0
:080040000000000000000000B8
:080048000000000000000000B0
:080050000000000000000000A8
:080058000000000000000000A0
:08006000000000000000000098
:08006800000000000000000090
:08007000000000000000000088
:08007800000000000000000080
:08008000000000000000000078
:08008800000000000000000070
:08009000000000000000000068
:08009800000000000000000060
:0800A000000000000000000058
:0800A800000000000000000050
:0800B000000000000000000048
:0800B800000000000000000040
:0800C000000000000000000038
:0800C800080000000000000028
:0800D000000000000000000028
:0800D800000000000000000020
:0800E000000000000000000018
:0800E800000000000000000010
:0800F000000000000000000008
:0800F800000000000000000000
:00000001FF
目次
前
次