2021年4月3日土曜日

Raspberry Pi PicoをUSBメモリーにしてみた

 MSCのサンプルアプリは8KByteのRAMドライブを使用していますがこのRAMドライブをフラッシュに置き換えていわゆるUSBメモリー的な動作をするようにしてみました。

まずはRAMドライブが512Byte/セクターになっているのでこれをフラッシュの消去単位である4KByte/セクターに変更します。ブロック数も256ブロックにして合計1MByteのドライブになるようにします。

enum
{
  DISK_BLOCK_NUM  = 256,
  DISK_BLOCK_SIZE = 4096
};


合わせてBPBの内容をこのサイズに合わせます。

uint8_t msc_disk4 /*DISK_BLOCK_NUM*/][DISK_BLOCK_SIZE] =
{
  {
      0xEB0x3C0x90,   /* BS_jmpBoot */
      0x4D0x530x440x4F0x530x350x2E0x30,   /* BS_OEMName "MSDOS5.0" */
      0x000x10,   /* ☆BPB_BytePerSec */
      0x01,         /* BPB_SecPerClus */
      0x010x00,   /* BPB_RsvdSecCnt */
      0x01,         /* BPB_NumFATs */
      0x800x00,   /* ☆BPB_RootEntCnt */
      0x000x01,   /* ☆BPB_TotSec16 */
      0xF8,         /* BPB_Media  */
      0x010x00,   /* BPB_FATSz16 */
      0x010x00,   /* BPB_SecPerTrk */
      0x010x00,   /* BPB_NumHeads */
      0x000x000x000x00/* BPB_HiddSec */
      0x000x000x000x00/* BPB_TotSec32 */

BPBの表記はリトルエンディアンなのでセクターサイズ(BPB_BytePerSec)4096=0x1000が0x00,0x10となり、セクターサイズが8倍になったのでルートエントリー(BPB_RootEntCnt)が0x10から0x80(128個)、トータルセクターサイズ(BPB_TotSec16)は256=0x100なので0x00,0x01となります。

管理情報を予め書いておかないとアクセスできないので未フォーマットの場合(BPBのシグネイチャーが書かれていないことで判断)管理情報をフラッシュに書き込みます。

void msc_init(void)
{
#if 1 /* 0にすると強制フォーマット */
  flash_read00temp_buff512);
  if (( temp_buff[510] != 0x55) || (temp_buff[511] != 0xaa))
#endif
  {
    flash_range_eraseFLASH_TARGET_OFFSETSECTOR_SIZE *4);
    flash_range_programFLASH_TARGET_OFFSETmsc_disk[0], SECTOR_SIZE *4);
  }
}


後は肝心の読み書きAPIのwrite10,read10をフラッシュの読み書きに置き換えます。

void flash_readint32_t secint32_t offsetuint8_t *p_buffint32_t size )
{
  memcpyp_buff, &flash_target_contents[sec*4096+offset], size);
}


void flash_writeint32_t secint32_t offsetuint8_t *p_buffint32_t size )
{
  if (offset == 0)
  {
    flash_range_eraseFLASH_TARGET_OFFSETsec*4096SECTOR_SIZE);
  }
  flash_range_programFLASH_TARGET_OFFSETsec*4096 +offsetp_buffsize);
}

ReadはRP2040のeXecute In Place(XIP)の仕組みのおかげでダイレクトにメモリにマッピングされているように見えますので単純にメモリーコピーで読めます。問題なのは書き込みの方でフラッシュに書き込みを行うとキャッシュのコヒーレンシーの問題が出てしまいキャッシュをクリアする必要があるのですが、このキャッシュの管理がコードを実行しているXIPと共通なのでコードの実行に支障が出て書き込み時にストールしてしまうことがあります。データシートを見ると何か回避策がありそうな感じですが。一般的なセオリーであるフラッシュ書き込み時には別のメモリー(RAM)でコードを実行するという方法で回避します。調べてみると実に簡単にRAM上で実行できました。

CMakeLists.txtに以下の1行を足すだけです。

pico_set_binary_type(TestMSC copy_to_ram)

これで1MByteのUSBメモリーになりました。


Pico側でもこのファイルを読み書きできるように実装すればPCとのデータ共有が楽になるのでアプリケーションの幅が広がるかなと思います。


2021年3月31日水曜日

デュアルコア用の機能

マルチコア用の仕組みはこれだけではなくコアの間にSIO(Single-cycle IO block)というブロックがありこの中にFIFO 0 to 1、FIFO 1 to 0というコア間でデータ転送する仕組みとHardware Spinlockというリソースの排他制御の仕組みなどがあります。


SIO(Single-cycle IO block)に含まれる機能
・CPUID
・GPIO Control
・Hardware Spinlock
・Inter-processor FIFO (Mailboxes)
・Integer Divider
・Interpolator



