STM32之MicroPython

买了2块mini开发板,一块STM32F407VE,另一块STM32H743VI。无他,唯体积小。看到OpenMV大行其道,开始折腾MicroPython

  • 环境: Ubuntu 16.04 x64
  • 开发板: DevEBox STM32H7XX_M Ver:V2.0 SN:1907

STM32H743VI开发板外观如下(STM32F407VE板样子都一样)。

1、安装makegcc

1
2
3
4
5
6
7
8
9
10
11
# 安装gcc相关
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
# 顺便安装g++-9
sudo apt-get install make gcc-9 g++-9

# 安装交叉编译相关
sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa
sudo apt update
sudo apt install gcc-arm-embedded

2、下载micropython源码

1
2
3
4
5
6
7
cd <SRC_ROOT>
git clone https://github.com/micropython/micropython.git
cd micropython
git submodule update --init

# (更新)拉取远程库
# git pull --progress -v --no-rebase "origin"

3、下载开发板的配置代码

1
2
3
4
5
6
7
8
9
10
11
cd <SRC_ROOT>/micropython/ports/stm32/boards
# H743的移植文件
git clone https://github.com/mcauser/MCUDEV_DEVEBOX_H7XX_M.git

# F407VE的一移植文件
# git clone https://github.com/mcauser/MCUDEV_DEVEBOX_F407VET6.git

# 修改后:
# H743: https://github.com/njuFerret/MCUDEV_DEVEBOX_H7XX_M.git
# F407Ve https://github.com/njuFerret/MCUDEV_DEVEBOX_F407VET6

此处编译出错,按照MCUDEV_DEVEBOX_H7XX_M中的Issues/1说明,编辑 MCUDEV_DEVEBOX_H7XX_M/mpconfigboard.h如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*-----------------------------------原定义----------------------------------------*/
// // HSE is 25MHz
// #define MICROPY_HW_CLK_PLLM (25) // divide external clock by this to get 1MHz
// #define MICROPY_HW_CLK_PLLN (160) // PLL clock in MHz
// #define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) // divide PLL clock by this to get core clock
// #define MICROPY_HW_CLK_PLLQ (4) // divide core clock by this to get 40MHz

/*---------------------------------修改后定义---------------------------------------*/
// The board has an 25MHz HSE, the following gives 480MHz CPU speed
#define MICROPY_HW_CLK_PLLM (5)
#define MICROPY_HW_CLK_PLLN (192)
#define MICROPY_HW_CLK_PLLP (2)
#define MICROPY_HW_CLK_PLLQ (5)
#define MICROPY_HW_CLK_PLLR (2)

// The USB clock is set using PLL3
#define MICROPY_HW_CLK_PLL3M (5)
#define MICROPY_HW_CLK_PLL3N (48)
#define MICROPY_HW_CLK_PLL3P (2)
#define MICROPY_HW_CLK_PLL3Q (5)
#define MICROPY_HW_CLK_PLL3R (2)

4、编译

1
2
3
4
5
6
cd <SRC_ROOT>/micropython
make -C mpy-cross
cd ports/stm32
make submodules
make BOARD=MCUDEV_DEVEBOX_H7XX_M clean
make BOARD=MCUDEV_DEVEBOX_H7XX_M

5、烧入开发板

编译完成后,在micropython/ports/stm32目录下会出现build-MCUDEV_DEVEBOX_H7XX_M目录,找到固件文件firmware.hex,使用STM32 ST-LINK Utility烧入即可。

烧写完成后出现low power的情况,插入usb之后会出现windows安装设备等提示,以一个U盘形式出现。

6、使用方法

两种模式,解释器模式和文件模式。前者使用putty打开串口,与其他解释器一样使用。后者直接修改main.py文件。IDE方面,VScode略显不便,可以采用Thonny,写完代码可以直接烧入开发板,比较方便。

7、例子

查看当前运行主频:

1
2
3
4
5
6
7
import machine
machine.freq()

# 或
#>>> import pyb
#>>> pyb.freq()
#(480000000, 240000000, 120000000, 120000000)

首先点个灯:

1
2
3
4
5
import pyb

tim = pyb.Timer(4,freq=2) # 2Hz ,500ms
tim.callback(lambda t:pyb.LED(1).toggle())
# tim.callback(None) # 停止回调

如果用sleep函数,必须等待其执行完成,或 Ctrl+C终止执行

1
2
3
4
import time
>>> for _ in range(30):
... pyb.LED(1).toggle()
... time.sleep(0.5)

再来一个PWM:

1
2
3
4
5
6
7
import pyb 

