jklincn


Stm32F103 + NuttX(二):板级配置与点灯例程


之前文章《Stm32F103 + NuttX(一):开发环境配置与系统安装》已经成功运行起了 NuttX,本文首先是对前文的补充,对板卡进行一些自定义的配置。其次,想在系统中跑一些应用,比如跑马灯,这也是官方的一个例程。

板级配置概述

nuttx\boards 文件夹用于存放特定硬件开发板配置文件,我们之前就是在这里找到了 stm3210e-eval 这个贴合我们板子的配置,可以参考此处具体的配置文档

这里我们尝试在树外树外添加自定义板级配置,复制 nuttx/boards/arm/stm32/stm32f103-minimum 目录到项目根目录下,形成以下目录结构。

lin@future:~/nuttxspace$ ls
apps  nuttx  stm32f103-minimum

这里采用 stm32f103-minimum 的配置是因为其预设中包含了 userled 这个例程,我们不用做太多的修改就可以点亮板子上的 LED 灯。等后期熟练掌握了之后,当然就可以自己写一个针对手头板子的配置文件夹,比如叫做 ATK-DNF103

stm32f103-minimum 文件夹中的内容:

  • configs:面向不同应用场景的配置预设。
  • include\board.h:定义了板子的物理属性,可以作为公共头文件提供给整个 Nuttx 系统使用。
  • scripts:链接脚本
  • src:硬件初始化代码

可以先看 include\board.h,这里涉及了时钟配置、总线时钟分频、USB 配置、SDIO 配置、按键定义、LED 定义等等。

链接脚本和之前 Stm32CubeMX 自动生成的差不多,就不细说了。

src 中比较重要的是 stm32f103_minimum.hstm32_bringup.c

stm32f103_minimum.h 中定义了非常多板上资源的宏,比如 LED,我们的板子上没有 4 个 LED,因此后续这里还需要做修改。

1
2
3
4
5
6
7
#ifdef CONFIG_STM32F103MINIMUM_BLACKPILL
#  define GPIO_LED1         (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
                            GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN12)
#else
#  define GPIO_LED1         (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
                             GPIO_OUTPUT_CLEAR|GPIO_PORTC|GPIO_PIN13)
#endif

stm32_bringup.c 中的 stm32_bringup() 函数是做板级特定的硬件初始化工作,像 LED 相关的代码就是

1
2
3
4
5
6
7
8
9
#ifdef CONFIG_USERLED
  /* Register the LED driver */

  ret = userled_lower_initialize("/dev/userleds");
  if (ret < 0)
    {
      syslog(LOG_ERR, "ERROR: userled_lower_initialize() failed: %d\n", ret);
    }
#endif

LED 例程分析

用户空间代码

configs\userled\defconfig 中可以看到

CONFIG_EXAMPLES_LEDS=y

说明它包含了 leds 这个例程,这个程序位于 apps/examples/leds

从源码可以看到,它创建了一个 led_daemon 后台守护进程,循环点亮不同的 LED 组合,每 500ms 改变一次状态。使用 ioctl 接口来操作 LED 硬件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  ledset       = 0;
  incrementing = true;

  while (g_led_daemon_started == true)
    {
      userled_set_t newset;
      userled_set_t tmp;

      // 计算下一个 LED 组合 (递增或递减)
      if (incrementing)
        {
          tmp = ledset;
          do
            {
              tmp++;
              newset = tmp & supported;
            }
          while (newset == ledset);

          /* REVISIT: There are flaws in this logic.  It would not work
           * correctly if there were spaces in the supported mask.
           */

          if (newset == 0)
            {
              incrementing = false;
              continue;
            }
        }
      else
        {
          /* REVISIT: There are flaws in this logic.  It would not work
           * correctly if there were spaces in the supported mask.
           */

          if (ledset == 0)
            {
              incrementing = true;
              continue;
            }

          tmp = ledset;
          do
            {
              tmp--;
              newset = tmp & supported;
            }
          while (newset == ledset);
        }

      ledset = newset;
      printf("led_daemon: LED set 0x%02x\n", (unsigned int)ledset);

      // 使用 ioctl 设置所有 LED
      ret = ioctl(fd, ULEDIOC_SETALL, ledset);
      if (ret < 0)
        {
          int errcode = errno;
          printf("led_daemon: ERROR: ioctl(ULEDIOC_SUPPORTED) failed: %d\n",
                 errcode);
          goto errout_with_fd;
        }

      usleep(500 * 1000L);  // 延时 500ms
    }

