IchigoJam R でオシロスコープ

# 何をするか

IchigoJam R を用い、オシロスコープのようなものを作ります。

# 速度が足りるかを確かめる

今回は、1秒あたり約1,000サンプルでのサンプリングをすることにしました。
そこで、まずはこの速度が出せるかを確かめます。

```
10 ' 1kHz ﾙｰﾌﾟ ﾃｽﾄ
20 CLT:S=16
30 FORI=0TO101:T=I*S
40 IFTICK(-1)<TGOTO40
50 [I]=TICK(-1):NEXT
60 FORI=0TO101:[I]=[I]-S*I:NEXT
70 FORI=0TO9
80 FORJ=0TO9:?[I*10+J];" ";:NEXT
90 ?CHR$(10);:NEXT
100 ?[100];" ";[101]
```

`TICK(-1)`は、1秒あたり`60*261 = 15660`進む時刻を返します。
この時刻で16ごとにサンプリングすることにすると、
サンプリング間隔は`16 / 15660 ≒ 0.00102`となり、約1ミリ秒ごとにサンプリングができます。

IchigoJam R (1.5b、`VER()=15001`) での実行結果は以下のようになりました。

```
8 5 5 6 6 6 3 3 3 3
3 4 4 4 4 3 3 3 5 5
5 5 5 5 5 6 6 6 6 6
6 6 3 3 5 5 4 4 4 4
4 4 4 4 4 4 5 5 3 3
3 5 6 6 6 6 6 6 6 6
3 3 3 3 3 3 3 5 5 5
5 5 5 5 5 5 5 5 5 6
6 3 3 3 5 3 4 4 4 4
4 4 4 4 4 4 4 3 3 3
5 6
```

サンプリングをしたい時刻とサンプリングをした時刻の差がだいたい3～6で安定しています。
これはビデオ出力がオンの状態で実験しました。

参考までに、IchigoKamuy (1.4.1、`VER()=14114`) での実験結果は以下のようになりました。

```
43 90 185 237 260 344 430 454 503 597
647 671 756 841 865 915 1010 1058 1082 1169
1252 1275 1328 1422 1469 1492 1581 1663 1686 1740
1835 1880 1903 1994 2074 2097 2153 2247 2291 2314
2406 2485 2508 2565 2660 2702 2725 2819 2895 2919
2978 3072 3113 3137 3231 3306 3330 3390 3485 3524
3549 3644 3717 3741 3803 3897 3935 3962 4056 4128
4152 4215 4310 4345 4374 4469 4539 4563 4628 4722
4756 4787 4881 4950 4973 5040 5135 5167 5199 5294
5361 5384 5453 5547 5578 5612 5706 5772 5795 5865
5960 5989
```

実行速度が足りず、どんどん遅れてしまっている様子がうかがえます。
ビデオ出力をオフにして(`VIDEO0`を実行して)実験すると、以下のようになりました。

```
53 86 119 152 186 219 251 284 318 351
384 417 450 483 516 550 582 615 649 682
715 748 781 814 847 881 914 946 980 1013
1046 1079 1112 1145 1178 1211 1245 1278 1310 1344
1377 1410 1443 1476 1509 1542 1576 1609 1641 1675
1708 1741 1774 1807 1840 1873 1907 1940 1973 2005
2039 2072 2105 2138 2171 2204 2237 2271 2304 2337
2370 2403 2436 2469 2502 2535 2568 2602 2635 2668
2701 2734 2767 2800 2834 2866 2899 2932 2966 2999
3032 3065 3098 3131 3164 3198 3230 3263 3297 3330
3363 3396
```

ビデオ出力をオフにすると少しは速くなりましたが、それでも実行速度が足りないようです。

# オシロスコープの実装

## 解説

タイトルを置き、使用する変数と定数を初期化します。
矢印の文字を用いるので、キャラクターパターンも初期化します。