p = pyb.Pin('D12') # Tim4, PWM ch1, PD12
tim = pyb.Timer(4, freq=1000)
ch = tim.channel(1, pyb.Timer.PWM, pin=p)
ch.pulse_width_percent(50)

8、使用板载Flash vs MCU内置Flash

上述编译中,使用的是MCU内置flashhex固件约1.26M,烧完之后,几乎没啥空间了,考虑折腾板载的外置Flash

H7xx开发板板载一颗W25Q648MByte flash,F407开发板板载的是W25Q162MByte flash。前者支持QSPI,后者不支持。

SPI协议包括:Standard SPIDual SPIQueued SPI三种协议,分别对应3-wire, 4-wire, 6-wire

  1. Standard SPI,有4根信号线,分别为CLK、CS、MOSI和MISO。数据线工作在全双工。

  2. Dual SPI,只针对SPI Flash而言,不是针对所有SPI外设。对于SPI Flash,全双工并不常用,因此扩展了mosi和miso的用法,让它们工作在半双工,用以加倍数据传输。也就是对于Dual SPI Flash,可以发送一个命令字节进入dual mode,这样mosi变成SIO0(serial io 0),mosi变成SIO1(serial io 1),这样一个时钟周期内就能传输2个bit数据,加倍了数据传输。

  3. 类似的,QSPI也是针对SPI FlashQual SPI Flash增加了两根I/O线(SIO2,SIO3),目的是一个时钟内传输4个bit

8.1 增加管脚定义

micropython自带的PYBD_SF开发板是很好的参考资料,位于ports\stm32\boards\PYBD_SF2

  1. 可参考ports/stm32/boards/PYBD_SF2中相关内容
  2. 也可参考 MCUDEV_DEVEBOX_F407VET6中相关内容

打开MCUDEV_DEVEBOX_H7XX_M/mpconfigboard.h,增加如下内容:

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
// 1 = use internal flash (2048 KByte)
// 0 = use onboard SPI flash (8 MByte) Winbond W25Q64 with QSPI
#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)

// If using onboard SPI flash
#if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE

#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1)

// Winbond W25Q16 SPI Flash = 64 Mbit (8 MByte)
#define MICROPY_HW_SPIFLASH_SIZE_BITS (64 * 1024 * 1024)
#define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (26)
// 引脚参照原理图
#define MICROPY_HW_QSPIFLASH_CS (pin_B6)
#define MICROPY_HW_QSPIFLASH_SCK (pin_B2)
#define MICROPY_HW_QSPIFLASH_IO0 (pin_D11)
#define MICROPY_HW_QSPIFLASH_IO1 (pin_D12)
#define MICROPY_HW_QSPIFLASH_IO2 (pin_E2)
#define MICROPY_HW_QSPIFLASH_IO3 (pin_D13)

#define MICROPY_BOARD_EARLY_INIT Mcudev_Devebox_H743VI_board_early_init
void Mcudev_Devebox_H743VI_board_early_init(void);

extern const struct _mp_spiflash_config_t spiflash_config;
extern struct _spi_bdev_t spi_bdev;
#define MICROPY_HW_BDEV_IOCTL(op, arg) ( \
(op) == BDEV_IOCTL_NUM_BLOCKS ? (MICROPY_HW_SPIFLASH_SIZE_BITS / 8 / FLASH_BLOCK_SIZE) : \
(op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(&spi_bdev, (op), (uint32_t)&spiflash_config) : \
spi_bdev_ioctl(&spi_bdev, (op), (arg)) \
)

#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(&spi_bdev, (dest), (bl), (n))
#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(&spi_bdev, (src), (bl), (n))

#endif

8.2 补全QSPI实现函数

增加bdev.cboard_init.c文件

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
// bdev.c

#include "storage.h"

#if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE

// External SPI flash uses QSPI interface
STATIC const mp_soft_qspi_obj_t qspi_bus = {
.cs = MICROPY_HW_QSPIFLASH_CS,
.clk = MICROPY_HW_QSPIFLASH_SCK,
.io0 = MICROPY_HW_QSPIFLASH_IO0,
.io1 = MICROPY_HW_QSPIFLASH_IO1,
.io2 = MICROPY_HW_QSPIFLASH_IO2,
.io3 = MICROPY_HW_QSPIFLASH_IO3,
};

STATIC mp_spiflash_cache_t spi_bdev_cache;

const mp_spiflash_config_t spiflash_config = {
.bus_kind = MP_SPIFLASH_BUS_QSPI,
.bus.u_qspi.data = (void*)&qspi_bus,
.bus.u_qspi.proto = &mp_soft_qspi_proto,
#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
.cache = &spi_bdev_cache,
#endif
};

spi_bdev_t spi_bdev;

