Arduino、DAC、オーディオアンプで高度なXBeeワイヤレスマイクを自作
必要な部品と用品
| 部品 | 数量 | 備考 |
|---|---|---|
| Goldilocks Analogue | 1 | プロトタイプ段階。MCP4822 DAC、マイクアンプ、ヘッドホンアンプ、Arduino Unoで機能再現可能 |
| MAX9744 | 1 | |
| MAX9814 | 1 | |
| MCP4921 DAC | 1 | |
| Arduino UNO | 1 | |
| Arduino Wireless Shield (XBee) | 1 |
このプロジェクトについて
AVR ATmega1284p MCUベースの高度なArduinoクローンを製作中です。12ビットDAC MCP4822、ヘッドホンアンプ、2×SPIメモリ(SRAM、EEPROM)、SDカードなど特別な機能を搭載しています。アナログ出力の実用例は多いですが、Arduinoプラットフォームに内蔵DAC機能がないため、アナログ信号を扱う公開プロジェクトは非常に少ないです。ワイヤレスマイク(ウォーキートーキー)は、デジタルとアナログを組み合わせてシンプルながら非常に実用的なプロジェクトを作る好例です。
Goldilocks Analogue - プロトタイプ3

実際のワイヤレスマイク機能はほんの数行のコードですが、アナログ入力(サンプリング)、SPIバス経由のMCP4822 DACへのアナログ出力、サンプリングタイミングルーチン、XBeeデジタル無線プラットフォームという基盤の上に成り立っています。トップダウンで掘り下げていきましょう。
XBee無線
XBee Pro S2B無線機を使用し、ポイントツーポイント通信に設定しています。XBee Proでは1台をコーディネーター、もう1台をルーターとして設定する必要があります。設定ガイドはインターネット上にあります。
最大キャラクタ間時間を待ってからパケットを送信するよう設定しており、パケットが満杯(84バイト)になった時のみ送信されます。これにより無線スループットを最大化しています。理論スループットは250kbpsですが、実際のユーザーデータレートは約32kbpsに制限されます。これがサンプリングレート、ひいては音声品質に影響します。
8ビットサンプルで約3kHzサンプリングが、圧縮なしで送信可能な限界です。圧縮は別プロジェクトとして残します。
XBee無線機はATモードで設定され、2つのエンドポイント間で透過的なシリアルパイプとして動作します。これが2デバイスをデジタル無線で接続する最もシンプルな方法です。無線プラットフォームの動作を心配する前に、ワイヤーで簡単なテストができました。

ロジックアナライザーのトレースを見ると、XBeeデータパケットがシリアルポートの(紫の)Rxラインに到着しているのがわかります。受信パケットデータはリングバッファに格納され、一定レートで再生されます。受信リングバッファには最大255バイトまで許容しており、XBeeパケットサイズが84バイトなので十分です。
他方のデバイスへ送信するサンプルは(青の)Txラインで送信され、サンプル周期ごとにバッファリングされて送信されます。XBee無線機はこれらのバイトを最大0xFFシンボル間隔(設定可能)までバッファリングし、パケットが満杯になると相手エンドポイントに送信します。
サンプリングレート
伝送リンクのビット予算を見て、XBee無線プラットフォームをオーバーロードせず、サンプル損失を起こさずに送信できるデータ量を計算します。音声サンプルを明示的に圧縮しないので、8ビットサンプル × 3,000Hzサンプリング = 24kbpsの送信が必要です。これはかなりうまく機能します。4kHzサンプリングも試しましたが、理論上の最大値に近すぎて効果的ではありませんでした。

ロジックアナライザーを見ると、Rxラインに0x7Eと0x7Cで始まるパケットバイトの到着が見えます。マイクアンプとDAC出力はともに0x7F(FF)付近でバイアスされているため、ここで取得・送信されている信号レベルは非常に低いことがわかります。サンプルレートは3,000Hzです。
サンプル処理
サンプリング割り込み処理時のタイミングを確認するため、ある出力に「ピング」を置きました(黄色のトレース)。このアプリケーションでは、割り込み処理に費やされる時間は、利用可能な総時間に比べて非常に短いことがわかります。何らかのデータ圧縮を実装する余地があります。

