電音の歩み

電子楽器を中心とし、ものづくり関係も含めて紹介していきます。

OLED(SSD1306)用の激軽arduinoライブラリ リリース版 その2

メンバ関数の紹介をします。

特徴は、AVRのFLASH領域に置いたSTRINGSを描画する関数も用意したことです。

■■コンストラクタ (任意のDC,RESET,CS ピンの指定)

OLEDHY( unsigned char DC, unsigned char RESET, unsigned char CS );         

■■メンバ関数(メソッド)の定義

begin( void );    // 初期化   

disp_on( void );     // 表示する    

disp_off( void );    // 表示を消す HardwareBufferは消えない         

■ clear/fill系 HardwareBufferを0x00クリアまたは、8bitのPagePatternでフィル  

clearFullPage( void );    // 全画面消去

clearRect( unsigned char XstartBit, unsigned char YstartPage, unsigned char XlengthBit, unsigned char YlengthPage );    //  領域消去

void fillRect( unsigned char PagePat, unsigned char XstartBit, unsigned char YstartPage, unsigned char XlengthBit, unsigned char YlengthPage );    // 領域を埋める

■area fill系 領域bitmapをまとめて転送 SRAM領域とFlash領域用(_pgm)がある 

drawRect( unsigned char *p, unsigned char XstartBit, unsigned char YstartPage, unsigned char XlengthBit, unsigned char YlengthPage );    // SRAM領域bitmap

drawRect_pgm( unsigned char *p, unsigned char XstartBit, unsigned char YstartPage, unsigned char XlengthBit, unsigned char YlengthPage );    // FASH領域bitmap

■draw singleFont HYs: small 1 font(5*7) / 縦1page(8dot),   HYm: 1 midlle font(6*11)/ 縦2page(16dot),   fontwidth: 横幅(fontの横dot数以上に設定のこと) 

draw_VfontHYm( int code,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth );    // 中フォントで1文字描画

draw_VfontHYs( int code,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth );    // 小フォントで1文字描画 

■ draw strings HYs: 1 Ssize font(5*7) / 1 page line(8dot),  HYm: 1 Msize font(6*11)/ 2 page line(16dot)  ,   attribute有無の2種あり attribute:  0: non modification  1: under line , 2: reverse B/W 

draw_stringHYm( char *s, unsigned char XstartBit,unsigned char YstartPage, unsigned char fontwidth, unsigned char att0 );    // 中フォントで1文字列描画 attribute有

draw_stringHYm( char *s, unsigned char XstartBit,unsigned char YstartPage, unsigned char fontwidth );       // 中フォントで1文字列描画

draw_stringHYs( char *s, unsigned char XstartBit,unsigned char YstartPage, unsigned char fontwidth, unsigned char att0 );    // 小フォントで1文字列描画 attribute有

draw_stringHYs( char *s, unsigned char XstartBit,unsigned char YstartPage, unsigned char fontwidth );       // 小フォントで1文字列描画

■ draw dual strings  2 Msize font strings / 3 page line( 24dot)   , attribute有無の2種ありattribute:  0: non modification  1: under line , 2: reverse B/W    

draw_dual_stringHYm( char *s0, char *s1, unsigned char XstartBit,unsigned char YstartPage, unsigned char fontwidth, unsigned char att0, unsigned char att1 );     // 中フォントで2文字列描画 attribute有 

draw_dual_stringHYm( char *s0, char *s1, unsigned char XstartBit,unsigned char YstartPage, unsigned char fontwidth);      // 中フォントで2文字列描画  

■ 整数の表示    

draw_num_m( int num,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth, unsigned char att0 );  //   中フォントで整数描画 attribute有  

draw_num_m( int num,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth );   //   中フォントで整数描画  

draw_num_s( int num,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth, unsigned char att0 );    //   小フォントで整数描画 attribute有    

draw_num_s( int num,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth );   //   小フォントで整数描画

OLED(SSD1306)用の激軽arduinoライブラリ リリース版 その1