#endif
1
2
3
4
5
6
7
8
9
10
11
12
// board_init.c

#include "py/mphal.h"

void Mcudev_Devebox_H743VI_board_early_init(void) {
#include "py/mphal.h"

void Mcudev_Devebox_H743VI_board_early_init(void) {
// set SPI flash CS pin high
// mp_hal_pin_output(pin_A15);
// mp_hal_pin_write(pin_A15, 0);
}

8.3 补全管脚定义

打开MCUDEV_DEVEBOX_H7XX_M/pins.csv,增加如下内容

1
2
3
4
5
6
QSPIFLASH_CS,PB6
QSPIFLASH_SCK,PB2
QSPIFLASH_IO0,PD11
QSPIFLASH_IO1,PD12
QSPIFLASH_IO2,PE2
QSPIFLASH_IO3,PD13

8.4 编译

可根据情况,选取是否使用SPI Flash,修改宏定义后,重新编译即可。

1
2
3
// 1 = use internal flash (2048 KByte)
// 0 = use onboard SPI flash (8 MByte) Winbond W25Q64 with QSPI
#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)

9、 Mini F407VE

  • F407VE板已经自带了bdev.cboard_init.c,只是缺少一个开启cache的宏,可以参考PYBD_SF2bdev.c中分开加,也可以参考STM32L476DISCmpconfigboard.h中一次性加入。
  • 另外需要注意的是,F407VE板使用的是标准SPI,可以直接copySTM32L476DISC内容,只是需要增加一个开启关闭使用外部Flash的宏,最后注意SPI低电平有效,需要预先将管脚位置低。

最后放上patch

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
 board_init.c    |  3 ++-
mpconfigboard.h | 19 +++++++++++--------
2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/board_init.c b/board_init.c
index c60141e..896f698 100644
--- a/board_init.c
+++ b/board_init.c
@@ -3,5 +3,6 @@
void Mcudev_Devebox_F407VE_board_early_init(void) {
// set SPI flash CS pin high
mp_hal_pin_output(pin_A15);
- mp_hal_pin_write(pin_A15, 1);
+ // mp_hal_pin_write(pin_A15, 1);
+ mp_hal_pin_write(pin_A15, 0);
}
diff --git a/mpconfigboard.h b/mpconfigboard.h
index 13f6306..e7a2b31 100644
--- a/mpconfigboard.h
+++ b/mpconfigboard.h
@@ -1,10 +1,6 @@
#define MICROPY_HW_BOARD_NAME "MCUDEV DEVEBOX STM32F407VE"
#define MICROPY_HW_MCU_NAME "STM32F407VE"
-#define MICROPY_HW_FLASH_FS_LABEL "DEVEBOXF407VE"
-
-// 1 = use internal flash (512 KByte)
-// 0 = use onboard SPI flash (2 MByte) Winbond W25Q16
-#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1)
+#define MICROPY_HW_FLASH_FS_LABEL "MiniF407VE"

#define MICROPY_HW_HAS_SWITCH (1) // has 1 button KEY0
#define MICROPY_HW_HAS_FLASH (1)
@@ -15,9 +11,9 @@
#define MICROPY_HW_ENABLE_SDCARD (0) // it has a sd scard, but i am not sure what the detect pin is, yet

// HSE is 8MHz
-#define MICROPY_HW_CLK_PLLM (8) // divide external clock by this to get 1MHz
-#define MICROPY_HW_CLK_PLLN (336) // PLL clock in MHz
-#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) // divide PLL clock by this to get core clock
+#define MICROPY_HW_CLK_PLLM (4) // divide external clock by this to get 1MHz
+#define MICROPY_HW_CLK_PLLN (168) // PLL clock in MHz
+#define MICROPY_HW_CLK_PLLP (2) // divide PLL clock by this to get core clock
#define MICROPY_HW_CLK_PLLQ (7) // divide core clock by this to get 48MHz

// The board has a 32kHz crystal for the RTC
@@ -106,9 +102,16 @@
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))

+// 1 = use internal flash (512 KByte)
+// 0 = use onboard SPI flash (2 MByte) Winbond W25Q16
+#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)
+
// If using onboard SPI flash
#if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE

+// fix "error: unknown type name 'mp_spiflash_cache_t'"
+#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1)
+
// Winbond W25Q16 SPI Flash = 16 Mbit (2 MByte)
#define MICROPY_HW_SPIFLASH_SIZE_BITS (16 * 1024 * 1024)
#define MICROPY_HW_SPIFLASH_CS (pin_A15)

参考

  1. https://blog.csdn.net/liaoze22/article/details/106708796
  2. https://blog.csdn.net/wangguchao/article/details/105593303