サンプリング割り込み中には、DACにサンプルを配置して音声出力を生成し、ADCを読み取って音声サンプルを取得しUSARTバッファに送信するという2つの主要な処理を行います。
これはaudioCodec_dsp関数で行われ、タイマー割り込みから呼び出されます。
AVR 8ビットTimer0を使って定期的なサンプル間隔を生成し、割り込みをトリガーします。MCUのFCPU周波数として標準オーディオ周波数の2進数倍を使用することで、クロックプリスケーラー64で8ビットタイマーのみを使って正確な再生サンプリングレートを生成できます。44,100Hzのような奇数のオーディオ周波数を生成するには、クロックプリスケーラーなしで十分な精度を得るために16ビットTimer1を使用できます。
ATmega1284pのADCはフリーランモードに設定し、192kHzにスケールダウンしています。これはATmega ADCの文書化された最大取得速度に近いですが、8ビットサンプルではまだ仕様内です。
この割り込みは14μsで完了し、サンプル周期333μsに対して非常に短いです。ユーザーインターフェースの実行やさらなるオーディオ処理など、他の処理に十分な時間があります。
SPIトランザクション
最も詳細なレベルでは、MCP4822 DACへの出力サンプルの実際のSPIトランザクションが見えます。

このアプリケーションは標準SPIバスを使用するGoldilocks Analogueプロトタイプ2上に構築されているため、トランザクションは通常です。後のプロトタイプではATmega1284pのUSART 1のマスターSPIモードを使用し、ダブルバッファリングでSPIトランザクションをわずかに高速化し、通常のSPIバスをSDカードやSPIメモリの同時読み書き(オーディオストリーミング用)に解放しています。ワイヤレスマイクアプリケーションではオーディオを取得する必要がないため、古いプロトタイプと通常のSPIバスを使用してもデメリットはありません。
まとめ
既存ツールと数行のコードを使って、デジタル暗号化されたワイヤレスマイクを素早く構築できます。理解可能な音声品質で通信可能です。これでCB無線のトラック運転手たちが家族の会話を盗み聞きすることはありません。
これはMAX9814ベースのマイク入力をGoldilocks Analogueに追加するテストでした。プロトタイプ3を改訂し、このワイヤレスマイク例やボイスチェンジャー、ボーカルコントロール音楽シンセサイザーなど、音声入力を必要とするアプリケーションをサポートするマイク増幅回路を追加予定です。
XBee無線機とマイクアンプを搭載した2つのGoldilocks Analogueプロトタイプ。

