IchigoJamでAquesTalk pico LSIに喋らせる

# 概要

IchigoJamのI2C機能を用いて、AquesTalk pico LSIにデータを送って喋らせます。

## 使用ツール

### IchigoJam

2,000円前後で購入することができ、BASICによるプログラミングが可能なパソコンです。

[こどもパソコン IchigoJam - はじめてのプログラミングパソコン(1500円)](https://ichigojam.net/)

また、プログラムをWebブラウザ上で実行できるサービスもあります。

[IchigoJam web by WebAssembly](https://fukuno.jig.jp/app/IchigoJam/)

※「IchigoJam」はjig.jpの登録商標です

### AquesTalk pico LSI

株式会社アクエスト様の製品で、UART・I2C・SPIで文字列を送って発声させることができるICチップです。
[音声合成LSI - AquesTalk pico LSI](https://www.a-quest.com/products/aquestalkpicolsi.html)

[秋月電子通商](http://akizukidenshi.com/catalog/top.aspx)などで購入することができます。
今回は、ATP3011F1-PU (秋月電子通商の通販コード：I-06220) を用います。

## 免責

この記事で紹介する回路は筆者の手元環境で実験を行っていますが、
特に音声出力まわりについては正直に言ってよくわかっていないので、適切でない可能性があります。
また、記事の記述を参考にする際、配線を間違えたり操作を誤ったりすると、
ショートなどにより各種素子を破壊する可能性があります。
記事の内容を実践する際は、ミスの無いよう十分注意した上で、**自己責任で**お願いします。

# ステップ1：とりあえず喋らせてみる

IchigoJamとの連携は後で行うこととして、まずはAquesTalk pico LSIに喋らせてみます。
電源および音声出力まわりの配線を行い、UARTを用いて文字列を送ります。
UARTとは、IchigoJamでキーボード相当の入力を入れたり`PRINT`した内容が出たりする
「シリアル通信」のことであり、USB経由でパソコンと接続することができるデバイスが各種あります。

## 配線

今回使うICは、「28ピンDIP」というタイプです。
上から見下ろし、くぼみが左側になるように横長に置いた時、左下のピンが「1」であり、
そこから反時計回りにピンの番号が増えていきます。

![28ピンDIP ICのピン配置](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/230a1f3d-1b04-5fc8-242a-64703925f19c.png)

今回は、電源もUSB-UART変換デバイスから取って動かしました。
3.3Vまたは5Vのタイプであれば、多分使えます。
以下のように配線しました。書いていないピンはオープン(特に接続しない)です。
「送信→受信」の接続にするので、TXとRXを接続します。
(これがいいとは限りません。これで聴けましたが、音声出力まわりは適当なので改良の余地があるはずです)

![ATP3011とUSB-UART変換器の接続](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/1c437c81-a421-1c6e-63c3-2618b93028f6.png)

## 操作

[Tera Term](https://ja.osdn.net/projects/ttssh2/)などのシリアル通信の制御ができるソフトウェアを用いて操作を行います。
AquesTalk pico LSIに送るコマンドの区切りはCR(`\r` / `\x0d`)なので、送信用の改行コードをCRに設定しておきます。

AquesTalk pico LSIへの電源投入後、最初に`?`を送ります。これによってシリアル通信の速さを合わせます。
その後、コマンドを送ります。

音声記号列やコマンドについて詳しくは、[株式会社アクエスト様のページ](https://www.a-quest.com/products/aquestalkpicolsi.html)にあるデータシートを参照してください。
今回扱っているのは「ATP3011XX」です。

### 音声記号列

音声記号列は、基本的にはローマ字にアクセントの指定を加えたものです。
アクセントの指定は、「アクセントの位置の指定」と「区切り」からなります。
「アクセントの位置の指定」は、下がる位置に`'`を書きます。
これは区切り～区切りに1個まで書けます。書かなくてもいいです。
「区切り」の記号は、`.`(文の終わり)、`,`(小停止)、`/`(停止の無い区切り)などがあります。

例えば、「阿澄佳奈はいいぞ」→`a'sumi/ka'na/wa/i'izo`のように書けます。

### コマンド

<dl>
<dt>発声</dt>
<dd>音声記号列に続けてCRを送ると、それに従って発声します。</dd>
<dt>チャイム</dt>
<dd><code>#J</code>または<code>#K</code>に続けてCRを送ると、それぞれ対応するチャイムが鳴ります。</dd>
<dt>バージョンの確認</dt>
<dd><code>#V</code>に続けてCRを送ると、バージョンコードが通信で返ってきます。</dd>
</dl>

# ステップ2：IchigoJamとの連携

とりあえず喋らせることができたら、次にIchigoJamとの連携を行います。
UARTを使おうとすると関係ない出力と競合しそうなので、
今回はIchigoJamにAPIが用意されているI2Cを用います。

## 配線

I2Cの特長として、「アドレスがぶつからなければ、同じバスに複数のデバイスを接続できる」というものがあります。
すなわち、I2Cを用いるEEPROMと併用することができます。
今回は併用する配線を行います。
I2Cバスにはプルアップが必要ですが、今回はEEPROMモジュールに含まれています。
`SMOD0`をGNDに接続することで、AquesTalk pico LSIをI2C通信を使うモードにします。

![ATP3011とIchigoJamの接続](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/4910d26b-b43a-74ad-0d6f-185eab9683f0.png)


## IchigoJamのI2C API

IchigoJamにおけるI2Cの使い方については、公式のリファレンスに加え
[IchigoJamのI2Cインタフェースについて: 猫にコ・ン・バ・ン・ワ](http://nuneno.cocolog-nifty.com/blog/2016/09/ichigojami2c-8b.html)
の情報も参考になりました。

IchigoJamでI2Cを扱うAPIには、以下のものがあります。

<dl>
<dt><code>I2CR(deviceAddr, commandAddr, commandLength, dataAddr, dataLength)</code></dt>
<dd>
I2Cアドレス<code>deviceAddr</code>のI2Cスレーブに対して<br>
メモリの仮想アドレス<code>commandAddr</code>から<code>commandLength</code>バイトのデータを送信した後、<br>
<code>dataLength</code>バイトのデータを受信してメモリの仮想アドレス<code>dataAddr</code>に書き込みます。<br>
成功すれば<code>0</code>、失敗すれば<code>1</code>を返します。
</dd>
<dt><code>I2CW(deviceAddr, commandAddr, commandLength, dataAddr, dataLength)</code></dt>
<dd>
I2Cアドレス<code>deviceAddr</code>のI2Cスレーブに対して<br>
メモリの仮想アドレス<code>commandAddr</code>から<code>commandLength</code>バイトのデータを送信した後、<br>
さらにメモリの仮想アドレス<code>dataAddr</code>から<code>dataLength</code>バイトのデータを送信します。<br>
成功すれば<code>0</code>、失敗すれば<code>1</code>を返します。
</dd>
</dl>

`deviceAddr`は、最下位のR/Wを指定するビットを除いた7ビットで指定します。
AquesTalk pico LSI (ATP3011) のデフォルトでは`#2E`です。

`dataLength`はある程度大きくても大丈夫なようですが、
`commandLength`は1～4の範囲でないとうまく動かないことがあるようです。(0でもダメ)

## I2C APIを用いた音声記号列の送信

### 基本

#### ソースコード

```
10 'AquesTalk pico ﾃｽﾄ (ｷﾎﾝ)
20 'a'sumi/ka'na/wa/i'izo.
30 PRINT "playing..."
40 L=20:GOSUB500
50 FORI=0TOS-1:POKE#700+I,PEEK(P+I):NEXT
60 POKE#700+S,13:S=S+1
70 P=#700:GOSUB 600
80 IF IN(1)=0 GOTO 80
90 PRINT "finished."
100 END
500 P=#C00
510 N=PEEK(P)+PEEK(P+1)*256:S=PEEK(P+2)-1
520 IF N=L P=P+4:S=S-(PEEK(P+S-1)=0):RETURN
530 IF N=0 P=0:RETURN
540 P=P+4+S+1:GOTO510
600 X=I2CW(#2E,P,1,P+1,S-1):RETURN
```

#### 実行結果

YouTube動画 (0分22秒)
[![実行結果(YouTube)](http://img.youtube.com/vi/wliPnF0RKyA/0.jpg)](http://www.youtube.com/watch?v=wliPnF0RKyA)

#### 解説

音声記号列を1文字ずつ`POKE`でメモリに置いて利用するという選択肢もあるが、それだとわかりにくくなってしまうため、
今回はソースコードにコメントとして音声記号列を埋め込み、メモリ上の位置を探して利用することにしました。

IchigoJamのソースコードは、仮想アドレス`#C00`から以下のような形でメモリ上に格納されています。
![IchigoJamのソースコードのデータ構造](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/b0a8c92f-c584-d639-7c15-70e81df07a64.png)
行番号はリトルエンディアン(下位のバイトが先に来る)です。
「行内容」には、それぞれの行のテキストデータが入っています。ただしここの最後のバイトは0x00のことがあります。
そこで、この構造を利用して探索していけば、行番号からその行の内容があるアドレスと行の長さを求めることができます。
500行目～540行目で、実際にこの探索をしています。
`L`に求めたい行番号を入れて呼び出すと、`P`にその行の2文字目のアドレスを、`S`にその行の長さ-1を入れます。
コメントの行に対して用いることを想定しているため、最初の文字(コメントを表す`'`を想定)をカットするようにしました。
また、行内容の最後が0x00のとき、`S`はこの0x00を除いた値になります。

40行目で、このサブルーチンを利用して20行目のコメントに埋め込んだ音声記号列のアドレスと長さを求めます。
50行目でこの音声記号列を仮想アドレス`#700`からの領域にコピーし、60行目でCRを加えます。
そして、70行目でCRを加えた音声記号列を送信するサブルーチンを呼び出します。
このサブルーチン(600行目)では、コマンドの長さの制約に対応するため、
最初の1文字をコマンド、残りの文字をデータとして送信しています。

80行目で再生が終わるのを待っています。
再生中をLOWで表す`/PLAY`ピンを`IN1`に接続しているため、これで待つことができます。

### 数値の読み上げ

#### ソースコード

```
10 'AquesTalk pico ﾃｽﾄ (ｽｳﾁ)
20 'nyu-ryoku/sareta/su'-ti/wa,
30 'mainasu
40 '<NUMK VAL=
50 '>desu.
60 POKE #710,13
70 INPUT V:M=V<0:D=#710
80 D=D-1:POKE D,ABS(V%10)+48
90 V=V/10:IF V<>0 GOTO 80
100 L=20:GOSUB500:GOSUB600
110 IF M L=30:GOSUB500:GOSUB600
120 L=40:GOSUB500:GOSUB600
130 P=D:S=#710-D:GOSUB600
140 L=50:GOSUB500:GOSUB600
150 P=#710:S=1:GOSUB600
160 IF IN(1)=0 GOTO 160
170 GOTO 50
500 P=#C00
510 N=PEEK(P)+PEEK(P+1)*256:S=PEEK(P+2)-1
520 IF N=L P=P+4:S=S-(PEEK(P+S-1)=0):RETURN
530 IF N=0 P=0:RETURN
540 P=P+4+S+1:GOTO510
600 X=I2CW(#2E,P,1,P+1,S-1):RETURN
```

#### 実行結果

YouTube動画 (0分50秒)
[![実行結果(YouTube)](http://img.youtube.com/vi/l_OSyubnlkQ/0.jpg)](http://www.youtube.com/watch?v=l_OSyubnlkQ)

#### 解説

基本のサンプルと同様の、行のアドレス取得サブルーチンと送信サブルーチンを用いています。
音声記号列は分割して送信してもいいので、今回はIchigoJamのメモリ上で結合させるのではなく、
パーツごとに送る形式にしました。
数値読みタグの`NUMK`を用いており、音声記号列を組み立てると
`nyu-ryoku/sareta/su'-ti/wa,<NUMK VAL=15>desu.`のような形になります。
70行目で数値の入力を受け付けた後、80行目と90行目でその数値を文字列に変換し、読ませます。
数値を文字列に変換すると下の桁から決まっていくため、メモリに書き込む位置を手前にずらしていきます。

# まとめ

IchigoJamのI2C通信機能を用いて、AquesTalk pico LSIにコマンドを送り、喋らせることができました。
数値を扱う遊びなどへの応用が期待できます。