arduino(ATmega328, AVR64DB )リリース版ライブラリができましたので説明します。

スイッチサイエンスで販売している私のボード用ですが、arduino UNOなど、他のボードでも使用できます。

 前回のブログでの仕様に対してより多くの行を表示するため、3ベージ( 3行のこと) あたり2行の中フォントを表示するメンバ関数を追加しています。

■インストール方法

(1) ライブラリZIPファイル、「oledHYLib1.zip」をダウンロードする。

oledHYLib1.zip - Google ドライブ

(2) arduinoIDE  →「スケッチ」→「ライブラリをインクルード」→

「.zip形式のライブラリをインストール」で

 ダウンロードしたoledHYLib1.zipを選択しインストールする。

■ライブラリのドキュメント

 ダウンロードしたoledHYLib1.zipを解凍する。

 oledHYLib1.h  ライブラリのメンバ関数一覧を参照できます。

 OLED_HY_libtest1_3.ino デモプログラムのスケッチファイル。

■スケッチの書き方 サンプル例

 OLED_HY_libtest1_3.ino にそった形で説明します。

18|  #include "oledHYLib1.h"    クラスライブラリの定義をインクルード

44|  OLEDHY dispHY( OLED_DC, OLED_RESET, OLED_CS );   インスタンス生成

 このとき、oled の7ピン信号のうち、SPI ( SCK, MOSI ) は、ハードウエアSPIでは固定なので、ピン番号の指定はありません。ちなみに、ATmega328では( SCK = 13 ,MOSI = 11 )、AVR64DB32では( SCK = 6, MOSI = 4 )です。

 DC,RESET,CS は、任意のGPIOが使えますが、スイッチサイエンスで販売しているATmega328 極小Arduino互換Board(OLED付き)(0.96”OLED 直結仕様) — スイッチサイエンス

AVR64DB32 極小Arduino互換Board(OLED付き)(0.96”OLED 直結仕様) — スイッチサイエンス

 では、ATmega328ではOLEDHY dispHY(  6, 8, 7 )、AVR64DB32ではOLEDHY dispHY( 2, A21, 3 )になっています。他のボードの場合は、各自の接続に合わせます。

あとは、    dispHY.begin();   // initialize 

をすれば、oledHYLib1.h にある全てのメンバー関数が使えます。

 

デジタル版Frequency Shifterの検討 その5 RP2040 + I2S ( D/A )+SPI( A/D ) ソフト編2

どんどん検討方向がずれて来ていますが、Arduino IDE でRP2040の開発をする前提で検討しています。まだ、ArduinoIDEでのRP2040サポートが

 

 

■RP2040 arduino(ボード Raspberry Pi PICO)で利用できる低レベル関数

・GPIOのR/Wでは、digitalWriteと比べてgpio_putなどの低レベル関数の方が圧倒的に速い。// 8ns  STM32のように5Vトレラントで無い点は要注意。

・内蔵12bitADCは4個しか無いがなかなか速い。adc_read() // 2usec  もちろんanalogRead()は遅い。

・タイマー割り込みの機能は一つしか無いが、8つもあるPWMカウンタで割り込みを書けれるので困ることはない。

・PWMの使い方には癖がある。PWMは独立したものが8個(1個に付き比較器は2個)あり、これを8スライスと呼ぶ。どのスライスを使うかは、GPIO番号により固定されている。、スラスイ0からスラスイ7まで割り振られており変更できない。スライス番号を使用してPWMカウンタでタイマー割り込みを掛けることもできる。

・2コアは、setup1(),loop1()を用意するだけで簡単に起動できる。

 

■まず I2S D/A は?

RP2040 arduino I2S は、ハードではなく、PIO プロセッサを用いてサポートされている。ライブラリの初期化時にPIO プロセッサにコードが送り込まれ起動するようです。PIOプロセッサのコードは、arduinoでは書けない。(Pythonではできるのに)

D/A チップには、定番らしいTi社のPCM5102 を使用。DIPボード化品をAliで安価に入手できた。