这里的 fd 是打开 CONFIG_EXAMPLES_LEDS_DEVPATH

1
  fd = open(CONFIG_EXAMPLES_LEDS_DEVPATH, O_WRONLY);

CONFIG_EXAMPLES_LEDS_DEVPATH 被定义为 "/dev/userleds"

1
2
# nuttx\build\include\nuttx\config.h:267
#define CONFIG_EXAMPLES_LEDS_DEVPATH "/dev/userleds"

因此可以追溯一下 "/dev/userleds" 这个设备文件的由来

LED 代码分析

讲 LED 初始化那必须要从整个系统的初始化开始讲,但篇幅可能会太多,因此放新的一篇文章中吧,这篇文章还是先点灯,简单借助 AI 知识过一下初始化流程。

  1. nuttx/arch/arm/src/stm32/stm32_start.c__start

  2. nuttx/sched/init/nx_start.cnx_start

  3. nuttx/sched/init/nx_bringup.cnx_bringup

  4. nuttx/sched/init/nx_bringup.cnx_create_initthread

  5. nuttx/sched/init/nx_bringup.cnx_start_application

  6. nuttx/boards/arm/stm32/stm32f103zet6/src/stm32_boot.cboard_late_initialize

  7. nuttx/boards/arm/stm32/stm32f103zet6/src/stm32_bringup.cstm32_bringup

stm32_bringup 函数中进入用户 LED 的初始化

1
2
3
4
5
6
7
8
9
#ifdef CONFIG_USERLED
  /* Register the LED driver */

  ret = userled_lower_initialize("/dev/userleds");
  if (ret < 0)
    {
      syslog(LOG_ERR, "ERROR: userled_lower_initialize() failed: %d\n", ret);
    }
#endif

我们从 userled_lower_initialize 这个函数开始,根据注释,它初始化了通用的 LED 下半部驱动,将它绑定并注册到上半部驱动中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/****************************************************************************
 * Name: userled_lower_initialize
 *
 * Description:
 *   Initialize the generic LED lower half driver, bind it and register
 *   it with the upper half LED driver as devname.
 *
 ****************************************************************************/

int userled_lower_initialize(FAR const char *devname)
{
  g_lednum = board_userled_initialize();
  return userled_register(devname, &g_userled_lower);
}

这里涉及到上半部和下半部的概念,在 nuttx\include\nuttx\leds\userled.h 中有这样一段注释,讲得非常清楚。上半部是通用的 LED 驱动,提供通用的用户访问 LED 的接口。下半部是板卡特定的驱动,为上半部驱动提供具体的硬件操作接口,即 userled_lowerhalf_s

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/* The user LED driver is a two-part driver:
 *
 * 1) A common upper half driver that provides the common user interface to
 *    the LEDs,
 * 2) Platform-specific lower half drivers that provide the interface
 *    between the common upper half and the platform discrete LED outputs.
 *
 * This structure defines the interface between an instance of the lower
 * half driver and the common upper half driver.  Such an instance is
 * passed to the upper half driver when the driver is initialized, binding
 * the upper and lower halves into one driver.
 */
struct userled_lowerhalf_s
{
  /* Return the set of LEDs supported by the board */

  CODE userled_set_t
  (*ll_supported)(FAR const struct userled_lowerhalf_s *lower);

  /* Set the current state of one LED */

  CODE void (*ll_setled)(FAR const struct userled_lowerhalf_s *lower,
                         int led, bool ledon);

  /* Set the state of all LEDs */

  CODE void (*ll_setall)(FAR const struct userled_lowerhalf_s *lower,
                         userled_set_t ledset);

#ifdef CONFIG_USERLED_LOWER_READSTATE
  /* Get the state of all LEDs */

  CODE void (*ll_getall)(FAR const struct userled_lowerhalf_s *lower,
                         userled_set_t *ledset);
#endif

#ifdef CONFIG_USERLED_EFFECTS
  /* Return the LED effects supported by a given LED */

  CODE void (*ll_effect_sup)(FAR const struct userled_lowerhalf_s *lower,
                             FAR struct userled_effect_sup_s *supp);

  /* Set the effect of one LED */

  CODE int (*ll_effect_set)(FAR const struct userled_lowerhalf_s *lower,
                            FAR struct userled_effect_set_s *effect);

#endif
};

