kopia lustrzana https://github.com/micropython/micropython
Merge ca93048ab9
into e60e8079a7
commit
8e27ad3264
|
@ -23,3 +23,12 @@ jobs:
|
|||
run: make -C examples/embedding -f micropython_embed.mk && make -C examples/embedding
|
||||
- name: Run
|
||||
run: ./examples/embedding/embed | grep "hello world"
|
||||
|
||||
embedding-full:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: make -C examples/embedding-full -f micropython_embed.mk submodules && make -C examples/embedding-full -f micropython_embed.mk && make -C examples/embedding-full
|
||||
- name: Run
|
||||
run: ./examples/embedding-full/embed
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# This file is part of the MicroPython project, http://micropython.org/
|
||||
# The MIT License (MIT)
|
||||
# Copyright (c) 2022-2023 Damien P. George
|
||||
#
|
||||
# This is a very simple makefile that demonstrates how to build the embed port.
|
||||
# All it needs to do is build all *.c files in the micropython_embed directory.
|
||||
# This makefile would be replaced with your custom build system.
|
||||
|
||||
EMBED_DIR = micropython_embed
|
||||
PROG = embed
|
||||
|
||||
CFLAGS += -I.
|
||||
CFLAGS += -I$(EMBED_DIR)
|
||||
CFLAGS += -I$(EMBED_DIR)/port
|
||||
CFLAGS += -I$(EMBED_DIR)/lib/littlefs
|
||||
CFLAGS += -Wall -Og -fno-common
|
||||
|
||||
SRC += main.c mphal.c modules/c_hello/modc_hello.c
|
||||
SRC += $(wildcard $(EMBED_DIR)/*/*.c)
|
||||
# Filter out lib because the files in there cannot be compiled separately, they
|
||||
# are #included by other .c files.
|
||||
SRC += $(filter-out $(EMBED_DIR)/lib/%.c,$(wildcard $(EMBED_DIR)/*/*/*.c))
|
||||
OBJ += $(SRC:.c=.o)
|
||||
|
||||
$(PROG): $(OBJ)
|
||||
$(CC) -o $@ $^ -lm
|
||||
|
||||
clean:
|
||||
/bin/rm -f $(OBJ) $(PROG)
|
|
@ -0,0 +1,43 @@
|
|||
Example of embedding MicroPython in a standalone C application (full)
|
||||
=====================================================================
|
||||
|
||||
This directory contains a simple example of how to embed a full-featured
|
||||
version of MicroPython in an existing C application.
|
||||
See also _embedding_ for a more minimal version.
|
||||
|
||||
A C application is represented here by the file `main.c`. It executes two
|
||||
simple Python scripts which print things to the standard output.
|
||||
Functions used by the MicroPython core that need to be provided by the
|
||||
application are implemented in `mphal.c`.
|
||||
|
||||
Building the example
|
||||
--------------------
|
||||
|
||||
First build the embed port using:
|
||||
|
||||
$ make -C mpy-cross
|
||||
$ cd examples/embedding-full
|
||||
$ make -f micropython_embed.mk submodules
|
||||
$ make -f micropython_embed.mk
|
||||
|
||||
This will generate the `micropython_embed` directory which is a self-contained
|
||||
copy of MicroPython suitable for embedding. The .c files in this directory need
|
||||
to be compiled into your project, in whatever way your project can do that. The
|
||||
example here uses make and a provided `Makefile`.
|
||||
|
||||
To build the example project, based on `main.c`, use:
|
||||
|
||||
$ make
|
||||
|
||||
That will create an executable called `embed` which you can run:
|
||||
|
||||
$ ./embed
|
||||
|
||||
Out of tree build
|
||||
-----------------
|
||||
|
||||
This example is set up to work out of the box, being part of the MicroPython
|
||||
tree. Your application will be outside of this tree, but the only thing you
|
||||
need to do for that is to change `MICROPYTHON_TOP` (found in `micropython_embed.mk`)
|
||||
to point to the location of the MicroPython repository. The MicroPython
|
||||
repository may, for example, be a git submodule in your project.
|
|
@ -0,0 +1,113 @@
|
|||
/* This file is part of the MicroPython project, http://micropython.org/
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2022-2023 Damien P. George
|
||||
*/
|
||||
|
||||
#include "port/micropython_embed.h"
|
||||
#include "py/stackctrl.h"
|
||||
|
||||
// This is example 1 script, which will be compiled and executed.
|
||||
static const char *example_1 =
|
||||
"print('hello world!', list(x + 1.5 for x in range(10)), end='eol\\n')";
|
||||
|
||||
// This is example 2 script, which will be compiled and executed.
|
||||
static const char *example_2 =
|
||||
"for i in range(10):\n"
|
||||
" print('iter {:08}'.format(i))\n"
|
||||
"\n"
|
||||
"try:\n"
|
||||
" 1//0\n"
|
||||
"except Exception as er:\n"
|
||||
" print('caught exception', repr(er))\n"
|
||||
"\n"
|
||||
"import gc\n"
|
||||
"print('run GC collect')\n"
|
||||
"gc.collect()\n"
|
||||
"\n"
|
||||
"print('help(\\'modules\\'):')\n"
|
||||
"help('modules')\n"
|
||||
"import sys\n"
|
||||
"help(sys)\n"
|
||||
// Skip testing stdin when using the POSIX implementation, i.e. it is
|
||||
// connected to actual stdin, because that makes the program wait for input,
|
||||
// which can be inconvenient or confusing (giving the appearance of being
|
||||
// stuck). There is no harm enabling it if you remember to provide some
|
||||
// input.
|
||||
#if !MICROPY_VFS_POSIX
|
||||
"print('sys.stdin.read(3):', repr(sys.stdin.read(3)))\n"
|
||||
"print('sys.stdin.buffer.read(3):', repr(sys.stdin.buffer.read(3)))\n"
|
||||
#endif
|
||||
"sys.stdout.write('hello stdout text\\n')\n"
|
||||
"sys.stdout.buffer.write(b'hello stdout binary\\n')\n"
|
||||
"sys.stdout.write('hello stderr text\\n')\n"
|
||||
"sys.stdout.buffer.write(b'hello stderr binary\\n')\n"
|
||||
"import os\n"
|
||||
"help(os)\n"
|
||||
"print('os.uname():', os.uname())\n"
|
||||
"import random\n"
|
||||
"help(random)\n"
|
||||
"import time\n"
|
||||
"help(time)\n"
|
||||
"print('time.gmtime(736622952) = 2023-05-05T17:29:12Z:', time.gmtime(736622952))\n"
|
||||
"import math\n"
|
||||
"help(math)\n"
|
||||
"import uctypes\n"
|
||||
"help(uctypes)\n"
|
||||
"import frozenhello\n"
|
||||
"help(frozenhello)\n"
|
||||
"print('frozenhello.hello():', frozenhello.hello())\n"
|
||||
"import c_hello\n"
|
||||
"help(c_hello)\n"
|
||||
"print('c_hello.hello():', c_hello.hello())\n"
|
||||
"import fsimage, vfs\n"
|
||||
"vfs.mount(fsimage.BlockDev(), '/lfs', readonly=True)\n"
|
||||
"print('os.listdir(\\'/lfs\\'):', os.listdir('/lfs'))\n"
|
||||
"with open('/lfs/lfshello.py', 'rb') as f:\n"
|
||||
" print('lfshello.py:', f.read())\n"
|
||||
"sys.path.append('/lfs')\n"
|
||||
"import lfshello\n"
|
||||
"help(lfshello)\n"
|
||||
"print('lfshello.hello():', lfshello.hello())\n"
|
||||
"vfs.mount(vfs.VfsPosix('.'), '/posix')\n"
|
||||
"print('os.listdir(\\'/posix\\'):', os.listdir('/posix'))\n"
|
||||
"with open('/posix/posixhello.py', 'wb') as f:\n"
|
||||
" f.write(b'def hello():\\n return \"Hello from a POSIX file!\"\\n')\n"
|
||||
"with open('/posix/posixhello.py', 'rb') as f:\n"
|
||||
" print('posixhello.py:', f.read())\n"
|
||||
"sys.path.append('/posix')\n"
|
||||
"import posixhello\n"
|
||||
"help(posixhello)\n"
|
||||
"print('posixhello.hello():', posixhello.hello())\n"
|
||||
"os.unlink('/posix/posixhello.py')"
|
||||
"\n"
|
||||
"print('finish')\n"
|
||||
;
|
||||
|
||||
// This array is the MicroPython GC heap.
|
||||
static char heap[16 * 1024];
|
||||
|
||||
int main() {
|
||||
#if MICROPY_STACK_CHECK
|
||||
// Set the stack limit, otherwise the default is zero and we will end up in
|
||||
// nlr_jump_fail() immediately.
|
||||
mp_stack_set_limit(10240);
|
||||
#endif
|
||||
// Initialise MicroPython.
|
||||
//
|
||||
// Note: &stack_top below should be good enough for many cases.
|
||||
// However, depending on environment, there might be more appropriate
|
||||
// ways to get the stack top value.
|
||||
// eg. pthread_get_stackaddr_np, pthread_getattr_np,
|
||||
// __builtin_frame_address/__builtin_stack_address, etc.
|
||||
int stack_top;
|
||||
mp_embed_init(&heap[0], sizeof(heap), &stack_top);
|
||||
|
||||
// Run the example scripts (they will be compiled first).
|
||||
mp_embed_exec_str(example_1);
|
||||
mp_embed_exec_str(example_2);
|
||||
|
||||
// Deinitialise MicroPython.
|
||||
mp_embed_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
module("frozenhello.py", base_path="modules")
|
||||
module("fsimage.py", base_path="modules")
|
|
@ -0,0 +1,32 @@
|
|||
# This file is part of the MicroPython project, http://micropython.org/
|
||||
# The MIT License (MIT)
|
||||
# Copyright (c) 2022-2023 Damien P. George
|
||||
|
||||
# Set the location of the top of the MicroPython repository.
|
||||
MICROPYTHON_TOP = ../..
|
||||
|
||||
# Include modules from extmod in the output.
|
||||
EMBED_EXTRA = extmod
|
||||
|
||||
# Include helper sources for the time module in the output.
|
||||
EMBED_EXTRA += \
|
||||
shared/timeutils/timeutils.c \
|
||||
shared/timeutils/timeutils.h \
|
||||
shared/timeutils/modtime_mphal.h
|
||||
|
||||
# Include source for mphal-backed stdio in the output.
|
||||
# Disable when using POSIX-backed stdio (MICROPY_VFS_POSIX).
|
||||
#EMBED_EXTRA += \
|
||||
# shared/runtime/sys_stdio_mphal.c
|
||||
|
||||
# Include library sources for littlefs 2 in the output.
|
||||
EMBED_EXTRA += littlefs2
|
||||
|
||||
# Freeze Python modules.
|
||||
FROZEN_MANIFEST ?= manifest.py
|
||||
|
||||
# Add C modules.
|
||||
USER_C_MODULES = modules
|
||||
|
||||
# Include the main makefile fragment to build the MicroPython component.
|
||||
include $(MICROPYTHON_TOP)/ports/embed/embed.mk
|
|
@ -0,0 +1,2 @@
|
|||
C_HELLO_MOD_DIR := $(USERMOD_DIR)
|
||||
SRC_USERMOD_C += $(C_HELLO_MOD_DIR)/modc_hello.c
|
|
@ -0,0 +1,23 @@
|
|||
// Check examples/usercmodule for more info about how this works.
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/objstr.h"
|
||||
|
||||
static mp_obj_t c_hello_hello() {
|
||||
static const MP_DEFINE_STR_OBJ(hello_str, "Hello from C!");
|
||||
return MP_OBJ_FROM_PTR(&hello_str);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(c_hello_hello_obj, c_hello_hello);
|
||||
|
||||
static const mp_rom_map_elem_t c_hello_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_c_hello) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_hello), MP_ROM_PTR(&c_hello_hello_obj) },
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(c_hello_module_globals, c_hello_module_globals_table);
|
||||
|
||||
const mp_obj_module_t c_hello_module = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&c_hello_module_globals,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_c_hello, c_hello_module);
|
|
@ -0,0 +1,2 @@
|
|||
def hello():
|
||||
return "Hello from the cold!"
|
|
@ -0,0 +1,27 @@
|
|||
# a minimal read-only block device containing a tiny littlefs image
|
||||
|
||||
|
||||
class BlockDev:
|
||||
def readblocks(self, block_num, buf, offset=0):
|
||||
addr = block_num * 128 + offset
|
||||
for i in range(len(buf)):
|
||||
buf[i] = (
|
||||
b"\x03\x00\x00\x00\xf0\x0f\xff\xf7littlefs/\xe0\x00\x10\x01\x00\x02\x00\x80\x00\x00\x00\x03\x00\x00\x00"
|
||||
b"\xff\x00\x00\x00\xff\xff\xff\x7f\xfe\x03\x00\x00 \x00\x04\x13lfshello.py 0\x00\x03\x02"
|
||||
b"\x00\x00\x006\x00\x00\x00\x100\x00\x00\x00\xda#\xed=!\xb7\x17`\x1f\xf8!\x99\xf8T\x19\xff\xff\xff\xff\xff"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x02\x00\x00\x00\xf0\x0f\xff\xf7littlefs/\xe0\x00\x10\x01\x00\x02\x00\x80\x00\x00\x00\x03\x00\x00\x00"
|
||||
b"\xff\x00\x00\x00\xff\xff\xff\x7f\xfe\x03\x00\x00\x7f\xef\xfc\x10 \x00\x00\x00R\xaa\xf5\xe6\x0f\xe0\x00\x0cq)\x81\xa1"
|
||||
b"\x90\x0f\xf8\x04@\x00\x00\x0blfshello.py \x00\x00\x0bp\x1f\xf8%\x12\xbc\x179\xff"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b'def hello():\n return "Hello f'
|
||||
b'rom a littlefs file!"\n\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
)[addr + i]
|
||||
|
||||
def ioctl(self, op, arg):
|
||||
if op == 4: # block count
|
||||
return 3
|
||||
if op == 5: # block size
|
||||
return 128
|
|
@ -0,0 +1,69 @@
|
|||
/* This file is part of the MicroPython project, http://micropython.org/
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2022-2023 Damien P. George
|
||||
*/
|
||||
|
||||
// Include common MicroPython embed configuration.
|
||||
#include <port/mpconfigport_common.h>
|
||||
|
||||
// Use the same starting configuration as on most bare-metal targets.
|
||||
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
|
||||
|
||||
// MicroPython configuration.
|
||||
#define MICROPY_ENABLE_COMPILER (1)
|
||||
#define MICROPY_ENABLE_GC (1)
|
||||
#define MICROPY_PY_GC (1)
|
||||
|
||||
#define MICROPY_PY_SYS_PLATFORM "embedded"
|
||||
#define MICROPY_PY_OS_UNAME (1)
|
||||
#define MICROPY_HW_BOARD_NAME "embedded"
|
||||
#define MICROPY_HW_MCU_NAME "C"
|
||||
|
||||
// Enable the VFS subsystem, littlefs and POSIX VFS, importing from VFS, and
|
||||
// the vfs module.
|
||||
#define MICROPY_VFS (1)
|
||||
#define MICROPY_VFS_LFS2 (1)
|
||||
#define MICROPY_VFS_POSIX (1)
|
||||
#define MICROPY_READER_VFS (1)
|
||||
#define MICROPY_PY_VFS (1)
|
||||
|
||||
// Enable floating point numbers and the math module.
|
||||
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
|
||||
|
||||
// Enable more functions in the time module. Requires additions to EMBED_EXTRA,
|
||||
// see micropython_embed.mk.
|
||||
#define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1)
|
||||
#define MICROPY_PY_TIME_TIME_TIME_NS (1)
|
||||
#define MICROPY_PY_TIME_INCLUDEFILE "shared/timeutils/modtime_mphal.h"
|
||||
|
||||
// Requires shared/readline/readline.h, don't bother as we have no input.
|
||||
#define MICROPY_PY_BUILTINS_INPUT (0)
|
||||
|
||||
// Requires MICROPY_EVENT_POLL_HOOK, don't bother as we have no pollable objects.
|
||||
#define MICROPY_PY_SELECT (0)
|
||||
|
||||
// On by default and works in this configuration, but disable it to avoid a
|
||||
// linker error if you don't include extmod/moductypes.c in the build.
|
||||
//#define MICROPY_PY_UCTYPES (0)
|
||||
|
||||
// Can be enabled once either shared/runtime/sys_stdio_mphal.c or
|
||||
// extmod/vfs_posix_file.c is included in the build.
|
||||
//#define MICROPY_PY_SYS_STDFILES (0)
|
||||
|
||||
// Can be enabled if you provide an implementation of
|
||||
// mp_hal_set_interrupt_char() in mphal.c or include
|
||||
// shared/runtime/interrupt_char.c in the build.
|
||||
#define MICROPY_KBD_EXCEPTION (0)
|
||||
|
||||
// We have our own implementation of mp_hal_stdout_tx_strn_cooked().
|
||||
#undef MP_PLAT_PRINT_STRN
|
||||
|
||||
// Enable freezing of Python modules. These would be set automatically for the
|
||||
// port build based on the presence of FROZEN_MANIFEST by py/mkrules.mk, but
|
||||
// must be set explicitly for the application build.
|
||||
#define MICROPY_MODULE_FROZEN_MPY 1
|
||||
#define MICROPY_MODULE_FROZEN_STR 1
|
||||
#define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool
|
||||
// must match MPY_TOOL_FLAGS or defaults for mpy-tool.py arguments
|
||||
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
|
||||
#define MPZ_DIG_SIZE (16)
|
|
@ -0,0 +1,106 @@
|
|||
/* This file is part of the MicroPython project, http://micropython.org/
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2022-2023 Damien P. George
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "py/builtin.h"
|
||||
#include "py/compile.h"
|
||||
#include "py/mperrno.h"
|
||||
#if MICROPY_PY_SYS_STDFILES
|
||||
#include "py/stream.h"
|
||||
#endif
|
||||
|
||||
#if !MICROPY_VFS
|
||||
|
||||
mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
|
||||
mp_raise_OSError(MP_ENOENT);
|
||||
}
|
||||
|
||||
mp_import_stat_t mp_import_stat(const char *path) {
|
||||
(void)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
|
||||
|
||||
#if MICROPY_PY_ASYNCIO
|
||||
|
||||
mp_uint_t mp_hal_ticks_ms(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_TIME
|
||||
|
||||
void mp_hal_delay_ms(mp_uint_t ms) {
|
||||
}
|
||||
|
||||
void mp_hal_delay_us(mp_uint_t us) {
|
||||
}
|
||||
|
||||
mp_uint_t mp_hal_ticks_us(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mp_uint_t mp_hal_ticks_cpu(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_TIME_TIME_TIME_NS
|
||||
|
||||
uint64_t mp_hal_time_ns(void) {
|
||||
// Nanoseconds since the Epoch.
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Text-mode standard output
|
||||
// (When MICROPY_PY_SYS_STDFILES && MICROPY_VFS_POSIX, this is only used for
|
||||
// mp_plat_print (mostly debug output), but not for print() and sys.stdout.)
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
|
||||
// This is a simplistic implementation for demonstration purposes. A real
|
||||
// one would probably want to prefix every line, not just at the start of a
|
||||
// write operation.
|
||||
static int start_of_line = 1;
|
||||
if (start_of_line) printf("py: ");
|
||||
printf("%.*s", (int)len, str);
|
||||
start_of_line = (len > 0 && (str[len-1] == '\n' || str[len-1] == '\r'));
|
||||
}
|
||||
|
||||
#if MICROPY_PY_SYS_STDFILES && !MICROPY_VFS_POSIX
|
||||
|
||||
// Binary-mode standard input
|
||||
// Receive single character, blocking until one is available.
|
||||
int mp_hal_stdin_rx_chr(void) {
|
||||
return 'X';
|
||||
}
|
||||
|
||||
// Binary-mode standard output
|
||||
// Send the string of given length.
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
||||
printf("tx: %.*s", (int)len, str);
|
||||
return len;
|
||||
}
|
||||
|
||||
uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
|
||||
uintptr_t ret = 0;
|
||||
if ((poll_flags & MP_STREAM_POLL_RD) /* && can_read */) {
|
||||
ret |= MP_STREAM_POLL_RD;
|
||||
}
|
||||
if ((poll_flags & MP_STREAM_POLL_WR) /* && can_write */) {
|
||||
ret |= MP_STREAM_POLL_WR;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,8 +1,9 @@
|
|||
Example of embedding MicroPython in a standalone C application
|
||||
==============================================================
|
||||
Example of embedding MicroPython in a standalone C application (minimal)
|
||||
========================================================================
|
||||
|
||||
This directory contains a simple example of how to embed MicroPython in an
|
||||
existing C application.
|
||||
This directory contains a simple example of how to embed a minimal version of
|
||||
MicroPython in an existing C application.
|
||||
See also _embedding-full_ for a more full-featured version.
|
||||
|
||||
A C application is represented here by the file `main.c`. It executes two
|
||||
simple Python scripts which print things to the standard output.
|
||||
|
|
|
@ -29,9 +29,15 @@
|
|||
#if MICROPY_PY_VFS
|
||||
|
||||
#include "extmod/vfs.h"
|
||||
#if MICROPY_VFS_FAT
|
||||
#include "extmod/vfs_fat.h"
|
||||
#endif
|
||||
#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2
|
||||
#include "extmod/vfs_lfs.h"
|
||||
#endif
|
||||
#if MICROPY_VFS_POSIX
|
||||
#include "extmod/vfs_posix.h"
|
||||
#endif
|
||||
|
||||
#if !MICROPY_VFS
|
||||
#error "MICROPY_PY_VFS requires MICROPY_VFS"
|
||||
|
|
|
@ -12,6 +12,15 @@ include $(MICROPYTHON_TOP)/py/mkenv.mk
|
|||
|
||||
# Include py core make definitions.
|
||||
include $(TOP)/py/py.mk
|
||||
ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod)
|
||||
include $(TOP)/extmod/extmod.mk
|
||||
endif
|
||||
|
||||
# The parts of EMBED_EXTRA that name literal files and don't need special handling.
|
||||
EMBED_EXTRA_FILES = $(filter-out extmod littlefs1 littlefs2,$(EMBED_EXTRA))
|
||||
|
||||
# Extra files need to be searched for QSTRs.
|
||||
SRC_QSTR += $(addprefix $(TOP)/,$(EMBED_EXTRA_FILES))
|
||||
|
||||
# Set the location of the MicroPython embed port.
|
||||
MICROPYTHON_EMBED_PORT = $(MICROPYTHON_TOP)/ports/embed
|
||||
|
@ -31,6 +40,11 @@ GENHDR_OUTPUT = $(addprefix $(BUILD)/genhdr/, \
|
|||
root_pointers.h \
|
||||
)
|
||||
|
||||
# Define the module freezing output.
|
||||
ifneq ($(FROZEN_MANIFEST),)
|
||||
FROZEN_OUTPUT = $(BUILD)/frozen_content.c
|
||||
endif
|
||||
|
||||
# Define the top-level target, the generated output files.
|
||||
.PHONY: all
|
||||
all: micropython-embed-package
|
||||
|
@ -43,23 +57,62 @@ clean-micropython-embed-package:
|
|||
|
||||
PACKAGE_DIR ?= micropython_embed
|
||||
PACKAGE_DIR_LIST = $(addprefix $(PACKAGE_DIR)/,py extmod shared/runtime genhdr port)
|
||||
ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod)
|
||||
PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,lib/uzlib lib/crypto-algorithms lib/re1.5)
|
||||
endif
|
||||
ifneq ($(filter littlefs1 littlefs2,$(EMBED_EXTRA)),)
|
||||
PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,lib/littlefs libsrc/littlefs)
|
||||
endif
|
||||
ifneq ($(FROZEN_MANIFEST),)
|
||||
PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,frozen)
|
||||
endif
|
||||
ifneq ($(EMBED_EXTRA_FILES),)
|
||||
PACKAGE_DIR_LIST += $(addprefix $(PACKAGE_DIR)/,$(filter-out ./,$(sort $(dir $(EMBED_EXTRA_FILES)))))
|
||||
endif
|
||||
|
||||
.PHONY: micropython-embed-package
|
||||
micropython-embed-package: $(GENHDR_OUTPUT)
|
||||
micropython-embed-package: $(GENHDR_OUTPUT) $(FROZEN_OUTPUT)
|
||||
$(ECHO) "Generate micropython_embed output:"
|
||||
$(Q)$(RM) -rf $(PACKAGE_DIR_LIST)
|
||||
$(Q)$(MKDIR) -p $(PACKAGE_DIR_LIST)
|
||||
$(ECHO) "- py"
|
||||
$(Q)$(CP) $(TOP)/py/*.[ch] $(PACKAGE_DIR)/py
|
||||
$(ECHO) "- extmod"
|
||||
ifeq ($(filter extmod,$(EMBED_EXTRA)),extmod)
|
||||
$(Q)$(CP) $(TOP)/extmod/*.[ch] $(PACKAGE_DIR)/extmod
|
||||
$(ECHO) "- lib"
|
||||
$(Q)$(CP) $(TOP)/lib/uzlib/*.[ch] $(PACKAGE_DIR)/lib/uzlib
|
||||
$(Q)$(CP) $(TOP)/lib/crypto-algorithms/*.[ch] $(PACKAGE_DIR)/lib/crypto-algorithms
|
||||
$(Q)$(CP) $(TOP)/lib/re1.5/*.[ch] $(PACKAGE_DIR)/lib/re1.5
|
||||
else
|
||||
$(Q)$(CP) $(TOP)/extmod/modplatform.h $(PACKAGE_DIR)/extmod
|
||||
endif
|
||||
ifneq ($(filter littlefs1 littlefs2,$(EMBED_EXTRA)),)
|
||||
$(ECHO) "- libsrc"
|
||||
ifeq ($(filter littlefs1,$(EMBED_EXTRA)),littlefs1)
|
||||
$(Q)$(CP) $(TOP)/lib/littlefs/lfs1*.h $(PACKAGE_DIR)/lib/littlefs
|
||||
$(Q)$(CP) $(TOP)/lib/littlefs/lfs1*.c $(PACKAGE_DIR)/libsrc/littlefs
|
||||
endif
|
||||
ifeq ($(filter littlefs2,$(EMBED_EXTRA)),littlefs2)
|
||||
$(Q)$(CP) $(TOP)/lib/littlefs/lfs2*.h $(PACKAGE_DIR)/lib/littlefs
|
||||
$(Q)$(CP) $(TOP)/lib/littlefs/lfs2*.c $(PACKAGE_DIR)/libsrc/littlefs
|
||||
endif
|
||||
endif
|
||||
$(ECHO) "- shared"
|
||||
$(Q)$(CP) $(TOP)/shared/runtime/gchelper.h $(PACKAGE_DIR)/shared/runtime
|
||||
$(Q)$(CP) $(TOP)/shared/runtime/gchelper_generic.c $(PACKAGE_DIR)/shared/runtime
|
||||
$(ECHO) "- genhdr"
|
||||
$(Q)$(CP) $(GENHDR_OUTPUT) $(PACKAGE_DIR)/genhdr
|
||||
ifneq ($(FROZEN_MANIFEST),)
|
||||
$(ECHO) "- frozen"
|
||||
$(Q)$(CP) $(FROZEN_OUTPUT) $(PACKAGE_DIR)/frozen
|
||||
endif
|
||||
$(ECHO) "- port"
|
||||
$(Q)$(CP) $(MICROPYTHON_EMBED_PORT)/port/*.[ch] $(PACKAGE_DIR)/port
|
||||
ifneq ($(EMBED_EXTRA_FILES),)
|
||||
$(ECHO) "- extra"
|
||||
$(Q)$(foreach FILE,$(EMBED_EXTRA_FILES),$(CP) $(TOP)/$(FILE) $(dir $(PACKAGE_DIR)/$(FILE)) &&) true
|
||||
endif
|
||||
|
||||
# Include remaining core make rules.
|
||||
include $(TOP)/py/mkrules.mk
|
||||
|
|
|
@ -40,3 +40,13 @@ typedef long mp_off_t;
|
|||
#endif
|
||||
|
||||
#define MICROPY_MPHALPORT_H "port/mphalport.h"
|
||||
|
||||
// Default implementation for applications that want output to go to the system
|
||||
// printf(). If you don't, or don't have printf() available, or use
|
||||
// shared/libc/printf.c (MICROPY_USE_INTERNAL_PRINTF) which would be a circular
|
||||
// definition, #undef this and implement mp_hal_stdout_tx_strn_cooked().
|
||||
#define MP_PLAT_PRINT_STRN(str, len) \
|
||||
do { \
|
||||
extern int printf(const char *fmt, ...); \
|
||||
printf("%.*s", (int)len, str); \
|
||||
} while (0)
|
||||
|
|
|
@ -1,2 +1,26 @@
|
|||
// Define so there's no dependency on extmod/virtpin.h
|
||||
#define mp_hal_pin_obj_t
|
||||
#define mp_hal_pin_obj_t mp_obj_t
|
||||
|
||||
#if MICROPY_KBD_EXCEPTION
|
||||
void mp_hal_set_interrupt_char(int c);
|
||||
#endif
|
||||
|
||||
#if MICROPY_VFS_POSIX
|
||||
// This macro is used to implement PEP 475 to retry specified syscalls on EINTR
|
||||
#define MP_HAL_RETRY_SYSCALL(ret, syscall, raise) { \
|
||||
for (;;) { \
|
||||
MP_THREAD_GIL_EXIT(); \
|
||||
ret = syscall; \
|
||||
MP_THREAD_GIL_ENTER(); \
|
||||
if (ret == -1) { \
|
||||
int err = errno; \
|
||||
if (err == EINTR) { \
|
||||
mp_handle_pending(true); \
|
||||
continue; \
|
||||
} \
|
||||
raise; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -251,8 +251,8 @@ endif
|
|||
submodules:
|
||||
$(ECHO) "Updating submodules: $(GIT_SUBMODULES)"
|
||||
ifneq ($(GIT_SUBMODULES),)
|
||||
$(Q)git submodule sync $(addprefix $(TOP)/,$(GIT_SUBMODULES))
|
||||
$(Q)git submodule update --init $(addprefix $(TOP)/,$(GIT_SUBMODULES))
|
||||
$(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES)
|
||||
$(Q)cd $(TOP) && git submodule update --init $(GIT_SUBMODULES)
|
||||
endif
|
||||
.PHONY: submodules
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022-2023 Damien P. George
|
||||
* Copyright (c) 2013-2023 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -24,10 +24,29 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/mphal.h"
|
||||
#include "shared/timeutils/timeutils.h"
|
||||
|
||||
// Send string of given length to stdout, converting \n to \r\n.
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
|
||||
printf("%.*s", (int)len, str);
|
||||
// Return the localtime as an 8-tuple.
|
||||
static mp_obj_t mp_time_localtime_get(void) {
|
||||
mp_int_t seconds = mp_hal_time_ns() / 1000000000;
|
||||
timeutils_struct_time_t tm;
|
||||
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
|
||||
mp_obj_t tuple[8] = {
|
||||
tuple[0] = mp_obj_new_int(tm.tm_year),
|
||||
tuple[1] = mp_obj_new_int(tm.tm_mon),
|
||||
tuple[2] = mp_obj_new_int(tm.tm_mday),
|
||||
tuple[3] = mp_obj_new_int(tm.tm_hour),
|
||||
tuple[4] = mp_obj_new_int(tm.tm_min),
|
||||
tuple[5] = mp_obj_new_int(tm.tm_sec),
|
||||
tuple[6] = mp_obj_new_int(tm.tm_wday),
|
||||
tuple[7] = mp_obj_new_int(tm.tm_yday),
|
||||
};
|
||||
return mp_obj_new_tuple(8, tuple);
|
||||
}
|
||||
|
||||
// Returns the number of seconds, as an integer, since the Epoch.
|
||||
static mp_obj_t mp_time_time_get(void) {
|
||||
return mp_obj_new_int(mp_hal_time_ns() / 1000000000);
|
||||
}
|
Ładowanie…
Reference in New Issue