PCM5102は、16/24/32bit が2チャンネルありS/Nは100dB以上。3.3V単一でありながらチャージポンプを内蔵して、AC 2.1V(RMS)が出力できます。もちろんデルタシグマのオーバーサンプリング処理なのでアンチエリアシングフィルタもディジタルで内蔵されています。(SPI D/Aと比べると夢のような仕様で明らかに高音質が期待できます)

I2S マスター/スレーブが使用でき、fsck分周比も多数サポート、特に嬉しいのが、PICO I2Sでは、MCKクロックが出力されないが、PCM5102のMCK端子をGNDに接続すると内蔵PLLでMCKを発生してくれること。 

テストしたのは、16bitと24bit。fsck = 44.1KHz、DMA bufferは、L/R合わせて32bit*8本です。24bitの場合、LRCLKに44.1KHzでは、BCLKは、44.1KHz*24*2 =2.1168MHz をスレーブのPCM5102に与えると、内部でオーバーサンプリング用クロックMCKが生成され、デルタシグマ処理とデジタルフィルタを動作させてくれます。

注意すべき点は、I2S は完全な同期処理なので、マイコンがマスターとはいえ、ソフトはこのタイミングに従う作りにする必要があります。実際にはDMAバッファの空き具合に合わせることになります。

テストの結果は良好で、今まで苦労したS/Nが嘘のように改善され、惚れてしまいました。また、24bitにすると、アナログでのゲイン調整を省略しても支障なさそうなので、アナログがとてもシンプルにできます。

このD/Aが使えるだけでもRP2040にする価値があると思います。

 

■次に I2S A/Dは?

リーズナブルな I2S A/Dとして、同じくTiの定番ぽいPCM1808を検討しました。Ali でも多数ある。

結果から言うと、数々の課題に当たり、今回は使用するのを断念しました

本当は、安価で24bit でアンチエリアシング内蔵のA/D というだけでも、すごい魅力があるわけですが・・・ 残念。

一応、検討メモとして残しておきます。( 私自身I2S初心者のため、あちこち寄り道していますが・・)

PCM1808は、MSCKを与える必要がありかつ24bit固定
D/AにはPCM5102を使うことは決定しているので、D/AだけでなくA/Dも完全同期処理になる。D/A とA/Dで物理的に誤差なくFsck レートを合わせる必要がある。もしD/AとA/Dで異なるビット精度を選択すると各々のBCLKが異なることになり、Fsckを完全に合わすのは難しくなる。
またPCM1808はPLL でMCKを内部で生成してくれないので、普通は外部オシレータで生成する必要がある。
・参考にした、PCM1808 -> PCM5102 直結動作例
 PCM1808をマスターモードに設定し、外部オシレータからMCKを与え分周比を設定すると、PCM1808は、BCLK、LRCLK、Doutを出力する。このMCK、BCLK、LRCLKをPCM5102(スレーブ固定) に与えると、分周比は自動で設定され、A/D D/Aの完全同期が達成されるというエレガントな方法です。もちろん24bit固定のPCM1809に合わせてPCM5102も24bitに統一します。 
ところが、実際問題として、RP2040 arduinoI2S のライブラリがマスタ固定なので、この構成では、D/A には出力できるが、A/Dからの入力ができないことが判明しこのやり方は断念。(今後のライブラリ進化に期待)
 
