NuttX for Raspberry Pi PicoでPico Display Packを使う


Raspberry Pi Pico専用の液晶モジュール Pico Display Pack が秋月電子でも買えるようになりました。

この液晶モジュール、コントローラのST7789はNuttXにも既にドライバが存在するので、NuttXでも簡単に動かせるのではないかと試してみました(が、ちょっとハマりました)。

接続

まず最初の問題が、Display PackとPicoとの接続です。商品ページの写真の通り、Pico専用に作られたモジュールなのでピンヘッダを付けたPicoであればそのまま刺さります。が、挿してしまうと他のGPIOピンから信号を取れなくなってしまうため、UARTが繋げなくなってしまいます。現状USB CDCをサポートしていないPicoのNuttXではこれは大問題。
やむなく、Display Packの液晶関連の各ピンとPicoの同じピンをブレッドボード上で繋ぐことに。せっかくの専用モジュールが台無しですが仕方ない…。

以下のピン同士を接続します。

端子名 ピン番号
LCD_DC 21
LCD_CS 22
GND 18,23
LCD_SCLK 24
LCD_MOSI 25
BL_EN 26
LCD_RESET 30
3V3 36
  • モジュール上のスイッチやLEDは今回は使わないので、SW_*とLED_*はそのままです。
  • どうやらモジュール側ではGNDがすべてつながっておらず、Pico側で全GNDピン同士がつながっていることを期待しているようです。最低限、18,23のGNDを繋いでおかないとうまく動きませんでした。

NuttX側の対応

前述したとおり、コントローラのST7789は既にドライバがあって、接続に使用するSPIも既にサポート済みです。後は、基本的にはPico対応コード側でSPIポートとST7789ドライバ間を繋いでやればよいのですが、以下の対応が必要でした。

CMD/DATA信号サポート

通常、SPIではホストからデバイスへのデータ(TX / MOSI)、デバイスからホストへのデータ(RX / MISO)、クロック(SCK)、チップセレクト(CS)の4つの線で制御しますが、ST7789にはRXが出ていない1一方で、CMD/DATA(LCD_DC)という信号で、これから送るのがコントローラへのコマンドなのかそのデータなのかを区別する必要があります。
NuttXのSPIドライバでもCONFIG_SPI_CMDDATAというコンフィグレーションでこれをサポートしているので、これを有効にしたうえでLCD_DC信号を制御する関数を登録します。

boards/arm/rp2040/raspberrypi-pico/src/rp2040_spi.c
#ifdef CONFIG_SPI_CMDDATA
int rp2040_spi0cmddata(FAR struct spi_dev_s *dev, uint32_t devid, bool cmd)
{
#ifdef CONFIG_LCD_ST7789
  if (devid == SPIDEV_DISPLAY(0))
    {
      /*  This is the Data/Command control pad which determines whether the
       *  data bits are data or a command.
       */

      rp2040_gpio_put(CONFIG_RP2040_SPI0_GPIO, !cmd);

      return OK;
    }
#endif

  return -ENODEV;
}
#endif
#endif

表示位置オフセット、回転サポート

液晶コントローラの中にはフレームバッファRAMがあって、ホストはSPIのコマンドを使ってこのRAMにデータを書き込む一方で、液晶パネルはこのRAMを読んで表示を行います。
ST7789は240×320ピクセル分のRAMを搭載しているのですが、Display Packの液晶パネルにはなぜか、このRAMの中央の135×240ピクセルが表示されるようになっています。ST7789を使った液晶モジュールは240×240の液晶を搭載しているものがほとんどで、これは素直にフレームバッファの上側をそのまま使っているのですが、何故こんなややこしいことを。

ST7789のコマンドでは、データを書き込む際の位置をフレームバッファ上のX,Y座標で指定するようになっているので、ずれている分だけのオフセットを指定してやればよいのですが、NuttXのドライバにはその機能がなかったので追加が必要でした。

また、ST7789を使った既存の液晶モジュールではこのフレームバッファを横方向にスキャンする使い方をするのですが、Display Packは縦方向にスキャンするようになっていたため、スキャン方向を指定するコマンドをコントローラに送信する対応を追加しています。
コンフィグレーション CONFIG_LCD_LANDSCAPE を有効にするとスキャン方向が横に90度傾きます。
ちなみに CONFIG_LCD_RLANDSCAPE を有効にすると、表示方向の天地が逆になります。