我们先看 board_userled_initialize,这是下半部驱动,位于板级配置的目录下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// stm32f103-minimum/src/stm32_userleds.c
uint32_t board_userled_initialize(void)
{
  int i;

  /* Configure LED GPIOs for output */

  for (i = 0; i < BOARD_NLEDS; i++)
    {
      stm32_configgpio(g_ledcfg[i]);
    }

  return BOARD_NLEDS;
}

可以看到就是将板卡定义的 LED 进行 GPIO 的配置,并把 LED 数量返回给上层捆绑到 g_lednum 这个全局变量中。stm32_configgpio 这段代码比较长,并且我们也不用详细看,知道是根据 GPIO 定义来进行配置即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// nuttx/arch/arm/src/stm32/stm32_gpio.c

/****************************************************************************
 * Name: stm32_configgpio
 *
 * Description:
 *   Configure a GPIO pin based on bit-encoded description of the pin.
 *   Once it is configured as Alternative (GPIO_ALT|GPIO_CNF_AFPP|...)
 *   function, it must be unconfigured with stm32_unconfiggpio() with
 *   the same cfgset first before it can be set to non-alternative function.
 *
 * Returned Value:
 *   OK on success
 *   A negated errno value on invalid port, or when pin is locked as ALT
 *   function.
 *
 * To-Do: Auto Power Enable
 ****************************************************************************/

/****************************************************************************
 * Name: stm32_configgpio (for the STM32F10xxx family)
 ****************************************************************************/

#if defined(CONFIG_STM32_STM32F10XX)
int stm32_configgpio(uint32_t cfgset){}
#endif

在完成板级 GPIO 配置后,就进入上半部的驱动初始化 userled_register。这是一个很标准的设备注册函数,传入的是全局的 LED 下半部驱动接口 g_userled_lower,实例化了一个 userled_upperhalf_s,并进行初始化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// nuttx/drivers/leds/userled_lower.c
static const struct userled_lowerhalf_s g_userled_lower =
{
  userled_supported,    /* ll_supported */
  userled_setled,       /* ll_setled */
  userled_setall        /* ll_setall */
#ifdef CONFIG_USERLED_LOWER_READSTATE
  , userled_getall      /* ll_getall */
#endif
};


// nuttx/drivers/leds/userled_upper.c
struct userled_upperhalf_s
{
  /* Saved binding to the lower half LED driver */

  FAR const struct userled_lowerhalf_s *lu_lower;

  userled_set_t lu_supported; /* The set of supported LEDs */
  userled_set_t lu_ledset;    /* Current state of LEDs */
  mutex_t lu_lock;            /* Supports exclusive access to the device */

  /* The following is a singly linked list of open references to the
   * LED device.
   */

  FAR struct userled_open_s *lu_open;
};

/****************************************************************************
 * Name: userled_register
 *
 * Description:
 *   Bind the lower half LED driver to an instance of the upper half
 *   LED driver and register the composite character driver as the
 *   specified device.
 *
 * Input Parameters:
 *   devname - The name of the LED device to be registered.
 *     This should be a string of the form "/dev/ledN" where N is the
 *     minor device number.
 *   lower - An instance of the platform-specific LED lower half driver.
 *
 * Returned Value:
 *   Zero (OK) is returned on success.  Otherwise a negated errno value is
 *   returned to indicate the nature of the failure.
 *
 ****************************************************************************/

int userled_register(FAR const char *devname,
                     FAR const struct userled_lowerhalf_s *lower)
{
  FAR struct userled_upperhalf_s *priv;
  int ret;

  DEBUGASSERT(devname && lower);

  // 1. 分配一个新的 LED 驱动实例
  priv = (FAR struct userled_upperhalf_s *)
    kmm_zalloc(sizeof(struct userled_upperhalf_s));

  if (!priv)
    {
      lederr("ERROR: Failed to allocate device structure\n");
      return -ENOMEM;
    }

  // 2. 初始化 LED 驱动实例

  //   2.1 绑定 lower 接口
  priv->lu_lower = lower;
  nxmutex_init(&priv->lu_lock);

  DEBUGASSERT(lower && lower->ll_supported);
  //   2.2 获取 LED 位图(比如 2 个灯就是 0b0011,表示支持 LED0 和 LED1)
  priv->lu_supported = lower->ll_supported(lower);

  DEBUGASSERT(lower && lower->ll_setall);
  //   2.3 初始化 LED 状态为 OFF
  priv->lu_ledset = 0;
  lower->ll_setall(lower, priv->lu_ledset);

  // 3. 在系统中注册设备驱动,创建了 /dev/userleds 文件,操作集是 g_userled_fops 
  ret = register_driver(devname, &g_userled_fops, 0666, priv);
  if (ret < 0)
    {
      lederr("ERROR: register_driver failed: %d\n", ret);
      goto errout_with_priv;
    }

  return OK;

errout_with_priv:
  nxmutex_destroy(&priv->lu_lock);
  kmm_free(priv);
  return ret;
}