以下の変数と定数を使用します。

* 定数
  * `S`：サンプリング間隔 (speed)
  * `N`：サンプル数 (number)
  * `M`：1回の操作で表示位置が動く幅 (move)
* 変数
  * `T`：トリガレベル (trigger)
  * `E`：トリガモード (edge)
  * `D`：表示位置 (delta)
  * `C`：連続モードフラグ (continuous)
  * `H`：ホールドフラグ (hold)

```
10 ' ｵｼﾛｽｺｰﾌﾟ
20 CLP:S=16:T=20:E=0:D=0:C=1:H=0:N=200:M=10
```

サンプリングを実行します。
`ANA()`は0～1023の値を返すので、表示用の0～31の値に変換して格納します。

```
30 CLT:FORI=1TON:W=I*S
40 IFTICK(-1)<WGOTO40
50 POKE#7FF+I,ANA()>>5:NEXT
```

サンプリングが完了したので、連続モードでない場合はホールドフラグを立てます。

```
60 IF!CH=1
```

状態を描画します。

```
70 CLS:?"ﾄﾘｶﾞ:";
80 IFE<2?DEC$(T,2);CHR$(#E2+E);ELSE?"ﾅｼ ";
90 IFC?" ﾚﾝｿﾞｸ";ELSE?"      ";
100 IFH?" HOLD"ELSE?""
```

波形のグラフと表示位置を描画します。
以下の変数を使用します。

* `B`：表示位置を表すバーの表示範囲に相当する長さ (bar)
* `L`：表示位置の右端の位置 (limit)
* `X`：表示位置を表すバーの左端の位置

```
110 DRAW0,45-T:FORI=1TO62:DRAWI,45-PEEK(#7FF+D+I),I+1,45-PEEK(#800+D+I):NEXT
120 B=64*63/N:L=N-63:X=D*(64-B)/L:DRAWX,47,X+B-1,47
```

トリガ判定用に、前回の計測値を表す変数を用意します。

```
130 V=ANA()>>5
```

キー操作を受け付けます。
シリアル通信でも操作できるよう、矢印キーだけでなくアルファベットキーでの入力も受け付け、
さらにアルファベットの小文字が入力された場合は大文字に変換して判定します。
更新後の値を指定の範囲に収めつつ、1行で値を更新して`GOTO`命令を実行できるよう、
`IF`命令を使わずに計算式で工夫をしています。

```
140 K=INKEY():IF#61<=KANDK<=#7AK=K-#20
150 IFK=#41ORK=LEFTD=(D-M)*(D>=M):GOTO70
160 IFK=#44ORK=RIGHTD=(D+M)*(D<=L-M)+L*(D>L-M):GOTO70
170 IFK=#57ORK=UPT=T+(T<30):GOTO70
180 IFK=#53ORK=DOWNT=T-(T>1):GOTO70
190 IFK=#54:E=(E+1)%3:GOTO70
200 IFK=#43:C=!C:GOTO70
210 IFK=#48:H=!H:GOTO70
```

値を計測し、トリガの判定を行います。
トリガの条件を満たした場合はサンプリングへ、満たしていない場合はキー入力へ行きます。

```
230 W=ANA()>>5
240 IFHGOTO280
250 IFE=2GOTO30
260 IFE=0ANDV<TANDW>=TGOTO30
270 IFE=1ANDV>TANDW<=TGOTO30
280 V=W:GOTO140
```

## コード全体

