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のドライバーの知識が必要になります。
続く....