Skip to content

Commit f6bba86

Browse files
committed
2 parents 2caf3c7 + 42e45bd commit f6bba86

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1597
-117
lines changed

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
#
7575
# We don't follow "The short X.Y version" vs "The full version, including alpha/beta/rc tags"
7676
# breakdown, so use the same version identifier for both to avoid confusion.
77-
version = release = '1.11'
77+
version = release = '1.12'
7878

7979
# The language for content autogenerated by Sphinx. Refer to documentation
8080
# for a list of supported languages.

docs/develop/cmodules.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _cmodules:
2+
13
MicroPython external C modules
24
==============================
35

@@ -17,6 +19,10 @@ more sense to keep this external to the main MicroPython repository.
1719
This chapter describes how to compile such external modules into the
1820
MicroPython executable or firmware image.
1921

22+
An alternative approach is to use :ref:`natmod` which allows writing custom C
23+
code that is placed in a .mpy file, which can be imported dynamically in to
24+
a running MicroPython system without the need to recompile the main firmware.
25+
2026

2127
Structure of an external C module
2228
---------------------------------

docs/develop/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ See the `getting started guide
1111

1212
cmodules.rst
1313
qstr.rst
14+
natmod.rst

docs/develop/natmod.rst

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
.. _natmod:
2+
3+
Native machine code in .mpy files
4+
=================================
5+
6+
This section describes how to build and work with .mpy files that contain native
7+
machine code from a language other than Python. This allows you to
8+
write code in a language like C, compile and link it into a .mpy file, and then
9+
import this file like a normal Python module. This can be used for implementing
10+
functionality which is performance critical, or for including an existing
11+
library written in another language.
12+
13+
One of the main advantages of using native .mpy files is that native machine code
14+
can be imported by a script dynamically, without the need to rebuild the main
15+
MicroPython firmware. This is in contrast to :ref:`cmodules` which also allows
16+
defining custom modules in C but they must be compiled into the main firmware image.
17+
18+
The focus here is on using C to build native modules, but in principle any
19+
language which can be compiled to stand-alone machine code can be put into a
20+
.mpy file.
21+
22+
A native .mpy module is built using the ``mpy_ld.py`` tool, which is found in the
23+
``tools/`` directory of the project. This tool takes a set of object files
24+
(.o files) and links them together to create a native .mpy files.
25+
26+
Supported features and limitations
27+
----------------------------------
28+
29+
A .mpy file can contain MicroPython bytecode and/or native machine code. If it
30+
contains native machine code then the .mpy file has a specific architecture
31+
associated with it. Current supported architectures are (these are the valid
32+
options for the ``ARCH`` variable, see below):
33+
34+
* ``x86`` (32 bit)
35+
* ``x64`` (64 bit x86)
36+
* ``armv7m`` (ARM Thumb 2, eg Cortex-M3)
37+
* ``armv7emsp`` (ARM Thumb 2, single precision float, eg Cortex-M4F, Cortex-M7)
38+
* ``armv7emdp`` (ARM Thumb 2, double precision float, eg Cortex-M7)
39+
* ``xtensa`` (non-windowed, eg ESP8266)
40+
* ``xtensawin`` (windowed with window size 8, eg ESP32)
41+
42+
When compiling and linking the native .mpy file the architecture must be chosen
43+
and the corresponding file can only be imported on that architecture. For more
44+
details about .mpy files see :ref:`mpy_files`.
45+
46+
Native code must be compiled as position independent code (PIC) and use a global
47+
offset table (GOT), although the details of this varies from architecture to
48+
architecture. When importing .mpy files with native code the import machinery
49+
is able to do some basic relocation of the native code. This includes
50+
relocating text, rodata and BSS sections.
51+
52+
Supported features of the linker and dynamic loader are:
53+
54+
* executable code (text)
55+
* read-only data (rodata), including strings and constant data (arrays, structs, etc)
56+
* zeroed data (BSS)
57+
* pointers in text to text, rodata and BSS
58+
* pointers in rodata to text, rodata and BSS
59+
60+
The known limitations are:
61+
62+
* data sections are not supported; workaround: use BSS data and initialise the
63+
data values explicitly
64+
65+
* static BSS variables are not supported; workaround: use global BSS variables
66+
67+
So, if your C code has writable data, make sure the data is defined globally,
68+
without an initialiser, and only written to within functions.
69+
70+
Defining a native module
71+
------------------------
72+
73+
A native .mpy module is defined by a set of files that are used to build the .mpy.
74+
The filesystem layout consists of two main parts, the source files and the Makefile:
75+
76+
* In the simplest case only a single C source file is required, which contains all
77+
the code that will be compiled into the .mpy module. This C source code must
78+
include the ``py/dynruntime.h`` file to access the MicroPython dynamic API, and
79+
must at least define a function called ``mpy_init``. This function will be the
80+
entry point of the module, called when the module is imported.
81+
82+
The module can be split into multiple C source files if desired. Parts of the
83+
module can also be implemented in Python. All source files should be listed in
84+
the Makefile, by adding them to the ``SRC`` variable (see below). This includes
85+
both C source files as well as any Python files which will be included in the
86+
resulting .mpy file.
87+
88+
* The ``Makefile`` contains the build configuration for the module and list the
89+
source files used to build the .mpy module. It should define ``MPY_DIR`` as the
90+
location of the MicroPython repository (to find header files, the relevant Makefile
91+
fragment, and the ``mpy_ld.py`` tool), ``MOD`` as the name of the module, ``SRC``
92+
as the list of source files, optionally specify the machine architecture via ``ARCH``,
93+
and then include ``py/dynruntime.mk``.
94+
95+
Minimal example
96+
---------------
97+
98+
This section provides a fully working example of a simple module named ``factorial``.
99+
This module provides a single function ``factorial.factorial(x)`` which computes the
100+
factorial of the input and returns the result.
101+
102+
Directory layout::
103+
104+
factorial/
105+
├── factorial.c
106+
└── Makefile
107+
108+
The file ``factorial.c`` contains:
109+
110+
.. code-block:: c
111+
112+
// Include the header file to get access to the MicroPython API
113+
#include "py/dynruntime.h"
114+
115+
// Helper function to compute factorial
116+
STATIC mp_int_t factorial_helper(mp_int_t x) {
117+
if (x == 0) {
118+
return 1;
119+
}
120+
return x * factorial_helper(x - 1);
121+
}
122+
123+
// This is the function which will be called from Python, as factorial(x)
124+
STATIC mp_obj_t factorial(mp_obj_t x_obj) {
125+
// Extract the integer from the MicroPython input object
126+
mp_int_t x = mp_obj_get_int(x_obj);
127+
// Calculate the factorial
128+
mp_int_t result = factorial_helper(x);
129+
// Convert the result to a MicroPython integer object and return it
130+
return mp_obj_new_int(result);
131+
}
132+
// Define a Python reference to the function above
133+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(factorial_obj, factorial);
134+
135+
// This is the entry point and is called when the module is imported
136+
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
137+
// This must be first, it sets up the globals dict and other things
138+
MP_DYNRUNTIME_INIT_ENTRY
139+
140+
// Make the function available in the module's namespace
141+
mp_store_global(MP_QSTR_factorial, MP_OBJ_FROM_PTR(&factorial_obj));
142+
143+
// This must be last, it restores the globals dict
144+
MP_DYNRUNTIME_INIT_EXIT
145+
}
146+
147+
The file ``Makefile`` contains:
148+
149+
.. code-block:: make
150+
151+
# Location of top-level MicroPython directory
152+
MPY_DIR = ../../..
153+
154+
# Name of module
155+
MOD = features0
156+
157+
# Source files (.c or .py)
158+
SRC = features0.c
159+
160+
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
161+
ARCH = x64
162+
163+
# Include to get the rules for compiling and linking the module
164+
include $(MPY_DIR)/py/dynruntime.mk
165+
166+
Compiling the module
167+
--------------------
168+
169+
Be sure to select the correct ``ARCH`` for the target you are going to run on.
170+
Then build with::
171+
172+
$ make
173+
174+
Without modifying the Makefile you can specify the target architecture via::
175+
176+
$ make ARCH=armv7m
177+
178+
Module usage in MicroPython
179+
---------------------------
180+
181+
Once the module is built there should be a file called ``factorial.mpy``. Copy
182+
this so it is accessible on the filesystem of your MicroPython system and can be
183+
found in the import path. The module con now be accessed in Python just like any
184+
other module, for example::
185+
186+
import factorial
187+
print(factorial.factorial(10))
188+
# should display 3628800
189+
190+
Further examples
191+
----------------
192+
193+
See ``examples/natmod/`` for further examples which show many of the available
194+
features of native .mpy modules. Such features include:
195+
196+
* using multiple C source files
197+
* including Python code alongside C code
198+
* rodata and BSS data
199+
* memory allocation
200+
* use of floating point
201+
* exception handling
202+
* including external C libraries

docs/esp32/general.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ For your convenience, some of technical specifications are provided below:
5252
* I2S: 2
5353
* ADC: 12-bit SAR ADC up to 18 channels
5454
* DAC: 2 8-bit DACs
55+
* RMT: 8 channels allowing accurate pulse transmit/receive
5556
* Programming: using BootROM bootloader from UART - due to external FlashROM
5657
and always-available BootROM bootloader, the ESP32 is not brickable
5758

docs/esp32/quickref.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,20 @@ Notes:
365365

366366
p1 = Pin(4, Pin.OUT, None)
367367

368+
RMT
369+
---
370+
371+
The RMT is ESP32-specific and allows generation of accurate digital pulses with
372+
12.5ns resolution. See :ref:`esp32.RMT <esp32.RMT>` for details. Usage is::
373+
374+
import esp32
375+
from machine import Pin
376+
377+
r = esp32.RMT(0, pin=Pin(18), clock_div=8)
378+
r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8)
379+
# The channel resolution is 100ns (1/(source_freq/clock_div)).
380+
r.write_pulses((1, 20, 2, 40), start=0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns
381+
368382
OneWire driver
369383
--------------
370384

docs/library/esp32.rst

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. currentmodule:: esp32
2+
13
:mod:`esp32` --- functionality specific to the ESP32
24
====================================================
35

@@ -86,6 +88,91 @@ Constants
8688

8789
Used in `Partition.find` to specify the partition type.
8890

91+
92+
.. _esp32.RMT:
93+
94+
RMT
95+
---
96+
97+
The RMT (Remote Control) module, specific to the ESP32, was originally designed
98+
to send and receive infrared remote control signals. However, due to a flexible
99+
design and very accurate (as low as 12.5ns) pulse generation, it can also be
100+
used to transmit or receive many other types of digital signals::
101+
102+
import esp32
103+
from machine import Pin
104+
105+
r = esp32.RMT(0, pin=Pin(18), clock_div=8)
106+
r # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8)
107+
# The channel resolution is 100ns (1/(source_freq/clock_div)).
108+
r.write_pulses((1, 20, 2, 40), start=0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns
109+
110+
The input to the RMT module is an 80MHz clock (in the future it may be able to
111+
configure the input clock but, for now, it's fixed). ``clock_div`` *divides*
112+
the clock input which determines the resolution of the RMT channel. The
113+
numbers specificed in ``write_pulses`` are multiplied by the resolution to
114+
define the pulses.
115+
116+
``clock_div`` is an 8-bit divider (0-255) and each pulse can be defined by
117+
multiplying the resolution by a 15-bit (0-32,768) number. There are eight
118+
channels (0-7) and each can have a different clock divider.
119+
120+
So, in the example above, the 80MHz clock is divided by 8. Thus the
121+
resolution is (1/(80Mhz/8)) 100ns. Since the ``start`` level is 0 and toggles
122+
with each number, the bitstream is ``0101`` with durations of [100ns, 2000ns,
123+
100ns, 4000ns].
124+
125+
For more details see Espressif's `ESP-IDF RMT documentation.
126+
<https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/rmt.html>`_.
127+
128+
.. Warning::
129+
The current MicroPython RMT implementation lacks some features, most notably
130+
receiving pulses and carrier transmit. RMT should be considered a
131+
*beta feature* and the interface may change in the future.
132+
133+
134+
.. class:: RMT(channel, \*, pin=None, clock_div=8)
135+
136+
This class provides access to one of the eight RMT channels. *channel* is
137+
required and identifies which RMT channel (0-7) will be configured. *pin*,
138+
also required, configures which Pin is bound to the RMT channel. *clock_div*
139+
is an 8-bit clock divider that divides the source clock (80MHz) to the RMT
140+
channel allowing the resolution to be specified.
141+
142+
.. method:: RMT.source_freq()
143+
144+
Returns the source clock frequency. Currently the source clock is not
145+
configurable so this will always return 80MHz.
146+
147+
.. method:: RMT.clock_div()
148+
149+
Return the clock divider. Note that the channel resolution is
150+
``1 / (source_freq / clock_div)``.
151+
152+
.. method:: RMT.wait_done(timeout=0)
153+
154+
Returns True if `RMT.write_pulses` has completed.
155+
156+
If *timeout* (defined in ticks of ``source_freq / clock_div``) is specified
157+
the method will wait for *timeout* or until `RMT.write_pulses` is complete,
158+
returning ``False`` if the channel continues to transmit.
159+
160+
.. Warning::
161+
Avoid using ``wait_done()`` if looping is enabled.
162+
163+
.. method:: RMT.loop(enable_loop)
164+
165+
Configure looping on the channel, allowing a stream of pulses to be
166+
indefinitely repeated. *enable_loop* is bool, set to True to enable looping.
167+
168+
.. method:: RMT.write_pulses(pulses, start)
169+
170+
Begin sending *pulses*, a list or tuple defining the stream of pulses. The
171+
length of each pulse is defined by a number to be multiplied by the channel
172+
resolution ``(1 / (source_freq / clock_div))``. *start* defines whether the
173+
stream starts at 0 or 1.
174+
175+
89176
The Ultra-Low-Power co-processor
90177
--------------------------------
91178

docs/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ implementation and the best practices to use them.
2121

2222
glossary.rst
2323
repl.rst
24+
mpyfiles.rst
2425
isr_rules.rst
2526
speed_python.rst
2627
constrained.rst

0 commit comments

Comments
 (0)