I2Sからポーリングで出力する

いつもの飲み会の前に何とか動かそうと、11月の終わり頃「エイヤッ」と作ったDMA版I2S駆動プログラムは当然のように動きませんでした。俺のプログラムがそんなに動くわけがない。しかし未知のハードウェアですし、闇雲につつき回しても動くはずがありません。じっくり時間をかけられるタイミングを探していましたが、大掃除も終わった年末、ようやく時間をとれました。押し入れのオシロスコープが唯一の味方です。僕は測定器が少ない。
プログラムからDMA関連のコードを切り離してI2SSTATEレジスタFIFO長を監視しながらI2Sにポーリングで送信するプログラムに書き換えました。メインループはこんな感じです

while (1)
{
  if ( TXFIFO長が0 )
  {
    I2STXFIFO = 送信データ;
    フラグピンをトグル;
  }
}

動かしてみると、データがI2S_SDAから出てきません。PINSELを変更し、TX_SDAピンをGPIOにしてトグルしてみるとぱたぱた動きますからショートしているわけではないようです。また、フラグピンはちゃんと24kHzの矩形波でトグルしています。今にして思えばここで気づくべきでしたが、この後各レジスタのチェックや値の修正で半日かけてしまいました。
ついにやることが無くなってしまい呆然とオシロの画面を見ていると、例のフラグピンの信号が揺らいでいます。TXFIFOへの書き込みになにかありそうです。やけっぱちでプログラムを次のように修正したのが夕食後です。

while (1)
{
  if ( TXFIFO長が8未満 )
  {
    I2STXFIFO = 送信データ;
    フラグピンをトグル;
  }
}

これで見事データがI2S_SDAから出てきました。えええ?と首をかしげながらフラグピンを確認すると、針のようなパルスが48kHz周期で出力されています。しばらく考えた後、以下のような結論にたどり着きました。
フラグピンが針のようなパルスになっていると言うことは、LRデータを立て続けに書き込んでいると言うことで、言い換えるとI2SペリフェラルはLRデータをTXFIFOから立て続けに取り出していることになります。そこで、最初のプログラムでは次のような事が起きたのだと思われます。

  1. TXFIFOが空の状態を検知すると、プログラムは1ワードをTXFIFOに書き込む
  2. I2Sは次の送信タイミングが来ると、LRのデータを連続して取り出そうとする
  3. しかし、FIFOにはLのデータしかないため、I2Sはアンダーランと判断してデータを読み捨て、0*1をLRに出力する
  4. 1の状態に戻る

さてこの仮説を立ててみて、UM10360に以下のような記述があることを思い出しました。

When the transmit FIFO contains insufficient data the transmit channel will repeat transmitting the last data until new data is available.

ここにはLRをペアで扱うなどとは一言も書いていません。しかし、しばらく後に次のような一文が現れます。これは今回まで見落としていました。

Data is read from the transmit FIFO after the falling edge of WS, it will be transferred to the transmit clock domain after the rising edge of WS. On the next falling edge of WS the left data will be loaded in the shift register and transmitted and on the following rising edge of WS the right data is loaded and transmitted.

送信シフトレジスタにはLRのデータがそれぞれWSの立ち下がり、立ち上がりで書き込まれます。しかし、TXFIFOからはWSの立ち下がりで一度読み込まれるだけです。つまり、LRデータは一つとして扱われており、これが不完全なら先の一文によってデータは破棄され、以前のデータが再送されるわけです。どうやら仮説は正しいようです。
初めて使うプロセッサは、ペリフェラルの挙動も未知です。ライブラリ関数を使って扱えばいいのですが、ライブラリ関数も説明は不完全なことが多く、しかも例題が自分の必要とする動作モードでなければ結局は手探りになります。私はペリフェラルの感触をつかむまではライブラリ関数を使わず、自分でレジスタにアクセスすることが多いです。当然、茨の道です。

*1:TXFIFOの初期値