ATtiny10 用プログラム (10)

ATtiny10 版の MIDI to CV プログラムを作っています。
ATmega168 用の同種のプログラムを元に処理を削り、ピッチベンド等のサポートのない、7 ビットの MIDI ノート番号を CV として PWM で出力し、ゲート信号を ON/OFF するだけの最低限の機能のプログラムで、フラッシュ・メモリは 700 バイト、グローバル変数 は 18 バイト程度になりました。
プログラム領域にはまだ余裕があるので、削った処理を復活して、もう少し機能を追加しようと考えており、プログラムを公開できるようになるまで、まだ少し時間がかかると思います。
そこで、まずは、このプログラム中で使っているソフトウェア・シリアル受信ルーチンについて説明したいと思います。
ATtiny10 にはハードウェア UART は内蔵されていないので、MIDI メッセージ受信のためには、ソフトウェアでシリアル受信を行わなければなりません。
最も簡単なのが、ソフトウェア・ループでタイミング調整をして、受信データ (RxD) を各ビットの中央付近の位置でサンプリングしてデータバイトを組み立てることです。
この方法では、スタートビットの検出に始まり、最終のデータビットのサンプリングまで、タイミングを維持するためプログラムは受信に専念する必要があり、受信したデータの処理に費やせる時間は、最悪値で、最終データビットの残り半分と、ストップビット 1 ビット分との、合計 1.5 ビット・タイムしかありません。
現状の MIDI2CV プログラムでは大した処理はしておらず、1.5 ビット・タイムの中におさまり、実際上この方法でも問題はありません。
この方法のデメリットは、CPU が喰われることで、メリットは、データ・メモリの消費が少ないことです。
CPU が喰われないようにするには割り込みを使う方法があり、たとえば Atmel のアプリケーション・ノート AVR304 (Half Duplex Interrupt Driven Sofware UART) では、外部割込みと、8 ビット・タイマを 1 個使って半二重 (送信と受信は同時に行わない) のソフトウェア UART を実現しています。
この方法では、受信中も CPU は他の仕事を実行できるメリットがありますが、一方で、割り込み時のレジスタ退避のためにスタックが消費されるデメリットがあります。
ATtiny10 (および ATtiny13 など) では、ハードウェア・タイマは 1 個しかなく、PWM DAC として CV 出力用に使うので、ソフトウェア UART 用に使えるハードウェア・タイマはありません。
したがって、普通では割り込み方式でのソフトウェア UART の実現はできないことになります。
単に「タイマ」としてなら、「ウォッチドッグ・タイマ」もあり、リセットばかりではなく、周期的な割り込みの発生源とすることも可能ですが、周期が長すぎ、UART の用途には向きません。
ここで、もうひとつ、周期的な割り込みの発生源として利用可能なのが AD コンバータの変換終了割り込みです。
AD コンバータの動作モードには、変換開始のトリガのかけかたにより次の 3 つのモードがあります。

  1. 単一変換モード (Single Conversion)
  2. 連続変換モード (Free Running Conversion)
  3. 自動トリガモード (Auto Triggered Conversion)

3. のオートトリガ・モードは、ADC 「外部」からのトリガに従って変換を開始するもので、すでに「外部」に周期的なトリガ源があるのなら、なにもわざわざ ADC を経由する必要もありません。
したがって、2. のフリーランニング・モードが、唯一利用可能な周期的割り込み発生源です。
フリーランニング・モードを開始した最初の変換には 25 ADC クロックを要し、それ以降の変換には 13 ADC クロックを費やします。
この 13 クロック周期が、(レガシー) MIDI (31.25 kbps) あるいはシリアル MIDI (38.4 kbps) のボーレートと一致していなければなりません。
最初の変換には 25 クロックかかるので、最初の変換終了割り込みでデータビットの LSB をサンプリングするためには、1 ビット・タイムを 25 ADC クロック以上に選ぶ必要があり、それ以降が 13 クロック周期であることを考慮すると、

1 ビット・タイム = 2 × 13 = 26 ADC clock

と選ぶことになります。
AD 変換終了割り込みが 13 ADC クロックごとに発生しますが、割り込みサービスルーチンでは、2 回に 1 回だけ RxD 入力をサンプリングしてシフトレジスタに入力して受信データを組み立てていくことになります。
ADC クロックの周波数を計算すると、まず、シリアル MIDI の場合は

fADC_clk = 38.4 [kbps] × 2 × 13 = 998.4 [kHz]

ADC クロック・プリスケーラを 1/8 に選ぶと、CPU クロックは

fCPU = 998.4 [kHz] × 8 = 7.9872 [MHz]

となります。
CPU クロックが正確に 8 MHz であった場合の誤差を計算すると +0.16 % になります。
したがって、目的の電源電圧で ATtiny10 の内部 8 MHz オシレータの周波数誤差が小さければ、OSCCAL はデフォルト値のままで、特に調整しなくてすみます。
レガシー MIDI の場合には、

fADC_clk = 31.25 [kbps] × 2 × 13 = 812.5 [kHz]
fCPU = 812.5 [kHz] × 8 = 6.5 [MHz]

となるので、必ず OSCCAL の値を調整して、CPU クロック周波数を 6.5 MHz に合わせ込むことが必要です。
AD コンバータが、フルの 8 ビット分解能で動作するためには、ADC クロックは 200 kHz 以下にする必要がありますが、この場合、限度をはるかに超える約 1 MHz を入力しているので、本来の目的のアナログ電圧をディジタル値に変換する機能は失われてしまいます。
プログラムを動作させた場合の波形写真を下に示します。

上のトレースがシリアル MIDI (38.4 kbps)の RxD の波形で、スタートビット、データビットの b0 (LSB)、b1 付近を見ています。
下のトレースは、後で示すソースリスト中で「UART_DEBUG」シンボルを定義した場合の出力で、割り込みサービス・ルーチンの最初で「H」レベルを出力し、最後で「L」レベルを出力するようになっています。
通常はシンセへのゲート信号出力となる 3 番ピンを、UART 処理ルーチンのデバッグ用の出力として利用しています。
「スタートビット」の検出には、「ピン変化割り込み」(PCINT: Pin Change INTerrupt) を利用しています。
PCINT では H → L、L → H の両方のエッジで割り込みがかかりますが、スタートビットのエッジの H → L を検出するために、ピン変化割り込みサービス・ルーチン内で RxD 信号をサンプリングして、「L」レベルかどうかを確認しています。
PCINT の処理期間を示すパルスの幅が広くなっていますが、これは、ADC 割り込みのタイミングがデータビットの中央付近に位置するように時間調整を行ってから ADC をスタートさせているためです。
データビットの b0 の中央付近のパルスがフリーラン・モードでの最初の AD 変換終了割り込みです。
この割り込みサービス・ルーチン内の処理で RxD の値をサンプリングします。
b0 と b1 の境界付近の細いパルスが 2 回目の AD 変換終了割り込みで、これはサンプリング・タイミングではないので、なにも処理はせず割り込みサービス・ルーチンを抜けているので、パルス幅が「細く」、つまり処理時間が短くなっています。
同様に、b1 の中央付近の ADC int では RxD のサンプリングを行うため、パルスが「太く」なっています。
実際の MIDI メッセージを受信した場合の波形写真を下に示します。
0x90 から始まる、3 バイトの「ノート・オン」メッセージです。

下のトレースで、他と少し離れているのがスタートビットのエッジを検出している PCINT の処理の部分で、密集しているのが AD 変換終了割り込みの処理部分です。