为micropython启用文件系统(3)
启用oofatfs
oofatfs的代码已经存在于micropython的代码仓库中,但是需要在makefile中把相关的编译选项打开。extmod/extmod.mk文件中包含了extmod目录下很多组件的编译配置代码段,其中也包含了oofatfs的部分:
# VFS FAT FS
OOFATFS_DIR = lib/oofatfs
# this sets the config file for FatFs
CFLAGS_MOD += -DFFCONF_H=\"$(OOFATFS_DIR)/ffconf.h\"
ifeq ($(MICROPY_VFS_FAT),1)
CFLAGS_MOD += -DMICROPY_VFS_FAT=1
SRC_MOD += $(addprefix $(OOFATFS_DIR)/,\
ff.c \
ffunicode.c \
)
endif
extmod.mk已经包含在micropython的makefile中了。在 ports/mm32/Makefile 中,添加CFLAGS_MOD和SRC_MOD:
…
CFLAGS += $(CFLAGS_MOD)
…
SRC_HAL_MM32_C += \
$(MCU_DIR)/devices/$(CMSIS_MCU)/system_$(CMSIS_MCU).c \
$(MCU_DIR)/drivers/hal_rcc.c \
$(MCU_DIR)/drivers/hal_gpio.c \
$(MCU_DIR)/drivers/hal_uart.c \
$(MCU_DIR)/drivers/hal_sdio.c \
SRC_BRD_MM32_C += \
$(BOARD_DIR)/clock_init.c \
$(BOARD_DIR)/pin_init.c \
$(BOARD_DIR)/board_init.c \
$(BOARD_DIR)/machine_pin_board_pins.c \
$(BOARD_DIR)/sdcard_sdio.c \
SRC_C += \
main.c \
fatfs_port.c \
…
$(SRC_MOD) \
这里还在SRC_C中手动补完了fatfs_port.c文件。
此时,需要指定“MICROPY_VFS_FAT”的配置值为1,才能解锁对oofatfs相关源文件的包含。参考nrf的port中的做法,在 ports\mm32\boards\MB_F3270\mpconfigboard.mk 中添加:
MCU_SERIES = mm32f3270
CMSIS_MCU = mm32f3277g7
LD_FILES = boards/mm32f3277g7_flash.ld
MICROPY_VFS_FAT ?= 1
此处特别注意,在makefile系统中的“MICROPY_VFS_FAT”和C预编译系统中的“MICROPY_VFS_FAT”是两个域的东西,在mk文件中直接定义“MICROPY_VFS_FAT ?= 1”仅作用于makefile系统,“CFLAGS_MOD += -DMICROPY_VFS_FAT=1”才是作用在C预编译系统中的,作用于C源代码的内容。
另外,还要在代码中添加FATFS和VFS的宏开关,在micropython内核中启用与文件系统相关的功能,在编译的过程中包含到最终的可执行文件中。在 ports\mm32\ mpconfigport.h 文件中添加如下宏定义开关:
// fatfs configuration used in ffconf.h
#define MICROPY_VFS (1)
//#define MICROPY_VFS_FAT (1) /* already included in cflags. */
#define MICROPY_FATFS (1)
#define MICROPY_FATFS_ENABLE_LFN (1)
#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
#define MICROPY_FATFS_USE_LABEL (1)
#define MICROPY_FATFS_RPATH (2)
#define MICROPY_FATFS_MULTI_PARTITION (1)
…
在mpconfigport.h中添加mp_import_stat、mp_builtin_open和mp_builtin_open_opj等函数到vfs组件中对应函数的映射,同时注销掉之正在main.c文件中的空实现。
// use vfs's functions for import stat and builtin open
#define mp_import_stat mp_vfs_import_stat
#define mp_builtin_open mp_vfs_open
#define mp_builtin_open_obj mp_vfs_open_obj
#if 0
mp_import_stat_t mp_import_stat(const char *path) {
return MP_IMPORT_STAT_NO_EXIST;
}
mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
#endif
一番折腾之后,终于在包含oofatfs的情况下通过了编译。
将sd卡访问函数框架注册到vfs
这一步还没有直接调用sdcard_sdio中的函数,但是要创建调用框架。
在 ports/mm32 目录下创建machine_sdcard.h/.c 文件。没错,除了系统默认的访问操作,sdcard模块将要挂在machine模块之下。
从网页上复制下来代码种子的代码到machien_sdcard源文件中,对象名字也要从“pyb_flash”改成“machine_sdcard”。
在使用代码注入种子的时候,还是遇到了很多问题。
需要在machine_sdcard.h中添加代码,包含必要的声明和引用:
/* machine_sdcard.h */
#ifndef __MACHINE_SDCARD_H__
#define __MACHINE_SDCARD_H__
#include "py/runtime.h"
#include "extmod/vfs.h"
#include "extmod/vfs_fat.h"
extern const struct _mp_obj_type_t machine_sdcard_type;
extern const struct _mp_obj_base_t machine_sdcard_obj;
void machine_sdcard_init_vfs(fs_user_mount_t *vfs);
#endif /* __MACHINE_SDCARD_H__ */
在machine_sdcard.c中,需要删除 machine_sdcard_obj 声明之前的“STATIC”。
const mp_obj_base_t machine_sdcard_obj = { &machine_sdcard_type };
machine_sdcard_ioctl()函数中的命令宏,也要换成“MP_BLOCKDEV_IOCTL_XXX”(这些宏来自于extmod/vfs.h)。
STATIC mp_obj_t machine_sdcard_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) {
mp_int_t cmd = mp_obj_get_int(cmd_in);
switch (cmd) {
case MP_BLOCKDEV_IOCTL_INIT:
machine_sdcard_init();
if(b_machine_sdcard_is_initialised)
{
return MP_OBJ_NEW_SMALL_INT(0);
}
return MP_OBJ_NEW_SMALL_INT(1);
case MP_BLOCKDEV_IOCTL_DEINIT:
machine_sdcard_flush();
return MP_OBJ_NEW_SMALL_INT(0); // TODO properly
case MP_BLOCKDEV_IOCTL_SYNC:
machine_sdcard_flush();
return MP_OBJ_NEW_SMALL_INT(0);
case MP_BLOCKDEV_IOCTL_BLOCK_COUNT:
return MP_OBJ_NEW_SMALL_INT(machine_sdcard_get_block_count());
case MP_BLOCKDEV_IOCTL_BLOCK_SIZE:
return MP_OBJ_NEW_SMALL_INT(machine_sdcard_get_block_size());
default:
return mp_const_none;
}
}
vfs->flags、vfs->readblocks[]、vfs->writeblocks[]等写法,也要插入blockdev:
void machine_sdcard_init_vfs(fs_user_mount_t *vfs)
{
vfs->base.type = &mp_fat_vfs_type;
vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL;
vfs->fatfs.drv = vfs;
vfs->blockdev.readblocks[0] = MP_OBJ_FROM_PTR(&machine_sdcard_readblocks_obj);
vfs->blockdev.readblocks[1] = MP_OBJ_FROM_PTR(&machine_sdcard_obj);
vfs->blockdev.readblocks[2] = MP_OBJ_FROM_PTR(machine_sdcard_read_blocks); // native version
vfs->blockdev.writeblocks[0] = MP_OBJ_FROM_PTR(&machine_sdcard_writeblocks_obj);
vfs->blockdev.writeblocks[1] = MP_OBJ_FROM_PTR(&machine_sdcard_obj);
vfs->blockdev.writeblocks[2] = MP_OBJ_FROM_PTR(machine_sdcard_write_blocks); // native version
vfs->blockdev.u.ioctl[0] = MP_OBJ_FROM_PTR(&machine_sdcard_ioctl_obj);
vfs->blockdev.u.ioctl[1] = MP_OBJ_FROM_PTR(&machine_sdcard_obj);
}
此处参考了 ports\cc3200\mods\pybflash.c文件中的写法。
一顿操作猛如虎之后,确保编译无误。
在main.c中调用vfs
此处参考 nrf中main.c的写法
// create vfs object
fs_user_mount_t *vfs = m_new_obj_maybe(fs_user_mount_t);
if (vfs == NULL) {
goto no_mem_for_sd;
}
vfs->str = "/sd";
vfs->len = 3;
vfs->flags = MP_BLOCKDEV_FLAG_FREE_OBJ;
sdcard_init_vfs(vfs);
// put the sd device in slot 1 (it will be unused at this point)
MP_STATE_PORT(fs_user_mount)[1] = vfs;
FRESULT res = f_mount(&vfs->fatfs, vfs->str, 1);
if (res != FR_OK) {
printf("MPY: can't mount SD card\n");
MP_STATE_PORT(fs_user_mount)[1] = NULL;
m_del_obj(fs_user_mount_t, vfs);
} else {
// TODO these should go before the /flash entries in the path
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_sd));
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_sd_slash_lib));
// use SD card as current directory
f_chdrive("/sd");
}
对vfs初始化的代码要放在 mp_init()之后,repl之前。
machine_init();
gc_init(&_heap_start, &_heap_end);
mp_init();
mp_obj_list_init(mp_sys_path, 0);
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script)
mp_obj_list_init(mp_sys_argv, 0);
readline_init0();
。。。
之前在main函数中对vfs注册的代码有误,最后还是参考stm32的porting,勉强过了编译,但在linker环节出了问题。
MindMotion@PF2LD92H MSYS /d/_git_repos/micropython_su/micropython-1.16/ports/mm32
$ make
Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.
GEN build-MB_F3270/genhdr/qstrdefs.collected.h
QSTR not updated
CC main.c
CC machine_sdcard.c
LINK build-MB_F3270/firmware.elf
C:\msys64\usr\gcc-arm-none-eabi-10-2020-q4-major\bin\arm-none-eabi-ld.exe: build-MB_F3270/extmod/vfs_fat.o: in function `fat_vfs_stat':
vfs_fat.c:(.text.fat_vfs_stat+0x70): undefined reference to `timeutils_seconds_since_2000'
make: *** [Makefile:131: build-MB_F3270/firmware.elf] Error 1
这个“timeutils_seconds_since_2000”是什么鬼?在lib\timeutils\timeutils.h 文件中找到的它的声明,在对应的c文件中找到了它的定义。也就是说,这个函数不需要我额外实现,只要想办法把它包到makefile里就好。先看看别家的的makefile怎么包timeutilis的。好吧,无论是在stm32、nrf还是mimxrt,都是直接在makefile里直接包含的,不像之前extmod可以“四两拨千斤”。那我也直接写到makefile里,在SRC_C中添加lib/timeutils/timeutils.c
SRC_C = \
main.c \
led.c \
pin.c \
ticks.c \
tusb_port.c \
board_init.c \
$(BOARD_DIR)/flash_config.c \
machine_adc.c \
machine_led.c \
machine_pin.c \
machine_rtc.c \
machine_timer.c \
machine_uart.c \
mimxrt_flash.c \
modutime.c \
modmachine.c \
modmimxrt.c \
moduos.c \
mphalport.c \
hal/flexspi_nor_flash.c \
lib/mp-readline/readline.c \
lib/libc/string0.c \
lib/timeutils/timeutils.c \
lib/utils/gchelper_native.c \
lib/utils/mpirq.c \
lib/utils/printf.c \
lib/utils/pyexec.c \
lib/utils/stdout_helpers.c \
lib/utils/sys_stdio_mphal.c \
drivers/bus/softspi.c \
extmod/modonewire.c \
$(SRC_TINYUSB_C) \
$(SRC_HAL_IMX_C) \
日它先人板板,竟然通了。
MindMotion@PF2LD92H MSYS /d/_git_repos/micropython_su/micropython-1.16/ports/mm32
$ make
Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.
GEN build-MB_F3270/genhdr/qstrdefs.collected.h
QSTR not updated
mkdir -p build-MB_F3270/lib/timeutils/
CC ../../lib/timeutils/timeutils.c
LINK build-MB_F3270/firmware.elf
text data bss dec hex filename
103928 16 2968 106912 1a1a0 build-MB_F3270/firmware.elf
这个时候下载代码肯定是不行的,因为在底层还没有跟真正的sdcard_sdio关联起来。
突然想到一点,可以在现在假的sdcard函数中“打桩”,看看哪些函数被调用了。
在machine_sdcard中调用sdcard_sdio
在machine_sdcard中调用sdcard_sdio中的函数,打通到硬件的访问。