drivers/lcd/st7789.c
/****************************************************************************
 * Name: st7789_setorientation
 *
 * Description:
 *   Set screen orientation.
 *
 ****************************************************************************/

static void st7789_setorientation(FAR struct st7789_dev_s *dev)
{
  /* No need to change the orientation in PORTRAIT mode */

#if !defined(CONFIG_LCD_PORTRAIT)
  st7789_sendcmd(dev, ST7789_MADCTL);
  st7789_select(dev->spi, 8);

#  if defined(CONFIG_LCD_RLANDSCAPE)
  /* RLANDSCAPE : MY=1 MV=1 */

  SPI_SEND(dev->spi, 0xa0);

#  elif defined(CONFIG_LCD_LANDSCAPE)
  /* LANDSCAPE : MX=1 MV=1 */

  SPI_SEND(dev->spi, 0x70);

#  elif defined(CONFIG_LCD_RPORTRAIT)
  /* RPORTRAIT : MX=1 MY=1 */

  SPI_SEND(dev->spi, 0xc0);
#  endif

  st7789_deselect(dev->spi);
#endif
}

ビルドと起動

NuttXのコンフィグレーション raspberrypi-pico:displaypack を指定してビルドします。

$ git clone https://github.com/apache/incubator-nuttx.git nuttx
$ git clone https://github.com/apache/incubator-nuttx-apps.git apps
$ cd nuttx
$ ./tools/configure.sh raspberrypi-pico:displaypack
$ make

nxhelloコマンドで、以下のようにHello Worldが表示されます2

ターミナルエミュレータの実行

NuttXのappsには、ウィンドウシステムやターミナルエミュレータも含まれているので、そのコンフィグレーションも有効にしてみます。

以下のdefconfigを使用しました。

#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_FS_PROCFS_EXCLUDE_ENVIRON is not set
# CONFIG_LIBC_LONG_LONG is not set
# CONFIG_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
# CONFIG_NSH_DISABLE_DATE is not set
# CONFIG_NSH_DISABLE_LOSMART is not set
# CONFIG_NSH_DISABLE_PRINTF is not set
# CONFIG_NSH_DISABLE_TRUNCATE is not set
# CONFIG_NXFONTS_DISABLE_16BPP is not set
# CONFIG_NX_DISABLE_16BPP is not set
# CONFIG_NX_PACKEDMSFIRST is not set
# CONFIG_NX_WRITEONLY is not set
# CONFIG_STANDARD_SERIAL is not set
CONFIG_ARCH="arm"
CONFIG_ARCH_BOARD="raspberrypi-pico"
CONFIG_ARCH_BOARD_RASPBERRYPI_PICO=y
CONFIG_ARCH_CHIP="rp2040"
CONFIG_ARCH_CHIP_RP2040=y
CONFIG_ARCH_RAMVECTORS=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARDCTL_RESET=y
CONFIG_BOARD_LOOPSPERMSEC=10450
CONFIG_BUILTIN=y
CONFIG_DEBUG_ASSERTIONS=y
CONFIG_DEBUG_ERROR=y
CONFIG_DEBUG_FEATURES=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_WARN=y
CONFIG_DISABLE_POSIX_TIMERS=y
CONFIG_DRIVERS_VIDEO=y
CONFIG_EXAMPLES_FB=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_EXAMPLES_NXDEMO=y
CONFIG_EXAMPLES_NXDEMO_BPP=16
CONFIG_EXAMPLES_NXHELLO=y
CONFIG_EXAMPLES_NXHELLO_BPP=16
CONFIG_EXAMPLES_NXTERM=y
CONFIG_EXAMPLES_NXTEXT=y
CONFIG_EXAMPLES_NXTEXT_BPP=16
CONFIG_FS_PROCFS=y
CONFIG_FS_PROCFS_REGISTER=y
CONFIG_HAVE_CXX=y
CONFIG_I2C=y
CONFIG_LCD=y
CONFIG_LCD_DEV=y
CONFIG_LCD_FRAMEBUFFER=y
CONFIG_LCD_MAXCONTRAST=255
CONFIG_LCD_NOGETRUN=y
CONFIG_LCD_ST7789=y
CONFIG_LCD_ST7789_FREQUENCY=64000000
CONFIG_LCD_ST7789_XOFFSET=53
CONFIG_LCD_ST7789_XRES=135
CONFIG_LCD_ST7789_YOFFSET=40
CONFIG_LCD_ST7789_YRES=240
CONFIG_MAX_TASKS=8
CONFIG_MQ_MAXMSGSIZE=64
CONFIG_NFILE_DESCRIPTORS=6
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_NX=y
CONFIG_NXFONTS_PACKEDMSFIRST=y
CONFIG_NXFONT_SANS17X23B=y
CONFIG_NXTERM=y
CONFIG_NXWIDGETS=y
CONFIG_NXWIDGETS_BPP=16
CONFIG_NXWIDGETS_SIZEOFCHAR=1
CONFIG_NXWM=y
CONFIG_NX_BLOCKING=y
CONFIG_NX_KBD=y
CONFIG_NX_XYINPUT_MOUSE=y
CONFIG_RAM_SIZE=270336
CONFIG_RAM_START=0x20000000
CONFIG_READLINE_CMD_HISTORY=y
CONFIG_RP2040_SPI0=y
CONFIG_RP2040_SPI0_GPIO=16
CONFIG_RP2040_SPI=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_HPWORK=y
CONFIG_SCHED_LPWORK=y
CONFIG_SCHED_ONEXIT=y
CONFIG_SCHED_WAITPID=y
CONFIG_SDCLONE_DISABLE=y
CONFIG_SPI_CMDDATA=y
CONFIG_START_DAY=9
CONFIG_START_MONTH=2
CONFIG_START_YEAR=2021
CONFIG_SYSLOG_CONSOLE=y
CONFIG_SYSTEM_I2CTOOL=y
CONFIG_SYSTEM_NSH=y
CONFIG_SYSTEM_SPITOOL=y
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_OSTEST=y
CONFIG_UART0_SERIAL_CONSOLE=y
CONFIG_USER_ENTRYPOINT="nsh_main"
CONFIG_VIDEO=y
CONFIG_VIDEO_FB=y