・そこでひねり出してのが、PCM1808 ,PCM5102を共にスレーブにしかつ同期させる方法
2チャンネルのI2Sポートにそれぞれスレーブ設定のPCM1808とPCM5102を接続し、両Iチャンネルから全く同じ周波数のBCLK、LRCLKを出すように設定する作戦です。
マイコン側がマスターなのでBCLK,LRCLKは、マイコンのクロック基準で作られます。この際、MCKとして外部オシレータから非同期のものを与えると、PCM1808とPCM5102が完全に同期されない危険があり得るので(うまくいくかもしれないが)、MCKもマイコンに同期したものを出すことにします。BCLKは、RP2040のクロック133MHzを11分周して発生します。この12.09MHzから、256分周に設定したLRCLKは、47.23KHzになります。(この手法では、48KHzとか44.1KHzのサンプリングレートにはできないが、直接デジタルで外部に出すことは無いので、自己都合のサンプリングレートで良しとします)
ここまでは目論見通りに行ったのですが、実際にテストプログラムを組もうとすると、24bit固定のPCM1809をつないだI2Sの入力ライブラリが、24bitをサポートしていない様子。A/D 値がNGで行き詰まる。それに加えて、入力用に用意されている関数がブロッキングのread関数のみ。出力用I2Sでは用意されていた、availableのような関数も無い。ということで、この手法も断念。PIOプロセッサのコードとライブラリの改定に期待したい。
結局今回は、I2SのA/Dが使えないことになり、残念ながらSPI でいく方針に変更
arduinoで使いたいと考えている人以外は、解決策があると思いますが・・
 
 

OLED(SSD1306)用の激軽arduinoライブラリを作る その3

■デモ

下記ライブラリを使用したサンプルプログラムによるデモ動画です。

フレームパッファレスのライブラリでもこれくらいはできます。処理は軽いです。

ライブラリのSRAM消費量は249byteでした。

サンプルプログラム

  youtu.be

#0  オープニングメッセージ

#1 DrawString M/S   中/小フォントで文字列"FontWidth"を表示し、文字幅を変えています。

#2 DrawString M/S   #1にアンダーラインをつけています。

#3 DrawString M/S   #1の白黒反転

#4 DrawFont M 中フォントを列挙しています。

#5 DrawFont S 小フォントを列挙しています。

#6 DrawRect/FillRect/ DrawNum S  バーによるレベル表示例

 

https://drive.google.com/file/d/1lXw26WOLtMkpTJcAGS-ft_cxMua1YiMo/view?usp=drive_link

 

arduino (ATmega328/AVR64DB32)用ライブラリ

まず、arduino互換ボード用に、AVR用のライブラリを試作しました。スイッチサイエンスではもう少し評価してからアップしますので、仕様は変わるかもしれません。

//---------------------------------------------------------------------------
//  ## class library ##

// oledHYLib1.h
//---------------------------------------------------------------------------
class OLEDHY{
  public:
    // ---------- コンストラクタ (任意のDC,RESET,CS ピンの指定)
    OLEDHY( unsigned char DC, unsigned char RESET, unsigned char CS );

    
     // ---------- メンバ関数(メソッド)の定義
    void begin( void );       // GPIOとSPIの初期化
    void disp_on( void );     // 表示する
    void disp_off( void );    // 表示を消す SSD1306内のGDDRAMは消えない
    
    // ---- clear/fill系 GDDRAMをクリア、8bitのPagePatternで任意矩形領域にフィル

    void clearFullPage( void ); //
    void clearRect( unsigned char XstartBit, unsigned char YstartPage, unsigned char XlengthBit, unsigned char YlengthPage );
    void fillRect( unsigned char PagePat, unsigned char XstartBit, unsigned char YstartPage, unsigned char XlengthBit, unsigned char YlengthPage );

 

    // ---- area fill系 bitmapを任意矩形領域に転送

    //  SRAM領域用とFlash領域用(_pgm)がある
    void drawRect( unsigned char *p, unsigned char XstartBit, unsigned char YstartPage, unsigned char XlengthBit, unsigned char YlengthPage );
    void drawRect_pgm( unsigned char *p, unsigned char XstartBit, unsigned char YstartPage, unsigned char XlengthBit, unsigned char YlengthPage );

 

    // ---- singleFont/strings系 HYs: small font(5*7) 縦1page(8dot)  HYm: midlle font(6*11) 縦2page(16dot)
    //    fontwidth: 空白を含むフォント横幅(fontの横dot数以上に設定のこと)
    void draw_VfontHYm( char code,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth);
    void draw_VfontHYs( char code,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth);
    void draw_stringHYm( char *s, unsigned char XstartBit,unsigned char YstartPage, unsigned char fontwidth);
    void draw_stringHYs( char *s, unsigned char XstartBit,unsigned char YstartPage, unsigned char fontwidth);

 

