Stm32F103 + NuttX(二):板级配置与点灯例程
发布于 2025-12-27
|
更新于
2025-12-29
|
字数:4925
之前文章《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.h 与 stm32_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 知识过一下初始化流程。
-
nuttx/arch/arm/src/stm32/stm32_start.c:__start
-
nuttx/sched/init/nx_start.c:nx_start
-
nuttx/sched/init/nx_bringup.c:nx_bringup
-
nuttx/sched/init/nx_bringup.c:nx_create_initthread
-
nuttx/sched/init/nx_bringup.c:nx_start_application
-
nuttx/boards/arm/stm32/stm32f103zet6/src/stm32_boot.c:board_late_initialize
-
nuttx/boards/arm/stm32/stm32f103zet6/src/stm32_bringup.c:stm32_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 这个文件操作集,例程中的 open 与 ioctl 的具体实现都在这里。
这里的 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_SUPPORTED 和 ULEDIOC_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
然后在 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
观察桌上的开发板已经在红绿交替点亮了,红-绿-红绿-绿-红-灭
参考文章