STM32でNUTTXを使ってみる

デバイスドライバーの実装


こちらでuserledsデバイスの利用方法を紹介しました。
そこで、今回はデバイスドライバーの実装を少しだけ説明します。

[$HOME/nuttxspace/nuttx/configs/stm32f4discovery/src/stm32_bringup.c]
この関数でドライバーの登録を行っています。
  ret = userled_lower_initialize("/dev/userleds");


userled_lower_initialize()[$HOME/nuttxspace/nuttx/driver/leds/userled_lower.c] では
以下の2つの関数を呼び出しています。

@board_userled_initialize() [$HOME/nuttxspace/nuttx/configs/stm32f4discovery/src/stm32_userleds.c]
F4 Discoveryには4つのオンボードLEDがあるので、LED1からLED4のGPIOポートの初期化 を行います。

void board_userled_initialize(void)
{
   /* Configure LED1-4 GPIOs for output */

   stm32_configgpio(GPIO_LED1);
   stm32_configgpio(GPIO_LED2);
   stm32_configgpio(GPIO_LED3);
   stm32_configgpio(GPIO_LED4);
}

GPIOポートは以下で定義されています。
 [$HOME/nuttxspace/nuttx/configs/stm32f4discovery/src/stm32f4discovery.h]

/* LEDs */

#define GPIO_LED1       (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
                         GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN12)
#define GPIO_LED2       (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
                         GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN13)
#define GPIO_LED3       (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
                         GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN14)
#define GPIO_LED4       (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
                         GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN15)



Auserled_register();  [$HOME/nuttxspace/nuttx/driver/leds/userled_upper.c]
デバイスファイル(/dev/userleds)の作成と、仮想ファイルシステム関数ポインター、ボード依存関数ポインターの登録を行っていま す。

仮想ファイルシステム関数ポインターの構造体
static const struct file_operations userled_fops =
{
  userled_open,  /* open */
  userled_close, /* close */
  NULL,          /* read */
  userled_write, /* write */
  NULL,          /* seek */
  userled_ioctl  /* ioctl */
#ifndef CONFIG_DISABLE_POLL
  , NULL         /* poll */
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  , NULL         /* unlink */
#endif
};

ボード依存関数ポインターの構造体
static const struct userled_lowerhalf_s g_userled_lower =
{
  .ll_supported = userled_supported,
  .ll_led       = userled_led,
  .ll_ledset    = userled_ledset,
};

これでファイルI/O関数(ioctl)を呼び出すと、ファイルI/O関数内で仮想ファイルシステム関数(userled_ioctl)をコール します。
仮想ファイルシステム関数内でボード依存関数をコールします。
ボード依存関数は関数ポインターとして登録されているので、どこでコールしているのか、とても分かりにくいです。
どこを探してもuserled_ledset()を呼び出している個所は見つかりません。


ioctl(fd, ULEDIOC_SETALL, ledset);

userled_ioctl()

lower->ll_ledset(lower, ledset);
lower->ll_ledsetは以下の関数ポインタ
static void userled_ledset(FAR const struct userled_lowerhalf_s *lower,
                           userled_set_t ledset)
{
  board_userled_all(ledset);
}

void board_userled_all(uint8_t ledset)
{
  stm32_gpiowrite(GPIO_LED1, (ledset & BOARD_LED1_BIT) == 0);
  stm32_gpiowrite(GPIO_LED2, (ledset & BOARD_LED2_BIT) == 0);
  stm32_gpiowrite(GPIO_LED3, (ledset & BOARD_LED3_BIT) == 0);
  stm32_gpiowrite(GPIO_LED4, (ledset & BOARD_LED4_BIT) == 0);
}

アプリケーションからはGPIOポートは完全に隠蔽され、アプリケーションがGPIOポートを直接操作することはできません。
必ず仮想ファイルシステム経由で操作する必要があり、仮想ファイルシステムのデバイスドライバーを作成する必要が有ります。

2018年にSONYからSPRESENSEが発表されました。
SPRESENSEはNuttxが標準の開発環境ですが、SONY独自のNuttxにカスタマイズされています。
SPRESENSEでは、Spresense SDK Librayの一部として、GPIO Interface driverが提供さ れていて
アプリから直接、GPIOポートを操作できるようになっています。
こ ちらでSPRESENSEでGPIOを操作するコードが紹介されています。
オリジナルのNuttXにはそのような関数はありません。



NuttXはリソースも結構使います。
STM32F1の128Kクラス(F103C8やF103CB)では、SRAMが小さすぎて全く使い物になりません。
最低でもSTM32F1の256Kクラス(F103RCやF103VC)、真面目に動かそうとするとSTM32F4が必要になります。

NuttXで新しいデバイスドライバーを作るのは大変です。
Lチカするだけでも、上記の手順でLチカ用のデバイスドライバーを作る必要が有ります。
NuttXはシェル機能まで提供する非常に高機能なOSですが、
デバイスドライバーを作るためには、Linuxのドライバーの知識が必要になります。

続く....