    // ---- 整数の表示
    void draw_num_m( int num,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth);
    void draw_num_s( int num,  unsigned char XstartBit,unsigned char YstartPage , unsigned char fontwidth);
    
     unsigned char attr = 0;       // 0: non modification  1: under line , 2: reverse B/W   draw_stringHYm,draw_stringHYs に反映 
};

 

OLED(SSD1306)用の激軽arduinoライブラリを作る その2

■SSD1306

まずデータシートを見ると、

OLEDディスプレイとしては、SPIとI2Cが選べますが、ここでは高速なSPIを選択します。チップとしては、SPI、I2C、8bitパラレル(制御は懐かしの8080と6800が選べる)か゜サポートされています。内部にハードウエアフレームバッファ(GDDRAM)を持っているので、フレームバッファレスのライブラリにするには、これをどこまで活用できるかです。

 

フレームバッファ(GDDRAM)の構成は

128☓64dot構成で、縦方向はPAGEと呼ぶbyte構成の8スプリットに分割されています。まるでかつてのキャラクタVRAMを彷彿させる面白い構成です。

上下左右の方向や白黒も自由に設定でき、原点もそれに伴い変わります。経験的に最も使い慣れた左上原点の設定に固定します。

縦方向はPAGE内ははリトルエンディアンであり、横方向はdot単位で自由にアクセスできます。

このGDDRAMを使って1dot単位のフルグラッフィック描画を行うには、リード・モディファイ・ライト操作が必要で、PAGE内のByteデータの読み出しが必須です。しかし残念ながらSSD1306は、SPIモードは、ライトオンリーで読み出しはサポートされていません。

しかし、元々、グラフィックディスプレイは、機能が多いほど使うのが大変で、昔のキャラクタディスプレイは、とても手軽に使えました。

そこでライブラリの仕様としては、少し高機能なキャラクタディスプレイにセミグラッフィックを付加した基本とし、ソフトのバッファレスで実現できるものに絞ることにします。

■どんな仕様?

Adafruitのライブラリでの不満のひとつが、フォントのサイズです。用意されている5*7フォントは、0.96"の小さな画面にに、横21文字、縦8文字も表示できます。小さくても多くの情報が表示したいこともあるのですが、使用頻度のより高いのは明らかにもう少し大きいサイズです。Adafruitライブラリでは、フォントサイズを2倍に単純拡大するしかなく、文字品位も悪く、大きくなり過ぎで横10文字、縦4文字しか表示できなくなります。

もうひとつは、ソフトでオフライン描画したフレームパッファをGDDTAMにまとめて転送するため、高速なSPI でも数msec程度処理がストールすることです。

 

そこで、使いやすいサイズのフォントを用意し、GDDTAMの任意のローカルな領域にリアルタイムに分散転送させることにします。

フォントは、Adafruitに相当する5*7サイズのsmallFontと、見やすく使いやすいサイズとして、6*11サイズのmiddleFont(2 PAGEを使用)を作ります。

PAGEの制限内で使うため、縦方向は、smallFontでは1 PAGE単位、middleFontは2PAGEを単位(位置は1PAGE単位で指定可)に限定します。

その代わり横方向は、文字間隔も文字位置も1dot単位で自由に設定できるようにします。

これらのフォントは、PAGE構成に合わせたbit配置で作成ます。目玉は、使いやすくて美しい(と思う)middleFontです。

 

OLED(SSD1306)用の激軽arduinoライブラリを作る その1

■なぜOLED用の自前のライブラリ

今まで、SSD1306搭載の0.97インチOLED(SPI)とてもコスパが良く気に入っています。どんなのに使っているかというと、

このOLEDを使うことを前提とした「arduino互換のTynyBoard(ATmega328/AVR64DB32)」は言うまでもなく、

