刚到的 ART-Pi 开发板点灯总结

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-threadlibraries 文件创建符号链接。

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_modert_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 中的多线程》