再看下 g_userled_fops 这个文件操作集,例程中的 openioctl 的具体实现都在这里。

这里的 open 还做了一个 per-open 的思路,每次打开都会创建一个节点状态挂在驱动实例的链表下面,这里不细展开,具体可以看 userled_open_s 结构体。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// nuttx/drivers/leds/userled_upper.c:102
static const struct file_operations g_userled_fops =
{
  userled_open,   /* open */
  userled_close,  /* close */
  NULL,           /* read */
  userled_write,  /* write */
  NULL,           /* seek */
  userled_ioctl,  /* ioctl */
};

直接看我们关心的 userled_ioctl,从 APP 例程的 led_daemon 中我们可以发现仅使用了 ULEDIOC_SUPPORTEDULEDIOC_SETALL 这两个命令。

1
2
3
4
5
6
7
// apps/examples/leds/leds_main.c
ret = ioctl(fd, ULEDIOC_SUPPORTED, (unsigned long)((uintptr_t)&supported));
ret = ioctl(fd, ULEDIOC_SETALL, ledset);

// nuttx/include/nuttx/leds/userled.h
#define ULEDIOC_SUPPORTED  _ULEDIOC(0x0001)  // 查询硬件支持哪些 LED
#define ULEDIOC_SETALL     _ULEDIOC(0x0003)  // 设置所有 LED 的状态

结合 userled_ioctl 逐一来看,

  • ULEDIOC_SUPPORTED 很简单,把 lu_supported 成员变量返回即可,这样上层应用就知道有几个灯
  • ULEDIOC_SETALL 接收要点亮的 LED 位图ledset,在和支持的 LED 位图 lu_supported验证后,使用下半部驱动接口 lower->ll_setall 进行点亮。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
static int userled_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
  FAR struct inode *inode;
  FAR struct userled_upperhalf_s *priv;
  FAR const struct userled_lowerhalf_s *lower;
  int ret;

  DEBUGASSERT(filep->f_priv != NULL &&
              filep->f_inode != NULL);
  inode = filep->f_inode;
  DEBUGASSERT(inode->i_private);
  // 拿到之前初始化的驱动实例
  priv  = inode->i_private;

  // ... 锁操作

  ret = -EINVAL;
  switch (cmd)
    {
    case ULEDIOC_SUPPORTED:
      {
        FAR userled_set_t *supported = (FAR userled_set_t *)((uintptr_t)arg);

        /* Verify that a non-NULL pointer was provided */

        if (supported)
          {
            *supported = priv->lu_supported;
            ret = OK;
          }
      }
      break;
    // ...
    case ULEDIOC_SETALL:
      {
        userled_set_t ledset = (userled_set_t)((uintptr_t)arg);

        /* Verify that a valid LED set was provided */

        if ((ledset & ~priv->lu_supported) == 0)
          {
            /* Update the LED state */

            priv->lu_ledset = ledset;

            /* Set the new LED state */

            lower = priv->lu_lower;
            DEBUGASSERT(lower != NULL && lower->ll_setall != NULL);
            lower->ll_setall(lower, ledset);
            ret = OK;
          }
      }
      break;
    // ...
    default:
      lederr("ERROR: Unrecognized command: %d\n", cmd);
      ret = -ENOTTY;
      break;
    }

  nxmutex_unlock(&priv->lu_lock);
  return ret;
}

下半部驱动接口即 userled_setall,它调用板级接口 board_userled_all,这里会进行 GPIO 写操作,从而点亮 LED。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// nuttx/drivers/leds/userled_lower.c
static void userled_setall(FAR const struct userled_lowerhalf_s *lower,
                           userled_set_t ledset)
{
  board_userled_all(ledset);
}

