イサギダプロダクツから購入したデジタルスケールは、SHANのノギスとまった
く同じ信号でした。SHANのプログラムでそのままデータがとれた。写真はまだ
補正(fv = (val - (val /100)/1.27)/100;)をしてないのでちょっとずれていま
す。

今日はノギスデータの取り込みからはじめました。昨日作ったインターバルタ
イマによるサンプリングではせいぜい10KHzがいいとこ。ノギスの信号は90KHz
近くです。
限界までサンプリングするためにアセンブラで書き直しました。
.h8300h
.section .text
/* void port9_polling (int32_t loop, int8_t bit_position) */
.global _port9_polling
_port9_polling:
mov.l er4, @-sp
mov.l er5, @-sp
mov.l er6, @-sp
mov.b r1l, r4h
ターゲットのビットが引数r1lに入っているのでr4hに置いておきます。
mov.l #_la_high, er5
mov.l #_la_low, er6
サンプリングしたデータをとっておく配列の先頭アドレスです。
mov.l er0, er3
サンプリング回数は引数er0に入っているのでer3に置いておきます。
shll er3
shll er3
データは4byteなので、二回シフトして4倍しておきます。
add.l er5, er3 ; er3 max loop
サンプリングデータの終了地点をer3としておきます。
sub.l er0, er0
sub.l er1, er1
er0 = 0
er1 = 0
ph_loop: adds #1, er0 ; high count
mov.b @0xd2:8,r4l ; r = *P9_DR
btst r4h, r4l ; z = !(r & 0x10)
bne ph_loop ;
do {er0++;} while (*P9_DR & r4l)
ここが抜けた所でLOWです。
mov.l er1, @er6 ; 1st data of e[] is bogus.
mov.l er0, @er5
ここでer0の他にまだ計測していないer1までセーブしているのはHIGH区間の
計測と、LOW区間の計測の間の時間を均等にしたいため。この区間は結構暇。
adds #4, er5
adds #4, er6
次のデータに向けてインクリメント(4byte)
sub.l er0, er0
sub.l er1, er1
次の計測に向けて。
er0 = 0
er1 = 0
pl_loop: adds #1, er1 ; low count
mov.b @0xd2:8,r4l ; r = *P9_DR
btst r4h, r4l ; z = !(r & 0x10)
beq pl_loop
do {er1++;} while (!(*P9_DR & r4l));
cmp.l er5, er3
bne ph_loop
サンプリングの終了判定。ここが結構時間かかるので、er1のセーブを上でしている。
mov.l @sp+, er6
mov.l @sp+, er5
mov.l @sp+, er4
rts
これでクロック信号をサンプリングすると、
拡張RAM バス幅8bit 3ステートだと
ExtRAM 8bit 3state.
47, 4 3
48, 4 3
49, 241783 52
50, 4 38
51, 4 3
52, 4 3
53, 4 3
54, 4 3
55, 4 3
56, 4 4
57, 4 3
58, 4 3
59, 4 3
60, 4 3
61, 4 3
62, 4 3
63, 4 4
64, 4 3
65, 4 3
66, 4 3
67, 4 3
68, 4 3
69, 4 3
70, 3 4
71, 4 4
72, 4 3
73, 4 3
74, 4 82
75, 4 3
76, 4 3
77, 4 3
78, 4 4
79, 4 3
80, 4 3
81, 4 3
82, 4 3
83, 4 3
84, 4 3
85, 3 4
86, 4 4
87, 4 3
88, 4 3
89, 4 3
90, 4 3
91, 4 3
92, 4 3
93, 4 4
94, 4 3
95, 4 3
96, 4 3
97, 4 3
98, 241784 52
99, 4 37
100, 4 3
拡張ラム バス幅8bit 2ステートだと、
Ext RAM 8bit 2state.
48, 6 5
49, 346907 76
50, 6 55
51, 6 6
52, 6 5
53, 6 6
54, 6 5
55, 6 6
56, 6 5
57, 6 6
58, 6 5
59, 6 6
60, 5 6
61, 6 6
62, 6 6
63, 6 5
64, 6 6
65, 6 5
66, 6 6
67, 6 5
68, 6 6
69, 6 5
70, 6 6
71, 6 5
72, 6 6
73, 6 6
74, 6 118
75, 6 6
76, 6 5
77, 6 6
78, 6 5
79, 6 6
80, 6 6
81, 6 5
82, 6 6
83, 6 5
84, 6 6
85, 6 5
86, 6 6
87, 6 5
88, 6 6
89, 6 5
90, 6 6
91, 6 6
92, 6 5
93, 6 6
94, 6 5
95, 6 6
96, 6 5
97, 6 6
98, 346908 75
99, 6 54
100, 6 6
内蔵ロム バス幅16bit 2ステートだと
ROM 16bit 2state
47, 11 11
48, 11 12
49, 613759 136
50, 11 99
51, 11 11
52, 11 12
53, 11 12
54, 11 12
55, 11 11
56, 11 12
57, 11 12
58, 11 12
59, 11 11
60, 11 12
61, 11 12
62, 11 11
63, 11 12
64, 11 12
65, 11 12
66, 11 11
67, 11 12
68, 11 12
69, 11 12
70, 11 11
71, 11 12
72, 11 12
73, 11 11
74, 11 213
75, 11 11
76, 11 12
77, 11 12
78, 11 11
79, 11 12
80, 11 12
81, 11 12
82, 11 11
83, 11 12
84, 11 12
85, 11 12
86, 11 11
87, 11 12
88, 11 12
89, 11 12
90, 11 11
91, 11 12
92, 11 12
93, 10 12
94, 11 12
95, 11 12
96, 11 12
97, 11 11
98, 613760 135
99, 11 99
100, 11 12
になる。ここでもメモリアクセスのスピードがそのまま結果に出た。バス幅が
半分になって速度半分、2ステートから3ステートになってさらに1.5倍時間がか
かっている。さすがにこの信号を8bit 3stateではとりきれないので、スタート
アップでメモリチェックをすること前提に8bit 2stateにしました。これなら
なんとかギリギリいける。たまにCS5につながってるRAMが失敗する。
あと、gccのコンパイルオプションで-mint32(intを32bitとする、指定しなけれ
ば16bit)を外すと、実行速度があがる。-mint32にすると例えば構造体の配列の
アクセスに32bitの乗算が無条件に呼ばれてしまう(mulsi3とか)のでだめ。int
16bitでも、コーディングに気をつけないとすぐ呼ばれてしまう。m[i] みたい
のはだめで、最初にp = m; p++形式にしないといいコードを吐いてくれない。
以前のCで書いた信号取り込みルーチンは内蔵RAM/ROM上では大丈夫なのだけど、
拡張RAM上ではほとんど取りこめない。アセンブラに。いずれここはキュンキュ
ンにチューンしたいと思ってたとこだし。
アセンブラの際はできるだけ、バイト相手にしておくと命令長を小さくできる。
結構、レジスタを使わずに処理できるのでよく命令のデータシートを読む。実
はCISCのアセンブラを書くのは初めてで驚きが多いです。「これがレジスタ一
つも使わずに一命令かよ!...」みたいな。
#define CLK_PORT @0xd2:8
#define CLK_BIT #4
#define DATA_PORT @0xd2:8
#define DATA_BIT #5
#define FUNC_NAME _irq4
#define HI_LIMIT #20
.h8300h
.section .text
.global FUNC_NAME
FUNC_NAME: ;; LOW
mov.l er0, @-sp
mov.l er1, @-sp
mov.l er2, @-sp
mov.l er3, @-sp
sub.l er2, er2 ; caliper data
mov.l #1, er3 ; bit
立ち下がりで割り込みが入る。ここからクロックがはじまるまでに50usecあるので
このくらい用意しておいても大丈夫。
wait_rising_edge0:
mov.b CLK_PORT, r0l
btst CLK_BIT, r0l
beq wait_rising_edge0
count_high_level:
;; HIGH
mov.b HI_LIMIT r0h ; limit count.
wait_falling_edge0:
add.b #-1, r0h
beq error_return_0 ; too long high region.
mov.b CLK_PORT, r0l
btst CLK_BIT, r0l
bne wait_falling_edge0
クロックがはじまって、立ち下がりがいつまでたってもこなければ、間違えた
ところでエッジをとってしまったので、次のターンを待ちます。
;; LOW
mov.b #20 r0h
wait_rising_edge1:
add.b #-1, r0h
beq sampling_start ; data phase start.
mov.b CLK_PORT, r0l
btst CLK_BIT, r0l
beq wait_rising_edge1
bra count_high_level
最初の24クロックは、本来とるべきデータの相補な値を供給するのだけれど、
ここはタイミングとりだけに使ってます。後半までに110usecのLOW区間があるので
それを検出して、データ取りにうつります。
sampling_start:
;; still LOW.
mov.b #23, r1l ; r1l counter.
24クロックとります。カウンタが8bitから16bitになると命令長が倍になるので、
できるだけ小さく。(レジスタを使う量は増えるけれど...)
wait_rising_edge2:
mov.b CLK_PORT, r0l ; waiting for rising edge.
btst CLK_BIT, r0l
beq wait_rising_edge2
;;
;; data phase
;;
;; HIGH
sampling_loop: ; wait for falling edge.
mov.b HI_LIMIT r0h ; limit count.
wait_falling_edge1:
add.b #-1, r0h
beq error_return_1 ; too long high region. return.
mov.b CLK_PORT, r0l
btst CLK_BIT, r0l
bne wait_falling_edge1
なんらかの失敗でHIGH区間が続いてしまった場合は、もうデータはとれないので
次のターンを待ちます。
;; LOW
mov.b DATA_PORT, r0l ; sample data bit.
btst DATA_BIT, r0l
beq data_bit_not_set
or.l er3, er2 ; set data.
data_bit_not_set: ; wait for rising edge
立ち下がりの後にデータを取ります。
mov.b CLK_PORT, r0l
btst CLK_BIT, r0l
beq data_bit_not_set
;; HIGH
shll.l er3 ; next bit.
ループに一回ずつシフトすることで、1bitシフトしかないのをカバー。
;; 24clock?
add.b #-1, r1l
bpl sampling_loop
;;; sampling done.
mov.l #_shan_data, er0
mov.l er2, @(0x0, er0) ; copy data.
うまくいったので、データを所定のメモリに置きます。
mov.l #_shan_data_updated, er0
bset #0, @er0 ; update flag.
データがアップデートされたことを知らせます。
bra success_return
error_return_0:
mov.l #_shan_error, er0
bset #0, @er0
bra success_return
error_return_1:
mov.l #_shan_error, er0
mov.b r1l, @_shan_error_cause,
bset #1, @er0
失敗したので、失敗したことを知らせ、その原因もとっておきます。
success_return:
mov.l @sp+, er3
mov.l @sp+, er2
mov.l @sp+, er1
return:
mov.l @_shan_thread, er0
jsr @_thread_wakeup
割り込みスレッドを起して残りの処理をまかせます。
mov.l @sp+, er0
bclr #4, @0xf6:8 ; ISR clear flag during data sampling.
この割り込み処理中に立ち下がりエッジは何度もでてきているので、割り込み待ち
状態になっている。それはいらない待ちなので、クリアして次を待ちます。
rte
void
shan_main ()
{
shan_thread = current_thread;
iprintf ("shan_thread=%x\n", shan_thread);
thread_priority (current_thread, PRI_HIGH); // interrupt thread.
*P9_DDR = ~0x30; // 4, 5 Input.
*INTC_ISCR = 0x10; // Falling edge. IRQ4
*INTC_IER = 0x10;
intr_enable ();
while (/*CONSTCOND*/1)
{
int32_t d = 0;
thread_sleep (current_thread);
割り込みハンドラでデータが更新された、あるいはエラーが起きたら、起こされます。
int s = intr_suspend ();
if (shan_error)
{
iprintf ("error=%d cause=%d\n", shan_error, shan_error_cause);
shan_error = 0;
}
if (shan_data_updated)
{
d = shan_data[0];
shan_data_updated = 0;
}
intr_resume (s);
割り込みハンドラと共有するデータなので、割り込みを禁止して取りこみます。
shan_print (d);
後は呑気に表示します。
}
}
void
shan_print (int32_t a)
{
if (a & 0x800000)
a |= 0xff000000;
a >>= 3;
LCD_PRINTF ("%d, %d\n", a, *INTC_ISR & 0x10);
}
こんな感じに実装したくて、延々とRTOS書いてた。やっぱりレディキューから
全て抜かれた時にコンテキストスイッチのルーチンでスピンループして割り込
みを待つようにしないと、データの落としがひどいです。
あと、たまにデータがおかしくなる。それは
$ bit 1388
.....................|.|.||.||.. 10 8 6 5 3 2 [0x0000056c] 1388
$ bit 1516
.....................|.||||.||.. 10 8 7 6 5 3 2 [0x000005ec] 1516
取りこめてない時がある様子。
最近のコメント