Raspberry Pi Picoの環境を、Macbook Pro 2019, Mac Os Ventura OS13.5にインストールします。 インストールするのは、CMake, GNU Embedded Toolchain for Arm, Pico SDK, Pico Example の4つです。
インストールは 「Raspberry Pi Picoをセットアップしよう」 を参照しました。
CMakeはこちらのダウンロードサイトからダウンロードします。ページを下へ辿ると macOS 10.13 or laterという項目があります。私は、cmake-3.27.5-macos-universal.dmgを ダウンロードしました。
CMakeのアイコンをApplicationsにコピーします。
CMakeのアプリケーションにPATHを通します。Terminalを開き、以下の行をコピー&ペーストしてください。
> echo export PATH='/Applications/CMake.app/Contents/bin:$PATH' >> ~/.zshrc
GNU Embedded Toolchain for Armはこちらのダウンロードサイトからダウンロードします。ページを下へ辿り、gcc-arm-none-eabi-10.3-2021.10-mac.pkg をダウンロードします。
ARMの開発環境へPATHを通します。
> echo export PATH='/Applications/ARM/bin:$PATH' >> ~/.zshrc > echo export PATH='/Applications/ARM/arm-none-eabi/bin:$PATH' >> ~/.zshrc
まず、picoの作業用のフォルダを作成します。場所はどこでも良いのですが、以下のようにターミナルで作成しました。
> mkdir ~/pico
pico sdkとpico exampleをgithubからダウンロードします。
> cd ~/pico > git clone https://github.com/raspberrypi/pico-sdk.git --branch master > cd pico-sdk > git submodule update --init > cd .. > git clone https://github.com/raspberrypi/pico-examples.git --branch master
pico-exampleをbuildします。まず、buildフォルダをpicoフォルダの下に作成します。 その上で、buildフォルダに移動しcmakeを行います。
> cd ~/pico > mkdir build > cd build > cmake ../pico-examples
その上でmakeします。
> make
これで、buildフォルダーにExampleをコンパイルした結果が格納されています。 例えば、build/blink/blink.uf2のような.uf2ファイルがpicoボードにインストールできるファイルです。 build フォルダーの下にどのようなuf2ファイルがあるかfindコマンドで調べてみます。
> cd build > find ./ -name '*.uf2' .//blink/blink.uf2 .//pio/clocked_input/pio_clocked_input.uf2 .//pio/differential_manchester/pio_differential_manchester.uf2 .//pio/hello_pio/hello_pio.uf2 .//pio/ws2812/pio_ws2812.uf2 .//pio/ws2812/pio_ws2812_parallel.uf2 .//pio/pwm/pio_pwm.uf2 .//pio/manchester_encoding/pio_manchester_encoding.uf2 .//pio/i2c/pio_i2c_bus_scan.uf2 .//pio/uart_tx/pio_uart_tx.uf2 .//pio/apa102/pio_apa102.uf2 .//pio/logic_analyser/pio_logic_analyser.uf2 .//pio/squarewave/pio_squarewave.uf2 .//pio/spi/pio_spi_flash.uf2 .//pio/spi/pio_spi_loopback.uf2 .//pio/st7789_lcd/pio_st7789_lcd.uf2 .//pio/ir_nec/ir_loopback/pio_ir_loopback.uf2 .//pio/quadrature_encoder/pio_quadrature_encoder.uf2 .//pio/pio_blink/pio_blink.uf2 .//pio/onewire/pio_onewire.uf2 .//pio/hub75/pio_hub75.uf2 .//pio/uart_rx/pio_uart_rx_intr.uf2 .//pio/uart_rx/pio_uart_rx.uf2 .//pio/addition/pio_addition.uf2 .//hello_world/usb/hello_usb.uf2 .//hello_world/serial/hello_serial.uf2 .//cmake/build_variants/build_variant1.uf2 .//cmake/build_variants/build_variant2.uf2 .//picoboard/blinky/picoboard_blinky.uf2 .//picoboard/button/picoboard_button.uf2 .//pwm/hello_pwm/hello_pwm.uf2 .//pwm/led_fade/pwm_led_fade.uf2 .//pwm/measure_duty_cycle/pwm_measure_duty_cycle.uf2 .//clocks/hello_gpout/hello_gpout.uf2 .//clocks/hello_48MHz/hello_48MHz.uf2 .//clocks/detached_clk_peri/clocks_detached_clk_peri.uf2 .//clocks/hello_resus/hello_resus.uf2 .//multicore/multicore_runner/multicore_runner.uf2 .//multicore/hello_multicore/hello_multicore.uf2 .//multicore/multicore_fifo_irqs/multicore_fifo_irqs.uf2 .//multicore/multicore_runner_queue/multicore_runner_queue.uf2 .//interp/hello_interp/hello_interp.uf2 .//timer/hello_timer/hello_timer.uf2 .//timer/timer_lowlevel/timer_lowlevel.uf2 .//timer/periodic_sampler/periodic_sampler.uf2 .//i2c/ssd1306_i2c/ssd1306_i2c.uf2 .//i2c/lcd_1602_i2c/lcd_1602_i2c.uf2 .//i2c/lis3dh_i2c/lis3dh_i2c.uf2 .//i2c/ht16k33_i2c/ht16k33_i2c.uf2 .//i2c/mpu6050_i2c/mpu6050_i2c.uf2 .//i2c/bmp280_i2c/bmp280_i2c.uf2 .//i2c/slave_mem_i2c/slave_mem_i2c.uf2 .//i2c/pcf8523_i2c/pcf8523_i2c.uf2 .//i2c/bus_scan/i2c_bus_scan.uf2 .//i2c/mpl3115a2_i2c/mpl3115a2_i2c.uf2 .//i2c/pa1010d_i2c/pa1010d_i2c.uf2 .//i2c/mma8451_i2c/mma8451_i2c.uf2 .//i2c/mcp9808_i2c/mcp9808_i2c.uf2 .//usb/host/tinyusb_host_examples/cdc_msc_hid/tinyusb_host_cdc_msc_hid.uf2 .//usb/host/tinyusb_host_examples/hid_controller/tinyusb_host_hid_controller.uf2 .//usb/host/tinyusb_host_examples/msc_file_explorer/tinyusb_host_msc_file_explorer.uf2 .//usb/host/tinyusb_host_examples/bare_api/tinyusb_host_bare_api.uf2 .//usb/host/host_cdc_msc_hid/host_cdc_msc_hid.uf2 .//usb/device/dev_hid_composite/dev_hid_composite.uf2 .//usb/device/tinyusb_device_examples/cdc_msc/tinyusb_dev_cdc_msc.uf2 .//usb/device/tinyusb_device_examples/webusb_serial/tinyusb_dev_webusb_serial.uf2 .//usb/device/tinyusb_device_examples/hid_multiple_interface/tinyusb_dev_hid_multiple_interface.uf2 .//usb/device/tinyusb_device_examples/hid_generic_inout/tinyusb_dev_hid_generic_inout.uf2 .//usb/device/tinyusb_device_examples/msc_dual_lun/tinyusb_dev_msc_dual_lun.uf2 .//usb/device/tinyusb_device_examples/audio_test/tinyusb_dev_audio_test.uf2 .//usb/device/tinyusb_device_examples/dfu/tinyusb_dev_dfu.uf2 .//usb/device/tinyusb_device_examples/midi_test/tinyusb_dev_midi_test.uf2 .//usb/device/tinyusb_device_examples/audio_4_channel_mic/tinyusb_dev_audio_4_channel_mic.uf2 .//usb/device/tinyusb_device_examples/usbtmc/tinyusb_dev_usbtmc.uf2 .//usb/device/tinyusb_device_examples/video_capture/tinyusb_dev_video_capture.uf2 .//usb/device/tinyusb_device_examples/hid_composite/tinyusb_dev_hid_composite.uf2 .//usb/device/tinyusb_device_examples/uac2_headset/tinyusb_dev_uac2_headset.uf2 .//usb/device/tinyusb_device_examples/cdc_dual_ports/tinyusb_dev_cdc_dual_ports.uf2 .//usb/device/tinyusb_device_examples/board_test/tinyusb_dev_board_test.uf2 .//usb/device/tinyusb_device_examples/dfu_runtime/tinyusb_dev_dfu_runtime.uf2 .//usb/device/tinyusb_device_examples/dynamic_configuration/tinyusb_dev_dynamic_configuration.uf2 .//usb/device/dev_lowlevel/dev_lowlevel.uf2 .//system/hello_double_tap/hello_double_tap.uf2 .//system/unique_board_id/unique_board_id.uf2 .//system/narrow_io_write/narrow_io_write.uf2 .//flash/cache_perfctr/flash_cache_perfctr.uf2 .//flash/program/flash_program.uf2 .//flash/xip_stream/flash_xip_stream.uf2 .//flash/ssi_dma/flash_ssi_dma.uf2 .//flash/nuke/flash_nuke.uf2 .//spi/bme280_spi/bme280_spi.uf2 .//spi/spi_dma/spi_dma.uf2 .//spi/max7219_32x8_spi/max7219_32x8_spi.uf2 .//spi/mpu9250_spi/mpu9250_spi.uf2 .//spi/max7219_8x7seg_spi/max7219_8x7seg_spi.uf2 .//spi/spi_master_slave/spi_master/spi_master.uf2 .//spi/spi_master_slave/spi_slave/spi_slave.uf2 .//spi/spi_flash/spi_flash.uf2 .//watchdog/hello_watchdog/hello_watchdog.uf2 .//adc/joystick_display/joystick_display.uf2 .//adc/adc_console/adc_console.uf2 .//adc/microphone_adc/microphone_adc.uf2 .//adc/onboard_temperature/onboard_temperature.uf2 .//adc/hello_adc/hello_adc.uf2 .//adc/read_vsys/read_vsys.uf2 .//adc/dma_capture/adc_dma_capture.uf2 .//divider/hello_divider.uf2 .//rtc/rtc_alarm/rtc_alarm.uf2 .//rtc/hello_rtc/hello_rtc.uf2 .//rtc/rtc_alarm_repeat/rtc_alarm_repeat.uf2 .//dma/channel_irq/dma_channel_irq.uf2 .//dma/control_blocks/dma_control_blocks.uf2 .//dma/sniff_crc/sniff_crc.uf2 .//dma/hello_dma/hello_dma.uf2 .//gpio/hello_gpio_irq/hello_gpio_irq.uf2 .//gpio/dht_sensor/dht.uf2 .//gpio/hello_7segment/hello_7segment.uf2 .//uart/lcd_uart/lcd_uart.uf2 .//uart/hello_uart/hello_uart.uf2 .//uart/uart_advanced/uart_advanced.uf2
例えば、MyAppというフォルダを作ったとします。ディレクトリはどこでも良いです。 このフォルダには以下のファイルが入っているものとします。
MyApp |- MyApp.cpp |- MyApp.h |- CMakeLists.txt
このとき、cmakeを実行するためにCMakeLists.txtは以下のように記載します。
cmake_minimum_required(VERSION 3.12) # Pull in SDK (must be before project) include($ENV{PICO_EXAMPLE_PATH}/pico_sdk_import.cmake) include($ENV{PICO_EXAMPLE_PATH}/pico_extras_import_optional.cmake) # Initialize the SDK pico_sdk_init() set(Proj "MyApp") project (${Proj}) add_executable(${Proj} MyApp.c MyApp.h ) # pull in common dependencies target_link_libraries(${Proj} pico_stdlib) # create map/bin/hex file etc. pico_add_extra_outputs(${Proj} ) # compile option add_compile_options(-Wall -g3 -O0)
これでいずれかにbuildフォルダを作成し、cmake、その後makeをすればuf2ファイルができます。 例えば、MyAppの下にbuildを作ったとすると
MyApp |- MyApp.cpp |- MyApp.h |- CMakeLists.txt |- build
> mkdir build > cd build > cmake .. > make
の順でコマンドを実行します。
USB-MIDIでMIDIを扱うサンプルプログラムは以下のフォルダーにあります。
~/pico/pico-sdk/lib/tinyusb/examples/device/midi_test
uf2ファイルは、上で行ったmakeの結果以下のフォルダーにあります。
~/pico/build/usb/device/tinyusb_device_examples/midi_test
このmidi_test.uf2ファイルをpicoにインストールすると、連続してNote On, Note OffのMIDIが出力されます。
pico-sdkに付属しているUSBライブラリ、tinyusbを使ってUSB MIDIを扱うプログラムを書いてみます。
受信したMIDIを、そのまま送信するプログラムを作成します。まず、midi_thrフォルターを作成します。
このフォルダーには、新たに作成するmain.cと
tinyusbのサンプルプログラムの一つmidi_testからusb_config.hとusb_discriptor.cを
以下のディレクトリからコピーします。
さらにpico_exampleからpico_sdk_import.cmakeをコピーします。
~/pico/pico-sdk/lib/tinyusb/examples/device/midi_test
以下のプロフラムをmain.cとしてmidi_thrフォルダーに保存します。
#include "bsp/board.h" #include "tusb.h" /*------------- MAIN -------------*/ int main(void) { uint8_t packet[4]; board_init(); // init device stack on configured roothub port tud_init(BOARD_TUD_RHPORT); while (1) { tud_task(); // tinyusb device task while ( tud_midi_available() ){ tud_midi_packet_read(packet); // receive MIDI packet tud_midi_packet_write(packet); // send MIDI packet } } return 0; }
CMakeLists.txtを以下のように作成します。
cmake_minimum_required(VERSION 3.5) set(FAMILY rp2040) include($ENV{PICO_SDK_PATH}/lib/tinyusb/hw/bsp/family_support.cmake) include(pico_sdk_import.cmake) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) project(${PROJECT}) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(PICO_EXAMPLE_PATH $ENV{PICO_EXAMPLE_PATH}) add_executable(${PROJECT}) family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) target_sources(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/main.c ${CMAKE_CURRENT_SOURCE_DIR}/usb_descriptors.c ) target_include_directories(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) family_configure_device_example(${PROJECT})
midi_thrフォルダーは以下のようになります。
midi_thr |- CMakeLists.txt |- main.c |- usb_descriptors.c |- tusb_config.h |- pico_sdk_import.cmake
buildフォルダーを作成し、makeします。
> mkdir build > cd build > cmake .. > make
buildフォルダーの中にmidi_thr.uf2ができているはずです。これをpicoにインストールします。 Send and Receive MIDIで確認すると、 TinyUSB Deviceとして認識されています。
midi_thrにUSB-MIDIで受け取ったMIDIデータをprintfで出力し、シリアルモニターに出力します。
動作させたいプログラムは以下の通りです。 受信したMIDIデータをprintしています。 main.cとします。
<<main.c>> #include "bsp/board.h" #include "pico/stdlib.h" #include "tusb.h" /*------------- MAIN -------------*/ int main(void) { uint8_t packet[4]; board_init(); stdio_init_all(); // init device stack on configured roothub port tud_init(BOARD_TUD_RHPORT); while (1) { tud_task(); // tinyusb device task while ( tud_midi_available() ){ tud_midi_packet_read(packet); // receive MIDI packet tud_midi_packet_write(packet); // send MIDI packet printf("%02x %02x %02x %02x\n",packet[0],packet[1],packet[2],packet[3]); } } return 0; }
また、ディレクトリ構成を以下のようにします。
midi_thr_print |- CMakeLists.txt |- main.c |- usb_descriptors.c |- tusb_config.h |- pico_sdk_import.cmake
tinyUSBを使って2つ以上のUSBの種類(ここではMIDIとシリアル)を扱うには、2つのファイル、 tusb_config.h、usb_descriptors.cを書き換えます。 以下の2つのページを参照させていただきました。
Miscellaneousから2つのファイル、tusb_config.h、usb_descriptors.cをダウンロードします。 これらのファイルは3つのUSBデバイスクラス、CDC(Communications Device Class:シリアル通信)、 MIDI、MSC(USBマスストレージクラス) を使う設定になっていますので、ここからMSCを削除します。修正点を以下に示します。
<<tusb_config.h>> //------------- CLASS -------------// #define CFG_TUD_CDC 1 #define CFG_TUD_MSC 0 //<--- Clear #define CFG_TUD_HID 0 #define CFG_TUD_MIDI 1 #define CFG_TUD_VENDOR 0
修正後
<<usb_descriptors.c>> //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ enum { ITF_NUM_MIDI = 0, ITF_NUM_MIDI_STREAMING, ITF_NUM_CDC, ITF_NUM_TOTAL }; #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MIDI_DESC_LEN) //<--- Add #define EPNUM_CDC_NOTIF 0x81 #define EPNUM_CDC_OUT 0x02 #define EPNUM_CDC_IN 0x82 #define EPNUM_MIDI 0x03 uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), // Interface number, string index, EP notification address and size, EP data address (out, in) and size. TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), // Interface number, string index, EP Out & EP In address, EP size TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 0, EPNUM_MIDI, 0x80 | EPNUM_MIDI, 64) };
修正前
<<usb_descriptors.c>> //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ enum { ITF_NUM_MIDI = 0, ITF_NUM_MIDI_STREAMING, ITF_NUM_TOTAL }; #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MIDI_DESC_LEN) #if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... #define EPNUM_MIDI_OUT 0x02 #define EPNUM_MIDI_IN 0x02 #elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X // On Bridgetek FT9xx endpoint numbers must be unique... #define EPNUM_MIDI_OUT 0x02 #define EPNUM_MIDI_IN 0x03 #else #define EPNUM_MIDI_OUT 0x01 #define EPNUM_MIDI_IN 0x01 #endif uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), // Interface number, string index, EP Out & EP In address, EP size TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 0, EPNUM_MIDI_OUT, (0x80 | EPNUM_MIDI_IN), 64) };
CMakeLists.txtに1行挿入します。
<<CMakeLists.txt>> cmake_minimum_required(VERSION 3.5) set(FAMILY rp2040) include($ENV{PICO_SDK_PATH}/lib/tinyusb/hw/bsp/family_support.cmake) include(pico_sdk_import.cmake) family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) project(${PROJECT}) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(PICO_EXAMPLE_PATH $ENV{PICO_EXAMPLE_PATH}) add_executable(${PROJECT}) family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) target_sources(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/main.c ${CMAKE_CURRENT_SOURCE_DIR}/usb_descriptors.c ) target_include_directories(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) # enable usb output, disable uart output pico_enable_stdio_usb(${PROJECT} 1) family_configure_device_example(${PROJECT})
以下のコマンドでコンパイルすることができます。
> cd midi_thr_print > mkdir build > cd build > cmake .. > make
buildの下にmidi_thr_print.uf2が出来ていれば成功です。 しかし、makeの途中で以下のようなERRORが出る場合があります。
pico/pico-sdk/src/rp2_common/hardware_flash/flash.c: In function 'flash_range_erase': pico/pico-sdk/src/rp2_common/hardware_flash/flash.c:71:30 error: declaration of 'flash_range_erase' shadows a global declaration [-Werror=shadow] 71 | rom_flash_range_erase_fn flash_range_erase = (rom_flash_range_erase_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_ERASE); | ^~~~~~~~~~~~~~~~~ compilation terminated due to -Wfatal-errors. cc1: all warnings being treated as errors make[2]: *** [CMakeFiles/midi_thr_print.dir/pico/pico-sdk/src/rp2_common/hardware_flash/flash.c.obj] Error 1 make[1]: *** [CMakeFiles/midi_thr_print.dir/all] Error 2 make: *** [all] Error 2
この場合はpico/pico-sdk/lib/tinyusb/hw/bsp/family_support.cmakeを1箇所コメントアウトします。
# -Wshadow
これでエラーが消えると思います。 MIDI Send and RecieveとSerial MonitorでTinyUSBデバイスを確認することができます。
Send And Receive MIDI
シリアルモニタ