ART-Pi 是 RT-Thread 团队经过半年的精心准备,专门为嵌入式软件工程师、开源创客设计的一款极具扩展功能的 DIY 开源硬件。
官网地址:http://art-pi.gitee.io/website/
MDK5 烧写示例程序
买回来没自信看文档,所以导致板子的 boot_loader 被擦除了,于是乎烧写进去的程序没有反应。
资源下载
首先去 GitHub 下载相关资源:https://github.com/RT-Thread-Studio/sdk-bsp-stm32h750-realthread-artpi
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
| $ sdk-bsp-stm32h750-realthread-artpi
├── README.md
├── RealThread_STMH750-ART-Pi.yaml
├── debug
├── documents
│ ├── coding_style_cn.md
│ ├── RT-Thread 编程指南.pdf
│ ├── UM5001-RT-Thread ART-Pi 快速上手.md
│ ├── UM5002-RT-Thread ART-Pi 开发手册.md
│ ├── UM5003-RT-Thread ART-Pi BT_WIFI 模块固件下载手册.md
│ ├── UM5004-RT-Thread ART-Pi 代码贡献手册.md
│ ├── board
│ └── figures
├── libraries
│ ├── STM32H7xx_HAL
│ ├── drivers
│ ├── rt_ota_lib
│ └── wlan_wiced_lib
├── projects
│ ├── art_pi_blink_led
│ ├── art_pi_bootloader
│ ├── art_pi_factory
│ ├── art_pi_wifi
│ └── industry_io_gateway
├── rt-thread
└── tools
|
- RealThread_STMH750-ART-Pi.yaml
描述 ART-Pi 的硬件信息
- debug
QSPI FLASH 下载算法等
- documents
图纸,文档,图片以及 datasheets 等
- libraries
STM32H7 固件库,通用外设驱动,rt_ota 固件库,wlan 固件库等
- projects
示例工程文件夹,包含出厂程序,网关程序等
- rt-thread
rt-thread 源码
- tools
wifi 固件,BT 固件,rbl 打包工具等
符号链接
接下来需要使用 mklink
命令来分别为 rt-thread
及 libraries
文件创建符号链接。
1
2
3
4
5
| E:\project\sdk-bsp-stm32h750-realthread-artpi\projects\art_pi_blink_led>mklink /D rt-thread ..\..\rt-thread
为 rt-thread <<===>> ..\..\rt-thread 创建的符号链接
E:\project\sdk-bsp-stm32h750-realthread-artpi\projects>mklink /D libraries ..\libraries
为 libraries <<===>> ..\libraries 创建的符号链接
|
上面的配置可以说是一劳永逸,因为可以方便的包 rt-thread 和 libraries 的头文件到当前工程,除了配置符号链接外,还可以更改工程 art_pi_blink_led
根目录的 SConstruct
文件中关于 rt-thread 和 libraries 的路径:
1
2
3
4
5
6
7
8
9
10
11
| if os.path.exists('rt-thread'):
RTT_ROOT = os.path.normpath(os.getcwd() + '/rt-thread')
else:
RTT_ROOT = os.path.normpath(os.getcwd() + '../../../rt-thread')
# 省略....
if os.path.exists('libraries'):
libraries_path_prefix = 'libraries'
else:
libraries_path_prefix = '../../libraries'
|
这样就可以关联 rt-thread 系统和 libraries 库文件了。但是这种方式有一些明显缺陷,例如使用 vscode
等工具打开找不到对应的库和 rt-thread 的相关头文件。
当然,我们可以在 C/C++ Configurations
中配置头文件路径,如下的第 7 和 8 行配置,但是比较繁琐不是很建议。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| {
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../../rt-thread/**",
"${workspaceFolder}/../../libraries/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"compilerPath": "C:/Program Files/x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin/gcc.exe",
"cStandard": "gnu17",
"cppStandard": "gnu++14",
"intelliSenseMode": "gcc-arm"
}
],
"version": 4
}
|
配置 Kconfig
如果我们要使用 menuconfig
命令来配置,则需要检查 art_pi_blink_led
工程根目录下的 Kconfig
文件中的相关路径是否正确,否则会报错导致打不开配置面板。
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
| config BSP_DIR
string
option env="BSP_ROOT"
default "."
config RTT_DIR
string
option env="RTT_ROOT"
default "rt-thread"
config PKGS_DIR
string
option env="PKGS_ROOT"
default "packages"
source "$RTT_DIR/Kconfig"
source "$PKGS_DIR/Kconfig"
source "$RTT_DIR/../libraries/Kconfig"
config RT_STUDIO_BUILT_IN
bool
select ARCH_ARM_CORTEX_M7
select RT_USING_COMPONENTS_INIT
select RT_USING_USER_MAIN
default y
|
BSP_DIR : 是一个变量,指的是我们的板级支持包的目录路径是当前路径。
RTT_DIR : 指定的是 rt-thread 系统的包路径。
PAGS_DIR : 配置的是 packages 的路径。
同样的道理,还需要检查相关联的文件夹下的 Kconfig
文件是否存在,或者检查内部的配置路径是否和工程一致。
烧写 bootloader
使用 ENV 工具在 art_pi_bootloader
目录执行 scons --target=mdk5
来生成 MDK5 工程(注意:这里我是用的是 MDK5 其他开发工具不讨论)
需要安装 pack 支持包,可以去 keil 官网下载 Keil.STM32H7xx_DFP.2.7.0.pack
并安装,因为我们的板子处理器是 STM32H7 系列。
接下来配置一下 Flash Download
:
这里需要注意的是如果烧写不进去,有可能已经存在 bootloader 则跳过此步骤尝试烧写 led 示例程序,或者勾选 Dowload Function 中的 Erase Full Chip
选项先擦除重新烧写 bootloader.
安装下载算法
接下来我们用同样的方式使用 ENV 将点灯示例 art_pi_blink_led
生成 MDK5 工程。
这里需要注意的是我们需要安装新算法,复制 sdk-bsp-stm32h750-realthread-artpi\debug\flm
目录下的 ART-Pi_W25Q64.FLM
文件到 Keil 的安装目录,例如我的是 C:\Keil_v5\ARM\Flash
.
然后去配置 Flash Download
:
这里特别需要注意的是 Address Range
的配置一定要从 90000000H 开始到 907FFFFFH。
按键切换 LED 实验
最后送上一个开场小实验,按键切换点灯实验。
电路原理
查看 sdk-bsp-stm32h750-realthread-artpi\documents\board\ART-Pi_HW_V1.5\ART-Pi_SCH_V1.5_Release.pdf
文件发现 GPIO_LED_B 和 GPIO_LED_R 分别对应处理器的 PI8 和 PC15 两个管脚,而 GPIO_USER_KEY1 对应 PH4 管脚。
源码
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
| #include <rtthread.h>
#include <rtdevice.h>
#include "drv_common.h"
#define THREAD_STACK_SIZE 200 //线程栈大小(字节)
#define THREAD_TIMESLICE 40 //占用的滴答时钟数
#define NULL_LED -1
#define LED_PIN_RED GET_PIN(C, 15) //红灯管脚
#define LED_PIN_BLUE GET_PIN(I, 8) //蓝灯管脚
#define KEY_SWITCH_LED GET_PIN(H, 4) //点亮红灯按钮
static rt_thread_t key_led_thread = RT_NULL;
static rt_uint8_t thread_priority = 20;
//根据下标点亮LED
void change_lighting_led(int lightIndex)
{
switch(lightIndex){
case 0:
rt_pin_write(LED_PIN_RED, PIN_LOW);
rt_pin_write(LED_PIN_BLUE, PIN_HIGH);
break;
case 1:
rt_pin_write(LED_PIN_RED, PIN_LOW);
rt_pin_write(LED_PIN_BLUE, PIN_LOW);
break;
case 2:
rt_pin_write(LED_PIN_RED, PIN_HIGH);
rt_pin_write(LED_PIN_BLUE, PIN_LOW);
break;
default:
rt_pin_write(LED_PIN_RED, PIN_HIGH);
rt_pin_write(LED_PIN_BLUE, PIN_HIGH);
break;
}
}
//读取按键
void read_key_lighting_led()
{
int lightIndex = NULL_LED;
change_lighting_led(lightIndex); //熄灭所有LED
while (1)
{
if(rt_pin_read(KEY_SWITCH_LED) == PIN_LOW)
{
rt_thread_mdelay(50); //去抖动
if(rt_pin_read(KEY_SWITCH_LED) == PIN_LOW)
{
if(lightIndex == NULL_LED){
lightIndex = 0;
}else{
lightIndex = lightIndex % 3;
}
change_lighting_led(lightIndex); //切换点亮的LED
lightIndex++;
}
}
rt_thread_mdelay(50);
}
}
//线程函数
void key_control_led_entry(void *param)
{
//设置管脚的模式
rt_pin_mode(LED_PIN_RED, PIN_MODE_OUTPUT);
rt_pin_mode(LED_PIN_BLUE, PIN_MODE_OUTPUT);
rt_pin_mode(KEY_SWITCH_LED, PIN_MODE_INPUT);
read_key_lighting_led();
}
//创建键盘控制LED线程
int create_key_control_led_thread(void)
{
key_led_thread = rt_thread_create("led_test", key_control_led_entry,
RT_NULL, THREAD_STACK_SIZE, thread_priority, THREAD_TIMESLICE);
if(key_led_thread != RT_NULL)
{
rt_thread_startup(key_led_thread);
}
return 0;
}
INIT_APP_EXPORT(create_key_control_led_thread);
|
代码分析
RT-Thread 中关于 GPIO 的函数主要有三个:rt_pin_mode
和 rt_pin_read
还有 rt_pin_write
可以自行查阅 API.
这里建议在 MDK 中设置 Browse Information
跟踪代码,设置好后记得重新编译(勾选后编译比较慢,请耐心等待一会)。
然后我们追踪 GET_PIN 发现在 drv_common.h
中定义的一个宏。
1
2
| #define __STM32_PORT(port) GPIO##port##_BASE
#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN)
|
我们从官网的产品规格书中可以看到 PC15 和 PI8 分别对应的是管脚 10 和 7, 如下图:
而在 CubeMX 生成的 HAL 硬件抽象层的 stm32h7xx_hal_gpio.h
头文件中定义了这几个 GPIO 管脚。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| /** @defgroup GPIO_pins_define GPIO pins define
* @{
*/
#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */
#define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */
|
可以看到 PC15 应该对应的是 GPIO_PIN_10 而 PI8 应该对应的是 GPIO_PIN_7. 转载请说明出处:https://dp2px.com
RT-Thread 系统为我们重新编写了 GPIO 驱动文件,根据起始地址 GPIOA_BASE
和管脚分组的容量大小计算出了对应的管脚地址,我们并没有采用 stm32h7xx_hal_gpio.h
中定义的管脚。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| /*!< D3_AHB1PERIPH peripherals */
#define GPIOA_BASE (D3_AHB1PERIPH_BASE + 0x0000UL)
#define GPIOB_BASE (D3_AHB1PERIPH_BASE + 0x0400UL)
#define GPIOC_BASE (D3_AHB1PERIPH_BASE + 0x0800UL)
#define GPIOD_BASE (D3_AHB1PERIPH_BASE + 0x0C00UL)
#define GPIOE_BASE (D3_AHB1PERIPH_BASE + 0x1000UL)
#define GPIOF_BASE (D3_AHB1PERIPH_BASE + 0x1400UL)
#define GPIOG_BASE (D3_AHB1PERIPH_BASE + 0x1800UL)
#define GPIOH_BASE (D3_AHB1PERIPH_BASE + 0x1C00UL)
#define GPIOI_BASE (D3_AHB1PERIPH_BASE + 0x2000UL)
#define GPIOJ_BASE (D3_AHB1PERIPH_BASE + 0x2400UL)
#define GPIOK_BASE (D3_AHB1PERIPH_BASE + 0x2800UL)
#define RCC_BASE (D3_AHB1PERIPH_BASE + 0x4400UL)
#define PWR_BASE (D3_AHB1PERIPH_BASE + 0x4800UL)
#define CRC_BASE (D3_AHB1PERIPH_BASE + 0x4C00UL)
#define BDMA_BASE (D3_AHB1PERIPH_BASE + 0x5400UL)
#define DMAMUX2_BASE (D3_AHB1PERIPH_BASE + 0x5800UL)
#define ADC3_BASE (D3_AHB1PERIPH_BASE + 0x6000UL)
#define ADC3_COMMON_BASE (D3_AHB1PERIPH_BASE + 0x6300UL)
#define HSEM_BASE (D3_AHB1PERIPH_BASE + 0x6400UL)
#define RAMECC3_BASE (D3_AHB1PERIPH_BASE + 0x7000UL)
|
而关于 RT-Thread 中的线程创建和 INIT_APP_EXPORT 使用请参考我的另一篇博文 《RT-Thread 中的多线程》