```
10 ' ｵｼﾛｽｺｰﾌﾟ
20 CLP:S=16:T=20:E=0:D=0:C=1:H=0:N=200:M=10
30 CLT:FORI=1TON:W=I*S
40 IFTICK(-1)<WGOTO40
50 POKE#7FF+I,ANA()>>5:NEXT
60 IF!CH=1
70 CLS:?"ﾄﾘｶﾞ:";
80 IFE<2?DEC$(T,2);CHR$(#E2+E);ELSE?"ﾅｼ ";
90 IFC?" ﾚﾝｿﾞｸ";ELSE?"      ";
100 IFH?" HOLD"ELSE?""
110 DRAW0,45-T:FORI=1TO62:DRAWI,45-PEEK(#7FF+D+I),I+1,45-PEEK(#800+D+I):NEXT
120 B=64*63/N:L=N-63:X=D*(64-B)/L:DRAWX,47,X+B-1,47
130 V=ANA()>>5
140 K=INKEY():IF#61<=KANDK<=#7AK=K-#20
150 IFK=#41ORK=LEFTD=(D-M)*(D>=M):GOTO70
160 IFK=#44ORK=RIGHTD=(D+M)*(D<=L-M)+L*(D>L-M):GOTO70
170 IFK=#57ORK=UPT=T+(T<30):GOTO70
180 IFK=#53ORK=DOWNT=T-(T>1):GOTO70
190 IFK=#54:E=(E+1)%3:GOTO70
200 IFK=#43:C=!C:GOTO70
210 IFK=#48:H=!H:GOTO70
230 W=ANA()>>5
240 IFHGOTO280
250 IFE=2GOTO30
260 IFE=0ANDV<TANDW>=TGOTO30
270 IFE=1ANDV>TANDW<=TGOTO30
280 V=W:GOTO140
```

# 使い方

## 計測を行う信号の入力

計測を行う信号は、BTN端子に入力します。
0V～電源電圧(3.3V)が有効です。

## トリガの設定

### トリガモードの設定

`T`キーを押すと、トリガモードが切り替わります。
以下のトリガモードがあります。

* 信号がトリガレベル未満からトリガレベル以上になった時に計測を実行 (「↑」で表示)
* 信号がトリガレベル超からトリガレベル以下になった時に計測を実行 (「↓」で表示)
* 無条件で計測を実行 (「ナシ」で表示)

### トリガレベルの設定

`W`キーまたは`↑`キーを押すと、トリガレベルが上がります。
`S`キーまたは`↓`キーを押すと、トリガレベルが下がります。
トリガレベルは、「トリガ：」の右の数字と画面左端の点で表示されます。

### 連続モードの設定

`C`キーを押すと、連続モードかどうかが切り替わります。
連続モードでは、「レンゾク」と表示され、繰り返し測定を行います。
連続モードでない場合、1回計測をすると自動でホールドモード(後述)になります。

## ホールドモード

`H`キーを押すと、ホールドモードかどうかが切り替わります。
ホールドモードでは「HOLD」と表示され、トリガの条件を満たしても計測を実行しません。

## 表示位置の移動

`A`キーまたは`←`キーを押すと、波形の表示位置が左に移動します。
`D`キーまたは`→`キーを押すと、波形の表示位置が右に移動します。

# 実行結果

## UART信号の計測

300bpsのUARTで「UI」(0x55 0x49)を送信した時の信号を計測してみました。

![「UI」を送信した結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/5bb737cc-2e24-50ff-3d74-39625df69682.png)

このUARTでは、1バイトのデータを

* スタートビット (1ビットの0(LOW))
* データ (下位のビットから上位のビットの順に、0をLOW、1をHIGHで表す)
* ストップビット (1ビットの1(HIGH))

の10ビットで表しますが、この様子が見えています。

## 音声信号の計測

[秋月電子通商](https://akizukidenshi.com/catalog/)の「高感度マイクアンプキット」(K-05757)を接続し、音声の信号を計測してみました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/Y5M0JlPFS_Q" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

声を高くすると波の山の間隔が狭くなる様子が見られます。

# おわりに

IchigoJam R の高速動作を活かし、1秒あたり約1,000回サンプリングを行うオシロスコープを作ることができました。

* IchigoJamはjig.jpの登録商標です。
* IchigoKamuyは株式会社syushuの登録商標です。