STM32F103を使った「ライブ用アームテルミンサウンドモジュール」や「SynthScope」と、現在開発中のRP2040使用の「FrequencyShifter」など数多くあります。

これらのOLEDの制御ソフトには、ほとんどAdafruit製のライブラリを使用しています。

ATmega328では他に「u8g2」というライブラリも使えるので、スイッチサイエンスで販売しているarduino互換ボードの製品ページでは、両方のドライバの使い方を紹介しています。

https://www.switch-science.com/products/7683

https://www.switch-science.com/products/8276

 

Adafruitとu8g2のいずれのライブラリも、SRAMに確保したページメモリに描画し、まとめてSSD1306に転送するというごく普通の構成をとっています。

しかし私のような用途には、困った点が2つあります。

ひとつは、ATmega328の2KBしかないSRAMからページメモリを確保するので、半分以上がライブラリで使われてしまい、本来目的のアプリケーションの開発に制約を与えてしまいます。

もうひとつは、「サウンドモジュール」や「FrequencyShifter」は、32bitMPUなのでSRAM消費は気にしなくて良いが、このような音モノアプリでは、オフライン描画したものをまとめてSSD1306に転送する際のオーバーヘッドが問題になります。これらのマイコンでも数msecのオーバーヘッドがあり、その間の割り込みを使わない処理はストールするので、ノイズなど音質劣化に繋がります。

 

そこで、オフラインのページメモリを使わず、SSD1306の機能を直接使う低レベルライブラリを作ることにしました。

 

 

デジタル版Frequency Shifterの検討 その5 RP2040 + I2S ( D/A ,A/D ) ソフト編1

■RP2040 arduino(ボード Raspberry Pi PICO)で利用できる低レベル関数

・GPIOのR/Wでは、digitalWriteと比べてgpio_putなどの低レベル関数の方が圧倒的に速い。// 8ns  STM32のように5Vトレラントで無い点は要注意。

・内蔵12bitADCは4個しか無いがなかなか速い。adc_read() // 2usec  もちろんanalogRead()は遅い。

・タイマー割り込みの機能は一つしか無いが、8つもあるPWMカウンタで割り込みを書けれるので困ることはない。

・PWMの使い方には癖がある。PWMは独立したものが8個(1個に付き比較器は2個)あり、これを8スライスと呼ぶ。どのスライスを使うかは、GPIO番号により固定されている。、スラスイ0からスラスイ7まで割り振られており変更できない。スライス番号を使用してPWMカウンタでタイマー割り込みを掛けることもできる。

・2コアは、setup1(),loop1()を用意するだけで簡単に起動できる。

 

■まず I2S D/A は?

RP2040 arduino I2S は、ハードではなく、PIO プロセッサを用いてサポートされている。ライブラリの初期化時にPIO プロセッサにコードが送り込まれ起動するようです。PIOプロセッサのコードは、arduinoでは書けない。(Pythonではできるのに)

D/A チップには、定番らしいTi社のPCM5102 を使用。DIPボード化品をAliで安価に入手できた。

PCM5102は、16/24/32bit が2チャンネルありS/Nは100dB以上。3.3V単一でありながらチャージポンプを内蔵して、AC 2.1V(RMS)が出力できます。もちろんデルタシグマのオーバーサンプリング処理なのでアンチエリアシングフィルタもディジタルで内蔵されています。(SPI D/Aと比べると夢のような仕様で明らかに高音質が期待できます)

I2S マスター/スレーブが使用でき、fsck分周比も多数サポート、特に嬉しいのが、PICO I2Sでは、MCKクロックが出力されないが、PCM5102のMCK端子をGNDに接続すると内蔵PLLでMCKを発生してくれること。 

テストしたのは、16bitと24bit。fsck = 44.1KHz、DMA bufferは、L/R合わせて32bit*8本です。24bitの場合、LRCLKに44.1KHzでは、BCLKは、44.1KHz*24*2 =2.1168MHz をスレーブのPCM5102に与えると、内部でオーバーサンプリング用クロックMCKが生成され、デルタシグマ処理とデジタルフィルタを動作させてくれます。