有線LANとの同時使用

更に、NuttX for Raspberry Pi Picoで有線LAN接続で行ったENC28J60による有線LAN接続と同時に使ってみました。
Display PackがSPI0、ENC28J60がSPI1を使用します。

双方を合体させて、以下のようなコンフィグレーションになります。

#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_FS_PROCFS_EXCLUDE_ENVIRON is not set
# CONFIG_LIBC_LONG_LONG is not set
# CONFIG_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
# CONFIG_NSH_DISABLE_DATE is not set
# CONFIG_NSH_DISABLE_LOSMART is not set
# CONFIG_NSH_DISABLE_PRINTF is not set
# CONFIG_NSH_DISABLE_TRUNCATE is not set
# CONFIG_NXFONTS_DISABLE_16BPP is not set
# CONFIG_NX_DISABLE_16BPP is not set
# CONFIG_NX_PACKEDMSFIRST is not set
# CONFIG_NX_WRITEONLY is not set
# CONFIG_RP2040_DMAC is not set
# CONFIG_STANDARD_SERIAL is not set
CONFIG_ARCH="arm"
CONFIG_ARCH_BOARD="raspberrypi-pico"
CONFIG_ARCH_BOARD_RASPBERRYPI_PICO=y
CONFIG_ARCH_CHIP="rp2040"
CONFIG_ARCH_CHIP_RP2040=y
CONFIG_ARCH_RAMVECTORS=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARDCTL_RESET=y
CONFIG_BOARD_LOOPSPERMSEC=10450
CONFIG_BUILTIN=y
CONFIG_DEV_ZERO=y
CONFIG_DISABLE_POSIX_TIMERS=y
CONFIG_ENC28J60=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_EXAMPLES_NXDEMO=y
CONFIG_EXAMPLES_NXDEMO_BPP=16
CONFIG_EXAMPLES_NXHELLO=y
CONFIG_EXAMPLES_NXHELLO_BPP=16
CONFIG_EXAMPLES_NXTERM=y
CONFIG_EXAMPLES_NXTEXT=y
CONFIG_EXAMPLES_NXTEXT_BPP=16
CONFIG_FS_PROCFS=y
CONFIG_FS_PROCFS_REGISTER=y
CONFIG_HAVE_CXX=y
CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_I2C=y
CONFIG_LCD=y
CONFIG_LCD_DEV=y
CONFIG_LCD_FRAMEBUFFER=y
CONFIG_LCD_MAXCONTRAST=255
CONFIG_LCD_NOGETRUN=y
CONFIG_LCD_ST7789=y
CONFIG_LCD_ST7789_FREQUENCY=64000000
CONFIG_LCD_ST7789_XOFFSET=53
CONFIG_LCD_ST7789_XRES=135
CONFIG_LCD_ST7789_YOFFSET=40
CONFIG_LCD_ST7789_YRES=240
CONFIG_MQ_MAXMSGSIZE=64
CONFIG_NET=y
CONFIG_NETDB_DNSCLIENT=y
CONFIG_NETDB_DNSSERVER_NOADDR=y
CONFIG_NETDEVICES=y
CONFIG_NETINIT_DHCPC=y
CONFIG_NETINIT_DNS=y
CONFIG_NETINIT_DNSIPADDR=0x08080808
CONFIG_NETINIT_NOMAC=y
CONFIG_NETINIT_THREAD=y
CONFIG_NETUTILS_DHCPC=y
CONFIG_NETUTILS_TELNETD=y
CONFIG_NET_BROADCAST=y
CONFIG_NET_ICMP=y
CONFIG_NET_ICMP_SOCKET=y
CONFIG_NET_LOOPBACK=y
CONFIG_NET_ROUTE=y
CONFIG_NET_SOCKOPTS=y
CONFIG_NET_TCP=y
CONFIG_NET_UDP=y
CONFIG_NET_UDP_CHECKSUMS=y
CONFIG_NFILE_DESCRIPTORS=8
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_NX=y
CONFIG_NXFONTS_PACKEDMSFIRST=y
CONFIG_NXFONT_SANS17X23B=y
CONFIG_NXTERM=y
CONFIG_NXWIDGETS=y
CONFIG_NXWIDGETS_BPP=16
CONFIG_NXWIDGETS_SIZEOFCHAR=1
CONFIG_NXWM=y
CONFIG_NX_BLOCKING=y
CONFIG_NX_KBD=y
CONFIG_NX_XYINPUT_MOUSE=y
CONFIG_RAM_SIZE=270336
CONFIG_RAM_START=0x20000000
CONFIG_READLINE_CMD_HISTORY=y
CONFIG_RP2040_ENC28J60_INTR_GPIO=11
CONFIG_RP2040_ENC28J60_RESET_GPIO=10
CONFIG_RP2040_SPI0=y
CONFIG_RP2040_SPI0_GPIO=16
CONFIG_RP2040_SPI1=y
CONFIG_RP2040_SPI1_GPIO=12
CONFIG_RP2040_SPI=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_HPWORK=y
CONFIG_SCHED_HPWORKPRIORITY=192
CONFIG_SCHED_ONEXIT=y
CONFIG_SCHED_WAITPID=y
CONFIG_SDCLONE_DISABLE=y
CONFIG_SMP=y
CONFIG_SMP_NCPUS=2
CONFIG_SPI_CMDDATA=y
CONFIG_START_DAY=9
CONFIG_START_MONTH=2
CONFIG_START_YEAR=2021
CONFIG_SYSLOG_CONSOLE=y
CONFIG_SYSTEM_I2CTOOL=y
CONFIG_SYSTEM_NSH=y
CONFIG_SYSTEM_NTPC=y
CONFIG_SYSTEM_PING=y
CONFIG_SYSTEM_SPITOOL=y
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_OSTEST=y
CONFIG_UART0_SERIAL_CONSOLE=y
CONFIG_USER_ENTRYPOINT="nsh_main"
CONFIG_WQUEUE_NOTIFIER=y

ビルドして起動し、コンソールからnxtermを起動します。
動画の34秒あたりから背景が変わって文字が出ているのは、telnetで接続してnxtextという別のデモを実行させたものです。

実用性はともかく、かなり複雑なことができるようになってきました。
すべてNuttXのappsに入っているデモをそのまま使っています。まだアプリケーションコードはまったく書いていません(笑)。


  1. 正確には、ST7789自体はRX(MISO)の信号が出ていますがDisplay Packがそれを端子側に出していません。基本的にはホスト側からのコマンド/データ送信のみで表示には使えるので不要との判断でしょう。 

  2. twitterのコメントに書いたDMAがおかしい件は、ST7789ドライバで使用している16bit転送が正常に動作しない問題があったのが原因で、既に修正済みです。