2021年3月30日火曜日

Tiny USB のMIDIクラスの受信ルーチン

先日見つけた Tiny USB のサンプルコードは送信しかしていなかったので受信はどうするのかなと色々見てたのですが特にドキュメントも用意されていない様子。まぁこれぐらいのことはソースコードを読んで理解しろということなのかもしれません。

とりあえずサンプルコードで送信している関数tudi_midi_write24()をgrepしてみるとこれはmidi_device.hというヘッダーファイルにインライン関数として定義されていました。
同列に用意されているAPIは以下の通り。

//--------------------------------------------------------------------+
// Application API (Single Interface)
//--------------------------------------------------------------------+
static inline bool     tud_midi_mounted    (void);
static inline uint32_t tud_midi_available  (void);
static inline uint32_t tud_midi_read       (voidbufferuint32_t bufsize);
static inline void     tud_midi_read_flush (void);
static inline uint32_t tud_midi_write      (uint8_t jack_iduint8_t constbufferuint32_t bufsize);
static inline uint32_t tudi_midi_write24   (uint8_t jack_iduint8_t b1uint8_t b2uint8_t b3);
static inline bool     tud_midi_receive    (uint8_t packet[4]);
static inline bool     tud_midi_send       (uint8_t const packet[4]);

3バイトメッセージの送信関数はあるのに2バイトメッセージの送信関数は無いんだなという中途半端感はありますが、問題はそこではなく受信関数でした。この中で受信に関係ありそうなのはtud_midi_read()とtud_midi_read_flush()。ソースコードを確認してみたところtud_midi_available()も受信用APIでした。

static inline uint32_t tud_midi_available(void);
受信FIFOにあるデータ数を返す。

static inline uint32_t tud_midi_read(void* buffer, uint32_t bufsize);
受信FIFOにあるデータを指定されたバッファにコピーする。

static inline void     tud_midi_read_flush(void);
受信FIFOをクリアする。

ということらしいのでtud_midi_available()でデータを受信しているか確認してデータがあればtud_midi_read()で読み込むというのが正しそうです。

実際のコードにするとこんな感じ

void MIDI_ReadTaskvoid )
{
    uint8_t buff[RX_BUFFSIZE];
    
    uint32_t read_size = tud_midi_available();
    if ( read_size > 0 )
    {
        int32_t i;
        read_size = tud_midi_readbuffRX_BUFFSIZE );

        for ( i=0i<read_sizei++ )
        {
            printf"0x%02X "buff[i] );
        }
        puts(" ");
    }
}

試しにMIDI-OXからノートメッセージを送信してみるとコンソールに正しく表示されました。この関数をメインループの中からポーリングしても良いですがtud_midi_rx_cb()というコールバックの仕組みも用意されているのでこちらから呼ぶのがスマートですね。
RTOSが使えるようになったらここでタスクを起床してMIDI処理をさせる流れです。

void tud_midi_rx_cb(uint8_t itf)
{
    MIDI_ReadTask();
}



2021年3月29日月曜日

「Pico RGB Keypad」を買ってみた!

 秋月で自作基板用の部品を見繕っていたらPimoroniのPico RGB Keypadを見つけ、「これはMIDIコントローラーに良いかも!」と一緒に注文してしまいました。

それが届いたので早速組み立て。といっても半田付けするようなところはなくキーパッドのシリコンパッドをネジ止めするだけです。そして公式のGitリポジトリからサンプルコードを落として、コンパイル、焼きこみ、・・・何も起こらない。

サンプルのソースコードを見るとLEDは薄く点灯してパッドを押すとLEDの色が変化する感じのようです。とりあえず良くわからないのでサンプルコードでUSB-シリアルを使えるように変更してprintfデバッグしてみたところ、どうやらスイッチは正しく読めているがLEDが全く反応しない。PICOのSPI端子の所をオシロで見てもそれなりに動いてそうです。
Pimoroni公式に何か情報ないかと見て回ったのですが、この基板の回路図すらおいてない。まぁ動いてしまえば大したことやってる基板じゃないからサンプルコードだけあれば十分なんだろうけどね。楽しみにしていた分がっかりでした。



2021年3月28日日曜日

根岸公園に行ってきた!

 桜が満開ということなので根岸公園に行ってきました。このご時世なのでホントに散歩だけしに行ったのですが、人はそれなりに多かったですが通常よりもかなり少なく迷惑な酔っ払いもいないので非常にゆっくりできました。



自動販売機にファンタプレミアムなオレンジがあったので購入しました。
グレープはちょっとファンタっぽい感じがしなかったのですが、こちらはなかなか良い感じに仕上がってると思います。