注意すべき点は、I2S は完全な同期処理なので、マイコンがマスターとはいえ、ソフトはこのタイミングに従う作りにする必要があります。実際にはDMAバッファの空き具合に合わせることになります。

テストの結果は良好で、今まで苦労したS/Nが嘘のように改善され、惚れてしまいました。また、24bitにすると、アナログでのゲイン調整を省略しても支障なさそうなので、アナログがとてもシンプルにできます。

このD/Aが使えるだけでもRP2040にする価値があると思います。

 

■次に I2S A/Dは?

リーズナブルな I2S A/Dとして、同じくTiの定番ぽいPCM1808を検討しました。Ali でも多数ある。

結果から言うと、数々の課題に当たり、今回は使用するのを断念しました

本当は、安価で24bit でアンチエリアシング内蔵のA/D というだけでも、すごい魅力があるわけですが・・・ 残念。

一応、検討メモとして残しておきます。( 私自身I2S初心者のため、あちこち寄り道していますが・・)

PCM1808は、MSCKを与える必要がありかつ24bit固定
D/AにはPCM5102を使うことは決定しているので、D/AだけでなくA/Dも完全同期処理になる。D/A とA/Dで物理的に誤差なくFsck レートを合わせる必要がある。もしD/AとA/Dで異なるビット精度を選択すると各々のBCLKが異なることになり、Fsckを完全に合わすのは難しくなる。
またPCM1808はPLL でMCKを内部で生成してくれないので、普通は外部オシレータで生成する必要がある。
・参考にした、PCM1808 -> PCM5102 直結動作例
 PCM1808をマスターモードに設定し、外部オシレータからMCKを与え分周比を設定すると、PCM1808は、BCLK、LRCLK、Doutを出力する。このMCK、BCLK、LRCLKをPCM5102(スレーブ固定) に与えると、分周比は自動で設定され、A/D D/Aの完全同期が達成されるというエレガントな方法です。もちろん24bit固定のPCM1809に合わせてPCM5102も24bitに統一します。 
ところが、実際問題として、RP2040 arduinoI2S のライブラリがマスタ固定なので、この構成では、D/A には出力できるが、A/Dからの入力ができないことが判明しこのやり方は断念。(今後のライブラリ進化に期待)
 
・そこでひねり出してのが、PCM1808 ,PCM5102を共にスレーブにしかつ同期させる方法
2チャンネルのI2Sポートにそれぞれスレーブ設定のPCM1808とPCM5102を接続し、両Iチャンネルから全く同じ周波数のBCLK、LRCLKを出すように設定する作戦です。
マイコン側がマスターなのでBCLK,LRCLKは、マイコンのクロック基準で作られます。この際、MCKとして外部オシレータから非同期のものを与えると、PCM1808とPCM5102が完全に同期されない危険があり得るので(うまくいくかもしれないが)、MCKもマイコンに同期したものを出すことにします。BCLKは、RP2040のクロック133MHzを11分周して発生します。この12.09MHzから、256分周に設定したLRCLKは、47.23KHzになります。(この手法では、48KHzとか44.1KHzのサンプリングレートにはできないが、直接デジタルで外部に出すことは無いので、自己都合のサンプリングレートで良しとします)
ここまでは目論見通りに行ったのですが、実際にテストプログラムを組もうとすると、24bit固定のPCM1809をつないだI2Sの入力ライブラリが、24bitをサポートしていない様子。A/D 値がNGで行き詰まる。それに加えて、入力用に用意されている関数がブロッキングのread関数のみ。出力用I2Sでは用意されていた、availableのような関数も無い。ということで、この手法も断念。PIOプロセッサのコードとライブラリの改定に期待したい。
結局今回は、I2SのA/Dが使えないことになり、残念ながらSPI でいく方針に変更
arduinoで使いたいと考えている人以外は、解決策があると思いますが・・