ラベル TinyUSB の投稿を表示しています。 すべての投稿を表示
ラベル TinyUSB の投稿を表示しています。 すべての投稿を表示

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_disk[ 4 /*DISK_BLOCK_NUM*/][DISK_BLOCK_SIZE] =
{
  {
      0xEB, 0x3C, 0x90,   /* BS_jmpBoot */
      0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30,   /* BS_OEMName "MSDOS5.0" */
      0x00, 0x10,   /* ☆BPB_BytePerSec */
      0x01,         /* BPB_SecPerClus */
      0x01, 0x00,   /* BPB_RsvdSecCnt */
      0x01,         /* BPB_NumFATs */
      0x80, 0x00,   /* ☆BPB_RootEntCnt */
      0x00, 0x01,   /* ☆BPB_TotSec16 */
      0xF8,         /* BPB_Media  */
      0x01, 0x00,   /* BPB_FATSz16 */
      0x01, 0x00,   /* BPB_SecPerTrk */
      0x01, 0x00,   /* BPB_NumHeads */
      0x00, 0x00, 0x00, 0x00, /* BPB_HiddSec */
      0x00, 0x00, 0x00, 0x00, /* 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_read( 0, 0, temp_buff, 512);
  if (( temp_buff[510] != 0x55) || (temp_buff[511] != 0xaa))
#endif
  {
    flash_range_erase( FLASH_TARGET_OFFSET, SECTOR_SIZE *4);
    flash_range_program( FLASH_TARGET_OFFSET, msc_disk[0], SECTOR_SIZE *4);
  }
}


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

void flash_read( int32_t sec, int32_t offset, uint8_t *p_buff, int32_t size )
{
  memcpy( p_buff, &flash_target_contents[sec*4096+offset], size);
}


void flash_write( int32_t sec, int32_t offset, uint8_t *p_buff, int32_t size )
{
  if (offset == 0)
  {
    flash_range_erase( FLASH_TARGET_OFFSET+ sec*4096, SECTOR_SIZE);
  }
  flash_range_program( FLASH_TARGET_OFFSET+ sec*4096 +offset, p_buff, size);
}

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月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       (void* buffer, uint32_t bufsize);
static inline void     tud_midi_read_flush (void);
static inline uint32_t tud_midi_write      (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize);
static inline uint32_t tudi_midi_write24   (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_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_ReadTask( void )
{
    uint8_t buff[RX_BUFFSIZE];
    
    uint32_t read_size = tud_midi_available();
    if ( read_size > 0 )
    {
        int32_t i;
        read_size = tud_midi_read( buff, RX_BUFFSIZE );

        for ( i=0; i<read_size; i++ )
        {
            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月23日火曜日

USB-MIDIのサンプルコードはどこ?

まじめにTinyUSBのドキュメントを読み込むのは大変なのでUSB-MIDIのサンプルコードが無いかなと探していたところpico-sdk/lib/tinyusbにサンプルが含まれているという記事を見つけて、「そうかpico-exampleじゃなくてpico-sdkの方か!」と早速そのフォルダを見てみるとなんとカラでした。
WEBから見るGitリポジトリにはファイルが色々あるのでどうやら落とせてないだけみたい。Gitには詳しくないので色々調べてみたところ、Raspberry Pi Picoに直接関係が無いような部分はsubmoduleで管理しておりドキュメントで書かれているコマンドでは落ちてこないそうです。

ドキュメントに書かれているSDKを落とすコマンド
>git clone -b master https://github.com/raspberrypi/pico-sdk.git

こうするとsubmoduleも全部落としてくれる
>git clone --recursive https://github.com/raspberrypi/pico-sdk.git

私のように既に落としてしまっている場合はpico-sdkフォルダ上から
>git submodule update --init --recursive

とすると全部落としてくれます。


ちなみにUSB-MIDIのサンプルはここにありました。
pico-sdk\lib\tinyusb\examples\device\midi_test