シンプルな16bit DAC
2024.08.08
YouTube でも紹介しています。画像をクリックすると再生できます。
以前、Adafruit QT Py ESP32-S2 の内蔵DACを用いて、8ビット・モノラル WAVファイルの再生を行いました。
内蔵DACによるWAV再生
今回は、16bit DAC TM8211 を使って、16ビット・ステレオ WAV音源を鳴らせてみます。
●Features and Specification of TM8211
TM8211はPT8211の後継互換品です。デジタルオーディオ用に設計されたR-2Rラダー抵抗型CMOS16bitD/Aコンバータです。
低THD、両チャンネル位相差0(typ)、またラダーネットワークの採用で高速変換が可能です。
・3-6V Operating voltage, suitable for 3.3V & 5.0V operations.
・This D/A converter uses CMOS Technology
・It supports 3.3 V bus input level
・Low power consumption
・16-bit dynamic range
・Low total harmonic distortion
・In this microcontroller Two audio channel output in the same chip
・No phase shift between both output channel
PT8211 16-Bit DAC
TM8211を使うメリット
・符号付16ビットステレオWAV音源に対応している
・専用ライブラリを必要とせずArduino基本関数のみで構築可能
・WAV音源編集に対して自由度が高い
・周辺の電磁波ノイズの影響を受けにくい
PT8211 Pinout
PT8211 Interfacing_Diagram
データシートの参考回路通りに組んでもよいのですが、直接スピーカー・アンプに繋いで問題ありません。
電源ノイズ対策用のキャパシタも必要ありません。
●LM385N
参考回路通りに組む場合に使える単電源オペアンプです。
バイポーラー/
回路数:2/
動作電源電圧min.:3V/
動作電源電圧max.:32V/
入力オフセット電圧:2mV/
入力バイアス電流:20nA/
入力オフセット電流:2nA/
消費電流:0.7mA
●Adafruit QT Py ESP32-S2 WiFi Dev Board with STEMMA QT
・ESP32-S2 240MHz
・4 MB Flash & 2 MB PSRAM
・2.4 GHz Wi-Fi (SoC)
・Two I2C ports
・Hardware UART
・Hardware SPI
・Hardware I2S on any pins
・3.3V regulator with 600mA peak output
Ref.Adafruit QT Py ESP32-S2
Qt Py ESP32-S2 Pinout
●配線
ESP32-S2 | | TM8211 | | Speaker Amp. |
A6 | - | 1:BCK | | |
A1 | - | 2:WS | | |
A2 | - | 3:DIN | | |
GND | - | 4:GND | - | GND |
3V | - | 5:VCC | | |
| | 6:LCH | - | LEFT |
| | 8:RCH | - | RIGHT |
| | microSD | | |
A3 | - | CS | | |
SCK | - | SCK | | |
MISO | - | MISO | | |
MOSI | - | MOSI | | |
3V | - | Vin | | |
GND | - | GND | | |
回路を立体的に配置することで、ミニブレッドボード上に纏めています。
左がビルド用のラズベリーパイ、中心上が外部入力端子を持ったMP3ラジオです。
●音源の準備
AudaCityに音源を取り込んで、WAV形式で書き出します。
44.1KHz ステレオWAV音源では、SDカードモジュールからの読み込みに時間が掛かってしまい、演奏が遅延してしまいます。
サンプリング・レートを11025Hzに落として出力します。
●コード解説
掲載用にコードは簡素化しています。
tm8211.ino
#include <SPI.h>
#include <SD.h>
#include <FS.h>
#define SD_CS A3
#define SDSPEED 40000000
#define PIN_BCK A6 // 1:BCK
#define PIN_WS A1 // 2:WS
#define PIN_DIN A2 // 3:DIN
#define NOP __asm__ __volatile__ ("nop\n\t")
#define BUFFER_SIZE 2048
#define NORMAL 0.65
#define LIGHT_LOLI 0.6
#define HEAVY_LOLI 0.5
#define ETS_DELAY_US_RATE NORMAL
//#define ETS_DELAY_US_RATE LIGHT_LOLI
//#define ETS_DELAY_US_RATE HEAVY_LOLI
// The Canonical WAVE file format
typedef struct {
uint8_t ChunkID[4];
uint32_t ChunkSize;
uint8_t Format[4];
uint8_t Subchunk1ID[4];
uint32_t Subchunk1Size;
uint16_t AudioFormat;
uint16_t NumChannels;
uint32_t SampleRate;
uint32_t ByteRate;
uint16_t BlockAlign;
uint16_t BitPerSample;
uint8_t Subchunk2ID[4];
uint32_t Subchunk2Size;
} WAV_FORMAT;
File file;
WAV_FORMAT pwf;
uint8_t buf[BUFFER_SIZE];
boolean getWavFormat()
{
long len;
short cbsize;
if (file.read((uint8_t *)&pwf,20) == -1) return false;
if (memcmp(pwf.Subchunk1ID,"fmt ",4)==0) {
if((len = file.read((uint8_t *)&pwf+20,24))==-1) return false;
} else if (memcmp(pwf.Subchunk1ID,"LIST",4)==0) {
if (file.seek(20+pwf.Subchunk1Size)) {
if((len = file.read((uint8_t *)&pwf+12,24))==-1) return false;
if (pwf.Subchunk1Size == 18) {
// skip cbsize on WAVEFORMATEX
if((len = file.read((uint8_t *)&cbsize,2))==-1) return false;
}
if((len = file.read((uint8_t *)&pwf+36,8))==-1) return false;
} else {
return false;
}
} else {
return false;
}
if( memcmp(pwf.ChunkID, "RIFF", 4)
||memcmp(pwf.Format, "WAVE", 4)
||memcmp(pwf.Subchunk1ID, "fmt ", 4)
||memcmp(pwf.Subchunk2ID, "data", 4))
return false;
else return true;
}
void writeDACChannel(int16_t mono)
{
unsigned char pos = 16;
// Send data into PT8211 in least significant bit justified (LSBJ) format.
while(pos > 0) {
pos--;
digitalWrite(PIN_BCK, LOW);
digitalWrite(PIN_DIN, (mono & (1 << pos)) ? HIGH: LOW);
NOP;
// Toggle BCK.
digitalWrite(PIN_BCK, HIGH);
NOP;
}
}
boolean writeDAC(char *filename)
{
unsigned short left, right, delayus;
long block;
if(!(file = SD.open(filename))) return false;
getWavFormat();
delayus = (1000000 / pwf.SampleRate) * ETS_DELAY_US_RATE;
long len = pwf.Subchunk2Size;
while(len>0) {
if (len>BUFFER_SIZE) block = BUFFER_SIZE; else block = len;
file.readBytes((char*)buf, block);
for(int pos=0;pos<block;pos+=4)
{
left = buf[pos+1];
left = (left<<8) + buf[pos];
right = buf[pos+3];
right = (right<<8) + buf[pos+2];
digitalWrite(PIN_WS, LOW); // right channel
writeDACChannel(left);
digitalWrite(PIN_WS, HIGH); // left channel
writeDACChannel(right);
ets_delay_us(delayus);
}
len -= BUFFER_SIZE;
}
file.close();
return true;
}
void setup()
{
pinMode(PIN_BCK, OUTPUT);
pinMode(PIN_WS, OUTPUT);
pinMode(PIN_DIN, OUTPUT);
if (SD.begin( SD_CS, SPI, SDSPEED)) {
writeDAC("/sound/odeon_16bit11KHz.wav");
}
}
void loop() {}
getWavFormat();
WAVファイルのヘッダー情報を取得しています。
delayus = (1000000 / pwf.SampleRate) * ETS_DELAY_US_RATE;
取得したサンプリング周波数に応じて、TM8211へのデータ送信間隔を算出しています。
while(len>0) {
if (len>BUFFER_SIZE) block = BUFFER_SIZE; else block = len;
file.readBytes((char*)buf, block);
ここではある程度纏まった単位でデータを読み込んでいます。
ステレオ16ビットWAV音源では1データの最小単位は左右合わせて4バイトですが、 この単位で頻繁に読み込みを行うと処理への負荷が高まり、音の再生が安定しません。
for(int pos=0;pos<block;pos+=4) {
left = buf[pos+1];
left = (left<<8) + buf[pos];
right = buf[pos+3];
right = (right<<8) + buf[pos+2];
16ビット・ステレオデータは符号付き2バイト整数値で左→右→左→右のように格納されています。
さらにこの2バイトデータは下位バイト・上位バイトの並びで格納されているので、並び替えて変数に格納します。
digitalWrite(PIN_WS, LOW); // right channel
writeDACChannel(left);
digitalWrite(PIN_WS, HIGH); // left channel
writeDACChannel(right);
ここではTM8211を制御しています。
送信タイミングは下記のようになります。
WSピンをLOWあるいはHIGHにすることで、ステレオ音源の左右データのどちらを送信するかを指定します。
BCKピンをLOWにして、音源データ1ビットを送信後にHIGHに戻し、これを繰り返します。
for (int pos = 15; pos >= 0; pos--) {
digitalWrite(PIN_BCK, LOW);
digitalWrite(PIN_DIN, (mono & (1 << pos)) ? HIGH: LOW);
NOP;
// Toggle BCK.
digitalWrite(PIN_BCK, HIGH);
NOP;
}
音源データを上位ビットから送信しています。
また、送信直後にNOP(No Operation)命令を入れています。
NOP(No OPeration)に関して、インラインアセンブラで定義しています。
#define NOP __asm__ __volatile__ ("nop\n\t")
CPUが特定のデータを読み込んでから、次の処理を開始するためには、少しの間データを保持する必要があります。
その間、NOP命令を実行することで正確な処理が実行されるようになり、CPUの動作が安定します。
ノーオペレーション命令 NOPとは?
ets_delay_us(delayus);
左右1セットのデータを送信したあとで、次のデータ送信間隔を調整することで、周波数を圧縮、伸長することができます。
音声データであれば、データ送信間隔を短くすると高い声になります。
●開発環境
ソースコードのビルドには、PlatformIOを使用しています。
Arduino開発環境構築 PlatformIO
●WAV音源の再生
実際に再生した様子はYouTube動画をみてみてください。
暮れてゆく空は
アーティスト: 遊佐未森
アルバム: ハルモニオデオン
リリース: 1989年
●余談
ソースコードの下記の部分を変更してビルド、再生してみてください。
ちょっとだけ楽しめます。
#define ETS_DELAY_US_RATE NORMAL
//#define ETS_DELAY_US_RATE LIGHT_LOLI
//#define ETS_DELAY_US_RATE HEAVY_LOLI
↓
//#define ETS_DELAY_US_RATE NORMAL
//#define ETS_DELAY_US_RATE LIGHT_LOLI
#define ETS_DELAY_US_RATE HEAVY_LOLI
ここでは波形圧縮のみですが、TM8211を使うと独自に波形を作り込んでリアルタイムに再生するということも簡単にできてしまいます。
●参考情報
・dilshan/pt8211-dac.ino
|
Raspberry Pi(ラズベリー パイ)は、ARMプロセッサを搭載したシングルボードコンピュータ。イギリスのラズベリーパイ財団によって開発されている。
Arduinoで学ぶ組込みシステム入門(第2版)
●Arduinoを使って組込みシステム開発を理解する
・ハードウェアやソフトウェアなどの基礎知識/
・設計から実装までを系統的に説明するモデルベース開発/
・Arduinoを用いた実際の開発例
最新 使える! MATLAB 第3版
◆◆すぐに「使える!」 全ページフルカラー!◆◆
・MATLAB R2022bに対応し、解説もより詳しく!/
・コマンド・スクリプトの例が豊富で、動かして学べる!/
・超基本から解説。これから使いはじめる人にぴったり!/
・全編フルカラー、スクリーンショットも豊富!
Amazon Web Services基礎からのネットワーク&サーバー構築改訂4版
1.システム構築をインフラから始めるには/
2.ネットワークを構築する/
3.サーバーを構築する/
4.Webサーバーソフトをインストールする/
5.HTTPの動きを確認する/
6.プライベートサブネットを構築する/
7.NATを構築する/
8.DBを用いたブログシステムの構築/
9.TCP/IPによる通信の仕組みを理解する
C言語は第二の母国語: 独学学生時代から企業内IT職人時代に培った、独立のための技術とノウハウ 平田豊著
学生時代から独学でプログラミングをはじめ、企業内でデバイスドライバを開発し、そして独立後もたくさんのアプリケーション開発や技術書制作に携わってきた著者。その筆者が大事に使い続ける「C言語」の“昔と今”について、気づいたことや役立つ知識、使ってきたツールなどについて、これまで記してきたことを整理してまとめました。
本書では、現役プログラマーだけでなく、これからプログラミングを学ぶ学生などにも有益な情報やノウハウを、筆者の経験を元に紹介しています。
1冊ですべて身につくJavaScript入門講座
・最初の一歩が踏み出せる! 初心者に寄り添うやさしい解説
・最新の技術が身につく! 今のJavaScriptの書き方・使い方
・絶対に知っておきたい! アニメーションとイベントの知識
・プログラミングの基本から実装方法まですべて学べる
図解! Git & GitHubのツボとコツがゼッタイにわかる本
ソフトウェア開発では欠かすことのできないGit、GitHub。
これからGit、GitHubを使いたいという入門者の方でも、実際に手を動かしながら使い方を学べます。
C自作の鉄則!2023 (日経BPパソコンベストムック)
メーカー製のパソコンはスペックが中途半端で、自分が本当に欲しい機種がない――。そう思っている人には、ぜひ自作パソコンをお薦めします。自作パソコンのパーツは進化が速く、しかも驚くほど種類が豊富。価格も性能も、幅広く用意されているため、満足度100%の“自分だけの1台”を手に入れることができます。
Interface 2023年6月号
特集:第1部 フィルタ設計 基礎の基礎/
第2部 係数アプリや波形観測アプリで合点!FIR&IIRフィルタ作り/
第3部 配布プリント基板で体験!マイコンで動くフィルタ作り
日経Linux 2023年5月号
【特集 1】 AI時代の最強フリーソフト ~ 25のやりたいを実現!
【特集 2】 AWS、Azureのうまみを無料で体感!面倒なことはクラウドに任せよう
【特集 3】 新しいRaspberry Pi Cameraで遊んでみよう
【特集 4】 Linuxで旧型PCを復活! 1kg切るモバイルPCを「ChromeOS Flex」でChromebook化
ラズパイマガジン2022年秋号
特集:5大人気ボード 電子工作超入門
「半導体不足で在庫が不足し、電子工作のボードがなかなか買えない…」。そんな今にふさわしい特集を企画しました。5種の人気ボードにすべて対応した電子工作の入門特集です。「GPIO」や「I2C」を使った電子パーツの制御方法は、どのボードでも同じです。手に入れられたボードを使って、今こそ電子工作を始めましょう。
地方で稼ぐ! ITエンジニアのすすめ
学歴、理系の知識、専門スキル……全部なくてもITエンジニアになれる!
地方でも高収入でやりがいをもって働ける!ITエンジニアの魅力を一挙大公開
Raspberry Piのはじめ方2022
本書は、ラズパイやPicoの買い方やインストール、初期設定といった基本から、サーバー、電子工作、IoT、AIといったラズパイならではの活用方法まで、1冊でお届けします。
ラズパイをこれから始める方向けに、全36ページの入門マンガ「女子高生とラズベリーパイ」も巻末に掲載。これを読むだけでラズパイがどんなものなのか、すぐに分かって触れるようになります。
ハッカーの学校 IoTハッキングの教科書
生活にとけこみ、家電機器を便利にするIoT技術。
Webカメラなど、便利の裏側に潜むセキュリティの危険性をハッキングで検証。
専門家がパケットキャプチャからハードウェアハッキングまで、その攻撃と防御を徹底解説。
本書は2018年7月に刊行された「ハッカーの学校IoTハッキングの教科書」に一部修正を加えた第2版です。
攻撃手法を学んで防御せよ! 押さえておくべきIoTハッキング
本書は、経済産業省から2021年4月にリリースされた、IoTセキュリティを対象とした『機器のサイバーセキュリティ確保のためのセキュリティ検証の手引き』の『別冊2 機器メーカに向けた脅威分析及びセキュリティ検証の解説書』をもとに、IoT機器の開発者や品質保証の担当者が、攻撃者の視点に立ってセキュリティ検証を実践するための手法を、事例とともに詳細に解説しました。
ポチらせる文章術
販売サイト・ネット広告・メルマガ・ブログ・ホームページ・SNS…
全WEB媒体で効果バツグン!
カリスマコピーライターが教える「見てもらう」「買ってもらう」「共感してもらう」すべてに効くネット文章術
プログラマーは世界をどう見ているのか 西村博之著
イーロン・マスク(テスラ)、ジェフ・べゾス(Amazon)、ラリー・ペイジ(Google)…etc.
世界のトップはなぜプログラマーなのか?
ニーア オートマタ PLAY ARTS改 <ヨルハ 二号 B型 DX版> PVC製 塗装済み可動フィギュア
「NieR:Automata」より、ヨルハ二号B型こと2BがPLAY ARTS改に新たに登場!
高級感の感じられるコスチュームや髪の質感、洗練されたボディバランス、細かなデティールに至るまでこだわり抜かれた逸品。
DX版には通常版のラインナップに加え2Bの随行支援ユニット ポッド042などをはじめ“純白の美しい太刀"白の約定やエフェクトパーツ、自爆モードを再現できる換装用ボディパーツ、シーンに合わせて変えられる顔パーツ2種も付属する豪華な仕様に。
作中のあらゆるシーンを再現することが可能なファン必見の一品となっている。
Newtonライト2.0 ベイズ統計
ベイズ統計は,結果から原因を推定する統計学です。AIや医療などの幅広い分野で応用されています。その基礎となるのは18世紀に考えだされた「ベイズの定理」です。
この本では,ベイズ統計学のきほんをやさしく紹介していきます。
白光(HAKKO) ダイヤル式温度制御はんだ吸取器 ハンディタイプ FR301-81
無水エタノールP 500mlx2個パック(掃除)
ケイバ(KEIBA) マイクロニッパー MN-A04
サンハヤト SAD-101 ニューブレッドボード
白光(HAKKO) HEXSOL 巻はんだ 精密プリント基板用 150g FS402-02
[Amazon限定ブランド]【指定第2類医薬品】PHARMA CHOICE 解熱鎮痛薬 解熱鎮痛錠IP 100錠
|