// stm32f103-minimum/src/stm32_userleds.c
void board_userled_all(uint32_t ledset)
{
  int i;

  /* Configure LED GPIOs for output */

  for (i = 0; i < BOARD_NLEDS; i++)
    {
      stm32_gpiowrite(g_ledcfg[i], (ledset & (1 << i)) != 0);
    }
}

最后梳理一下整个点灯调用栈

  • 应用程序 ioctl
  • 内核上半部驱动 userled_ioctl
  • 内核下半部驱动 userled_setall
  • 板级配置 board_userled_all

点亮 LED

板级配置修改

内存区域

stm32f103-minimum 的 MCU 设定是 STM32F103C8T6,它的 Flash 和 SRAM 分别是 128KB 和 20KB,这也可以从 stm32f103-minimum/scripts/ld.script 中看出来,因此我们先修改一下内存区域。

首先将 stm32f103-minimum/configs/userled/defconfig 中的

CONFIG_RAM_SIZE=20480

改为

CONFIG_RAM_SIZE=65536

再修改 scripts\ld.script,将

MEMORY
{
  flash (rx) : ORIGIN = 0x08000000, LENGTH = 128K
  sram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}

改为

MEMORY
{
  flash (rx) : ORIGIN = 0x08000000, LENGTH = 512K
  sram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}

LED 相关

如果我们想使用精英板上的两个 LED,就需要手动适配板级配置,我们希望两个灯都作为 userled 来使用。

首先先修改 stm32f103-minimum/include/board.h 中的 BOARD_NLEDS,改成 2

1
#define BOARD_NLEDS       2

然后在 stm32f103_minimum.h 中修改宏 GPIO_LED1,并添加宏 GPIO_LED2

1
2
3
4
5
6
7
// LED0 连接到 PB5, 低电平亮灯,因此初始设为高
#define GPIO_LED0         (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
                           GPIO_OUTPUT_SET|GPIO_PORTB|GPIO_PIN5)

// LED1 连接到 PE5, 低电平亮灯,因此初始设为高
#define GPIO_LED1         (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\
                           GPIO_OUTPUT_SET|GPIO_PORTE|GPIO_PIN5)

然后修改 stm32_userleds.c 中的 g_ledcfg,添加 GPIO_LED0

1
2
3
4
5
static const uint32_t g_ledcfg[BOARD_NLEDS] =
{
  GPIO_LED0,
  GPIO_LED1,
};

因为我们是低电平亮灯,因此还需要修改 board_userled_all

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void board_userled_all(uint32_t ledset)
{
  int i;

  for (i = 0; i < BOARD_NLEDS; i++)
    {
      bool on = (ledset & (1 << i)) != 0;
      stm32_gpiowrite(g_ledcfg[i], !on);
    }
}

踩坑点:芯片型号

问题描述

作者在自己调试的时候发现,只亮 LED0 但不亮 LED1,APP 例程的输出是没问题的,拿到了 2 个灯的信息,并且在循环点亮。

最后发现是芯片型号问题,如下代码所示,默认的 CONFIG_ARCH_CHIP_STM32F103C8 只有 GPIO A-C,根本就没有端口 E。因此在给 LED 配置 GPIO 时,LED1 的 GPIO 端口是无效的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// nuttx/arch/arm/include/stm32/chip.h:749
#elif defined(CONFIG_ARCH_CHIP_STM32F103C8) || defined(CONFIG_ARCH_CHIP_STM32F103CB)
#  define STM32_NFSMC                    0   /* No FSMC */
#  define STM32_NATIM                    1   /* One advanced timer TIM1 */
#  define STM32_NGTIM                    3   /* General timers TIM2-4 */
#  define STM32_NGTIMNDMA                0   /* No 16-bit general timers without DMA */
#  define STM32_NBTIM                    0   /* No basic timers */
#  define STM32_NDMA                     1   /* DMA1, 7 channels */
#  define STM32_NSPI                     2   /* SPI1-2 */
#  define STM32_NI2S                     0   /* No I2S */
#  define STM32_NUSART                   3   /* USART1-3 */
#  define STM32_NLPUART                  0   /* No LPUART */
#  define STM32_NI2C                     2   /* I2C1-2 */
#  define STM32_NCAN                     1   /* bxCAN1 */
#  define STM32_NSDIO                    0   /* No SDIO */
#  define STM32_NLCD                     0   /* No LCD */
#  define STM32_NUSBOTG                  0   /* No USB OTG FS/HS */
#  define STM32_NGPIO                    37  /* GPIOA-C */
#  define STM32_NADC                     2   /* ADC1-2 */
#  define STM32_NDAC                     0   /* No DAC */
#  define STM32_NCAPSENSE                0   /* No capacitive sensing channels */
#  define STM32_NCRC                     1   /* CRC */
#  define STM32_NETHERNET                0   /* No Ethernet */
#  define STM32_NRNG                     0   /* No random number generator (RNG) */
#  define STM32_NDCMI                    0   /* No digital camera interface (DCMI) */

