081228

|


イサギダプロダクツから購入したデジタルスケールは、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
取りこめてない時がある様子。