ATmega1284pデバイスを標準の20MHzではなく、24.576MHzの高周波で動作させています。この特定の周波数により、48kHzから4kHz(さらに1,500Hzまで)までのオーディオサンプルの非常に精密な再生が可能です。サンプル周期あたりの余分なMCUクロックサイクルは、合成音楽生成時に大いに役立ちます。
コードはSourceforgeのAVR freeRTOSリポジトリにあります。Shuyang氏(SeeedStudio)のOPLは素晴らしく、多くのコンポーネントとPCBの供給源です。
コード
audioCodec_dsp関数 (C/C++)
void audioCodec_dsp(uint16_t *ch_A, uint16_t *ch_B) {
int16_t xn;
uint8_t cn;
/*----- Audio Rx -----*/
// リングバッファから次の文字を取得
if (ringBuffer_IsEmpty((ringBuffer_t*)&(xSerialPort.xRxedChars))) {
cn = 0x80 ^ 0x55; // A-Lawヌル信号を出力
} else if (ringBuffer_GetCount(&(xSerialPort.xRxedChars)) > (portSERIAL_BUFFER_RX >> 1)) {
// バッファが半分以上満杯の場合
cn = ringBuffer_Pop((ringBuffer_t*)&(xSerialPort.xRxedChars)); // 2サンプルポップして追いつく、最初は破棄
cn = ringBuffer_Pop((ringBuffer_t*)&(xSerialPort.xRxedChars));
} else {
cn = ringBuffer_Pop((ringBuffer_t*)&(xSerialPort.xRxedChars)); // 1サンプルポップ
}
alaw_expand1(&cn, &xn); // A-Law圧縮を展開
*ch_A = *ch_B = (uint16_t)(xn + 0x7fff); // 信号を正の値にシフト、A&Bチャネルに出力
/*----- Audio Tx -----*/
AudioCodec_ADC(&mod7_value.u16); // サンプルは10ビット左寄せ
xn = mod7_value.u16 - 0x7fe0; // 10ビット範囲の半分を引いてサンプルを0にセンタリング
IIRFilter(&tx_filter, &xn); // 送信サンプル列をフィルタリング
alaw_compress1(&xn, &cn); // A-Lawで圧縮
xSerialPutChar(&xSerialPort, cn); // サンプル送信
}
タイマー割り込みハンドラ (C/C++)
ISR(TIMER0_COMPA_vect) __attribute__ ((hot, flatten));
ISR(TIMER0_COMPA_vect) {
#if defined(DEBUG_PING)
// 割り込み開始マーク - デバッグ用(黄色トレース)
PORTD |= _BV(PORTD7);
#endif
// MCP4822データ転送ルーチン - ジッター低減のため最初に実行
DAC_out(ch_A_ptr, ch_B_ptr);
// オーディオ処理ルーチン - 入力に必要な処理、次サンプル用出力準備
if (audioHandler != NULL) audioHandler(ch_A_ptr, ch_B_ptr);
#if defined(DEBUG_PING)
// 割り込み終了マーク - デバッグ用(黄色トレース)
PORTD &= ~_BV(PORTD7);
#endif
}
DAC出力関数 (C/C++)
void DAC_out(const uint16_t *ch_A, const uint16_t *ch_B) {
DAC_command_t write;
if (ch_A != NULL) {
write.value.u16 = (*ch_A) >> 4;
write.value.u8[1] |= CH_A_OUT;
} else {
write.value.u8[1] = CH_A_OFF;
}
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // SS LowでDAC選択
SPDR = write.value.u8[1]; // ch_A送信開始
while (!(SPSR & _BV(SPIF)));
SPDR = write.value.u8[0]; // ch_A送信継続
if (ch_B != NULL) {
write.value.u16 = (*ch_B) >> 4;
write.value.u8[1] |= CH_B_OUT;
} else {
write.value.u8[1] = CH_B_OFF;
}
while (!(SPSR & _BV(SPIF))); // ch_A完了待ち
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // SS HighでDAC選択解除、値をラッチ
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // SS LowでDAC選択
SPDR = write.value.u8[1]; // ch_B送信開始
while (!(SPSR & _BV(SPIF)));
SPDR = write.value.u8[0]; // ch_B送信継続
while (!(SPSR & _BV(SPIF))); // ch_B完了待ち
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // SS HighでDAC選択解除、値をラッチ
}
AVR freeRTOS (Sourceforge)
AVR版freeRTOSのリポジトリ。このプロジェクトで使用したDAC.hとアナログテストファイルを含みます。リンク先のGitHubリポジトリは使用しないでください。最新コードはSourceforgeで:https://sourceforge.net/projects/avrfreertos/
回路図
完全に正確ではありません。MCP4822 DAC(SPI)ではなくMCP4725 DAC(I2C)を使用していますが、Fritzingに適切なAdafruitブレークアウトボードがありませんでした。
また、片方向のみ描画されています(RxとTxの相互接続を除く)。XBeeボードは単にRxとTxを接続する2本のワイヤーを置き換えます。十分なデータを伝送できる無線セットなら何でも動作します。

DAC出力とヘッドホンアンプの回路図。マイク入力アンプはプロトタイプ4で追加予定。
製造プロセス
- 大量生産スイス式マシニングの重要考慮事項
- CNC試作ガイド:精度、速度、コスト効率の良い生産
- エキスパートシャフト製造:精密プロセスとカスタムソリューション
- ステンレス鋼パッシベーション:耐食性と性能の向上
- Raspberry Piで単一GPIOピンを使ってアナログセンサーを測定
- MCP3008 ADCでRaspberry Piにアナログセンサーを読み取る
- DACベースカスタムPCBによる高精度波形生成
- スマート灌漑コントローラー:Windows 10 IoT、Raspberry Pi、XBee水分センサーで実現
- R-2R DACでオシロスコープに独自ビジュアルを描画
- 信号回路用高度シールドケーブル:実践的接続技術(後編)
- アナログ測定がプラントパフォーマンスを向上させる理由