WIP: after merge; before testing
commit
7c219600a2
|
@ -17,8 +17,6 @@
|
|||
tests/basics/string_cr_conversion.py -text
|
||||
tests/basics/string_crlf_conversion.py -text
|
||||
ports/stm32/pybcdc.inf_template -text
|
||||
ports/stm32/usbd_* -text
|
||||
ports/stm32/usbdev/** -text
|
||||
ports/stm32/usbhost/** -text
|
||||
ports/cc3200/hal/aes.c -text
|
||||
ports/cc3200/hal/aes.h -text
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
url = https://github.com/atgreen/libffi
|
||||
[submodule "lib/lwip"]
|
||||
path = lib/lwip
|
||||
url = http://git.savannah.gnu.org/r/lwip.git
|
||||
url = https://git.savannah.gnu.org/r/lwip.git
|
||||
[submodule "lib/berkeley-db-1.xx"]
|
||||
path = lib/berkeley-db-1.xx
|
||||
url = https://github.com/pfalcon/berkeley-db-1.xx
|
||||
|
|
16
.travis.yml
16
.travis.yml
|
@ -6,6 +6,7 @@ compiler:
|
|||
git:
|
||||
depth: 1
|
||||
env:
|
||||
- MAKEOPTS="-j4"
|
||||
- TRAVIS_BOARD=feather_huzzah
|
||||
- TRAVIS_BOARD=arduino_zero
|
||||
- TRAVIS_BOARD=circuitplayground_express
|
||||
|
@ -57,12 +58,17 @@ before_script:
|
|||
- ([[ $TRAVIS_BOARD != "feather52832" && $TRAVIS_BOARD != "pca10056" ]] || sudo ports/nrf/drivers/bluetooth/download_ble_stack.sh)
|
||||
# For huzzah builds
|
||||
- if [[ $TRAVIS_BOARD = "feather_huzzah" ]]; then wget https://github.com/jepler/esp-open-sdk/releases/download/2018-06-10/xtensa-lx106-elf-standalone.tar.gz && tar xavf xtensa-lx106-elf-standalone.tar.gz; PATH=$(readlink -f xtensa-lx106-elf/bin):$PATH; fi
|
||||
# For coverage testing (upgrade is used to get latest urllib3 version)
|
||||
- ([[ -z "$TRAVIS_TEST" ]] || sudo pip install --upgrade cpp-coveralls)
|
||||
- ([[ $TRAVIS_TEST != "docs" ]] || sudo pip install Sphinx sphinx-rtd-theme recommonmark)
|
||||
- gcc --version
|
||||
- ([[ -z "$TRAVIS_BOARD" ]] || arm-none-eabi-gcc --version)
|
||||
- python3 --version
|
||||
|
||||
- sudo apt-get install realpath
|
||||
# For coverage testing (a specific urllib3 version is needed for requests and cpp-coveralls to work together)
|
||||
- sudo pip install -Iv urllib3==1.22
|
||||
- sudo pip install cpp-coveralls
|
||||
|
||||
|
||||
script:
|
||||
# Build mpy-cross first because other builds depend on it.
|
||||
|
@ -110,9 +116,17 @@ script:
|
|||
- echo -en 'travis_fold:end:build_docs\\r'
|
||||
|
||||
|
||||
# test when input script comes from stdin
|
||||
- cat tests/basics/0prelim.py | ports/unix/micropython_coverage | grep -q 'abc'
|
||||
|
||||
# run coveralls coverage analysis (try to, even if some builds/tests failed)
|
||||
#- (cd ports/unix && coveralls --root ../.. --build-root . --gcov $(which gcov) --gcov-options '\-o build-coverage/' --include py --include extmod)
|
||||
|
||||
# run tests on stackless build
|
||||
# - rm -rf ports/unix/build-coverage
|
||||
# - make ${MAKEOPTS} -C ports/unix coverage CFLAGS_EXTRA="-DMICROPY_STACKLESS=1 -DMICROPY_STACKLESS_STRICT=1"
|
||||
# - (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests)
|
||||
|
||||
after_failure:
|
||||
- (cd tests && for exp in *.exp; do testbase=$(basename $exp .exp); echo -e "\nFAILURE $testbase"; diff -u $testbase.exp $testbase.out; done)
|
||||
- (grep "FAIL" ports/qemu-arm/build/console.out)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
:mod:`_thread` -- multithreading support
|
||||
========================================
|
||||
|
||||
.. module:: _thread
|
||||
:synopsis: multithreading support
|
||||
|
||||
|see_cpython_module| :mod:`python:_thread`.
|
||||
|
||||
This module implements multithreading support.
|
||||
|
||||
This module is highly experimental and its API is not yet fully settled
|
||||
and not yet described in this documentation.
|
|
@ -16,14 +16,14 @@ Classes
|
|||
.. class:: array.array(typecode, [iterable])
|
||||
|
||||
Create array with elements of given type. Initial contents of the
|
||||
array are given by an `iterable`. If it is not provided, an empty
|
||||
array are given by *iterable*. If it is not provided, an empty
|
||||
array is created.
|
||||
|
||||
.. method:: append(val)
|
||||
|
||||
Append new element to the end of array, growing it.
|
||||
Append new element *val* to the end of array, growing it.
|
||||
|
||||
.. method:: extend(iterable)
|
||||
|
||||
Append new elements as contained in an iterable to the end of
|
||||
Append new elements as contained in *iterable* to the end of
|
||||
array, growing it.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
:synopsis: simple BTree database
|
||||
|
||||
The ``btree`` module implements a simple key-value database using external
|
||||
storage (disk files, or in general case, a random-access stream). Keys are
|
||||
storage (disk files, or in general case, a random-access `stream`). Keys are
|
||||
stored sorted in the database, and besides efficient retrieval by a key
|
||||
value, a database also supports efficient ordered range scans (retrieval
|
||||
of values with the keys in a given range). On the application interface
|
||||
|
|
|
@ -150,6 +150,14 @@ Constants
|
|||
|
||||
Red Green Blue (16-bit, 5+6+5) color format
|
||||
|
||||
.. data:: framebuf.GS2_HMSB
|
||||
|
||||
Grayscale (2-bit) color format
|
||||
|
||||
.. data:: framebuf.GS4_HMSB
|
||||
|
||||
Grayscale (4-bit) color format
|
||||
|
||||
.. data:: framebuf.GS8
|
||||
|
||||
Grayscale (8-bit) color format
|
||||
|
|
|
@ -35,6 +35,18 @@ Functions
|
|||
compilation of scripts, and returns ``None``. Otherwise it returns the current
|
||||
optimisation level.
|
||||
|
||||
The optimisation level controls the following compilation features:
|
||||
|
||||
- Assertions: at level 0 assertion statements are enabled and compiled into the
|
||||
bytecode; at levels 1 and higher assertions are not compiled.
|
||||
- Built-in ``__debug__`` variable: at level 0 this variable expands to ``True``;
|
||||
at levels 1 and higher it expands to ``False``.
|
||||
- Source-code line numbers: at levels 0, 1 and 2 source-code line number are
|
||||
stored along with the bytecode so that exceptions can report the line number
|
||||
they occurred at; at levels 3 and higher line numbers are not stored.
|
||||
|
||||
The default optimisation level is usually level 0.
|
||||
|
||||
.. function:: alloc_emergency_exception_buf(size)
|
||||
|
||||
Allocate *size* bytes of RAM for the emergency exception buffer (a good
|
||||
|
@ -114,5 +126,14 @@ Functions
|
|||
the heap may be locked) and scheduling a function to call later will lift
|
||||
those restrictions.
|
||||
|
||||
There is a finite stack to hold the scheduled functions and `schedule`
|
||||
Note: If `schedule()` is called from a preempting IRQ, when memory
|
||||
allocation is not allowed and the callback to be passed to `schedule()` is
|
||||
a bound method, passing this directly will fail. This is because creating a
|
||||
reference to a bound method causes memory allocation. A solution is to
|
||||
create a reference to the method in the class constructor and to pass that
|
||||
reference to `schedule()`. This is discussed in detail here
|
||||
:ref:`reference documentation <isr_rules>` under "Creation of Python
|
||||
objects".
|
||||
|
||||
There is a finite stack to hold the scheduled functions and `schedule()`
|
||||
will raise a `RuntimeError` if the stack is full.
|
||||
|
|
|
@ -105,15 +105,15 @@ Constants
|
|||
|
||||
.. data:: stderr
|
||||
|
||||
Standard error stream.
|
||||
Standard error `stream`.
|
||||
|
||||
.. data:: stdin
|
||||
|
||||
Standard input stream.
|
||||
Standard input `stream`.
|
||||
|
||||
.. data:: stdout
|
||||
|
||||
Standard output stream.
|
||||
Standard output `stream`.
|
||||
|
||||
.. data:: version
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ This module implements "foreign data interface" for MicroPython. The idea
|
|||
behind it is similar to CPython's ``ctypes`` modules, but the actual API is
|
||||
different, streamlined and optimized for small size. The basic idea of the
|
||||
module is to define data structure layout with about the same power as the
|
||||
C language allows, and the access it using familiar dot-syntax to reference
|
||||
C language allows, and then access it using familiar dot-syntax to reference
|
||||
sub-fields.
|
||||
|
||||
.. seealso::
|
||||
|
@ -31,25 +31,25 @@ Following are encoding examples for various field types:
|
|||
|
||||
* Scalar types::
|
||||
|
||||
"field_name": uctypes.UINT32 | 0
|
||||
"field_name": offset | uctypes.UINT32
|
||||
|
||||
in other words, value is scalar type identifier ORed with field offset
|
||||
(in bytes) from the start of the structure.
|
||||
|
||||
* Recursive structures::
|
||||
|
||||
"sub": (2, {
|
||||
"b0": uctypes.UINT8 | 0,
|
||||
"b1": uctypes.UINT8 | 1,
|
||||
"sub": (offset, {
|
||||
"b0": 0 | uctypes.UINT8,
|
||||
"b1": 1 | uctypes.UINT8,
|
||||
})
|
||||
|
||||
i.e. value is a 2-tuple, first element of which is offset, and second is
|
||||
a structure descriptor dictionary (note: offsets in recursive descriptors
|
||||
are relative to a structure it defines).
|
||||
are relative to the structure it defines).
|
||||
|
||||
* Arrays of primitive types::
|
||||
|
||||
"arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2),
|
||||
"arr": (offset | uctypes.ARRAY, size | uctypes.UINT8),
|
||||
|
||||
i.e. value is a 2-tuple, first element of which is ARRAY flag ORed
|
||||
with offset, and second is scalar element type ORed number of elements
|
||||
|
@ -57,7 +57,7 @@ Following are encoding examples for various field types:
|
|||
|
||||
* Arrays of aggregate types::
|
||||
|
||||
"arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}),
|
||||
"arr2": (offset | uctypes.ARRAY, size, {"b": 0 | uctypes.UINT8}),
|
||||
|
||||
i.e. value is a 3-tuple, first element of which is ARRAY flag ORed
|
||||
with offset, second is a number of elements in array, and third is
|
||||
|
@ -65,21 +65,21 @@ Following are encoding examples for various field types:
|
|||
|
||||
* Pointer to a primitive type::
|
||||
|
||||
"ptr": (uctypes.PTR | 0, uctypes.UINT8),
|
||||
"ptr": (offset | uctypes.PTR, uctypes.UINT8),
|
||||
|
||||
i.e. value is a 2-tuple, first element of which is PTR flag ORed
|
||||
with offset, and second is scalar element type.
|
||||
|
||||
* Pointer to an aggregate type::
|
||||
|
||||
"ptr2": (uctypes.PTR | 0, {"b": uctypes.UINT8 | 0}),
|
||||
"ptr2": (offset | uctypes.PTR, {"b": 0 | uctypes.UINT8}),
|
||||
|
||||
i.e. value is a 2-tuple, first element of which is PTR flag ORed
|
||||
with offset, second is descriptor of type pointed to.
|
||||
|
||||
* Bitfields::
|
||||
|
||||
"bitf0": uctypes.BFUINT16 | 0 | 0 << uctypes.BF_POS | 8 << uctypes.BF_LEN,
|
||||
"bitf0": offset | uctypes.BFUINT16 | lsbit << uctypes.BF_POS | bitsize << uctypes.BF_LEN,
|
||||
|
||||
i.e. value is type of scalar value containing given bitfield (typenames are
|
||||
similar to scalar types, but prefixes with "BF"), ORed with offset for
|
||||
|
@ -88,20 +88,21 @@ Following are encoding examples for various field types:
|
|||
BF_POS and BF_LEN positions, respectively. Bitfield position is counted
|
||||
from the least significant bit, and is the number of right-most bit of a
|
||||
field (in other words, it's a number of bits a scalar needs to be shifted
|
||||
right to extra the bitfield).
|
||||
right to extract the bitfield).
|
||||
|
||||
In the example above, first UINT16 value will be extracted at offset 0
|
||||
In the example above, first a UINT16 value will be extracted at offset 0
|
||||
(this detail may be important when accessing hardware registers, where
|
||||
particular access size and alignment are required), and then bitfield
|
||||
whose rightmost bit is least-significant bit of this UINT16, and length
|
||||
is 8 bits, will be extracted - effectively, this will access
|
||||
least-significant byte of UINT16.
|
||||
whose rightmost bit is *lsbit* bit of this UINT16, and length
|
||||
is *bitsize* bits, will be extracted. For example, if *lsbit* is 0 and
|
||||
*bitsize* is 8, then effectively it will access least-significant byte
|
||||
of UINT16.
|
||||
|
||||
Note that bitfield operations are independent of target byte endianness,
|
||||
in particular, example above will access least-significant byte of UINT16
|
||||
in both little- and big-endian structures. But it depends on the least
|
||||
significant bit being numbered 0. Some targets may use different
|
||||
numbering in their native ABI, but ``uctypes`` always uses normalized
|
||||
numbering in their native ABI, but ``uctypes`` always uses the normalized
|
||||
numbering described above.
|
||||
|
||||
Module contents
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
|see_cpython_module| :mod:`cpython:io`.
|
||||
|
||||
This module contains additional types of stream (file-like) objects
|
||||
This module contains additional types of ``stream`` (file-like) objects
|
||||
and helper functions.
|
||||
|
||||
Conceptual hierarchy
|
||||
|
|
|
@ -14,11 +14,24 @@ data format.
|
|||
Functions
|
||||
---------
|
||||
|
||||
.. function:: dump(obj, stream)
|
||||
|
||||
Serialise *obj* to a JSON string, writing it to the given *stream*.
|
||||
|
||||
.. function:: dumps(obj)
|
||||
|
||||
Return ``obj`` represented as a JSON string.
|
||||
Return *obj* represented as a JSON string.
|
||||
|
||||
.. function:: load(stream)
|
||||
|
||||
Parse the given *stream*, interpreting it as a JSON string and
|
||||
deserialising the data to a Python object. The resulting object is
|
||||
returned.
|
||||
|
||||
Parsing continues until end-of-file is encountered.
|
||||
A :exc:`ValueError` is raised if the data in *stream* is not correctly formed.
|
||||
|
||||
.. function:: loads(str)
|
||||
|
||||
Parse the JSON ``str`` and return an object. Raises ValueError if the
|
||||
Parse the JSON *str* and return an object. Raises :exc:`ValueError` if the
|
||||
string is not correctly formed.
|
||||
|
|
|
@ -17,8 +17,9 @@ Supported operators are:
|
|||
``'.'``
|
||||
Match any character.
|
||||
|
||||
``'[]'``
|
||||
Match set of characters. Individual characters and ranges are supported.
|
||||
``'[...]'``
|
||||
Match set of characters. Individual characters and ranges are supported,
|
||||
including negated sets (e.g. ``[^a-c]``).
|
||||
|
||||
``'^'``
|
||||
|
||||
|
@ -38,18 +39,19 @@ Supported operators are:
|
|||
|
||||
``'|'``
|
||||
|
||||
``'()'``
|
||||
``'(...)'``
|
||||
Grouping. Each group is capturing (a substring it captures can be accessed
|
||||
with `match.group()` method).
|
||||
|
||||
Counted repetitions (``{m,n}``), more advanced assertions, named groups,
|
||||
etc. are not supported.
|
||||
**NOT SUPPORTED**: Counted repetitions (``{m,n}``), more advanced assertions
|
||||
(``\b``, ``\B``), named groups (``(?P<name>...)``), non-capturing groups
|
||||
(``(?:...)``), etc.
|
||||
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
.. function:: compile(regex_str)
|
||||
.. function:: compile(regex_str, [flags])
|
||||
|
||||
Compile regular expression, return `regex <regex>` object.
|
||||
|
||||
|
@ -67,6 +69,7 @@ Functions
|
|||
.. data:: DEBUG
|
||||
|
||||
Flag value, display debug information about compiled expression.
|
||||
(Availability depends on `MicroPython port`.)
|
||||
|
||||
|
||||
.. _regex:
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|see_cpython_module| :mod:`cpython:select`.
|
||||
|
||||
This module provides functions to efficiently wait for events on multiple
|
||||
streams (select streams which are ready for operations).
|
||||
`streams <stream>` (select streams which are ready for operations).
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
@ -35,14 +35,17 @@ Methods
|
|||
|
||||
.. method:: poll.register(obj[, eventmask])
|
||||
|
||||
Register *obj* for polling. *eventmask* is logical OR of:
|
||||
Register `stream` *obj* for polling. *eventmask* is logical OR of:
|
||||
|
||||
* ``select.POLLIN`` - data available for reading
|
||||
* ``select.POLLOUT`` - more data can be written
|
||||
* ``select.POLLERR`` - error occurred
|
||||
* ``select.POLLHUP`` - end of stream/connection termination detected
|
||||
* ``uselect.POLLIN`` - data available for reading
|
||||
* ``uselect.POLLOUT`` - more data can be written
|
||||
|
||||
*eventmask* defaults to ``select.POLLIN | select.POLLOUT``.
|
||||
Note that flags like ``uselect.POLLHUP`` and ``uselect.POLLERR`` are
|
||||
*not* valid as input eventmask (these are unsolicited events which
|
||||
will be returned from `poll()` regardless of whether they are asked
|
||||
for). This semantics is per POSIX.
|
||||
|
||||
*eventmask* defaults to ``uselect.POLLIN | uselect.POLLOUT``.
|
||||
|
||||
.. method:: poll.unregister(obj)
|
||||
|
||||
|
@ -52,16 +55,23 @@ Methods
|
|||
|
||||
Modify the *eventmask* for *obj*.
|
||||
|
||||
.. method:: poll.poll([timeout])
|
||||
.. method:: poll.poll(timeout=-1)
|
||||
|
||||
Wait for at least one of the registered objects to become ready. Returns
|
||||
list of (``obj``, ``event``, ...) tuples, ``event`` element specifies
|
||||
which events happened with a stream and is a combination of ``select.POLL*``
|
||||
constants described above. There may be other elements in tuple, depending
|
||||
on a platform and version, so don't assume that its size is 2. In case of
|
||||
timeout, an empty list is returned.
|
||||
Wait for at least one of the registered objects to become ready or have an
|
||||
exceptional condition, with optional timeout in milliseconds (if *timeout*
|
||||
arg is not specified or -1, there is no timeout).
|
||||
|
||||
Timeout is in milliseconds.
|
||||
Returns list of (``obj``, ``event``, ...) tuples. There may be other elements in
|
||||
tuple, depending on a platform and version, so don't assume that its size is 2.
|
||||
The ``event`` element specifies which events happened with a stream and
|
||||
is a combination of ``uselect.POLL*`` constants described above. Note that
|
||||
flags ``uselect.POLLHUP`` and ``uselect.POLLERR`` can be returned at any time
|
||||
(even if were not asked for), and must be acted on accordingly (the
|
||||
corresponding stream unregistered from poll and likely closed), because
|
||||
otherwise all further invocations of `poll()` may return immediately with
|
||||
these flags set for this stream again.
|
||||
|
||||
In case of timeout, an empty list is returned.
|
||||
|
||||
.. admonition:: Difference to CPython
|
||||
:class: attention
|
||||
|
@ -70,15 +80,15 @@ Methods
|
|||
|
||||
.. method:: poll.ipoll(timeout=-1, flags=0)
|
||||
|
||||
Like :meth:`poll.poll`, but instead returns an iterator which yields
|
||||
Like :meth:`poll.poll`, but instead returns an iterator which yields a
|
||||
``callee-owned tuples``. This function provides efficient, allocation-free
|
||||
way to poll on streams.
|
||||
|
||||
If *flags* is 1, one-shot behavior for events is employed: streams for
|
||||
which events happened, event mask will be automatically reset (equivalent
|
||||
to ``poll.modify(obj, 0)``), so new events for such a stream won't be
|
||||
processed until new mask is set with `poll.modify()`. This behavior is
|
||||
useful for asynchronous I/O schedulers.
|
||||
which events happened will have their event masks automatically reset
|
||||
(equivalent to ``poll.modify(obj, 0)``), so new events for such a stream
|
||||
won't be processed until new mask is set with `poll.modify()`. This
|
||||
behavior is useful for asynchronous I/O schedulers.
|
||||
|
||||
.. admonition:: Difference to CPython
|
||||
:class: attention
|
||||
|
|
|
@ -14,7 +14,7 @@ This module provides access to the BSD socket interface.
|
|||
.. admonition:: Difference to CPython
|
||||
:class: attention
|
||||
|
||||
For efficiency and consistency, socket objects in MicroPython implement a stream
|
||||
For efficiency and consistency, socket objects in MicroPython implement a `stream`
|
||||
(file-like) interface directly. In CPython, you need to convert a socket to
|
||||
a file-like object using `makefile()` method. This method is still supported
|
||||
by MicroPython (but is a no-op), so where compatibility with CPython matters,
|
||||
|
@ -245,7 +245,7 @@ Methods
|
|||
Not every ``MicroPython port`` supports this method. A more portable and
|
||||
generic solution is to use `uselect.poll` object. This allows to wait on
|
||||
multiple objects at the same time (and not just on sockets, but on generic
|
||||
stream objects which support polling). Example::
|
||||
`stream` objects which support polling). Example::
|
||||
|
||||
# Instead of:
|
||||
s.settimeout(1.0) # time in seconds
|
||||
|
|
|
@ -17,13 +17,13 @@ Functions
|
|||
|
||||
.. function:: ussl.wrap_socket(sock, server_side=False, keyfile=None, certfile=None, cert_reqs=CERT_NONE, ca_certs=None)
|
||||
|
||||
Takes a stream *sock* (usually usocket.socket instance of ``SOCK_STREAM`` type),
|
||||
Takes a `stream` *sock* (usually usocket.socket instance of ``SOCK_STREAM`` type),
|
||||
and returns an instance of ssl.SSLSocket, which wraps the underlying stream in
|
||||
an SSL context. Returned object has the usual stream interface methods like
|
||||
an SSL context. Returned object has the usual `stream` interface methods like
|
||||
``read()``, ``write()``, etc. In MicroPython, the returned object does not expose
|
||||
socket interface and methods like ``recv()``, ``send()``. In particular, a
|
||||
server-side SSL socket should be created from a normal socket returned from
|
||||
`accept()` on a non-SSL listening server socket.
|
||||
:meth:`~usocket.socket.accept()` on a non-SSL listening server socket.
|
||||
|
||||
Depending on the underlying module implementation in a particular
|
||||
``MicroPython port``, some or all keyword arguments above may be not supported.
|
||||
|
|
|
@ -27,7 +27,7 @@ Functions
|
|||
|
||||
.. class:: DecompIO(stream, wbits=0)
|
||||
|
||||
Create a stream wrapper which allows transparent decompression of
|
||||
Create a `stream` wrapper which allows transparent decompression of
|
||||
compressed data in another *stream*. This allows to process compressed
|
||||
streams with data larger than available heap size. In addition to
|
||||
values described in :func:`decompress`, *wbits* may take values
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
Distribution packages, package management, and deploying applications
|
||||
=====================================================================
|
||||
|
||||
Just as the "big" Python, MicroPython supports creation of "third party"
|
||||
packages, distributing them, and easily installing them in each user's
|
||||
environment. This chapter discusses how these actions are achieved.
|
||||
Some familiarity with Python packaging is recommended.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Steps below represent a high-level workflow when creating and consuming
|
||||
packages:
|
||||
|
||||
1. Python modules and packages are turned into distribution package
|
||||
archives, and published at the Python Package Index (PyPI).
|
||||
2. `upip` package manager can be used to install a distribution package
|
||||
on a `MicroPython port` with networking capabilities (for example,
|
||||
on the Unix port).
|
||||
3. For ports without networking capabilities, an "installation image"
|
||||
can be prepared on the Unix port, and transferred to a device by
|
||||
suitable means.
|
||||
4. For low-memory ports, the installation image can be frozen as the
|
||||
bytecode into MicroPython executable, thus minimizing the memory
|
||||
storage overheads.
|
||||
|
||||
The sections below describe this process in details.
|
||||
|
||||
Distribution packages
|
||||
---------------------
|
||||
|
||||
Python modules and packages can be packaged into archives suitable for
|
||||
transfer between systems, storing at the well-known location (PyPI),
|
||||
and downloading on demand for deployment. These archives are known as
|
||||
*distribution packages* (to differentiate them from Python packages
|
||||
(means to organize Python source code)).
|
||||
|
||||
The MicroPython distribution package format is a well-known tar.gz
|
||||
format, with some adaptations however. The Gzip compressor, used as
|
||||
an external wrapper for TAR archives, by default uses 32KB dictionary
|
||||
size, which means that to uncompress a compressed stream, 32KB of
|
||||
contguous memory needs to be allocated. This requirement may be not
|
||||
satisfiable on low-memory devices, which may have total memory available
|
||||
less than that amount, and even if not, a contiguous block like that
|
||||
may be hard to allocate due to memory fragmentation. To accommodate
|
||||
these constraints, MicroPython distribution packages use Gzip compression
|
||||
with the dictionary size of 4K, which should be a suitable compromise
|
||||
with still achieving some compression while being able to uncompressed
|
||||
even by the smallest devices.
|
||||
|
||||
Besides the small compression dictionary size, MicroPython distribution
|
||||
packages also have other optimizations, like removing any files from
|
||||
the archive which aren't used by the installation process. In particular,
|
||||
`upip` package manager doesn't execute ``setup.py`` during installation
|
||||
(see below), and thus that file is not included in the archive.
|
||||
|
||||
At the same time, these optimizations make MicroPython distribution
|
||||
packages not compatible with `CPython`'s package manager, ``pip``.
|
||||
This isn't considered a big problem, because:
|
||||
|
||||
1. Packages can be installed with `upip`, and then can be used with
|
||||
CPython (if they are compatible with it).
|
||||
2. In the other direction, majority of CPython packages would be
|
||||
incompatible with MicroPython by various reasons, first of all,
|
||||
the reliance on features not implemented by MicroPython.
|
||||
|
||||
Summing up, the MicroPython distribution package archives are highly
|
||||
optimized for MicroPython's target environments, which are highly
|
||||
resource constrained devices.
|
||||
|
||||
|
||||
``upip`` package manager
|
||||
------------------------
|
||||
|
||||
MicroPython distribution packages are intended to be installed using
|
||||
the `upip` package manager. `upip` is a Python application which is
|
||||
usually distributed (as frozen bytecode) with network-enabled
|
||||
`MicroPython ports <MicroPython port>`. At the very least,
|
||||
`upip` is available in the `MicroPython Unix port`.
|
||||
|
||||
On any `MicroPython port` providing `upip`, it can be accessed as
|
||||
following::
|
||||
|
||||
import upip
|
||||
upip.help()
|
||||
upip.install(package_or_package_list, [path])
|
||||
|
||||
Where *package_or_package_list* is the name of a distribution
|
||||
package to install, or a list of such names to install multiple
|
||||
packages. Optional *path* parameter specifies filesystem
|
||||
location to install under and defaults to the standard library
|
||||
location (see below).
|
||||
|
||||
An example of installing a specific package and then using it::
|
||||
|
||||
>>> import upip
|
||||
>>> upip.install("micropython-pystone_lowmem")
|
||||
[...]
|
||||
>>> import pystone_lowmem
|
||||
>>> pystone_lowmem.main()
|
||||
|
||||
Note that the name of Python package and the name of distribution
|
||||
package for it in general don't have to match, and oftentimes they
|
||||
don't. This is because PyPI provides a central package repository
|
||||
for all different Python implementations and versions, and thus
|
||||
distribution package names may need to be namespaced for a particular
|
||||
implementation. For example, all packages from `micropython-lib`
|
||||
follow this naming convention: for a Python module or package named
|
||||
``foo``, the distribution package name is ``micropython-foo``.
|
||||
|
||||
For the ports which run MicroPython executable from the OS command
|
||||
prompts (like the Unix port), `upip` can be (and indeed, usually is)
|
||||
run from the command line instead of MicroPython's own REPL. The
|
||||
commands which corresponds to the example above are::
|
||||
|
||||
micropython -m upip -h
|
||||
micropython -m upip install [-p <path>] <packages>...
|
||||
micropython -m upip install micropython-pystone_lowmem
|
||||
|
||||
[TODO: Describe installation path.]
|
||||
|
||||
|
||||
Cross-installing packages
|
||||
-------------------------
|
||||
|
||||
For `MicroPython ports <MicroPython port>` without native networking
|
||||
capabilities, the recommend process is "cross-installing" them into a
|
||||
"directory image" using the `MicroPython Unix port`, and then
|
||||
transferring this image to a device by suitable means.
|
||||
|
||||
Installing to a directory image involves using ``-p`` switch to `upip`::
|
||||
|
||||
micropython -m upip install -p install_dir micropython-pystone_lowmem
|
||||
|
||||
After this command, the package content (and contents of every depenency
|
||||
packages) will be available in the ``install_dir/`` subdirectory. You
|
||||
would need to transfer contents of this directory (without the
|
||||
``install_dir/`` prefix) to the device, at the suitable location, where
|
||||
it can be found by the Python ``import`` statement (see discussion of
|
||||
the `upip` installation path above).
|
||||
|
||||
|
||||
Cross-installing packages with freezing
|
||||
---------------------------------------
|
||||
|
||||
For the low-memory `MicroPython ports <MicroPython port>`, the process
|
||||
described in the previous section does not provide the most efficient
|
||||
resource usage,because the packages are installed in the source form,
|
||||
so need to be compiled to the bytecome on each import. This compilation
|
||||
requires RAM, and the resulting bytecode is also stored in RAM, reducing
|
||||
its amount available for storing application data. Moreover, the process
|
||||
above requires presence of the filesystem on a device, and the most
|
||||
resource-constrained devices may not even have it.
|
||||
|
||||
The bytecode freezing is a process which resolves all the issues
|
||||
mentioned above:
|
||||
|
||||
* The source code is pre-compiled into bytecode and store as such.
|
||||
* The bytecode is stored in ROM, not RAM.
|
||||
* Filesystem is not required for frozen packages.
|
||||
|
||||
Using frozen bytecode requires building the executable (firmware)
|
||||
for a given `MicroPython port` from the C source code. Consequently,
|
||||
the process is:
|
||||
|
||||
1. Follow the instructions for a particular port on setting up a
|
||||
toolchain and building the port. For example, for ESP8266 port,
|
||||
study instructions in ``ports/esp8266/README.md`` and follow them.
|
||||
Make sure you can build the port and deploy the resulting
|
||||
executable/firmware successfully before proceeding to the next steps.
|
||||
2. Build `MicroPython Unix port` and make sure it is in your PATH and
|
||||
you can execute ``micropython``.
|
||||
3. Change to port's directory (e.g. ``ports/esp8266/`` for ESP8266).
|
||||
4. Run ``make clean-frozen``. This step cleans up any previous
|
||||
modules which were installed for freezing (consequently, you need
|
||||
to skip this step to add additional modules, instead of starting
|
||||
from scratch).
|
||||
5. Run ``micropython -m upip install -p modules <packages>...`` to
|
||||
install packages you want to freeze.
|
||||
6. Run ``make clean``.
|
||||
7. Run ``make``.
|
||||
|
||||
After this, you should have the executable/firmware with modules as
|
||||
the bytecode inside, which you can deploy the usual way.
|
||||
|
||||
Few notes:
|
||||
|
||||
1. Step 5 in the sequence above assumes that the distribution package
|
||||
is available from PyPI. If that is not the case, you would need
|
||||
to copy Python source files manually to ``modules/`` subdirectory
|
||||
of the port port directory. (Note that upip does not support
|
||||
installing from e.g. version control repositories).
|
||||
2. The firmware for baremetal devices usually has size restrictions,
|
||||
so adding too many frozen modules may overflow it. Usually, you
|
||||
would get a linking error if this happens. However, in some cases,
|
||||
an image may be produced, which is not runnable on a device. Such
|
||||
cases are in general bugs, and should be reported and further
|
||||
investigated. If you face such a situation, as an initial step,
|
||||
you may want to decrease the amount of frozen modules included.
|
||||
|
||||
|
||||
Creating distribution packages
|
||||
------------------------------
|
||||
|
||||
Distribution packages for MicroPython are created in the same manner
|
||||
as for CPython or any other Python implementation, see references at
|
||||
the end of chapter. Setuptools (instead of distutils) should be used,
|
||||
because distutils do not support dependencies and other features. "Source
|
||||
distribution" (``sdist``) format is used for packaging. The post-processing
|
||||
discussed above, (and pre-processing discussed in the following section)
|
||||
is achieved by using custom ``sdist`` command for setuptools. Thus, packaging
|
||||
steps remain the same as for the standard setuptools, the user just
|
||||
needs to override ``sdist`` command implementation by passing the
|
||||
appropriate argument to ``setup()`` call::
|
||||
|
||||
from setuptools import setup
|
||||
import sdist_upip
|
||||
|
||||
setup(
|
||||
...,
|
||||
cmdclass={'sdist': sdist_upip.sdist}
|
||||
)
|
||||
|
||||
The sdist_upip.py module as referenced above can be found in
|
||||
`micropython-lib`:
|
||||
https://github.com/micropython/micropython-lib/blob/master/sdist_upip.py
|
||||
|
||||
|
||||
Application resources
|
||||
---------------------
|
||||
|
||||
A complete application, besides the source code, oftentimes also consists
|
||||
of data files, e.g. web page templates, game images, etc. It's clear how
|
||||
to deal with those when application is installed manually - you just put
|
||||
those data files in the filesystem at some location and use the normal
|
||||
file access functions.
|
||||
|
||||
The situation is different when deploying applications from packages - this
|
||||
is more advanced, streamlined and flexible way, but also requires more
|
||||
advanced approach to accessing data files. This approach is treating
|
||||
the data files as "resources", and abstracting away access to them.
|
||||
|
||||
Python supports resource access using its "setuptools" library, using
|
||||
``pkg_resources`` module. MicroPython, following its usual approach,
|
||||
implements subset of the functionality of that module, specifically
|
||||
``pkg_resources.resource_stream(package, resource)`` function.
|
||||
The idea is that an application calls this function, passing a
|
||||
resource identifier, which is a relative path to data file within
|
||||
the specified package (usually top-level application package). It
|
||||
returns a stream object which can be used to access resource contents.
|
||||
Thus, the ``resource_stream()`` emulates interface of the standard
|
||||
`open()` function.
|
||||
|
||||
Implementation-wise, ``resource_stream()`` uses file operations
|
||||
underlyingly, if distribution package is install in the filesystem.
|
||||
However, it also supports functioning without the underlying filesystem,
|
||||
e.g. if the package is frozen as the bytecode. This however requires
|
||||
an extra intermediate step when packaging application - creation of
|
||||
"Python resource module".
|
||||
|
||||
The idea of this module is to convert binary data to a Python bytes
|
||||
object, and put it into the dictionary, indexed by the resource name.
|
||||
This conversion is done automatically using overridden ``sdist`` command
|
||||
described in the previous section.
|
||||
|
||||
Let's trace the complete process using the following example. Suppose
|
||||
your application has the following structure::
|
||||
|
||||
my_app/
|
||||
__main__.py
|
||||
utils.py
|
||||
data/
|
||||
page.html
|
||||
image.png
|
||||
|
||||
``__main__.py`` and ``utils.py`` should access resources using the
|
||||
following calls::
|
||||
|
||||
import pkg_resources
|
||||
|
||||
pkg_resources.resource_stream(__name__, "data/page.html")
|
||||
pkg_resources.resource_stream(__name__, "data/image.png")
|
||||
|
||||
You can develop and debug using the `MicroPython Unix port` as usual.
|
||||
When time comes to make a distribution package out of it, just use
|
||||
overridden "sdist" command from sdist_upip.py module as described in
|
||||
the previous section.
|
||||
|
||||
This will create a Python resource module named ``R.py``, based on the
|
||||
files declared in ``MANIFEST`` or ``MANIFEST.in`` files (any non-``.py``
|
||||
file will be considered a resource and added to ``R.py``) - before
|
||||
proceeding with the normal packaging steps.
|
||||
|
||||
Prepared like this, your application will work both when deployed to
|
||||
filesystem and as frozen bytecode.
|
||||
|
||||
If you would like to debug ``R.py`` creation, you can run::
|
||||
|
||||
python3 setup.py sdist --manifest-only
|
||||
|
||||
Alternatively, you can use tools/mpy_bin2res.py script from the
|
||||
MicroPython distribution, in which can you will need to pass paths
|
||||
to all resource files::
|
||||
|
||||
mpy_bin2res.py data/page.html data/image.png
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
* Python Packaging User Guide: https://packaging.python.org/
|
||||
* Setuptools documentation: https://setuptools.readthedocs.io/
|
||||
* Distutils documentation: https://docs.python.org/3/library/distutils.html
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017-2018 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_DRIVERS_BUS_QSPI_H
|
||||
#define MICROPY_INCLUDED_DRIVERS_BUS_QSPI_H
|
||||
|
||||
#include "py/mphal.h"
|
||||
|
||||
enum {
|
||||
MP_QSPI_IOCTL_INIT,
|
||||
MP_QSPI_IOCTL_DEINIT,
|
||||
MP_QSPI_IOCTL_BUS_ACQUIRE,
|
||||
MP_QSPI_IOCTL_BUS_RELEASE,
|
||||
};
|
||||
|
||||
typedef struct _mp_qspi_proto_t {
|
||||
int (*ioctl)(void *self, uint32_t cmd);
|
||||
void (*write_cmd_data)(void *self, uint8_t cmd, size_t len, uint32_t data);
|
||||
void (*write_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src);
|
||||
uint32_t (*read_cmd)(void *self, uint8_t cmd, size_t len);
|
||||
void (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest);
|
||||
} mp_qspi_proto_t;
|
||||
|
||||
typedef struct _mp_soft_qspi_obj_t {
|
||||
mp_hal_pin_obj_t cs;
|
||||
mp_hal_pin_obj_t clk;
|
||||
mp_hal_pin_obj_t io0;
|
||||
mp_hal_pin_obj_t io1;
|
||||
mp_hal_pin_obj_t io2;
|
||||
mp_hal_pin_obj_t io3;
|
||||
} mp_soft_qspi_obj_t;
|
||||
|
||||
extern const mp_qspi_proto_t mp_soft_qspi_proto;
|
||||
|
||||
#endif // MICROPY_INCLUDED_DRIVERS_BUS_QSPI_H
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017-2018 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "drivers/bus/qspi.h"
|
||||
|
||||
#define CS_LOW(self) mp_hal_pin_write(self->cs, 0)
|
||||
#define CS_HIGH(self) mp_hal_pin_write(self->cs, 1)
|
||||
|
||||
#ifdef MICROPY_HW_SOFTQSPI_SCK_LOW
|
||||
|
||||
// Use externally provided functions for SCK control and IO reading
|
||||
#define SCK_LOW(self) MICROPY_HW_SOFTQSPI_SCK_LOW(self)
|
||||
#define SCK_HIGH(self) MICROPY_HW_SOFTQSPI_SCK_HIGH(self)
|
||||
#define NIBBLE_READ(self) MICROPY_HW_SOFTQSPI_NIBBLE_READ(self)
|
||||
|
||||
#else
|
||||
|
||||
// Use generic pin functions for SCK control and IO reading
|
||||
#define SCK_LOW(self) mp_hal_pin_write(self->clk, 0)
|
||||
#define SCK_HIGH(self) mp_hal_pin_write(self->clk, 1)
|
||||
#define NIBBLE_READ(self) ( \
|
||||
mp_hal_pin_read(self->io0) \
|
||||
| (mp_hal_pin_read(self->io1) << 1) \
|
||||
| (mp_hal_pin_read(self->io2) << 2) \
|
||||
| (mp_hal_pin_read(self->io3) << 3))
|
||||
|
||||
#endif
|
||||
|
||||
STATIC void nibble_write(mp_soft_qspi_obj_t *self, uint8_t v) {
|
||||
mp_hal_pin_write(self->io0, v & 1);
|
||||
mp_hal_pin_write(self->io1, (v >> 1) & 1);
|
||||
mp_hal_pin_write(self->io2, (v >> 2) & 1);
|
||||
mp_hal_pin_write(self->io3, (v >> 3) & 1);
|
||||
}
|
||||
|
||||
STATIC int mp_soft_qspi_ioctl(void *self_in, uint32_t cmd) {
|
||||
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
||||
|
||||
switch (cmd) {
|
||||
case MP_QSPI_IOCTL_INIT:
|
||||
mp_hal_pin_high(self->cs);
|
||||
mp_hal_pin_output(self->cs);
|
||||
|
||||
// Configure pins
|
||||
mp_hal_pin_write(self->clk, 0);
|
||||
mp_hal_pin_output(self->clk);
|
||||
//mp_hal_pin_write(self->clk, 1);
|
||||
mp_hal_pin_output(self->io0);
|
||||
mp_hal_pin_input(self->io1);
|
||||
mp_hal_pin_write(self->io2, 1);
|
||||
mp_hal_pin_output(self->io2);
|
||||
mp_hal_pin_write(self->io3, 1);
|
||||
mp_hal_pin_output(self->io3);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
STATIC void mp_soft_qspi_transfer(mp_soft_qspi_obj_t *self, size_t len, const uint8_t *src, uint8_t *dest) {
|
||||
// Will run as fast as possible, limited only by CPU speed and GPIO time
|
||||
mp_hal_pin_input(self->io1);
|
||||
mp_hal_pin_output(self->io0);
|
||||
if (self->io3) {
|
||||
mp_hal_pin_write(self->io2, 1);
|
||||
mp_hal_pin_output(self->io2);
|
||||
mp_hal_pin_write(self->io3, 1);
|
||||
mp_hal_pin_output(self->io3);
|
||||
}
|
||||
if (src) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
uint8_t data_out = src[i];
|
||||
uint8_t data_in = 0;
|
||||
for (int j = 0; j < 8; ++j, data_out <<= 1) {
|
||||
mp_hal_pin_write(self->io0, (data_out >> 7) & 1);
|
||||
mp_hal_pin_write(self->clk, 1);
|
||||
data_in = (data_in << 1) | mp_hal_pin_read(self->io1);
|
||||
mp_hal_pin_write(self->clk, 0);
|
||||
}
|
||||
if (dest != NULL) {
|
||||
dest[i] = data_in;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
uint8_t data_in = 0;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
mp_hal_pin_write(self->clk, 1);
|
||||
data_in = (data_in << 1) | mp_hal_pin_read(self->io1);
|
||||
mp_hal_pin_write(self->clk, 0);
|
||||
}
|
||||
if (dest != NULL) {
|
||||
dest[i] = data_in;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void mp_soft_qspi_qread(mp_soft_qspi_obj_t *self, size_t len, uint8_t *buf) {
|
||||
// Make all IO lines input
|
||||
mp_hal_pin_input(self->io2);
|
||||
mp_hal_pin_input(self->io3);
|
||||
mp_hal_pin_input(self->io0);
|
||||
mp_hal_pin_input(self->io1);
|
||||
|
||||
// Will run as fast as possible, limited only by CPU speed and GPIO time
|
||||
while (len--) {
|
||||
SCK_HIGH(self);
|
||||
uint8_t data_in = NIBBLE_READ(self);
|
||||
SCK_LOW(self);
|
||||
SCK_HIGH(self);
|
||||
*buf++ = (data_in << 4) | NIBBLE_READ(self);
|
||||
SCK_LOW(self);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void mp_soft_qspi_qwrite(mp_soft_qspi_obj_t *self, size_t len, const uint8_t *buf) {
|
||||
// Make all IO lines output
|
||||
mp_hal_pin_output(self->io2);
|
||||
mp_hal_pin_output(self->io3);
|
||||
mp_hal_pin_output(self->io0);
|
||||
mp_hal_pin_output(self->io1);
|
||||
|
||||
// Will run as fast as possible, limited only by CPU speed and GPIO time
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
nibble_write(self, buf[i] >> 4);
|
||||
SCK_HIGH(self);
|
||||
SCK_LOW(self);
|
||||
|
||||
nibble_write(self, buf[i]);
|
||||
SCK_HIGH(self);
|
||||
SCK_LOW(self);
|
||||
}
|
||||
|
||||
//mp_hal_pin_input(self->io1);
|
||||
}
|
||||
|
||||
STATIC void mp_soft_qspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t data) {
|
||||
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
||||
uint32_t cmd_buf = cmd | data << 8;
|
||||
CS_LOW(self);
|
||||
mp_soft_qspi_transfer(self, 1 + len, (uint8_t*)&cmd_buf, NULL);
|
||||
CS_HIGH(self);
|
||||
}
|
||||
|
||||
STATIC void mp_soft_qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
|
||||
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
||||
uint8_t cmd_buf[4] = {cmd, addr >> 16, addr >> 8, addr};
|
||||
CS_LOW(self);
|
||||
mp_soft_qspi_transfer(self, 4, cmd_buf, NULL);
|
||||
mp_soft_qspi_transfer(self, len, src, NULL);
|
||||
CS_HIGH(self);
|
||||
}
|
||||
|
||||
STATIC uint32_t mp_soft_qspi_read_cmd(void *self_in, uint8_t cmd, size_t len) {
|
||||
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
||||
uint32_t cmd_buf = cmd;
|
||||
CS_LOW(self);
|
||||
mp_soft_qspi_transfer(self, 1 + len, (uint8_t*)&cmd_buf, (uint8_t*)&cmd_buf);
|
||||
CS_HIGH(self);
|
||||
return cmd_buf >> 8;
|
||||
}
|
||||
|
||||
STATIC void mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
|
||||
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
||||
uint8_t cmd_buf[7] = {cmd, addr >> 16, addr >> 8, addr};
|
||||
CS_LOW(self);
|
||||
mp_soft_qspi_transfer(self, 1, cmd_buf, NULL);
|
||||
mp_soft_qspi_qwrite(self, 6, &cmd_buf[1]); // 3 addr bytes, 1 extra byte (0), 2 dummy bytes (4 dummy cycles)
|
||||
mp_soft_qspi_qread(self, len, dest);
|
||||
CS_HIGH(self);
|
||||
}
|
||||
|
||||
const mp_qspi_proto_t mp_soft_qspi_proto = {
|
||||
.ioctl = mp_soft_qspi_ioctl,
|
||||
.write_cmd_data = mp_soft_qspi_write_cmd_data,
|
||||
.write_cmd_addr_data = mp_soft_qspi_write_cmd_addr_data,
|
||||
.read_cmd = mp_soft_qspi_read_cmd,
|
||||
.read_cmd_qaddr_qdata = mp_soft_qspi_read_cmd_qaddr_qdata,
|
||||
};
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016-2018 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "drivers/bus/spi.h"
|
||||
|
||||
int mp_soft_spi_ioctl(void *self_in, uint32_t cmd) {
|
||||
mp_soft_spi_obj_t *self = (mp_soft_spi_obj_t*)self_in;
|
||||
|
||||
switch (cmd) {
|
||||
case MP_SPI_IOCTL_INIT:
|
||||
mp_hal_pin_write(self->sck, self->polarity);
|
||||
mp_hal_pin_output(self->sck);
|
||||
mp_hal_pin_output(self->mosi);
|
||||
mp_hal_pin_input(self->miso);
|
||||
break;
|
||||
|
||||
case MP_SPI_IOCTL_DEINIT:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mp_soft_spi_transfer(void *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
|
||||
mp_soft_spi_obj_t *self = (mp_soft_spi_obj_t*)self_in;
|
||||
uint32_t delay_half = self->delay_half;
|
||||
|
||||
// only MSB transfer is implemented
|
||||
|
||||
// If a port defines MICROPY_HW_SOFTSPI_MIN_DELAY, and the configured
|
||||
// delay_half is equal to this value, then the software SPI implementation
|
||||
// will run as fast as possible, limited only by CPU speed and GPIO time.
|
||||
#ifdef MICROPY_HW_SOFTSPI_MIN_DELAY
|
||||
if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
uint8_t data_out = src[i];
|
||||
uint8_t data_in = 0;
|
||||
for (int j = 0; j < 8; ++j, data_out <<= 1) {
|
||||
mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
|
||||
mp_hal_pin_write(self->sck, 1 - self->polarity);
|
||||
data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
|
||||
mp_hal_pin_write(self->sck, self->polarity);
|
||||
}
|
||||
if (dest != NULL) {
|
||||
dest[i] = data_in;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
uint8_t data_out = src[i];
|
||||
uint8_t data_in = 0;
|
||||
for (int j = 0; j < 8; ++j, data_out <<= 1) {
|
||||
mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
|
||||
if (self->phase == 0) {
|
||||
mp_hal_delay_us_fast(delay_half);
|
||||
mp_hal_pin_write(self->sck, 1 - self->polarity);
|
||||
} else {
|
||||
mp_hal_pin_write(self->sck, 1 - self->polarity);
|
||||
mp_hal_delay_us_fast(delay_half);
|
||||
}
|
||||
data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
|
||||
if (self->phase == 0) {
|
||||
mp_hal_delay_us_fast(delay_half);
|
||||
mp_hal_pin_write(self->sck, self->polarity);
|
||||
} else {
|
||||
mp_hal_pin_write(self->sck, self->polarity);
|
||||
mp_hal_delay_us_fast(delay_half);
|
||||
}
|
||||
}
|
||||
if (dest != NULL) {
|
||||
dest[i] = data_in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mp_spi_proto_t mp_soft_spi_proto = {
|
||||
.ioctl = mp_soft_spi_ioctl,
|
||||
.transfer = mp_soft_spi_transfer,
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016-2018 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_DRIVERS_BUS_SPI_H
|
||||
#define MICROPY_INCLUDED_DRIVERS_BUS_SPI_H
|
||||
|
||||
#include "py/mphal.h"
|
||||
|
||||
enum {
|
||||
MP_SPI_IOCTL_INIT,
|
||||
MP_SPI_IOCTL_DEINIT,
|
||||
};
|
||||
|
||||
typedef struct _mp_spi_proto_t {
|
||||
int (*ioctl)(void *self, uint32_t cmd);
|
||||
void (*transfer)(void *self, size_t len, const uint8_t *src, uint8_t *dest);
|
||||
} mp_spi_proto_t;
|
||||
|
||||
typedef struct _mp_soft_spi_obj_t {
|
||||
uint32_t delay_half; // microsecond delay for half SCK period
|
||||
uint8_t polarity;
|
||||
uint8_t phase;
|
||||
mp_hal_pin_obj_t sck;
|
||||
mp_hal_pin_obj_t mosi;
|
||||
mp_hal_pin_obj_t miso;
|
||||
} mp_soft_spi_obj_t;
|
||||
|
||||
extern const mp_spi_proto_t mp_soft_spi_proto;
|
||||
|
||||
int mp_soft_spi_ioctl(void *self, uint32_t cmd);
|
||||
void mp_soft_spi_transfer(void *self, size_t len, const uint8_t *src, uint8_t *dest);
|
||||
|
||||
#endif // MICROPY_INCLUDED_DRIVERS_BUS_SPI_H
|
|
@ -0,0 +1,35 @@
|
|||
# DHT11/DHT22 driver for MicroPython on ESP8266
|
||||
# MIT license; Copyright (c) 2016 Damien P. George
|
||||
|
||||
try:
|
||||
from esp import dht_readinto
|
||||
except:
|
||||
from pyb import dht_readinto
|
||||
|
||||
class DHTBase:
|
||||
def __init__(self, pin):
|
||||
self.pin = pin
|
||||
self.buf = bytearray(5)
|
||||
|
||||
def measure(self):
|
||||
buf = self.buf
|
||||
dht_readinto(self.pin, buf)
|
||||
if (buf[0] + buf[1] + buf[2] + buf[3]) & 0xff != buf[4]:
|
||||
raise Exception("checksum error")
|
||||
|
||||
class DHT11(DHTBase):
|
||||
def humidity(self):
|
||||
return self.buf[0]
|
||||
|
||||
def temperature(self):
|
||||
return self.buf[2]
|
||||
|
||||
class DHT22(DHTBase):
|
||||
def humidity(self):
|
||||
return (self.buf[0] << 8 | self.buf[1]) * 0.1
|
||||
|
||||
def temperature(self):
|
||||
t = ((self.buf[2] & 0x7f) << 8 | self.buf[3]) * 0.1
|
||||
if self.buf[2] & 0x80:
|
||||
t = -t
|
||||
return t
|
|
@ -58,7 +58,7 @@ STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t
|
|||
// If first argument isn't a Pin-like object, we filter out "invert"
|
||||
// from keyword arguments and pass them all to the exported Pin
|
||||
// constructor to create one.
|
||||
mp_obj_t pin_args[n_args + n_kw * 2];
|
||||
mp_obj_t *pin_args = mp_local_alloc((n_args + n_kw * 2) * sizeof(mp_obj_t));
|
||||
memcpy(pin_args, args, n_args * sizeof(mp_obj_t));
|
||||
const mp_obj_t *src = args + n_args;
|
||||
mp_obj_t *dst = pin_args + n_args;
|
||||
|
@ -88,6 +88,8 @@ STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t
|
|||
// will just ignore it as set a concrete type. If not, we'd need
|
||||
// to expose port's "default" pin type too.
|
||||
pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args);
|
||||
|
||||
mp_local_free(pin_args);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
|
|
@ -38,61 +38,6 @@
|
|||
#define MICROPY_PY_MACHINE_SPI_LSB (1)
|
||||
#endif
|
||||
|
||||
void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
|
||||
mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
|
||||
uint32_t delay_half = self->delay_half;
|
||||
|
||||
// only MSB transfer is implemented
|
||||
|
||||
// If a port defines MICROPY_PY_MACHINE_SPI_MIN_DELAY, and the configured
|
||||
// delay_half is equal to this value, then the software SPI implementation
|
||||
// will run as fast as possible, limited only by CPU speed and GPIO time.
|
||||
#ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
|
||||
if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
uint8_t data_out = src[i];
|
||||
uint8_t data_in = 0;
|
||||
for (int j = 0; j < 8; ++j, data_out <<= 1) {
|
||||
mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
|
||||
mp_hal_pin_write(self->sck, 1 - self->polarity);
|
||||
data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
|
||||
mp_hal_pin_write(self->sck, self->polarity);
|
||||
}
|
||||
if (dest != NULL) {
|
||||
dest[i] = data_in;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
uint8_t data_out = src[i];
|
||||
uint8_t data_in = 0;
|
||||
for (int j = 0; j < 8; ++j, data_out <<= 1) {
|
||||
mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
|
||||
if (self->phase == 0) {
|
||||
mp_hal_delay_us_fast(delay_half);
|
||||
mp_hal_pin_write(self->sck, 1 - self->polarity);
|
||||
} else {
|
||||
mp_hal_pin_write(self->sck, 1 - self->polarity);
|
||||
mp_hal_delay_us_fast(delay_half);
|
||||
}
|
||||
data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
|
||||
if (self->phase == 0) {
|
||||
mp_hal_delay_us_fast(delay_half);
|
||||
mp_hal_pin_write(self->sck, self->polarity);
|
||||
} else {
|
||||
mp_hal_pin_write(self->sck, self->polarity);
|
||||
mp_hal_delay_us_fast(delay_half);
|
||||
}
|
||||
}
|
||||
if (dest != NULL) {
|
||||
dest[i] = data_in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings for generic machine.SPI
|
||||
|
||||
|
@ -199,9 +144,9 @@ MP_DEFINE_CONST_DICT(mp_machine_spi_locals_dict, machine_spi_locals_dict_table);
|
|||
// Implementation of soft SPI
|
||||
|
||||
STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) {
|
||||
#ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
|
||||
if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
|
||||
return MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE;
|
||||
#ifdef MICROPY_HW_SOFTSPI_MIN_DELAY
|
||||
if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY) {
|
||||
return MICROPY_HW_SOFTSPI_MAX_BAUDRATE;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
@ -210,9 +155,9 @@ STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) {
|
|||
}
|
||||
|
||||
STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) {
|
||||
#ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
|
||||
if (baudrate >= MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE) {
|
||||
return MICROPY_PY_MACHINE_SPI_MIN_DELAY;
|
||||
#ifdef MICROPY_HW_SOFTSPI_MIN_DELAY
|
||||
if (baudrate >= MICROPY_HW_SOFTSPI_MAX_BAUDRATE) {
|
||||
return MICROPY_HW_SOFTSPI_MIN_DELAY;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
|
@ -229,8 +174,8 @@ STATIC void mp_machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in,
|
|||
mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u,"
|
||||
" sck=" MP_HAL_PIN_FMT ", mosi=" MP_HAL_PIN_FMT ", miso=" MP_HAL_PIN_FMT ")",
|
||||
baudrate_from_delay_half(self->delay_half), self->polarity, self->phase,
|
||||
mp_hal_pin_name(self->sck), mp_hal_pin_name(self->mosi), mp_hal_pin_name(self->miso));
|
||||
baudrate_from_delay_half(self->spi.delay_half), self->spi.polarity, self->spi.phase,
|
||||
mp_hal_pin_name(self->spi.sck), mp_hal_pin_name(self->spi.mosi), mp_hal_pin_name(self->spi.miso));
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
|
@ -253,9 +198,9 @@ STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n
|
|||
self->base.type = &mp_machine_soft_spi_type;
|
||||
|
||||
// set parameters
|
||||
self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
|
||||
self->polarity = args[ARG_polarity].u_int;
|
||||
self->phase = args[ARG_phase].u_int;
|
||||
self->spi.delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
|
||||
self->spi.polarity = args[ARG_polarity].u_int;
|
||||
self->spi.phase = args[ARG_phase].u_int;
|
||||
if (args[ARG_bits].u_int != 8) {
|
||||
mp_raise_ValueError("bits must be 8");
|
||||
}
|
||||
|