而直接将 CONFIG_ARCH_CHIP_STM32F103C8 改成 CONFIG_ARCH_CHIP_STM32F103ZE 的结果是编译失败,排查发现少了 CONFIG_USERLED_LOWER 这个配置。

追溯下来发现,USERLED_LOWER 需要 ARCH_HAVE_LEDS 前置依赖

# nuttx/drivers/leds/Kconfig:13
config USERLED_LOWER
  bool "Generic Lower Half LED Driver"
  default n
  depends on ARCH_HAVE_LEDS

ARCH_HAVE_LEDS 由板子型号 ARCH_BOARD_STM32F103_MINIMUM 选择,板子型号需要芯片型号 ARCH_CHIP_STM32F103C8 这个前置依赖,因此当修改芯片型号后 ARCH_BOARD_STM32F103_MINIMUM 无法启用, CONFIG_USERLED_LOWER 这个宏也就被丢弃了。

# nuttx/boards/Kconfig:2711
config ARCH_BOARD_STM32F103_MINIMUM
  bool "STM32F103C8T6 Minimum ARM Development Board"
  depends on ARCH_CHIP_STM32F103C8
  select ARCH_HAVE_LEDS
  select ARCH_HAVE_BUTTONS
  select ARCH_HAVE_IRQBUTTONS

解决方法

从最快点亮两个灯的角度来说,我们首先修改 stm32f103-minimum/configs/userled/defconfig,将

CONFIG_ARCH_CHIP_STM32F103C8=y

换成

CONFIG_ARCH_CHIP_STM32F103ZE=y

这样就可以启用全部的 GPIO 端口。

为了解决编译错误,直接修改 nuttx/boards/Kconfig,把这里的 depends on ARCH_CHIP_STM32F103C8 前置条件删除。

// nuttx/boards/Kconfig:2711

config ARCH_BOARD_STM32F103_MINIMUM
  bool "STM32F103C8T6 Minimum ARM Development Board"
  depends on ARCH_CHIP_STM32F103C8
  select ARCH_HAVE_LEDS
  select ARCH_HAVE_BUTTONS
  select ARCH_HAVE_IRQBUTTONS
  ---help---
    A configuration for the STM32F103 Minimum board.

重新配置编译

先删除原来的配置

直接删除 build 文件夹即可

配置

cmake -B build -DBOARD_CONFIG=../stm32f103-minimum/configs/userled -GNinja

使用命令编译或使用 ctrl+shift+B 一键编译烧录

cmake --build build

输出结果

[1105/1107] Linking C executable nuttx
Memory region         Used Size  Region Size  %age Used
           flash:       46840 B       512 KB      8.93%
            sram:        4132 B        64 KB      6.30%
[1107/1107] Generating System.map

烧录

openocd -f interface/stlink.cfg \
  -c "transport select swd" \
  -f target/stm32f1x.cfg -c "adapter speed 240" \
  -c "init" \
  -c "program build/nuttx verify reset" \
  -c "shutdown"

切换回 Windows VSCode 的 串行监视器窗口,在 nsh 终端中输入 leds

nsh> leds
leds_main: Starting the led_daemon
leds_main: led_daemon started

led_daemon (pid# 5): Running
led_daemon: Opening /dev/userleds
led_daemon: Supported LEDs 0x03
led_daemon: LED set 0x01
led_daemon: LED set 0x02
led_daemon: LED set 0x03
led_daemon: LED set 0x02
led_daemon: LED set 0x01
led_daemon: LED set 0x00

观察桌上的开发板已经在红绿交替点亮了,红-绿-红绿-绿-红-灭

参考文章


本站不记录浏览量,但如果您觉得本内容有帮助,请点个小红心,让我知道您的喜欢。