Merge remote-tracking branch 'upstream/master' into dev-8.0

This commit is contained in:
Themba Dube 2021-06-27 13:10:26 -04:00
commit 6d7ec6e424
150 changed files with 3351 additions and 639 deletions

24
.github/workflows/ports_javascript.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: javascript port
on:
push:
pull_request:
paths:
- '.github/workflows/*.yml'
- 'tools/**'
- 'py/**'
- 'extmod/**'
- 'lib/**'
- 'ports/javascript/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install packages
run: source tools/ci.sh && ci_javascript_setup
- name: Build
run: source tools/ci.sh && ci_javascript_build
- name: Run tests
run: source tools/ci.sh && ci_javascript_run_tests

65
LICENSE
View File

@ -19,3 +19,68 @@ 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.
--------------------------------------------------------------------------------
Unless specified otherwise (see below), the above license and copyright applies
to all files in this repository.
Individual files may include additional copyright holders.
The various ports of MicroPython may include third-party software that is
licensed under different terms. These licenses are summarised in the tree
below, please refer to these files and directories for further license and
copyright information. Note that (L)GPL-licensed code listed below is only
used during the build process and is not part of the compiled source code.
/ (MIT)
/drivers
/cc3000 (BSD-3-clause)
/cc3100 (BSD-3-clause)
/wiznet5k (BSD-3-clause)
/extmod
/crypto-algorithms (NONE)
/re15 (BSD-3-clause)
/uzlib (Zlib)
/lib
/asf4 (Apache-2.0)
/axtls (BSD-3-clause)
/config
/scripts
/config (GPL-2.0-or-later)
/Rules.mak (GPL-2.0)
/berkeley-db-1xx (BSD-4-clause)
/btstack (See btstack/LICENSE)
/cmsis (BSD-3-clause)
/libhydrogen (ISC)
/littlefs (BSD-3-clause)
/lwip (BSD-3-clause)
/mynewt-nimble (Apache-2.0)
/nrfx (BSD-3-clause)
/nxp_driver (BSD-3-Clause)
/oofatfs (BSD-1-clause)
/pico-sdk (BSD-3-clause)
/stm32lib (BSD-3-clause)
/tinytest (BSD-3-clause)
/tinyusb (MIT)
/logo (uses OFL-1.1)
/ports
/cc3200
/hal (BSD-3-clause)
/simplelink (BSD-3-clause)
/FreeRTOS (GPL-2.0 with FreeRTOS exception)
/stm32
/usbd*.c (MCD-ST Liberty SW License Agreement V2)
/stm32_it.* (MIT + BSD-3-clause)
/system_stm32*.c (MIT + BSD-3-clause)
/boards
/startup_stm32*.s (BSD-3-clause)
/*/stm32*.h (BSD-3-clause)
/usbdev (MCD-ST Liberty SW License Agreement V2)
/usbhost (MCD-ST Liberty SW License Agreement V2)
/teensy
/core (PJRC.COM)
/zephyr
/src (Apache-2.0)
/tools
/dfu.py (LGPL-3.0-only)

View File

@ -66,7 +66,7 @@ master_doc = 'index'
# General information about the project.
project = 'MicroPython'
copyright = '2014-2021, Damien P. George, Paul Sokolovsky, and contributors'
copyright = '- The MicroPython Documentation is Copyright © 2014-2021, Damien P. George, Paul Sokolovsky, and contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -74,7 +74,7 @@ copyright = '2014-2021, Damien P. George, Paul Sokolovsky, and contributors'
#
# We don't follow "The short X.Y version" vs "The full version, including alpha/beta/rc tags"
# breakdown, so use the same version identifier for both to avoid confusion.
version = release = '1.15'
version = release = '1.16'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -102,6 +102,14 @@ Once the network is established the :mod:`socket <usocket>` module can be used
to create and use TCP/UDP sockets as usual, and the ``urequests`` module for
convenient HTTP requests.
After a call to ``wlan.connect()``, the device will by default retry to connect
**forever**, even when the authentication failed or no AP is in range.
``wlan.status()`` will return ``network.STAT_CONNECTING`` in this state until a
connection succeeds or the interface gets disabled. This can be changed by
calling ``wlan.config(reconnects=n)``, where n are the number of desired reconnect
attempts (0 means it won't retry, -1 will restore the default behaviour of trying
to reconnect forever).
Delay and timing
----------------

View File

@ -10,8 +10,8 @@ and time.
Example usage::
rtc = machine.RTC()
rtc.init((2014, 5, 1, 4, 13, 0, 0, 0))
print(rtc.now())
rtc.datetime((2020, 1, 21, 2, 10, 32, 36, 0))
print(rtc.datetime())
Constructors
@ -24,6 +24,20 @@ Constructors
Methods
-------
.. method:: RTC.datetime([datetimetuple])
Get or set the date and time of the RTC.
With no arguments, this method returns an 8-tuple with the current
date and time. With 1 argument (being an 8-tuple) it sets the date
and time.
The 8-tuple has the following format:
(year, month, day, weekday, hours, minutes, seconds, subseconds)
The meaning of the ``subseconds`` field is hardware dependent.
.. method:: RTC.init(datetime)
Initialise the RTC. Datetime is a tuple of the form:

View File

@ -129,4 +129,5 @@ Methods
authmode Authentication mode supported (enumeration, see module constants)
password Access password (string)
dhcp_hostname The DHCP hostname to use
reconnects Number of reconnect attempts to make (integer, 0=none, -1=unlimited)
============= ===========

View File

@ -109,6 +109,16 @@ Methods
Return value: number of bytes sent.
.. method:: USB_VCP.irq(handler=None, trigger=IRQ_RX, hard=False)
Register *handler* to be called whenever an event specified by *trigger*
occurs. The *handler* function must take exactly one argument, which will
be the USB VCP object. Pass in ``None`` to disable the callback.
Valid values for *trigger* are:
- ``USB_VCP.IRQ_RX``: new data is available for reading from the USB VCP object.
Constants
---------
@ -117,3 +127,7 @@ Constants
USB_VCP.CTS
to select the flow control type.
.. data:: USB_VCP.IRQ_RX
IRQ trigger values for :meth:`USB_VCP.irq`.

View File

@ -240,6 +240,14 @@ TCP stream connections
This is a coroutine.
.. method:: Stream.readinto(buf)
Read up to n bytes into *buf* with n being equal to the length of *buf*.
Return the number of bytes read into *buf*.
This is a coroutine, and a MicroPython extension.
.. method:: Stream.readline()
Read a line and return it.

View File

@ -202,7 +202,7 @@ See :ref:`machine.RTC <machine.RTC>` ::
from machine import RTC
rtc = RTC()
rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time
rtc.datetime((2017, 8, 23, 2, 12, 48, 0, 0)) # set a specific date and time
rtc.datetime() # get date and time
WDT (Watchdog timer)

View File

@ -15,6 +15,7 @@ SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_IREF_SELECT = const(0xAD)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
@ -63,6 +64,8 @@ class SSD1306(framebuf.FrameBuffer):
0xFF, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
SET_IREF_SELECT,
0x30, # enable internal IREF during display on
# charge pump
SET_CHARGE_PUMP,
0x10 if self.external_vcc else 0x14,
@ -92,10 +95,11 @@ class SSD1306(framebuf.FrameBuffer):
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
if self.width != 128:
# narrow displays use centred columns
col_offset = (128 - self.width) // 2
x0 += col_offset
x1 += col_offset
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)

View File

@ -30,7 +30,6 @@ INC += -I$(BTSTACK_DIR)/3rd-party/yxml
SRC_BTSTACK = \
$(addprefix lib/btstack/src/, $(SRC_FILES)) \
$(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \
lib/btstack/platform/embedded/btstack_run_loop_embedded.c
ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1)
ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1)

View File

@ -86,6 +86,7 @@ STATIC int btstack_uart_open(void) {
STATIC int btstack_uart_close(void) {
mp_bluetooth_hci_controller_deinit();
mp_bluetooth_hci_uart_deinit();
return 0;
}

View File

@ -849,6 +849,11 @@ int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) {
int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) {
DEBUG_printf("mp_bluetooth_gap_advertise_start\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
uint16_t adv_int_min = interval_us / 625;
uint16_t adv_int_max = interval_us / 625;
uint8_t adv_type = connectable ? 0 : 2;
@ -885,6 +890,11 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons
void mp_bluetooth_gap_advertise_stop(void) {
DEBUG_printf("mp_bluetooth_gap_advertise_stop\n");
if (!mp_bluetooth_is_active()) {
return;
}
gap_advertisements_enable(false);
MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = 0;
MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = NULL;
@ -892,6 +902,11 @@ void mp_bluetooth_gap_advertise_stop(void) {
int mp_bluetooth_gatts_register_service_begin(bool append) {
DEBUG_printf("mp_bluetooth_gatts_register_service_begin\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
if (!append) {
// This will reset the DB.
// Becase the DB is statically allocated, there's no problem with just re-initing it.
@ -1064,16 +1079,27 @@ int mp_bluetooth_gatts_register_service_end(void) {
int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) {
DEBUG_printf("mp_bluetooth_gatts_read\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len);
}
int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) {
DEBUG_printf("mp_bluetooth_gatts_write\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len);
}
int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) {
DEBUG_printf("mp_bluetooth_gatts_notify\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
// Note: btstack doesn't appear to support sending a notification without a value, so include the stored value.
uint8_t *data = NULL;
size_t len = 0;
@ -1084,6 +1110,10 @@ int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) {
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) {
DEBUG_printf("mp_bluetooth_gatts_notify_send\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
// Attempt to send immediately. If it succeeds, btstack will copy the buffer.
MICROPY_PY_BLUETOOTH_ENTER
int err = att_server_notify(conn_handle, value_handle, value, value_len);
@ -1110,6 +1140,10 @@ int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle,
int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) {
DEBUG_printf("mp_bluetooth_gatts_indicate\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
uint8_t *data = NULL;
size_t len = 0;
mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len);
@ -1142,6 +1176,9 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) {
int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) {
DEBUG_printf("mp_bluetooth_gatts_set_buffer\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, len, append);
}
@ -1165,6 +1202,9 @@ int mp_bluetooth_set_preferred_mtu(uint16_t mtu) {
int mp_bluetooth_gap_disconnect(uint16_t conn_handle) {
DEBUG_printf("mp_bluetooth_gap_disconnect\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
gap_disconnect(conn_handle);
return 0;
}
@ -1195,6 +1235,10 @@ STATIC void scan_duration_timeout_handler(btstack_timer_source_t *ds) {
int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan) {
DEBUG_printf("mp_bluetooth_gap_scan_start\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
if (duration_ms > 0) {
btstack_run_loop_set_timer(&scan_duration_timeout, duration_ms);
btstack_run_loop_set_timer_handler(&scan_duration_timeout, scan_duration_timeout_handler);
@ -1209,6 +1253,9 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_
int mp_bluetooth_gap_scan_stop(void) {
DEBUG_printf("mp_bluetooth_gap_scan_stop\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
btstack_run_loop_remove_timer(&scan_duration_timeout);
gap_stop_scan();
mp_bluetooth_gap_on_scan_complete();
@ -1240,6 +1287,11 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr,
int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) {
DEBUG_printf("mp_bluetooth_gattc_discover_primary_services\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
uint8_t err;
if (uuid) {
if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) {
@ -1260,6 +1312,11 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_
int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const mp_obj_bluetooth_uuid_t *uuid) {
DEBUG_printf("mp_bluetooth_gattc_discover_characteristics\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
gatt_client_service_t service = {
// Only start/end handles needed for gatt_client_discover_characteristics_for_service.
.start_group_handle = start_handle,
@ -1287,6 +1344,11 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s
int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) {
DEBUG_printf("mp_bluetooth_gattc_discover_descriptors\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
gatt_client_characteristic_t characteristic = {
// Only start/end handles needed for gatt_client_discover_characteristic_descriptors.
.start_handle = start_handle,
@ -1301,12 +1363,19 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start
int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) {
DEBUG_printf("mp_bluetooth_gattc_read\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_read, conn_handle, value_handle));
}
int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) {
DEBUG_printf("mp_bluetooth_gattc_write\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
// We should be distinguishing between gatt_client_write_value_of_characteristic vs
// gatt_client_write_characteristic_descriptor_using_descriptor_handle.
// However both are implemented using send_gatt_write_attribute_value_request under the hood,

View File

@ -46,7 +46,7 @@ static void sha256_transform(CRYAL_SHA256_CTX *ctx, const BYTE data[])
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
for (i = 0, j = 0; i < 16; ++i, j += 4)
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
m[i] = ((uint32_t)data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
for ( ; i < 64; ++i)
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];

View File

@ -31,12 +31,22 @@
#if MICROPY_PY_UASYNCIO
// Used when task cannot be guaranteed to be non-NULL.
#define TASK_PAIRHEAP(task) ((task) ? &(task)->pairheap : NULL)
#define TASK_STATE_RUNNING_NOT_WAITED_ON (mp_const_true)
#define TASK_STATE_DONE_NOT_WAITED_ON (mp_const_none)
#define TASK_STATE_DONE_WAS_WAITED_ON (mp_const_false)
#define TASK_IS_DONE(task) ( \
(task)->state == TASK_STATE_DONE_NOT_WAITED_ON \
|| (task)->state == TASK_STATE_DONE_WAS_WAITED_ON)
typedef struct _mp_obj_task_t {
mp_pairheap_t pairheap;
mp_obj_t coro;
mp_obj_t data;
mp_obj_t waiting;
mp_obj_t state;
mp_obj_t ph_key;
} mp_obj_task_t;
@ -103,7 +113,7 @@ STATIC mp_obj_t task_queue_push_sorted(size_t n_args, const mp_obj_t *args) {
assert(mp_obj_is_small_int(args[2]));
task->ph_key = args[2];
}
self->heap = (mp_obj_task_t *)mp_pairheap_push(task_lt, &self->heap->pairheap, &task->pairheap);
self->heap = (mp_obj_task_t *)mp_pairheap_push(task_lt, TASK_PAIRHEAP(self->heap), TASK_PAIRHEAP(task));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(task_queue_push_sorted_obj, 2, 3, task_queue_push_sorted);
@ -146,9 +156,6 @@ STATIC const mp_obj_type_t task_queue_type = {
/******************************************************************************/
// Task class
// For efficiency, the task object is stored to the coro entry when the task is done.
#define TASK_IS_DONE(task) ((task)->coro == MP_OBJ_FROM_PTR(task))
// This is the core uasyncio context with cur_task, _task_queue and CancelledError.
STATIC mp_obj_t uasyncio_context = MP_OBJ_NULL;
@ -159,7 +166,7 @@ STATIC mp_obj_t task_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
mp_pairheap_init_node(task_lt, &self->pairheap);
self->coro = args[0];
self->data = mp_const_none;
self->waiting = mp_const_none;
self->state = TASK_STATE_RUNNING_NOT_WAITED_ON;
self->ph_key = MP_OBJ_NEW_SMALL_INT(0);
if (n_args == 2) {
uasyncio_context = args[1];
@ -218,24 +225,6 @@ STATIC mp_obj_t task_cancel(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_cancel_obj, task_cancel);
STATIC mp_obj_t task_throw(mp_obj_t self_in, mp_obj_t value_in) {
// This task raised an exception which was uncaught; handle that now.
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
// Set the data because it was cleared by the main scheduling loop.
self->data = value_in;
if (self->waiting == mp_const_none) {
// Nothing await'ed on the task so call the exception handler.
mp_obj_t _exc_context = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR__exc_context));
mp_obj_dict_store(_exc_context, MP_OBJ_NEW_QSTR(MP_QSTR_exception), value_in);
mp_obj_dict_store(_exc_context, MP_OBJ_NEW_QSTR(MP_QSTR_future), self_in);
mp_obj_t Loop = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_Loop));
mp_obj_t call_exception_handler = mp_load_attr(Loop, MP_QSTR_call_exception_handler);
mp_call_function_1(call_exception_handler, _exc_context);
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(task_throw_obj, task_throw);
STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
if (dest[0] == MP_OBJ_NULL) {
@ -244,32 +233,24 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
dest[0] = self->coro;
} else if (attr == MP_QSTR_data) {
dest[0] = self->data;
} else if (attr == MP_QSTR_waiting) {
if (self->waiting != mp_const_none && self->waiting != mp_const_false) {
dest[0] = self->waiting;
}
} else if (attr == MP_QSTR_state) {
dest[0] = self->state;
} else if (attr == MP_QSTR_done) {
dest[0] = MP_OBJ_FROM_PTR(&task_done_obj);
dest[1] = self_in;
} else if (attr == MP_QSTR_cancel) {
dest[0] = MP_OBJ_FROM_PTR(&task_cancel_obj);
dest[1] = self_in;
} else if (attr == MP_QSTR_throw) {
dest[0] = MP_OBJ_FROM_PTR(&task_throw_obj);
dest[1] = self_in;
} else if (attr == MP_QSTR_ph_key) {
dest[0] = self->ph_key;
}
} else if (dest[1] != MP_OBJ_NULL) {
// Store
if (attr == MP_QSTR_coro) {
self->coro = dest[1];
dest[0] = MP_OBJ_NULL;
} else if (attr == MP_QSTR_data) {
if (attr == MP_QSTR_data) {
self->data = dest[1];
dest[0] = MP_OBJ_NULL;
} else if (attr == MP_QSTR_waiting) {
self->waiting = dest[1];
} else if (attr == MP_QSTR_state) {
self->state = dest[1];
dest[0] = MP_OBJ_NULL;
}
}
@ -278,15 +259,12 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
STATIC mp_obj_t task_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
(void)iter_buf;
mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in);
if (self->waiting == mp_const_none) {
// The is the first access of the "waiting" entry.
if (TASK_IS_DONE(self)) {
// Signal that the completed-task has been await'ed on.
self->waiting = mp_const_false;
} else {
// Lazily allocate the waiting queue.
self->waiting = task_queue_make_new(&task_queue_type, 0, 0, NULL);
}
if (TASK_IS_DONE(self)) {
// Signal that the completed-task has been await'ed on.
self->state = TASK_STATE_DONE_WAS_WAITED_ON;
} else if (self->state == TASK_STATE_RUNNING_NOT_WAITED_ON) {
// Allocate the waiting queue.
self->state = task_queue_make_new(&task_queue_type, 0, 0, NULL);
}
return self_in;
}
@ -299,7 +277,7 @@ STATIC mp_obj_t task_iternext(mp_obj_t self_in) {
} else {
// Put calling task on waiting queue.
mp_obj_t cur_task = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task));
mp_obj_t args[2] = { self->waiting, cur_task };
mp_obj_t args[2] = { self->state, cur_task };
task_queue_push_sorted(2, args);
// Set calling task's data to this task that it waits on, to double-link it.
((mp_obj_task_t *)MP_OBJ_TO_PTR(cur_task))->data = self_in;

View File

@ -1030,7 +1030,6 @@ int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle,
if (om == NULL) {
return MP_ENOMEM;
}
// TODO: check that notify_custom takes ownership of om, if not os_mbuf_free_chain(om).
return ble_hs_err_to_errno(ble_gattc_notify_custom(conn_handle, value_handle, om));
}

View File

@ -175,6 +175,10 @@ def run_until_complete(main_task=None):
if not exc:
t.coro.send(None)
else:
# If the task is finished and on the run queue and gets here, then it
# had an exception and was not await'ed on. Throwing into it now will
# raise StopIteration and the code below will catch this and run the
# call_exception_handler function.
t.data = None
t.coro.throw(exc)
except excs_all as er:
@ -185,22 +189,32 @@ def run_until_complete(main_task=None):
if isinstance(er, StopIteration):
return er.value
raise er
# Schedule any other tasks waiting on the completion of this task
waiting = False
if hasattr(t, "waiting"):
while t.waiting.peek():
_task_queue.push_head(t.waiting.pop_head())
waiting = True
t.waiting = None # Free waiting queue head
if not waiting and not isinstance(er, excs_stop):
# An exception ended this detached task, so queue it for later
# execution to handle the uncaught exception if no other task retrieves
# the exception in the meantime (this is handled by Task.throw).
_task_queue.push_head(t)
# Indicate task is done by setting coro to the task object itself
t.coro = t
# Save return value of coro to pass up to caller
t.data = er
if t.state:
# Task was running but is now finished.
waiting = False
if t.state is True:
# "None" indicates that the task is complete and not await'ed on (yet).
t.state = None
else:
# Schedule any other tasks waiting on the completion of this task.
while t.state.peek():
_task_queue.push_head(t.state.pop_head())
waiting = True
# "False" indicates that the task is complete and has been await'ed on.
t.state = False
if not waiting and not isinstance(er, excs_stop):
# An exception ended this detached task, so queue it for later
# execution to handle the uncaught exception if no other task retrieves
# the exception in the meantime (this is handled by Task.throw).
_task_queue.push_head(t)
# Save return value of coro to pass up to caller.
t.data = er
elif t.state is None:
# Task is already finished and nothing await'ed on the task,
# so call the exception handler.
_exc_context["exception"] = exc
_exc_context["future"] = t
Loop.call_exception_handler(_exc_context)
# Create a new task from a coroutine and run it until it finishes

View File

@ -30,6 +30,10 @@ class Stream:
yield core._io_queue.queue_read(self.s)
return self.s.read(n)
async def readinto(self, buf):
yield core._io_queue.queue_read(self.s)
return self.s.readinto(buf)
async def readexactly(self, n):
r = b""
while n:
@ -103,15 +107,7 @@ class Server:
async def wait_closed(self):
await self.task
async def _serve(self, cb, host, port, backlog):
import usocket as socket
ai = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
s = socket.socket()
s.setblocking(False)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(ai[-1])
s.listen(backlog)
async def _serve(self, s, cb):
# Accept incoming connections
while True:
try:
@ -133,9 +129,20 @@ class Server:
# Helper function to start a TCP stream server, running as a new task
# TODO could use an accept-callback on socket read activity instead of creating a task
async def start_server(cb, host, port, backlog=5):
s = Server()
s.task = core.create_task(s._serve(cb, host, port, backlog))
return s
import usocket as socket
# Create and bind server socket.
host = socket.getaddrinfo(host, port)[0] # TODO this is blocking!
s = socket.socket()
s.setblocking(False)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(host[-1])
s.listen(backlog)
# Create and return server object and task.
srv = Server()
srv.task = core.create_task(srv._serve(s, cb))
return srv
################################################################################

View File

@ -123,6 +123,7 @@ class Task:
def __init__(self, coro, globals=None):
self.coro = coro # Coroutine of this Task
self.data = None # General data for queue it is waiting on
self.state = True # None, False, True or a TaskQueue instance
self.ph_key = 0 # Pairing heap
self.ph_child = None # Paring heap
self.ph_child_last = None # Paring heap
@ -130,30 +131,30 @@ class Task:
self.ph_rightmost_parent = None # Paring heap
def __iter__(self):
if self.coro is self:
# Signal that the completed-task has been await'ed on.
self.waiting = None
elif not hasattr(self, "waiting"):
# Lazily allocated head of linked list of Tasks waiting on completion of this task.
self.waiting = TaskQueue()
if not self.state:
# Task finished, signal that is has been await'ed on.
self.state = False
elif self.state is True:
# Allocated head of linked list of Tasks waiting on completion of this task.
self.state = TaskQueue()
return self
def __next__(self):
if self.coro is self:
if not self.state:
# Task finished, raise return value to caller so it can continue.
raise self.data
else:
# Put calling task on waiting queue.
self.waiting.push_head(core.cur_task)
self.state.push_head(core.cur_task)
# Set calling task's data to this task that it waits on, to double-link it.
core.cur_task.data = self
def done(self):
return self.coro is self
return not self.state
def cancel(self):
# Check if task is already finished.
if self.coro is self:
if not self.state:
return False
# Can't cancel self (not supported yet).
if self is core.cur_task:
@ -172,13 +173,3 @@ class Task:
core._task_queue.push_head(self)
self.data = core.CancelledError
return True
def throw(self, value):
# This task raised an exception which was uncaught; handle that now.
# Set the data because it was cleared by the main scheduling loop.
self.data = value
if not hasattr(self, "waiting"):
# Nothing await'ed on the task so call the exception handler.
core._exc_context["exception"] = value
core._exc_context["future"] = self
core.Loop.call_exception_handler(core._exc_context)

View File

@ -213,3 +213,10 @@ mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday,
}
return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds);
}
// Calculate the weekday from the date.
// The result is zero based with 0 = Monday.
// by Michael Keith and Tom Craver, 1990.
int timeutils_calc_weekday(int y, int m, int d) {
return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) + 6) % 7;
}

View File

@ -100,4 +100,6 @@ static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_197
#endif
int timeutils_calc_weekday(int y, int m, int d);
#endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H

View File

@ -62,7 +62,9 @@
#define MICROPY_READER_POSIX (1)
#define MICROPY_ENABLE_RUNTIME (0)
#define MICROPY_ENABLE_GC (1)
#ifndef __EMSCRIPTEN__
#define MICROPY_STACK_CHECK (1)
#endif
#define MICROPY_HELPER_LEXER_UNIX (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_ENABLE_SOURCE_LINE (1)

View File

@ -50,10 +50,19 @@ To check out a copy of the IDF use git clone:
$ git clone -b v4.0.2 --recursive https://github.com/espressif/esp-idf.git
```
You can replace `v4.0.2` with `v4.1.1` or any other supported version.
You can replace `v4.0.2` with `v4.1.1` or `v4.2` or any other supported version.
(You don't need a full recursive clone; see the `ci_esp32_setup` function in
`tools/ci.sh` in this repository for more detailed set-up commands.)
If you already have a copy of the IDF then checkout a version compatible with
MicroPython and update the submodules using:
```bash
$ cd esp-idf
$ git checkout v4.2
$ git submodule update --init --recursive
```
After you've cloned and checked out the IDF to the correct version, run the
`install.sh` script:

View File

@ -31,6 +31,10 @@ CONFIG_IDLE_TASK_STACK_SIZE=4096
# Power Management
CONFIG_PM_ENABLE=y
# Memory protection
# This is required to allow allocating IRAM
CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
# FreeRTOS
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y

View File

@ -218,6 +218,10 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args,
mp_obj_get_array(pulses, &pulses_length, &pulses_ptr);
mp_uint_t num_items = (pulses_length / 2) + (pulses_length % 2);
if (self->loop_en && num_items > 63) {
mp_raise_ValueError(MP_ERROR_TEXT("too many pulses for loop"));
}
if (num_items > self->num_items) {
self->items = (rmt_item32_t *)m_realloc(self->items, num_items * sizeof(rmt_item32_t *));
self->num_items = num_items;
@ -241,12 +245,12 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args,
check_esp_err(rmt_set_tx_loop_mode(self->channel_id, false));
}
check_esp_err(rmt_wait_tx_done(self->channel_id, portMAX_DELAY));
check_esp_err(rmt_set_tx_intr_en(self->channel_id, false));
}
check_esp_err(rmt_write_items(self->channel_id, self->items, num_items, false /* non-blocking */));
if (self->loop_en) {
check_esp_err(rmt_set_tx_intr_en(self->channel_id, false));
check_esp_err(rmt_set_tx_loop_mode(self->channel_id, true));
}

View File

@ -411,6 +411,32 @@ mp_obj_t machine_hw_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_
}
self->base.type = &machine_hw_spi_type;
int8_t sck, mosi, miso;
if (args[ARG_sck].u_obj == MP_OBJ_NULL) {
sck = default_pins->sck;
} else if (args[ARG_sck].u_obj == mp_const_none) {
sck = -1;
} else {
sck = machine_pin_get_id(args[ARG_sck].u_obj);
}
if (args[ARG_mosi].u_obj == MP_OBJ_NULL) {
mosi = default_pins->mosi;
} else if (args[ARG_mosi].u_obj == mp_const_none) {
mosi = -1;
} else {
mosi = machine_pin_get_id(args[ARG_mosi].u_obj);
}
if (args[ARG_miso].u_obj == MP_OBJ_NULL) {
miso = default_pins->miso;
} else if (args[ARG_miso].u_obj == mp_const_none) {
miso = -1;
} else {
miso = machine_pin_get_id(args[ARG_miso].u_obj);
}
machine_hw_spi_init_internal(
self,
args[ARG_id].u_int,
@ -419,9 +445,9 @@ mp_obj_t machine_hw_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_
args[ARG_phase].u_int,
args[ARG_bits].u_int,
args[ARG_firstbit].u_int,
args[ARG_sck].u_obj == MP_OBJ_NULL ? default_pins->sck : machine_pin_get_id(args[ARG_sck].u_obj),
args[ARG_mosi].u_obj == MP_OBJ_NULL ? default_pins->mosi : machine_pin_get_id(args[ARG_mosi].u_obj),
args[ARG_miso].u_obj == MP_OBJ_NULL ? default_pins->miso : machine_pin_get_id(args[ARG_miso].u_obj));
sck,
mosi,
miso);
return MP_OBJ_FROM_PTR(self);
}

View File

@ -256,7 +256,14 @@ STATIC mp_obj_t sd_deinit(mp_obj_t self_in) {
DEBUG_printf("De-init host\n");
if (self->flags & SDCARD_CARD_FLAGS_HOST_INIT_DONE) {
self->host.deinit();
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
if (self->host.flags & SDMMC_HOST_FLAG_DEINIT_ARG) {
self->host.deinit_p(self->host.slot);
} else
#endif
{
self->host.deinit();
}
self->flags &= ~SDCARD_CARD_FLAGS_HOST_INIT_DONE;
}

View File

@ -1,5 +1,7 @@
# Set location of base MicroPython directory.
get_filename_component(MICROPY_DIR ${PROJECT_DIR}/../.. ABSOLUTE)
if(NOT MICROPY_DIR)
get_filename_component(MICROPY_DIR ${PROJECT_DIR}/../.. ABSOLUTE)
endif()
# Include core source components.
include(${MICROPY_DIR}/py/py.cmake)

View File

@ -1,19 +1,44 @@
import sys
# Combine bootloader, partition table and application into a final binary.
import os, sys
sys.path.append(os.getenv("IDF_PATH") + "/components/partition_table")
import gen_esp32part
OFFSET_BOOTLOADER = 0x1000
OFFSET_PARTITIONS = 0x8000
OFFSET_APPLICATION = 0x10000
def load_partition_table(filename):
with open(filename, "rb") as f:
return gen_esp32part.PartitionTable.from_binary(f.read())
partition_table = load_partition_table(sys.argv[2])
max_size_bootloader = OFFSET_PARTITIONS - OFFSET_BOOTLOADER
max_size_partitions = 0
offset_application = 0
max_size_application = 0
for part in partition_table:
if part.name == "nvs":
max_size_partitions = part.offset - OFFSET_PARTITIONS
elif part.type == gen_esp32part.APP_TYPE and offset_application == 0:
offset_application = part.offset
max_size_application = part.size
files_in = [
("bootloader", OFFSET_BOOTLOADER, sys.argv[1]),
("partitions", OFFSET_PARTITIONS, sys.argv[2]),
("application", OFFSET_APPLICATION, sys.argv[3]),
("bootloader", OFFSET_BOOTLOADER, max_size_bootloader, sys.argv[1]),
("partitions", OFFSET_PARTITIONS, max_size_partitions, sys.argv[2]),
("application", offset_application, max_size_application, sys.argv[3]),
]
file_out = sys.argv[4]
cur_offset = OFFSET_BOOTLOADER
with open(file_out, "wb") as fout:
for name, offset, file_in in files_in:
for name, offset, max_size, file_in in files_in:
assert offset >= cur_offset
fout.write(b"\xff" * (offset - cur_offset))
cur_offset = offset
@ -21,5 +46,14 @@ with open(file_out, "wb") as fout:
data = fin.read()
fout.write(data)
cur_offset += len(data)
print("%-12s% 8d" % (name, len(data)))
print("%-12s% 8d" % ("total", cur_offset))
print(
"%-12s@0x%06x % 8d (% 8d remaining)"
% (name, offset, len(data), max_size - len(data))
)
if len(data) > max_size:
print(
"ERROR: %s overflows allocated space of %d bytes by %d bytes"
% (name, max_size, len(data) - max_size)
)
sys.exit(1)
print("%-22s% 8d" % ("total", cur_offset))

View File

@ -142,12 +142,16 @@ static uint8_t wifi_sta_disconn_reason = 0;
static bool mdns_initialised = false;
#endif
static uint8_t conf_wifi_sta_reconnects = 0;
static uint8_t wifi_sta_reconnects;
// This function is called by the system-event task and so runs in a different
// thread to the main MicroPython task. It must not raise any Python exceptions.
static esp_err_t event_handler(void *ctx, system_event_t *event) {
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
ESP_LOGI("wifi", "STA_START");
wifi_sta_reconnects = 0;
break;
case SYSTEM_EVENT_STA_CONNECTED:
ESP_LOGI("network", "CONNECTED");
@ -199,15 +203,23 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) {
wifi_sta_connected = false;
if (wifi_sta_connect_requested) {
wifi_mode_t mode;
if (esp_wifi_get_mode(&mode) == ESP_OK) {
if (mode & WIFI_MODE_STA) {
// STA is active so attempt to reconnect.
esp_err_t e = esp_wifi_connect();
if (e != ESP_OK) {
ESP_LOGI("wifi", "error attempting to reconnect: 0x%04x", e);
}
if (esp_wifi_get_mode(&mode) != ESP_OK) {
break;
}
if (!(mode & WIFI_MODE_STA)) {
break;
}
if (conf_wifi_sta_reconnects) {
ESP_LOGI("wifi", "reconnect counter=%d, max=%d",
wifi_sta_reconnects, conf_wifi_sta_reconnects);
if (++wifi_sta_reconnects >= conf_wifi_sta_reconnects) {
break;
}
}
esp_err_t e = esp_wifi_connect();
if (e != ESP_OK) {
ESP_LOGI("wifi", "error attempting to reconnect: 0x%04x", e);
}
}
break;
}
@ -361,6 +373,7 @@ STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
ESP_EXCEPTIONS(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config));
}
wifi_sta_reconnects = 0;
// connect to the WiFi AP
MP_THREAD_GIL_EXIT();
ESP_EXCEPTIONS(esp_wifi_connect());
@ -395,7 +408,9 @@ STATIC mp_obj_t esp_status(size_t n_args, const mp_obj_t *args) {
if (wifi_sta_connected) {
// Happy path, connected with IP
return MP_OBJ_NEW_SMALL_INT(STAT_GOT_IP);
} else if (wifi_sta_connect_requested) {
} else if (wifi_sta_connect_requested
&& (conf_wifi_sta_reconnects == 0
|| wifi_sta_reconnects < conf_wifi_sta_reconnects)) {
// No connection or error, but is requested = Still connecting
return MP_OBJ_NEW_SMALL_INT(STAT_CONNECTING);
} else if (wifi_sta_disconn_reason == 0) {
@ -633,6 +648,14 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs
cfg.ap.max_connection = mp_obj_get_int(kwargs->table[i].value);
break;
}
case QS(MP_QSTR_reconnects): {
int reconnects = mp_obj_get_int(kwargs->table[i].value);
req_if = WIFI_IF_STA;
// parameter reconnects == -1 means to retry forever.
// here means conf_wifi_sta_reconnects == 0 to retry forever.
conf_wifi_sta_reconnects = (reconnects == -1) ? 0 : reconnects + 1;
break;
}
default:
goto unknown;
}
@ -666,13 +689,6 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs
case WIFI_IF_STA:
ESP_EXCEPTIONS(esp_wifi_get_mac(self->if_id, mac));
return mp_obj_new_bytes(mac, sizeof(mac));
#if !MICROPY_ESP_IDF_4
case ESP_IF_ETH:
esp_eth_get_mac(mac);
return mp_obj_new_bytes(mac, sizeof(mac));
#endif
default:
goto unknown;
}
@ -711,6 +727,11 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs
val = MP_OBJ_NEW_SMALL_INT(cfg.ap.max_connection);
break;
}
case QS(MP_QSTR_reconnects):
req_if = WIFI_IF_STA;
int rec = conf_wifi_sta_reconnects - 1;
val = MP_OBJ_NEW_SMALL_INT(rec);
break;
default:
goto unknown;
}
@ -753,12 +774,12 @@ STATIC mp_obj_t esp_phy_mode(size_t n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_phy_mode_obj, 0, 1, esp_phy_mode);
STATIC const mp_rom_map_elem_t mp_module_network_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) },
{ MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&esp_initialize_obj) },
{ MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&get_wlan_obj) },
#if !MICROPY_ESP_IDF_4
#if (ESP_IDF_VERSION_MAJOR == 4) && (ESP_IDF_VERSION_MINOR >= 1) && (CONFIG_IDF_TARGET_ESP32)
{ MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&get_lan_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&ppp_make_new_obj) },
@ -784,18 +805,22 @@ STATIC const mp_rom_map_elem_t mp_module_network_globals_table[] = {
#endif
{ MP_ROM_QSTR(MP_QSTR_AUTH_MAX), MP_ROM_INT(WIFI_AUTH_MAX) },
#if (ESP_IDF_VERSION_MAJOR == 4) && (ESP_IDF_VERSION_MINOR >= 1) && (CONFIG_IDF_TARGET_ESP32)
{ MP_ROM_QSTR(MP_QSTR_PHY_LAN8720), MP_ROM_INT(PHY_LAN8720) },
{ MP_ROM_QSTR(MP_QSTR_PHY_TLK110), MP_ROM_INT(PHY_TLK110) },
{ MP_ROM_QSTR(MP_QSTR_PHY_IP101), MP_ROM_INT(PHY_IP101) },
{ MP_ROM_QSTR(MP_QSTR_PHY_RTL8201), MP_ROM_INT(PHY_RTL8201) },
{ MP_ROM_QSTR(MP_QSTR_PHY_DP83848), MP_ROM_INT(PHY_DP83848) },
#if ESP_IDF_VERSION_MINOR >= 3
// PHY_KSZ8041 is new in ESP-IDF v4.3
{ MP_ROM_QSTR(MP_QSTR_PHY_KSZ8041), MP_ROM_INT(PHY_KSZ8041) },
#endif
// ETH Clock modes from ESP-IDF
#if !MICROPY_ESP_IDF_4
{ MP_ROM_QSTR(MP_QSTR_ETH_CLOCK_GPIO0_IN), MP_ROM_INT(ETH_CLOCK_GPIO0_IN) },
// Disabled at Aug 22nd 2018, reenabled Jan 28th 2019 in ESP-IDF
// Because we use older SDK, it's currently disabled
// { MP_ROM_QSTR(MP_QSTR_ETH_CLOCK_GPIO0_OUT), MP_ROM_INT(ETH_CLOCK_GPIO0_OUT) },
{ MP_ROM_QSTR(MP_QSTR_ETH_CLOCK_GPIO16_OUT), MP_ROM_INT(ETH_CLOCK_GPIO16_OUT) },
{ MP_ROM_QSTR(MP_QSTR_ETH_CLOCK_GPIO17_OUT), MP_ROM_INT(ETH_CLOCK_GPIO17_OUT) },
{ MP_ROM_QSTR(MP_QSTR_ETH_INITIALIZED), MP_ROM_INT(ETH_INITIALIZED)},
{ MP_ROM_QSTR(MP_QSTR_ETH_STARTED), MP_ROM_INT(ETH_STARTED)},
{ MP_ROM_QSTR(MP_QSTR_ETH_STOPPED), MP_ROM_INT(ETH_STOPPED)},
{ MP_ROM_QSTR(MP_QSTR_ETH_CONNECTED), MP_ROM_INT(ETH_CONNECTED)},
{ MP_ROM_QSTR(MP_QSTR_ETH_DISCONNECTED), MP_ROM_INT(ETH_DISCONNECTED)},
{ MP_ROM_QSTR(MP_QSTR_ETH_GOT_IP), MP_ROM_INT(ETH_GOT_IP)},
#endif
{ MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(STAT_IDLE)},

View File

@ -26,7 +26,8 @@
#ifndef MICROPY_INCLUDED_ESP32_MODNETWORK_H
#define MICROPY_INCLUDED_ESP32_MODNETWORK_H
enum { PHY_LAN8720, PHY_TLK110, PHY_IP101 };
enum { PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041 };
enum { ETH_INITIALIZED, ETH_STARTED, ETH_STOPPED, ETH_CONNECTED, ETH_DISCONNECTED, ETH_GOT_IP };
MP_DECLARE_CONST_FUN_OBJ_KW(get_lan_obj);
MP_DECLARE_CONST_FUN_OBJ_1(ppp_make_new_obj);

View File

@ -15,6 +15,9 @@ class NeoPixel:
self.pin.init(pin.OUT)
self.timing = timing
def __len__(self):
return self.n
def __setitem__(self, index, val):
offset = index * self.bpp
for i in range(self.bpp):

View File

@ -4,6 +4,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2017 "Eric Poulsen" <eric@zyxod.com>
* Copyright (c) 2021 "Tobias Eydam" <tobiaseydam@hotmail.com>
*
* Based on the ESP IDF example code which is Public Domain / CC0
*
@ -26,15 +27,19 @@
* THE SOFTWARE.
*/
#if !MICROPY_ESP_IDF_4
#include "py/runtime.h"
#include "py/mphal.h"
#include "eth_phy/phy.h"
#include "eth_phy/phy_tlk110.h"
#include "eth_phy/phy_lan8720.h"
#include "eth_phy/phy_ip101.h"
#include "tcpip_adapter.h"
#include "esp_idf_version.h"
// LAN only for ESP32 (not ESP32S2) and only for ESP-IDF v4.1 and higher
#if (ESP_IDF_VERSION_MAJOR == 4) && (ESP_IDF_VERSION_MINOR >= 1) && (CONFIG_IDF_TARGET_ESP32)
#include "esp_eth.h"
#include "esp_eth_mac.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "modnetwork.h"
@ -48,47 +53,43 @@ typedef struct _lan_if_obj_t {
int8_t phy_power_pin;
uint8_t phy_addr;
uint8_t phy_type;
eth_phy_check_link_func link_func;
eth_phy_power_enable_func power_func;
esp_eth_phy_t *phy;
esp_netif_t *eth_netif;
esp_eth_handle_t eth_handle;
} lan_if_obj_t;
const mp_obj_type_t lan_if_type;
STATIC lan_if_obj_t lan_obj = {{&lan_if_type}, ESP_IF_ETH, false, false};
STATIC uint8_t eth_status = 0;
STATIC void phy_power_enable(bool enable) {
lan_if_obj_t *self = &lan_obj;
if (self->phy_power_pin != -1) {
if (!enable) {
// Do the PHY-specific power_enable(false) function before powering down
self->power_func(false);
}
gpio_pad_select_gpio(self->phy_power_pin);
gpio_set_direction(self->phy_power_pin, GPIO_MODE_OUTPUT);
if (enable) {
gpio_set_level(self->phy_power_pin, 1);
} else {
gpio_set_level(self->phy_power_pin, 0);
}
// Allow the power up/down to take effect, min 300us
vTaskDelay(1);
if (enable) {
// Run the PHY-specific power on operations now the PHY has power
self->power_func(true);
}
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
eth_status = ETH_CONNECTED;
ESP_LOGI("ethernet", "Ethernet Link Up");
break;
case ETHERNET_EVENT_DISCONNECTED:
eth_status = ETH_DISCONNECTED;
ESP_LOGI("ethernet", "Ethernet Link Down");
break;
case ETHERNET_EVENT_START:
eth_status = ETH_STARTED;
ESP_LOGI("ethernet", "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
eth_status = ETH_STOPPED;
ESP_LOGI("ethernet", "Ethernet Stopped");
break;
case IP_EVENT_ETH_GOT_IP:
eth_status = ETH_GOT_IP;
ESP_LOGI("ethernet", "Ethernet Got IP");
break;
default:
break;
}
}
STATIC void init_lan_rmii() {
lan_if_obj_t *self = &lan_obj;
phy_rmii_configure_data_interface_pins();
phy_rmii_smi_configure_pins(self->mdc_pin, self->mdio_pin);
}
STATIC mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
lan_if_obj_t *self = &lan_obj;
@ -96,7 +97,7 @@ STATIC mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
return MP_OBJ_FROM_PTR(&lan_obj);
}
enum { ARG_id, ARG_mdc, ARG_mdio, ARG_power, ARG_phy_addr, ARG_phy_type, ARG_clock_mode };
enum { ARG_id, ARG_mdc, ARG_mdio, ARG_power, ARG_phy_addr, ARG_phy_type };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_mdc, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
@ -104,7 +105,6 @@ STATIC mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
{ MP_QSTR_power, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_phy_addr, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_phy_type, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_clock_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -123,56 +123,91 @@ STATIC mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
if (args[ARG_phy_addr].u_int < 0x00 || args[ARG_phy_addr].u_int > 0x1f) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid phy address"));
}
self->phy_addr = args[ARG_phy_addr].u_int;
if (args[ARG_phy_type].u_int != PHY_LAN8720 &&
args[ARG_phy_type].u_int != PHY_TLK110 &&
args[ARG_phy_type].u_int != PHY_IP101) {
args[ARG_phy_type].u_int != PHY_IP101 &&
args[ARG_phy_type].u_int != PHY_RTL8201 &&
#if ESP_IDF_VERSION_MINOR >= 3 // KSZ8041 is new in ESP-IDF v4.3
args[ARG_phy_type].u_int != PHY_KSZ8041 &&
#endif
args[ARG_phy_type].u_int != PHY_DP83848) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid phy type"));
}
if (args[ARG_clock_mode].u_int != -1 &&
args[ARG_clock_mode].u_int != ETH_CLOCK_GPIO0_IN &&
// Disabled due ESP-IDF (see modnetwork.c note)
// args[ARG_clock_mode].u_int != ETH_CLOCK_GPIO0_OUT &&
args[ARG_clock_mode].u_int != ETH_CLOCK_GPIO16_OUT &&
args[ARG_clock_mode].u_int != ETH_CLOCK_GPIO17_OUT) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid clock mode"));
}
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.smi_mdc_gpio_num = self->mdc_pin;
mac_config.smi_mdio_gpio_num = self->mdio_pin;
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
eth_config_t config;
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = self->phy_addr;
phy_config.reset_gpio_num = self->phy_power_pin;
self->phy = NULL;
switch (args[ARG_phy_type].u_int) {
case PHY_TLK110:
config = phy_tlk110_default_ethernet_config;
break;
case PHY_LAN8720:
config = phy_lan8720_default_ethernet_config;
self->phy = esp_eth_phy_new_lan8720(&phy_config);
break;
case PHY_IP101:
config = phy_ip101_default_ethernet_config;
self->phy = esp_eth_phy_new_ip101(&phy_config);
break;
case PHY_RTL8201:
self->phy = esp_eth_phy_new_rtl8201(&phy_config);
break;
case PHY_DP83848:
self->phy = esp_eth_phy_new_dp83848(&phy_config);
break;
case PHY_KSZ8041:
#if ESP_IDF_VERSION_MINOR >= 3 // KSZ8041 is new in ESP-IDF v4.3
self->phy = esp_eth_phy_new_ksz8041(&phy_config);
break;
#endif
default:
mp_raise_ValueError(MP_ERROR_TEXT("unknown phy"));
}
self->link_func = config.phy_check_link;
// Replace default power func with our own
self->power_func = config.phy_power_enable;
config.phy_power_enable = phy_power_enable;
config.phy_addr = args[ARG_phy_addr].u_int;
config.gpio_config = init_lan_rmii;
config.tcpip_input = tcpip_adapter_eth_input;
if (args[ARG_clock_mode].u_int != -1) {
config.clock_mode = args[ARG_clock_mode].u_int;
if (esp_netif_init() != ESP_OK) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_netif_init failed"));
}
if (esp_eth_init(&config) == ESP_OK) {
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
self->eth_netif = esp_netif_new(&cfg);
if (esp_eth_set_default_handlers(self->eth_netif) != ESP_OK) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_set_default_handlers failed (invalid parameter)"));
}
if (esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL) != ESP_OK) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_event_handler_register failed"));
}
if (esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL) != ESP_OK) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_event_handler_register failed"));
}
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, self->phy);
esp_err_t esp_err = esp_eth_driver_install(&config, &self->eth_handle);
if (esp_err == ESP_OK) {
self->active = false;
self->initialized = true;
} else {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_init() failed"));
if (esp_err == ESP_ERR_INVALID_ARG) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_driver_install failed with invalid argument"));
} else if (esp_err == ESP_ERR_NO_MEM) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_driver_install failed with no memory for driver"));
} else {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_eth_driver_install failed"));
}
}
if (esp_netif_attach(self->eth_netif, esp_eth_new_netif_glue(self->eth_handle)) != ESP_OK) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("esp_netif_attach failed"));
}
eth_status = ETH_INITIALIZED;
return MP_OBJ_FROM_PTR(&lan_obj);
}
MP_DEFINE_CONST_FUN_OBJ_KW(get_lan_obj, 0, get_lan);
@ -182,37 +217,87 @@ STATIC mp_obj_t lan_active(size_t n_args, const mp_obj_t *args) {
if (n_args > 1) {
if (mp_obj_is_true(args[1])) {
self->active = (esp_eth_enable() == ESP_OK);
self->active = (esp_eth_start(self->eth_handle) == ESP_OK);
if (!self->active) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ethernet enable failed"));
}
} else {
self->active = !(esp_eth_disable() == ESP_OK);
self->active = !(esp_eth_stop(self->eth_handle) == ESP_OK);
if (self->active) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ethernet disable failed"));
}
}
}
return mp_obj_new_bool(self->active);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lan_active_obj, 1, 2, lan_active);
STATIC mp_obj_t lan_status(mp_obj_t self_in) {
return mp_const_none;
return MP_OBJ_NEW_SMALL_INT(eth_status);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(lan_status_obj, lan_status);
STATIC mp_obj_t lan_isconnected(mp_obj_t self_in) {
lan_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
return self->active ? mp_obj_new_bool(self->link_func()) : mp_const_false;
return self->active ? mp_obj_new_bool(self->phy->get_link(self->phy) == ETH_LINK_UP) : mp_const_false;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(lan_isconnected_obj, lan_isconnected);
STATIC mp_obj_t lan_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
if (n_args != 1 && kwargs->used != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
}
lan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
#define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x)
if (kwargs->used != 0) {
for (size_t i = 0; i < kwargs->alloc; i++) {
if (mp_map_slot_is_filled(kwargs, i)) {
switch ((uintptr_t)kwargs->table[i].key) {
case QS(MP_QSTR_mac): {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(kwargs->table[i].value, &bufinfo, MP_BUFFER_READ);
if (bufinfo.len != 6) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid buffer length"));
}
esp_eth_ioctl(self->eth_handle, ETH_CMD_S_MAC_ADDR, bufinfo.buf);
break;
}
default:
break;
}
}
}
return mp_const_none;
}
if (n_args != 2) {
mp_raise_TypeError(MP_ERROR_TEXT("can query only one param"));
}
mp_obj_t val = mp_const_none;
switch ((uintptr_t)args[1]) {
case QS(MP_QSTR_mac): {
uint8_t mac[6];
esp_eth_ioctl(self->eth_handle, ETH_CMD_G_MAC_ADDR, mac);
return mp_obj_new_bytes(mac, sizeof(mac));
}
default:
mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
}
return val;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(lan_config_obj, 1, lan_config);
STATIC const mp_rom_map_elem_t lan_if_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&lan_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&lan_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&lan_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&esp_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&lan_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_ifconfig_obj) },
};
@ -224,4 +309,4 @@ const mp_obj_type_t lan_if_type = {
.locals_dict = (mp_obj_dict_t *)&lan_if_locals_dict,
};
#endif // !MICROPY_ESP_IDF_4
#endif

View File

@ -1,6 +1,5 @@
# Notes: the offset of the partition table itself is set in
# $ESPIDF/components/partition_table/Kconfig.projbuild and the
# offset of the factory/ota_0 partition is set in makeimg.py
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,

1 # Notes: the offset of the partition table itself is set in
2 # $ESPIDF/components/partition_table/Kconfig.projbuild and the # $IDF_PATH/components/partition_table/Kconfig.projbuild.
# offset of the factory/ota_0 partition is set in makeimg.py
3 # Name, Type, SubType, Offset, Size, Flags
4 nvs, data, nvs, 0x9000, 0x6000,
5 phy_init, data, phy, 0xf000, 0x1000,

View File

@ -1,8 +1,7 @@
# Notes: the offset of the partition table itself is set in
# $ESPIDF/components/partition_table/Kconfig.projbuild and the
# offset of the factory/ota_0 partition is set in makeimg.py
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x110000,
vfs, data, fat, 0x120000, 0xA0000,
factory, app, factory, 0x10000, 0x160000,
vfs, data, fat, 0x170000, 0x50000,

1 # Notes: the offset of the partition table itself is set in
2 # $ESPIDF/components/partition_table/Kconfig.projbuild and the # $IDF_PATH/components/partition_table/Kconfig.projbuild.
# offset of the factory/ota_0 partition is set in makeimg.py
3 # Name, Type, SubType, Offset, Size, Flags
4 nvs, data, nvs, 0x9000, 0x6000,
5 phy_init, data, phy, 0xf000, 0x1000,
6 factory, app, factory, 0x10000, 0x110000, factory, app, factory, 0x10000, 0x160000,
7 vfs, data, fat, 0x120000, 0xA0000, vfs, data, fat, 0x170000, 0x50000,

View File

@ -1,7 +1,6 @@
# Partition table for MicroPython with OTA support using 4MB flash
# Notes: the offset of the partition table itself is set in
# $ESPIDF/components/partition_table/Kconfig.projbuild and the
# offset of the factory/ota_0 partition is set in makeimg.py
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,

1 # Partition table for MicroPython with OTA support using 4MB flash
2 # Notes: the offset of the partition table itself is set in
3 # $ESPIDF/components/partition_table/Kconfig.projbuild and the # $IDF_PATH/components/partition_table/Kconfig.projbuild.
# offset of the factory/ota_0 partition is set in makeimg.py
4 # Name, Type, SubType, Offset, Size, Flags
5 nvs, data, nvs, 0x9000, 0x4000,
6 otadata, data, ota, 0xd000, 0x2000,

View File

@ -1,6 +1,5 @@
# Notes: the offset of the partition table itself is set in
# $ESPIDF/components/partition_table/Kconfig.projbuild and the
# offset of the factory/ota_0 partition is set in makeimg.py
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,

1 # Notes: the offset of the partition table itself is set in
2 # $ESPIDF/components/partition_table/Kconfig.projbuild and the # $IDF_PATH/components/partition_table/Kconfig.projbuild.
# offset of the factory/ota_0 partition is set in makeimg.py
3 # Name, Type, SubType, Offset, Size, Flags
4 nvs, data, nvs, 0x9000, 0x6000,
5 phy_init, data, phy, 0xf000, 0x1000,

View File

@ -0,0 +1,3 @@
import gc
gc.threshold((gc.mem_free() + gc.mem_alloc()) // 4)

View File

@ -1,3 +1,4 @@
freeze("$(BOARD_DIR)", "_boot.py", opt=3)
freeze("$(PORT_DIR)/modules", ("apa102.py", "neopixel.py", "ntptime.py", "port_diag.py"))
freeze("$(MPY_DIR)/drivers/dht", "dht.py")
freeze("$(MPY_DIR)/drivers/onewire")

View File

@ -237,7 +237,7 @@ STATIC mp_obj_t esp_scan(mp_obj_t self_in) {
while (esp_scan_list != NULL) {
// our esp_scan_cb is called via ets_loop_iter so it's safe to set the
// esp_scan_list variable to NULL without disabling interrupts
if (MP_STATE_VM(mp_pending_exception) != NULL) {
if (MP_STATE_THREAD(mp_pending_exception) != NULL) {
esp_scan_list = NULL;
mp_handle_pending(true);
}

View File

@ -15,6 +15,9 @@ class NeoPixel:
self.pin.init(pin.OUT)
self.timing = timing
def __len__(self):
return self.n
def __setitem__(self, index, val):
offset = index * self.bpp
for i in range(self.bpp):

View File

@ -1,10 +0,0 @@
--- JSBackend.cpp 2018-01-10 16:35:07.331418145 +1100
+++ JSBackend_mp_js.cpp 2018-01-10 16:40:04.804633134 +1100
@@ -4280,6 +4280,7 @@
void JSWriter::calculateNativizedVars(const Function *F) {
NativizedVars.clear();
+ return;
for (Function::const_iterator I = F->begin(), BE = F->end(); I != BE; ++I) {
auto BI = &*I;

View File

@ -6,24 +6,16 @@ QSTR_DEFS = qstrdefsport.h
include $(TOP)/py/py.mk
CC = emcc -g4
LD = emcc -g4
CC = emcc
LD = emcc
INC += -I.
INC += -I$(TOP)
INC += -I$(BUILD)
CPP = clang -E
ifdef EMSCRIPTEN
CPP += -isystem $(EMSCRIPTEN)/system/include/libc -cxx-isystem $(EMSCRIPTEN)/system/include/libcxx
endif
CFLAGS = -m32 -Wall -Werror -Wdouble-promotion -Wfloat-conversion $(INC) -std=c99 $(COPT)
LDFLAGS = -m32 -Wl,-Map=$@.map,--cref -Wl,--gc-sections
CFLAGS += -O0 -DNDEBUG
CFLAGS += -fdata-sections -ffunction-sections
CFLAGS += -std=c99 -Wall -Werror -Wdouble-promotion -Wfloat-conversion
CFLAGS += -O3 -DNDEBUG
CFLAGS += $(INC)
ifneq ($(FROZEN_MPY_DIR),)
# To use frozen bytecode, put your .py files in a subdirectory (eg frozen/) and
@ -46,12 +38,12 @@ SRC_C = \
SRC_QSTR += $(SRC_C)
OBJ =
OBJ = $(PY_O)
OBJ += $(PY_O)
OBJ += $(addprefix $(BUILD)/, $(SRC_LIB:.c=.o))
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
JSFLAGS = -O0 -s EXPORTED_FUNCTIONS="['_mp_js_init', '_mp_js_init_repl', '_mp_js_do_str', '_mp_js_process_char', '_mp_hal_get_interrupt_char', '_mp_sched_keyboard_interrupt']" -s EXTRA_EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap']" -s "BINARYEN_TRAP_MODE='clamp'" --memory-init-file 0 --js-library library.js
JSFLAGS += -s ASYNCIFY
JSFLAGS += -s EXPORTED_FUNCTIONS="['_mp_js_init', '_mp_js_init_repl', '_mp_js_do_str', '_mp_js_process_char', '_mp_hal_get_interrupt_char', '_mp_sched_keyboard_interrupt']" -s EXTRA_EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap']" -s --memory-init-file 0 --js-library library.js
all: $(BUILD)/micropython.js
@ -65,6 +57,6 @@ min: $(BUILD)/micropython.js
test: $(BUILD)/micropython.js $(TOP)/tests/run-tests.py
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
cd $(TOP)/tests && MICROPY_MICROPYTHON=../ports/javascript/node_run.sh ./run-tests.py
cd $(TOP)/tests && MICROPY_MICROPYTHON=../ports/javascript/node_run.sh ./run-tests.py -j1
include $(TOP)/py/mkrules.mk

View File

@ -7,17 +7,7 @@ Dependencies
------------
Building micropython.js bears the same requirements as the standard MicroPython
ports with the addition of Emscripten (and uglify-js for the minified file).
A standard installation of Emscripten should provide functional code, however
if memory errors are encountered it may be worthwhile to modify the tool.
`emscripten-fastcomp/lib/Target/JSBackend.cpp` may require the minor fix
found in JSBackend.patch. This patch attempts to address situations where
C code running through Emscripten is denied access to Javascript variables
leading to false-positives in the MicroPython garbage collector as variables
with pointers exclusively in Javascript will be erased prematurely.
Refer to Emscripten documentation for instructions on building Emscripten
from source.
ports with the addition of Emscripten (and uglify-js for the minified file).
Build instructions
------------------
@ -39,15 +29,15 @@ Access the repl with:
Stack size may be modified using:
$ node build/micropython.js -X stack=64K
$ node build/micropython.js -X stack=64K
Where stack size may be represented in Bytes, KiB or MiB.
MicroPython scripts may be executed using:
$ node build/micropython.js hello.py
$ node build/micropython.js hello.py
Alternatively micropython.js may by accessed by other javascript programs in node
Alternatively micropython.js may by accessed by other javascript programs in node
using the require command and the general API outlined below. For example:
```javascript
@ -85,7 +75,7 @@ demonstrates basic functionality:
```
MicroPython code execution will suspend the browser so be sure to atomize usage
within this environment. Unfortunately interrupts have not been implemented for the
within this environment. Unfortunately interrupts have not been implemented for the
browser.
Testing
@ -124,5 +114,5 @@ the repl.
mp_js_process_char(char)
```
Input character into MicroPython repl. `char` must be of type `number`. This
Input character into MicroPython repl. `char` must be of type `number`. This
will execute MicroPython code when necessary.

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George and 2017, 2018 Rami Ali
* Copyright (c) 2013-2021 Damien P. George and 2017, 2018 Rami Ali
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -36,6 +36,7 @@
#include "py/mperrno.h"
#include "lib/utils/pyexec.h"
#include "emscripten.h"
#include "library.h"
#if MICROPY_ENABLE_COMPILER
@ -70,8 +71,6 @@ int do_str(const char *src, mp_parse_input_kind_t input_kind) {
}
#endif
static char *stack_top;
int mp_js_do_str(const char *code) {
return do_str(code, MP_PARSE_FILE_INPUT);
}
@ -81,9 +80,6 @@ int mp_js_process_char(int c) {
}
void mp_js_init(int heap_size) {
int stack_dummy;
stack_top = (char *)&stack_dummy;
#if MICROPY_ENABLE_GC
char *heap = (char *)malloc(heap_size * sizeof(char));
gc_init(heap, heap + heap_size);
@ -105,15 +101,14 @@ void mp_js_init_repl() {
pyexec_event_repl_init();
}
STATIC void gc_scan_func(void *begin, void *end) {
gc_collect_root((void **)begin, (void **)end - (void **)begin + 1);
}
void gc_collect(void) {
// WARNING: This gc_collect implementation doesn't try to get root
// pointers from CPU registers, and thus may function incorrectly.
jmp_buf dummy;
if (setjmp(dummy) == 0) {
longjmp(dummy, 1);
}
gc_collect_start();
gc_collect_root((void *)stack_top, ((mp_uint_t)(void *)(&dummy + 1) - (mp_uint_t)stack_top) / sizeof(mp_uint_t));
emscripten_scan_stack(gc_scan_func);
emscripten_scan_registers(gc_scan_func);
gc_collect_end();
}

View File

@ -116,10 +116,15 @@ SRC_HAL_IMX_C += \
$(MCU_DIR)/xip/fsl_flexspi_nor_boot.c \
$(MCU_DIR)/project_template/clock_config.c \
$(MCU_DIR)/drivers/fsl_adc.c \
$(MCU_DIR)/drivers/fsl_cache.c \
$(MCU_DIR)/drivers/fsl_clock.c \
$(MCU_DIR)/drivers/fsl_dmamux.c \
$(MCU_DIR)/drivers/fsl_edma.c \
$(MCU_DIR)/drivers/fsl_gpio.c \
$(MCU_DIR)/drivers/fsl_gpt.c \
$(MCU_DIR)/drivers/fsl_common.c \
$(MCU_DIR)/drivers/fsl_lpspi.c \
$(MCU_DIR)/drivers/fsl_lpspi_edma.c \
$(MCU_DIR)/drivers/fsl_lpuart.c \
$(MCU_DIR)/drivers/fsl_flexram.c \
$(MCU_DIR)/drivers/fsl_flexspi.c \
@ -134,12 +139,15 @@ SRC_C = \
ticks.c \
tusb_port.c \
board_init.c \
dma_channel.c \
$(BOARD_DIR)/flash_config.c \
machine_adc.c \
machine_led.c \
machine_pin.c \
machine_rtc.c \
machine_spi.c \
machine_timer.c \
machine_uart.c \
mimxrt_flash.c \
modutime.c \
modmachine.c \
@ -149,7 +157,9 @@ SRC_C = \
hal/flexspi_nor_flash.c \
lib/mp-readline/readline.c \
lib/libc/string0.c \
lib/timeutils/timeutils.c \
lib/utils/gchelper_native.c \
lib/utils/mpirq.c \
lib/utils/printf.c \
lib/utils/pyexec.c \
lib/utils/stdout_helpers.c \
@ -262,13 +272,16 @@ SRC_QSTR += \
machine_led.c \
machine_pin.c \
machine_rtc.c \
machine_spi.c \
machine_timer.c \
machine_uart.c \
mimxrt_flash.c \
modutime.c \
modmachine.c \
modmimxrt.c \
moduos.c \
pin.c \
lib/utils/mpirq.c \
lib/utils/sys_stdio_mphal.c \
extmod/modonewire.c \
$(GEN_PINS_SRC) \

View File

@ -8,3 +8,28 @@
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin))
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin))
#define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1010_flexspi_nor_config.h"
#define MICROPY_HW_NUM_PIN_IRQS (2 * 32)
// Define mapping logical UART # to hardware UART #
// LPUART1 on D0/D1 -> 1
// LPUART3 on A0/D4 -> 3
// LPUART4 on D6/D7 -> 2
#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0])
#define MICROPY_HW_UART_INDEX { 0, 1, 4, 3 }
#define IOMUX_TABLE_UART \
{ IOMUXC_GPIO_10_LPUART1_TXD }, { IOMUXC_GPIO_09_LPUART1_RXD }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_08_LPUART3_TXD }, { IOMUXC_GPIO_AD_07_LPUART3_RXD }, \
{ IOMUXC_GPIO_AD_02_LPUART4_TXD }, { IOMUXC_GPIO_AD_01_LPUART4_RXD },
#define MICROPY_HW_SPI_INDEX { 1 }
#define IOMUX_TABLE_SPI \
{ IOMUXC_GPIO_AD_06_LPSPI1_SCK }, { IOMUXC_GPIO_AD_05_LPSPI1_PCS0 }, \
{ IOMUXC_GPIO_AD_04_LPSPI1_SDO }, { IOMUXC_GPIO_AD_03_LPSPI1_SDI },
#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx }
#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx }

View File

@ -34,24 +34,3 @@ PWM_CB,GPIO_05
ENC_A,GPIO_AD_05
ENC_B,GPIO_AD_06
LED_GREEN,GPIO_11
GPIO_01,GPIO_01
GPIO_02,GPIO_02
GPIO_03,GPIO_03
GPIO_04,GPIO_04
GPIO_05,GPIO_05
GPIO_06,GPIO_06
GPIO_08,GPIO_08
GPIO_09,GPIO_09
GPIO_10,GPIO_10
GPIO_11,GPIO_11
GPIO_AD_01,GPIO_AD_01
GPIO_AD_02,GPIO_AD_02
GPIO_AD_03,GPIO_AD_03
GPIO_AD_04,GPIO_AD_04
GPIO_AD_05,GPIO_AD_05
GPIO_AD_06,GPIO_AD_06
GPIO_AD_07,GPIO_AD_07
GPIO_AD_09,GPIO_AD_09
GPIO_AD_10,GPIO_AD_10
GPIO_AD_14,GPIO_AD_14
GPIO_SD_02,GPIO_SD_02

1 D0 GPIO_09
34 ENC_A GPIO_AD_05
35 ENC_B GPIO_AD_06
36 LED_GREEN GPIO_11
GPIO_01 GPIO_01
GPIO_02 GPIO_02
GPIO_03 GPIO_03
GPIO_04 GPIO_04
GPIO_05 GPIO_05
GPIO_06 GPIO_06
GPIO_08 GPIO_08
GPIO_09 GPIO_09
GPIO_10 GPIO_10
GPIO_11 GPIO_11
GPIO_AD_01 GPIO_AD_01
GPIO_AD_02 GPIO_AD_02
GPIO_AD_03 GPIO_AD_03
GPIO_AD_04 GPIO_AD_04
GPIO_AD_05 GPIO_AD_05
GPIO_AD_06 GPIO_AD_06
GPIO_AD_07 GPIO_AD_07
GPIO_AD_09 GPIO_AD_09
GPIO_AD_10 GPIO_AD_10
GPIO_AD_14 GPIO_AD_14
GPIO_SD_02 GPIO_SD_02

View File

@ -9,3 +9,42 @@
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))
#define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1020_flexspi_nor_config.h"
#define MICROPY_HW_NUM_PIN_IRQS (3 * 32)
// Define mapping logical UART # to hardware UART #
// D3/D5 LPUART1 Not usable, Since D3 is blocked.
// D0/D1 LPUART2 -> 1
// D6/D9 LPUART3 -> 2
// D10/D12 LPUART5 -> 3
// D14/D15 LPUART8 -> 4
// A0/A1 LPUART4 -> 5
#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0])
#define MICROPY_HW_UART_INDEX { 0, 2, 3, 5, 8, 4 }
#define IOMUX_TABLE_UART \
{ IOMUXC_GPIO_AD_B0_06_LPUART1_TX }, { IOMUXC_GPIO_AD_B0_07_LPUART1_RX }, \
{ IOMUXC_GPIO_AD_B1_08_LPUART2_TX }, { IOMUXC_GPIO_AD_B1_09_LPUART2_RX }, \
{ IOMUXC_GPIO_AD_B0_14_LPUART3_TX }, { IOMUXC_GPIO_AD_B0_15_LPUART3_RX }, \
{ IOMUXC_GPIO_AD_B1_10_LPUART4_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART4_RX }, \
{ IOMUXC_GPIO_AD_B0_10_LPUART5_TX }, { IOMUXC_GPIO_AD_B0_11_LPUART5_RX }, \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_SD_B1_02_LPUART8_TX }, { IOMUXC_GPIO_SD_B1_03_LPUART8_RX },
#define MICROPY_HW_SPI_INDEX { 1, 3 }
#define IOMUX_TABLE_SPI \
{ IOMUXC_GPIO_AD_B0_10_LPSPI1_SCK }, { IOMUXC_GPIO_AD_B0_11_LPSPI1_PCS0 }, \
{ IOMUXC_GPIO_AD_B0_12_LPSPI1_SDO }, { IOMUXC_GPIO_AD_B0_13_LPSPI1_SDI }, \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_12_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B1_13_LPSPI3_PCS0 }, \
{ IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B1_15_LPSPI3_SDI },
#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \
kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx }
#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \
kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx }

View File

@ -8,3 +8,36 @@
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))
#define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1050_flexspi_nor_config.h"
#define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3)
// Define mapping logical UART # to hardware UART #
// LPUART3 on D0/D1 -> 1
// LPUART2 on D7/D6 -> 2
// LPUART6 on D8/D9 -> 3
// LPUART8 on A1/A0 -> 4
#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0])
#define MICROPY_HW_UART_INDEX { 0, 3, 2, 6, 8 }
#define IOMUX_TABLE_UART \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_02_LPUART2_TX }, { IOMUXC_GPIO_AD_B1_03_LPUART2_RX }, \
{ IOMUXC_GPIO_AD_B1_06_LPUART3_TX }, { IOMUXC_GPIO_AD_B1_07_LPUART3_RX }, \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX },
#define MICROPY_HW_SPI_INDEX { 1 }
#define IOMUX_TABLE_SPI \
{ IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK }, { IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0 }, \
{ IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI },
#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \
kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx }
#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \
kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx }

View File

@ -29,23 +29,3 @@ SDI,GPIO_SD_B0_03
SDO,GPIO_SD_B0_02
CS,GPIO_SD_B0_01
LED_GREEN,GPIO_AD_B0_09
GPIO_AD_B1_07,GPIO_AD_B1_07
GPIO_AD_B1_06,GPIO_AD_B1_06
GPIO_AD_B0_11,GPIO_AD_B0_11
GPIO_AD_B1_08,GPIO_AD_B1_08
GPIO_AD_B0_09,GPIO_AD_B0_09
GPIO_AD_B0_10,GPIO_AD_B0_10
GPIO_AD_B1_02,GPIO_AD_B1_02
GPIO_AD_B1_03,GPIO_AD_B1_03
GPIO_AD_B0_03,GPIO_AD_B0_03
GPIO_AD_B0_02,GPIO_AD_B0_02
GPIO_SD_B0_01,GPIO_SD_B0_01
GPIO_SD_B0_02,GPIO_SD_B0_02
GPIO_SD_B0_03,GPIO_SD_B0_03
GPIO_SD_B0_00,GPIO_SD_B0_00
GPIO_AD_B1_01,GPIO_AD_B1_01
GPIO_AD_B1_00,GPIO_AD_B1_00
GPIO_AD_B1_10,GPIO_AD_B1_10
GPIO_AD_B1_11,GPIO_AD_B1_11
GPIO_AD_B1_04,GPIO_AD_B1_04
GPIO_AD_B1_05,GPIO_AD_B1_05

1 D0 GPIO_AD_B1_07
29 SDO GPIO_SD_B0_02
30 CS GPIO_SD_B0_01
31 LED_GREEN GPIO_AD_B0_09
GPIO_AD_B1_07 GPIO_AD_B1_07
GPIO_AD_B1_06 GPIO_AD_B1_06
GPIO_AD_B0_11 GPIO_AD_B0_11
GPIO_AD_B1_08 GPIO_AD_B1_08
GPIO_AD_B0_09 GPIO_AD_B0_09
GPIO_AD_B0_10 GPIO_AD_B0_10
GPIO_AD_B1_02 GPIO_AD_B1_02
GPIO_AD_B1_03 GPIO_AD_B1_03
GPIO_AD_B0_03 GPIO_AD_B0_03
GPIO_AD_B0_02 GPIO_AD_B0_02
GPIO_SD_B0_01 GPIO_SD_B0_01
GPIO_SD_B0_02 GPIO_SD_B0_02
GPIO_SD_B0_03 GPIO_SD_B0_03
GPIO_SD_B0_00 GPIO_SD_B0_00
GPIO_AD_B1_01 GPIO_AD_B1_01
GPIO_AD_B1_00 GPIO_AD_B1_00
GPIO_AD_B1_10 GPIO_AD_B1_10
GPIO_AD_B1_11 GPIO_AD_B1_11
GPIO_AD_B1_04 GPIO_AD_B1_04
GPIO_AD_B1_05 GPIO_AD_B1_05

View File

@ -8,3 +8,36 @@
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))
#define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1060_flexspi_nor_config.h"
#define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3)
// Define mapping logical UART # to hardware UART #
// LPUART3 on D0/D1 -> 1
// LPUART2 on D7/D6 -> 2
// LPUART6 on D8/D9 -> 3
// LPUART8 on A1/A0 -> 4
#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0])
#define MICROPY_HW_UART_INDEX { 0, 3, 2, 6, 8 }
#define IOMUX_TABLE_UART \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_02_LPUART2_TX }, { IOMUXC_GPIO_AD_B1_03_LPUART2_RX }, \
{ IOMUXC_GPIO_AD_B1_06_LPUART3_TX }, { IOMUXC_GPIO_AD_B1_07_LPUART3_RX }, \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX },
#define MICROPY_HW_SPI_INDEX { 1 }
#define IOMUX_TABLE_SPI \
{ IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK }, { IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0 }, \
{ IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI },
#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \
kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx }
#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \
kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx }

View File

@ -29,23 +29,3 @@ SDI,GPIO_SD_B0_03
SDO,GPIO_SD_B0_02
CS,GPIO_SD_B0_01
LED_GREEN,GPIO_AD_B0_09
GPIO_AD_B1_07,GPIO_AD_B1_07
GPIO_AD_B1_06,GPIO_AD_B1_06
GPIO_AD_B0_11,GPIO_AD_B0_11
GPIO_AD_B1_08,GPIO_AD_B1_08
GPIO_AD_B0_09,GPIO_AD_B0_09
GPIO_AD_B0_10,GPIO_AD_B0_10
GPIO_AD_B1_02,GPIO_AD_B1_02
GPIO_AD_B1_03,GPIO_AD_B1_03
GPIO_AD_B0_03,GPIO_AD_B0_03
GPIO_AD_B0_02,GPIO_AD_B0_02
GPIO_SD_B0_01,GPIO_SD_B0_01
GPIO_SD_B0_02,GPIO_SD_B0_02
GPIO_SD_B0_03,GPIO_SD_B0_03
GPIO_SD_B0_00,GPIO_SD_B0_00
GPIO_AD_B1_01,GPIO_AD_B1_01
GPIO_AD_B1_00,GPIO_AD_B1_00
GPIO_AD_B1_10,GPIO_AD_B1_10
GPIO_AD_B1_11,GPIO_AD_B1_11
GPIO_AD_B1_04,GPIO_AD_B1_04
GPIO_AD_B1_05,GPIO_AD_B1_05

1 D0 GPIO_AD_B1_07
29 SDO GPIO_SD_B0_02
30 CS GPIO_SD_B0_01
31 LED_GREEN GPIO_AD_B0_09
GPIO_AD_B1_07 GPIO_AD_B1_07
GPIO_AD_B1_06 GPIO_AD_B1_06
GPIO_AD_B0_11 GPIO_AD_B0_11
GPIO_AD_B1_08 GPIO_AD_B1_08
GPIO_AD_B0_09 GPIO_AD_B0_09
GPIO_AD_B0_10 GPIO_AD_B0_10
GPIO_AD_B1_02 GPIO_AD_B1_02
GPIO_AD_B1_03 GPIO_AD_B1_03
GPIO_AD_B0_03 GPIO_AD_B0_03
GPIO_AD_B0_02 GPIO_AD_B0_02
GPIO_SD_B0_01 GPIO_SD_B0_01
GPIO_SD_B0_02 GPIO_SD_B0_02
GPIO_SD_B0_03 GPIO_SD_B0_03
GPIO_SD_B0_00 GPIO_SD_B0_00
GPIO_AD_B1_01 GPIO_AD_B1_01
GPIO_AD_B1_00 GPIO_AD_B1_00
GPIO_AD_B1_10 GPIO_AD_B1_10
GPIO_AD_B1_11 GPIO_AD_B1_11
GPIO_AD_B1_04 GPIO_AD_B1_04
GPIO_AD_B1_05 GPIO_AD_B1_05

View File

@ -8,8 +8,8 @@ ivt_size = 0x00001000;
interrupts_start = 0x60002000;
interrupts_size = 0x00000400;
text_start = 0x60002400;
text_size = ((((text_start) + 1M) + (4k - 1)) & ~(4k - 1)) - (text_start); /* reserve 1M for code but align on 4k boundary */
vfs_start = (text_start) + (text_size);
vfs_start = 0x60100000;
text_size = ((vfs_start) - (text_start));
vfs_size = ((flash_end) - (vfs_start));
itcm_start = 0x00000000;
itcm_size = 0x00020000;

View File

@ -1,9 +1,41 @@
#define MICROPY_HW_BOARD_NAME "i.MX RT1064 EVK"
#define MICROPY_HW_MCU_NAME "MIMXRT1064DVL6A"
// MIMXRT1064_EVK has 1 user LED
#define MICROPY_HW_LED1_PIN (pin_GPIO_AD_B0_09)
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))
#define BOARD_FLASH_CONFIG_HEADER_H "evkmimxrt1064_flexspi_nor_config.h"
#define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3)
// Define mapping logical UART # to hardware UART #
// LPUART3 on D0/D1 -> 1
// LPUART2 on D7/D6 -> 2
// LPUART6 on D8/D9 -> 3
// LPUART8 on A1/A0 -> 4
#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0])
#define MICROPY_HW_UART_INDEX { 0, 3, 2, 6, 8 }
#define IOMUX_TABLE_UART \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_02_LPUART2_TX }, { IOMUXC_GPIO_AD_B1_03_LPUART2_RX }, \
{ IOMUXC_GPIO_AD_B1_06_LPUART3_TX }, { IOMUXC_GPIO_AD_B1_07_LPUART3_RX }, \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX },
#define MICROPY_HW_SPI_INDEX { 1 }
#define IOMUX_TABLE_SPI \
{ IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK }, { IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0 }, \
{ IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI },
#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \
kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx }
#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \
kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx }

View File

@ -29,23 +29,3 @@ SDI,GPIO_SD_B0_03
SDO,GPIO_SD_B0_02
CS,GPIO_SD_B0_01
LED_GREEN,GPIO_AD_B0_09
GPIO_AD_B1_07,GPIO_AD_B1_07
GPIO_AD_B1_06,GPIO_AD_B1_06
GPIO_AD_B0_11,GPIO_AD_B0_11
GPIO_AD_B1_08,GPIO_AD_B1_08
GPIO_AD_B0_09,GPIO_AD_B0_09
GPIO_AD_B0_10,GPIO_AD_B0_10
GPIO_AD_B1_02,GPIO_AD_B1_02
GPIO_AD_B1_03,GPIO_AD_B1_03
GPIO_AD_B0_03,GPIO_AD_B0_03
GPIO_AD_B0_02,GPIO_AD_B0_02
GPIO_SD_B0_01,GPIO_SD_B0_01
GPIO_SD_B0_02,GPIO_SD_B0_02
GPIO_SD_B0_03,GPIO_SD_B0_03
GPIO_SD_B0_00,GPIO_SD_B0_00
GPIO_AD_B1_01,GPIO_AD_B1_01
GPIO_AD_B1_00,GPIO_AD_B1_00
GPIO_AD_B1_10,GPIO_AD_B1_10
GPIO_AD_B1_11,GPIO_AD_B1_11
GPIO_AD_B1_04,GPIO_AD_B1_04
GPIO_AD_B1_05,GPIO_AD_B1_05

1 D0 GPIO_AD_B1_07
29 SDO GPIO_SD_B0_02
30 CS GPIO_SD_B0_01
31 LED_GREEN GPIO_AD_B0_09
GPIO_AD_B1_07 GPIO_AD_B1_07
GPIO_AD_B1_06 GPIO_AD_B1_06
GPIO_AD_B0_11 GPIO_AD_B0_11
GPIO_AD_B1_08 GPIO_AD_B1_08
GPIO_AD_B0_09 GPIO_AD_B0_09
GPIO_AD_B0_10 GPIO_AD_B0_10
GPIO_AD_B1_02 GPIO_AD_B1_02
GPIO_AD_B1_03 GPIO_AD_B1_03
GPIO_AD_B0_03 GPIO_AD_B0_03
GPIO_AD_B0_02 GPIO_AD_B0_02
GPIO_SD_B0_01 GPIO_SD_B0_01
GPIO_SD_B0_02 GPIO_SD_B0_02
GPIO_SD_B0_03 GPIO_SD_B0_03
GPIO_SD_B0_00 GPIO_SD_B0_00
GPIO_AD_B1_01 GPIO_AD_B1_01
GPIO_AD_B1_00 GPIO_AD_B1_00
GPIO_AD_B1_10 GPIO_AD_B1_10
GPIO_AD_B1_11 GPIO_AD_B1_11
GPIO_AD_B1_04 GPIO_AD_B1_04
GPIO_AD_B1_05 GPIO_AD_B1_05

View File

@ -8,3 +8,37 @@
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin))
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin))
#define BOARD_FLASH_CONFIG_HEADER_H "teensy40_flexspi_nor_config.h"
#define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3)
// UART config: 7 UARTs at the pins for Teensy 4.0
#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0])
#define MICROPY_HW_UART_INDEX { 0, 6, 4, 2, 3, 8, 1, 7 }
#define IOMUX_TABLE_UART \
{ IOMUXC_GPIO_AD_B0_12_LPUART1_TX }, { IOMUXC_GPIO_AD_B0_13_LPUART1_RX }, \
{ IOMUXC_GPIO_AD_B1_02_LPUART2_TX }, { IOMUXC_GPIO_AD_B1_03_LPUART2_RX }, \
{ IOMUXC_GPIO_AD_B1_06_LPUART3_TX }, { IOMUXC_GPIO_AD_B1_07_LPUART3_RX }, \
{ IOMUXC_GPIO_B1_00_LPUART4_TX }, { IOMUXC_GPIO_B1_01_LPUART4_RX }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \
{ IOMUXC_GPIO_EMC_31_LPUART7_TX }, { IOMUXC_GPIO_EMC_32_LPUART7_RX }, \
{ IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX },
#define MICROPY_HW_SPI_INDEX { 4, 3}
#define IOMUX_TABLE_SPI \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_15_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B0_03_LPSPI3_PCS0 }, \
{ IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B0_02_LPSPI3_SDI }, \
{ IOMUXC_GPIO_B0_03_LPSPI4_SCK }, { IOMUXC_GPIO_B0_00_LPSPI4_PCS0 }, \
{ IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI },
#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \
kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx }
#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \
kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx }

View File

@ -5,5 +5,3 @@ MICROPY_FLOAT_IMPL = double
deploy: $(BUILD)/firmware.hex
teensy_loader_cli --mcu=imxrt1062 -v -w $<
sleep 1
$(PYTHON) $(TOP)/tools/pyboard.py --device $(PORT) $(BOARD_DIR)/format.py

34
ports/mimxrt/boards/TEENSY41/mpconfigboard.h Executable file → Normal file
View File

@ -8,3 +8,37 @@
#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin))
#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin))
#define BOARD_FLASH_CONFIG_HEADER_H "teensy41_flexspi_nor_config.h"
#define MICROPY_HW_NUM_PIN_IRQS (4 * 32 + 3)
// UART config: 8 UARTs at the pins for Teensy 4.1
#define MICROPY_HW_UART_NUM (sizeof(uart_index_table) / sizeof(uart_index_table)[0])
#define MICROPY_HW_UART_INDEX { 0, 6, 4, 2, 3, 8, 1, 7, 5 }
#define IOMUX_TABLE_UART \
{ IOMUXC_GPIO_AD_B0_12_LPUART1_TX }, { IOMUXC_GPIO_AD_B0_13_LPUART1_RX }, \
{ IOMUXC_GPIO_AD_B1_02_LPUART2_TX }, { IOMUXC_GPIO_AD_B1_03_LPUART2_RX }, \
{ IOMUXC_GPIO_AD_B1_06_LPUART3_TX }, { IOMUXC_GPIO_AD_B1_07_LPUART3_RX }, \
{ IOMUXC_GPIO_B1_00_LPUART4_TX }, { IOMUXC_GPIO_B1_01_LPUART4_RX }, \
{ IOMUXC_GPIO_B1_12_LPUART5_TX }, { IOMUXC_GPIO_B1_13_LPUART5_RX }, \
{ IOMUXC_GPIO_AD_B0_02_LPUART6_TX }, { IOMUXC_GPIO_AD_B0_03_LPUART6_RX }, \
{ IOMUXC_GPIO_EMC_31_LPUART7_TX }, { IOMUXC_GPIO_EMC_32_LPUART7_RX }, \
{ IOMUXC_GPIO_AD_B1_10_LPUART8_TX }, { IOMUXC_GPIO_AD_B1_11_LPUART8_RX },
#define MICROPY_HW_SPI_INDEX { 4, 3, 1 }
#define IOMUX_TABLE_SPI \
{ IOMUXC_GPIO_SD_B0_00_LPSPI1_SCK }, { IOMUXC_GPIO_SD_B0_01_LPSPI1_PCS0 }, \
{ IOMUXC_GPIO_SD_B0_02_LPSPI1_SDO }, { IOMUXC_GPIO_SD_B0_03_LPSPI1_SDI }, \
{ 0 }, { 0 }, \
{ 0 }, { 0 }, \
{ IOMUXC_GPIO_AD_B1_15_LPSPI3_SCK }, { IOMUXC_GPIO_AD_B1_12_LPSPI3_PCS0 }, \
{ IOMUXC_GPIO_AD_B1_14_LPSPI3_SDO }, { IOMUXC_GPIO_AD_B1_13_LPSPI3_SDI }, \
{ IOMUXC_GPIO_B0_03_LPSPI4_SCK }, { IOMUXC_GPIO_B0_00_LPSPI4_PCS0 }, \
{ IOMUXC_GPIO_B0_02_LPSPI4_SDO }, { IOMUXC_GPIO_B0_01_LPSPI4_SDI },
#define DMA_REQ_SRC_RX { 0, kDmaRequestMuxLPSPI1Rx, kDmaRequestMuxLPSPI2Rx, \
kDmaRequestMuxLPSPI3Rx, kDmaRequestMuxLPSPI4Rx }
#define DMA_REQ_SRC_TX { 0, kDmaRequestMuxLPSPI1Tx, kDmaRequestMuxLPSPI2Tx, \
kDmaRequestMuxLPSPI3Tx, kDmaRequestMuxLPSPI4Tx }

View File

@ -0,0 +1,51 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Robert Hammelrath
*
* 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 "dma_channel.h"
// List of channel flags: true: channel used, false: channel available
static bool channel_list[32] = { true, true, true, true, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false };
// allocate_channel(): retrieve an available channel. Return the number or -1
int allocate_dma_channel(void) {
for (int i = 0; i < ARRAY_SIZE(channel_list); i++) {
if (channel_list[i] == false) { // Channel available
channel_list[i] = true;
return i;
}
}
return -1;
}
// free_channel(n): Declare channel as free
void free_dma_channel(int n) {
if (n >= 0 && n <= ARRAY_SIZE(channel_list)) {
channel_list[n] = false;
}
}

View File

@ -0,0 +1,34 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Robert Hammelrath
*
* 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_MIMXRT_DMACHANNEL_H
#define MICROPY_INCLUDED_MIMXRT_DMACHANNEL_H
#include "py/runtime.h"
int allocate_dma_channel(void);
void free_dma_channel(int n);
#endif // MICROPY_INCLUDED_MIMXRT_DMACHANNEL_H

View File

@ -4,6 +4,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2020 Philipp Ebensberger
* Copyright (c) 2021 Robert Hammelrath
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -30,8 +31,8 @@
#include "py/runtime.h"
#include "py/mphal.h"
#include "lib/utils/mpirq.h"
#include "pin.h"
#include "mphalport.h"
// Local functions
STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
@ -68,6 +69,95 @@ const mp_obj_type_t machine_pin_board_pins_obj_type = {
.locals_dict = (mp_obj_t)&machine_pin_board_pins_locals_dict,
};
STATIC const mp_irq_methods_t machine_pin_irq_methods;
static GPIO_Type *gpiobases[] = GPIO_BASE_PTRS;
STATIC const uint16_t GPIO_combined_low_irqs[] = GPIO_COMBINED_LOW_IRQS;
STATIC const uint16_t GPIO_combined_high_irqs[] = GPIO_COMBINED_HIGH_IRQS;
STATIC const uint16_t IRQ_mapping[] = {kGPIO_NoIntmode, kGPIO_IntRisingEdge, kGPIO_IntFallingEdge, kGPIO_IntRisingOrFallingEdge};
#define GET_PIN_IRQ_INDEX(gpio_nr, pin) ((gpio_nr - 1) * 32 + pin)
int GPIO_get_instance(GPIO_Type *gpio) {
int gpio_nr;
for (gpio_nr = 0; gpio_nr < ARRAY_SIZE(gpiobases); gpio_nr++) {
if (gpio == gpiobases[gpio_nr]) {
return gpio_nr;
}
}
return 0;
}
void call_handler(GPIO_Type *gpio, int gpio_nr, int pin) {
uint32_t mask = 1 << pin;
uint32_t isr = gpio->ISR & gpio->IMR;
for (int i = 0; i < 16; i++, pin++, mask <<= 1) {
// Did the ISR fire? Consider only the bits that are enabled.
if (isr & mask) {
gpio->ISR = mask; // clear the ISR flag
int index = GET_PIN_IRQ_INDEX(gpio_nr, pin);
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[index]);
if (irq != NULL) {
irq->flags = irq->trigger;
mp_irq_handler(&irq->base);
}
}
}
}
// 10 GPIO IRQ handlers, each covering 16 bits.
void GPIO1_Combined_0_15_IRQHandler(void) {
call_handler(gpiobases[1], 1, 0);
}
void GPIO1_Combined_16_31_IRQHandler(void) {
call_handler(gpiobases[1], 1, 16);
}
void GPIO2_Combined_0_15_IRQHandler(void) {
call_handler(gpiobases[2], 2, 0);
}
void GPIO2_Combined_16_31_IRQHandler(void) {
call_handler(gpiobases[2], 2, 16);
}
void GPIO3_Combined_0_15_IRQHandler(void) {
call_handler(gpiobases[3], 3, 0);
}
void GPIO3_Combined_16_31_IRQHandler(void) {
call_handler(gpiobases[3], 3, 16);
}
void GPIO4_Combined_0_15_IRQHandler(void) {
call_handler(gpiobases[4], 4, 0);
}
void GPIO4_Combined_16_31_IRQHandler(void) {
call_handler(gpiobases[4], 4, 16);
}
void GPIO5_Combined_0_15_IRQHandler(void) {
call_handler(gpiobases[5], 5, 0);
}
void GPIO5_Combined_16_31_IRQHandler(void) {
call_handler(gpiobases[5], 5, 16);
}
// Deinit all pin IRQ handlers.
void machine_pin_irq_deinit(void) {
for (int i = 0; i < ARRAY_SIZE(MP_STATE_PORT(machine_pin_irq_objects)); ++i) {
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[i]);
if (irq != NULL) {
machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent);
GPIO_PortDisableInterrupts(self->gpio, 1U << self->pin);
MP_STATE_PORT(machine_pin_irq_objects[i]) = NULL;
}
}
}
// Simplified mode setting used by the extmod modules
void machine_pin_set_mode(const machine_pin_obj_t *self, uint8_t mode) {
gpio_pin_config_t pin_config = {kGPIO_DigitalInput, 1, kGPIO_NoIntmode};
@ -234,6 +324,69 @@ STATIC mp_obj_t machine_pin_init(size_t n_args, const mp_obj_t *args, mp_map_t *
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_init);
// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False)
STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_handler, ARG_trigger, ARG_hard };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_trigger, MP_ARG_INT, {.u_int = 3} },
{ MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
};
machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Get the IRQ object.
uint32_t gpio_nr = GPIO_get_instance(self->gpio);
uint32_t index = GET_PIN_IRQ_INDEX(gpio_nr, self->pin);
if (index >= ARRAY_SIZE(MP_STATE_PORT(machine_pin_irq_objects))) {
mp_raise_ValueError(MP_ERROR_TEXT("IRQ not supported on given Pin"));
}
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[index]);
// Allocate the IRQ object if it doesn't already exist.
if (irq == NULL) {
irq = m_new_obj(machine_pin_irq_obj_t);
irq->base.base.type = &mp_irq_type;
irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods;
irq->base.parent = MP_OBJ_FROM_PTR(self);
irq->base.handler = mp_const_none;
irq->base.ishard = false;
MP_STATE_PORT(machine_pin_irq_objects[index]) = irq;
}
if (n_args > 1 || kw_args->used != 0) {
// Configure IRQ.
uint32_t irq_num = self->pin < 16 ? GPIO_combined_low_irqs[gpio_nr] : GPIO_combined_high_irqs[gpio_nr];
// Disable all IRQs from the affected source while data is updated.
DisableIRQ(irq_num);
GPIO_PortDisableInterrupts(self->gpio, 1U << self->pin);
// Update IRQ data.
irq->base.handler = args[ARG_handler].u_obj;
irq->base.ishard = args[ARG_hard].u_bool;
irq->flags = 0;
if (args[ARG_trigger].u_int >= ARRAY_SIZE(IRQ_mapping)) {
mp_raise_ValueError(MP_ERROR_TEXT("IRQ mode not supported"));
}
irq->trigger = IRQ_mapping[args[ARG_trigger].u_int];
// Enable IRQ if a handler is given.
if (args[ARG_handler].u_obj != mp_const_none) {
// Set the pin mode
GPIO_PinSetInterruptConfig(self->gpio, self->pin, irq->trigger);
// Enable the specific Pin interrupt
GPIO_PortEnableInterrupts(self->gpio, 1U << self->pin);
}
// Enable LEVEL1 interrupt again
EnableIRQ(irq_num);
}
return MP_OBJ_FROM_PTR(irq);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq);
STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
// instance methods
{ MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_off_obj) },
@ -242,6 +395,7 @@ STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_on_obj) },
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) },
// class attributes
{ MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&machine_pin_board_pins_obj_type) },
{ MP_ROM_QSTR(MP_QSTR_cpu), MP_ROM_PTR(&machine_pin_cpu_pins_obj_type) },
@ -265,6 +419,9 @@ STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_POWER_5), MP_ROM_INT(PIN_DRIVE_POWER_5) }, // R0/6
{ MP_ROM_QSTR(MP_QSTR_POWER_6), MP_ROM_INT(PIN_DRIVE_POWER_6) }, // R0/7
{ MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(2) },
};
STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
@ -285,3 +442,37 @@ const mp_obj_type_t machine_pin_af_type = {
.make_new = mp_pin_make_new,
.locals_dict = (mp_obj_dict_t *)&machine_pin_locals_dict,
};
STATIC mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint32_t gpio_nr = GPIO_get_instance(self->gpio);
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[GET_PIN_IRQ_INDEX(gpio_nr, self->pin)]);
uint32_t irq_num = self->pin < 16 ? GPIO_combined_low_irqs[gpio_nr] : GPIO_combined_high_irqs[gpio_nr];
DisableIRQ(irq_num);
irq->flags = 0;
irq->trigger = new_trigger;
// Configure the interrupt.
GPIO_PinSetInterruptConfig(self->gpio, self->pin, irq->trigger);
// Enable LEVEL1 interrupt.
EnableIRQ(irq_num);
// Enable the specific pin interrupt.
GPIO_PortEnableInterrupts(self->gpio, 1U << self->pin);
return 0;
}
STATIC mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint32_t gpio_nr = GPIO_get_instance(self->gpio);
machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_objects[GET_PIN_IRQ_INDEX(gpio_nr, self->pin)]);
if (info_type == MP_IRQ_INFO_FLAGS) {
return irq->flags;
} else if (info_type == MP_IRQ_INFO_TRIGGERS) {
return irq->trigger;
}
return 0;
}
STATIC const mp_irq_methods_t machine_pin_irq_methods = {
.trigger = machine_pin_irq_trigger,
.info = machine_pin_irq_info,
};

View File

@ -26,6 +26,7 @@
*/
#include "py/runtime.h"
#include "lib/timeutils/timeutils.h"
#include "modmachine.h"
#include "ticks.h"
#include "fsl_snvs_lp.h"
@ -37,13 +38,7 @@ typedef struct _machine_rtc_obj_t {
// Singleton RTC object.
STATIC const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}};
// Calculate the weekday from the date.
// The result is zero based with 0 = Sunday.
// by Michael Keith and Tom Craver, 1990.
static int calc_weekday(int y, int m, int d) {
return (d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) % 7;
}
uint32_t us_offset = 0;
STATIC mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
// Check arguments.
@ -66,11 +61,11 @@ STATIC mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args)
mp_obj_new_int(srtc_date.year),
mp_obj_new_int(srtc_date.month),
mp_obj_new_int(srtc_date.day),
mp_obj_new_int(timeutils_calc_weekday(srtc_date.year, srtc_date.month, srtc_date.day)),
mp_obj_new_int(srtc_date.hour),
mp_obj_new_int(srtc_date.minute),
mp_obj_new_int(srtc_date.second),
mp_obj_new_int(ticks_us64() % 1000000),
mp_const_none,
mp_obj_new_int((ticks_us64() + us_offset) % 1000000),
};
return mp_obj_new_tuple(8, tuple);
} else {
@ -84,12 +79,14 @@ STATIC mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args)
srtc_date.year = year >= 100 ? year : year + 2000; // allow 21 for 2021
srtc_date.month = mp_obj_get_int(items[1]);
srtc_date.day = mp_obj_get_int(items[2]);
srtc_date.hour = mp_obj_get_int(items[3]);
srtc_date.minute = mp_obj_get_int(items[4]);
srtc_date.second = mp_obj_get_int(items[5]);
// Ignore weekday at items[3]
srtc_date.hour = mp_obj_get_int(items[4]);
srtc_date.minute = mp_obj_get_int(items[5]);
srtc_date.second = mp_obj_get_int(items[6]);
if (SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date) != kStatus_Success) {
mp_raise_ValueError(NULL);
}
us_offset = (1000000 + mp_obj_get_int(items[7]) - ticks_us64() % 1000000) % 1000000;
return mp_const_none;
}
@ -101,7 +98,21 @@ STATIC mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) {
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime);
STATIC mp_obj_t machine_rtc_now(mp_obj_t self_in) {
return machine_rtc_datetime_helper(1, &self_in);
// Get date and time in CPython order.
snvs_lp_srtc_datetime_t srtc_date;
SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date);
mp_obj_t tuple[8] = {
mp_obj_new_int(srtc_date.year),
mp_obj_new_int(srtc_date.month),
mp_obj_new_int(srtc_date.day),
mp_obj_new_int(srtc_date.hour),
mp_obj_new_int(srtc_date.minute),
mp_obj_new_int(srtc_date.second),
mp_obj_new_int((ticks_us64() + us_offset) % 1000000),
mp_const_none,
};
return mp_obj_new_tuple(8, tuple);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_rtc_now_obj, machine_rtc_now);
@ -112,16 +123,6 @@ STATIC mp_obj_t machine_rtc_init(mp_obj_t self_in, mp_obj_t date) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(machine_rtc_init_obj, machine_rtc_init);
STATIC mp_obj_t machine_rtc_weekday(mp_obj_t self_in) {
(void)self_in; // unused
int day;
snvs_lp_srtc_datetime_t srtc_date;
SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date);
day = calc_weekday(srtc_date.year, srtc_date.month, srtc_date.day);
return MP_OBJ_NEW_SMALL_INT((day + 6) % 7);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_rtc_weekday_obj, machine_rtc_weekday);
// calibration(cal)
// When the argument is a number in the range [-16 to 15], set the calibration value.
STATIC mp_obj_t machine_rtc_calibration(mp_obj_t self_in, mp_obj_t cal_in) {
@ -143,7 +144,6 @@ STATIC const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_rtc_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&machine_rtc_datetime_obj) },
{ MP_ROM_QSTR(MP_QSTR_now), MP_ROM_PTR(&machine_rtc_now_obj) },
{ MP_ROM_QSTR(MP_QSTR_weekday), MP_ROM_PTR(&machine_rtc_weekday_obj) },
{ MP_ROM_QSTR(MP_QSTR_calibration), MP_ROM_PTR(&machine_rtc_calibration_obj) },
};
STATIC MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table);

334
ports/mimxrt/machine_spi.c Normal file
View File

@ -0,0 +1,334 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Damien P. George
* Copyright (c) 2021 Robert Hammelrath
*
* 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 "py/runtime.h"
#include "py/mphal.h"
#include "py/mperrno.h"
#include "extmod/machine_spi.h"
#include "modmachine.h"
#include "dma_channel.h"
#include "fsl_cache.h"
#include "fsl_dmamux.h"
#include "fsl_iomuxc.h"
#include "fsl_lpspi.h"
#include "fsl_lpspi_edma.h"
#define DEFAULT_SPI_BAUDRATE (1000000)
#define DEFAULT_SPI_POLARITY (0)
#define DEFAULT_SPI_PHASE (0)
#define DEFAULT_SPI_BITS (8)
#define DEFAULT_SPI_FIRSTBIT (kLPSPI_MsbFirst)
#define DEFAULT_SPI_DRIVE (6)
#define CLOCK_DIVIDER (1)
#define MICROPY_HW_SPI_NUM MP_ARRAY_SIZE(spi_index_table)
#define SCK (iomux_table[index])
#define CS0 (iomux_table[index + 1])
#define SDO (iomux_table[index + 2])
#define SDI (iomux_table[index + 3])
typedef struct _machine_spi_obj_t {
mp_obj_base_t base;
uint8_t spi_id;
uint8_t mode;
uint8_t spi_hw_id;
bool transfer_busy;
LPSPI_Type *spi_inst;
lpspi_master_config_t *master_config;
} machine_spi_obj_t;
typedef struct _iomux_table_t {
uint32_t muxRegister;
uint32_t muxMode;
uint32_t inputRegister;
uint32_t inputDaisy;
uint32_t configRegister;
} iomux_table_t;
STATIC const uint8_t spi_index_table[] = MICROPY_HW_SPI_INDEX;
STATIC LPSPI_Type *spi_base_ptr_table[] = LPSPI_BASE_PTRS;
static const iomux_table_t iomux_table[] = {
IOMUX_TABLE_SPI
};
static uint16_t dma_req_src_rx[] = DMA_REQ_SRC_RX;
static uint16_t dma_req_src_tx[] = DMA_REQ_SRC_TX;
bool lpspi_set_iomux(int8_t spi, uint8_t drive) {
int index = (spi - 1) * 4;
if (SCK.muxRegister != 0) {
IOMUXC_SetPinMux(SCK.muxRegister, SCK.muxMode, SCK.inputRegister, SCK.inputDaisy, SCK.configRegister, 0U);
IOMUXC_SetPinConfig(SCK.muxRegister, SCK.muxMode, SCK.inputRegister, SCK.inputDaisy, SCK.configRegister,
0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT);
IOMUXC_SetPinMux(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister, 0U);
IOMUXC_SetPinConfig(CS0.muxRegister, CS0.muxMode, CS0.inputRegister, CS0.inputDaisy, CS0.configRegister,
0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT);
IOMUXC_SetPinMux(SDO.muxRegister, SDO.muxMode, SDO.inputRegister, SDO.inputDaisy, SDO.configRegister, 0U);
IOMUXC_SetPinConfig(SDO.muxRegister, SDO.muxMode, SDO.inputRegister, SDO.inputDaisy, SDO.configRegister,
0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT);
IOMUXC_SetPinMux(SDI.muxRegister, SDI.muxMode, SDI.inputRegister, SDI.inputDaisy, SDI.configRegister, 0U);
IOMUXC_SetPinConfig(SDI.muxRegister, SDI.muxMode, SDI.inputRegister, SDI.inputDaisy, SDI.configRegister,
0x1080u | drive << IOMUXC_SW_PAD_CTL_PAD_DSE_SHIFT);
return true;
} else {
return false;
}
}
STATIC void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
static const char *firstbit_str[] = {"MSB", "LSB"};
machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "SPI(%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, firstbit=%s, gap_ns=%d)",
self->spi_id, self->master_config->baudRate, self->master_config->cpol,
self->master_config->cpha, self->master_config->bitsPerFrame,
firstbit_str[self->master_config->direction], self->master_config->betweenTransferDelayInNanoSec);
}
mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_gap_ns, ARG_drive };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_SPI_BAUDRATE} },
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_POLARITY} },
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_PHASE} },
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_BITS} },
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_FIRSTBIT} },
{ MP_QSTR_gap_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_DRIVE} },
};
static bool clk_init = true;
// Parse the arguments.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Get the SPI bus id.
int spi_id = mp_obj_get_int(args[ARG_id].u_obj);
if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(spi_index_table)) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id);
}
// Get peripheral object.
uint8_t spi_hw_id = spi_index_table[spi_id]; // the hw spi number 1..n
machine_spi_obj_t *self = m_new_obj(machine_spi_obj_t);
self->base.type = &machine_spi_type;
self->spi_id = spi_id;
self->spi_inst = spi_base_ptr_table[spi_hw_id];
self->spi_hw_id = spi_hw_id;
uint8_t drive = args[ARG_drive].u_int;
if (drive < 1 || drive > 7) {
drive = DEFAULT_SPI_DRIVE;
}
if (clk_init) {
clk_init = false;
/*Set clock source for LPSPI*/
CLOCK_SetMux(kCLOCK_LpspiMux, 1); // Clock source is kCLOCK_Usb1PllPfd1Clk
CLOCK_SetDiv(kCLOCK_LpspiDiv, CLOCK_DIVIDER);
}
lpspi_set_iomux(spi_index_table[spi_id], drive);
LPSPI_Reset(self->spi_inst);
LPSPI_Enable(self->spi_inst, false); // Disable first before new settings are applies
self->master_config = m_new_obj(lpspi_master_config_t);
LPSPI_MasterGetDefaultConfig(self->master_config);
// Initialise the SPI peripheral.
self->master_config->baudRate = args[ARG_baudrate].u_int;
self->master_config->betweenTransferDelayInNanoSec = 1000000000 / self->master_config->baudRate * 2;
self->master_config->cpol = args[ARG_polarity].u_int;
self->master_config->cpha = args[ARG_phase].u_int;
self->master_config->bitsPerFrame = args[ARG_bits].u_int;
self->master_config->direction = args[ARG_firstbit].u_int;
if (args[ARG_gap_ns].u_int != -1) {
self->master_config->betweenTransferDelayInNanoSec = args[ARG_gap_ns].u_int;
}
LPSPI_MasterInit(self->spi_inst, self->master_config, CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (CLOCK_DIVIDER + 1));
return MP_OBJ_FROM_PTR(self);
}
STATIC void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_gap_ns };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_gap_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
// Parse the arguments.
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Reconfigure the baudrate if requested.
if (args[ARG_baudrate].u_int != -1) {
self->master_config->baudRate = args[ARG_baudrate].u_int;
self->master_config->betweenTransferDelayInNanoSec = 1000000000 / self->master_config->baudRate * 2;
}
// Reconfigure the format if requested.
if (args[ARG_polarity].u_int != -1) {
self->master_config->cpol = args[ARG_polarity].u_int;
}
if (args[ARG_phase].u_int != -1) {
self->master_config->cpha = args[ARG_phase].u_int;
}
if (args[ARG_bits].u_int != -1) {
self->master_config->bitsPerFrame = args[ARG_bits].u_int;
}
if (args[ARG_firstbit].u_int != -1) {
self->master_config->direction = args[ARG_firstbit].u_int;
}
if (args[ARG_gap_ns].u_int != -1) {
self->master_config->betweenTransferDelayInNanoSec = args[ARG_gap_ns].u_int;
}
LPSPI_Enable(self->spi_inst, false); // Disable first before new settings are applies
LPSPI_MasterInit(self->spi_inst, self->master_config, CLOCK_GetFreq(kCLOCK_Usb1PllPfd0Clk) / (CLOCK_DIVIDER + 1));
}
void LPSPI_EDMAMasterCallback(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, status_t status, void *self_in) {
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
self->transfer_busy = false;
}
STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
// Use DMA for large transfers if channels are available
const size_t dma_min_size_threshold = 16; // That's the FIFO size
int chan_tx = -1;
int chan_rx = -1;
if (len >= dma_min_size_threshold) {
// Use two DMA channels to service the two FIFOs
chan_rx = allocate_dma_channel();
chan_tx = allocate_dma_channel();
}
bool use_dma = chan_rx >= 0 && chan_tx >= 0;
if (use_dma) {
edma_config_t userConfig;
/* DMA MUX init*/
DMAMUX_Init(DMAMUX);
DMAMUX_SetSource(DMAMUX, chan_rx, dma_req_src_rx[self->spi_hw_id]); // ## SPIn source
DMAMUX_EnableChannel(DMAMUX, chan_rx);
DMAMUX_SetSource(DMAMUX, chan_tx, dma_req_src_tx[self->spi_hw_id]);
DMAMUX_EnableChannel(DMAMUX, chan_tx);
EDMA_GetDefaultConfig(&userConfig);
EDMA_Init(DMA0, &userConfig);
lpspi_master_edma_handle_t g_master_edma_handle;
edma_handle_t lpspiEdmaMasterRxRegToRxDataHandle;
edma_handle_t lpspiEdmaMasterTxDataToTxRegHandle;
// Set up lpspi EDMA master
EDMA_CreateHandle(&(lpspiEdmaMasterRxRegToRxDataHandle), DMA0, chan_rx);
EDMA_CreateHandle(&(lpspiEdmaMasterTxDataToTxRegHandle), DMA0, chan_tx);
LPSPI_MasterTransferCreateHandleEDMA(self->spi_inst, &g_master_edma_handle, LPSPI_EDMAMasterCallback, self,
&lpspiEdmaMasterRxRegToRxDataHandle,
&lpspiEdmaMasterTxDataToTxRegHandle);
// Start master transfer
lpspi_transfer_t masterXfer;
masterXfer.txData = (uint8_t *)src;
masterXfer.rxData = (uint8_t *)dest;
masterXfer.dataSize = len;
masterXfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap;
// Reconfigure the TCR, required after switch between DMA vs. non-DMA
LPSPI_Enable(self->spi_inst, false); // Disable first before new settings are applied
self->spi_inst->TCR = LPSPI_TCR_CPOL(self->master_config->cpol) | LPSPI_TCR_CPHA(self->master_config->cpha) |
LPSPI_TCR_LSBF(self->master_config->direction) | LPSPI_TCR_FRAMESZ(self->master_config->bitsPerFrame - 1) |
(self->spi_inst->TCR & LPSPI_TCR_PRESCALE_MASK) | LPSPI_TCR_PCS(self->master_config->whichPcs);
LPSPI_Enable(self->spi_inst, true);
self->transfer_busy = true;
if (dest) {
L1CACHE_DisableDCache();
} else if (src) {
DCACHE_CleanByRange((uint32_t)src, len);
}
LPSPI_MasterTransferEDMA(self->spi_inst, &g_master_edma_handle, &masterXfer);
while (self->transfer_busy) {
MICROPY_EVENT_POLL_HOOK
}
L1CACHE_EnableDCache();
}
// Release DMA channels, even if never allocated.
if (chan_rx >= 0) {
free_dma_channel(chan_rx);
}
if (chan_tx >= 0) {
free_dma_channel(chan_tx);
}
if (!use_dma) {
// Reconfigure the TCR, required after switch between DMA vs. non-DMA
LPSPI_Enable(self->spi_inst, false); // Disable first before new settings are applied
self->spi_inst->TCR = LPSPI_TCR_CPOL(self->master_config->cpol) | LPSPI_TCR_CPHA(self->master_config->cpha) |
LPSPI_TCR_LSBF(self->master_config->direction) | LPSPI_TCR_FRAMESZ(self->master_config->bitsPerFrame - 1) |
(self->spi_inst->TCR & LPSPI_TCR_PRESCALE_MASK) | LPSPI_TCR_PCS(self->master_config->whichPcs);
LPSPI_Enable(self->spi_inst, true);
lpspi_transfer_t masterXfer;
masterXfer.txData = (uint8_t *)src;
masterXfer.rxData = (uint8_t *)dest;
masterXfer.dataSize = len;
masterXfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap;
LPSPI_MasterTransferBlocking(self->spi_inst, &masterXfer);
}
}
STATIC const mp_machine_spi_p_t machine_spi_p = {
.init = machine_spi_init,
.transfer = machine_spi_transfer,
};
const mp_obj_type_t machine_spi_type = {
{ &mp_type_type },
.name = MP_QSTR_SPI,
.print = machine_spi_print,
.make_new = machine_spi_make_new,
.protocol = &machine_spi_p,
.locals_dict = (mp_obj_dict_t *)&mp_machine_spi_locals_dict,
};

465
ports/mimxrt/machine_uart.c Normal file
View File

@ -0,0 +1,465 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020-2021 Damien P. George
* Copyright (c) 2021 Robert Hammelrath
*
* 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 "py/runtime.h"
#include "py/stream.h"
#include "py/mphal.h"
#include "ticks.h"
#include "fsl_common.h"
#include "fsl_lpuart.h"
#include "fsl_iomuxc.h"
#define DEFAULT_UART_BAUDRATE (115200)
#define DEFAULT_BUFFER_SIZE (256)
#define MIN_BUFFER_SIZE (32)
#define MAX_BUFFER_SIZE (32766)
#define UART_INVERT_TX (1)
#define UART_INVERT_RX (2)
#define UART_INVERT_MASK (UART_INVERT_TX | UART_INVERT_RX)
typedef struct _machine_uart_obj_t {
mp_obj_base_t base;
struct _lpuart_handle handle;
lpuart_config_t config;
LPUART_Type *lpuart;
uint16_t timeout; // timeout waiting for first char (in ms)
uint16_t timeout_char; // timeout waiting between chars (in ms)
uint8_t id;
uint8_t invert;
uint16_t tx_status;
uint8_t *txbuf;
uint16_t txbuf_len;
bool new;
} machine_uart_obj_t;
typedef struct _iomux_table_t {
uint32_t muxRegister;
uint32_t muxMode;
uint32_t inputRegister;
uint32_t inputDaisy;
uint32_t configRegister;
} iomux_table_t;
extern const mp_obj_type_t machine_uart_type;
STATIC const uint8_t uart_index_table[] = MICROPY_HW_UART_INDEX;
STATIC LPUART_Type *uart_base_ptr_table[] = LPUART_BASE_PTRS;
static const iomux_table_t iomux_table_uart[] = {
IOMUX_TABLE_UART
};
STATIC const char *_parity_name[] = {"None", "", "0", "1"}; // Is defined as 0, 2, 3
STATIC const char *_invert_name[] = {"None", "INV_TX", "INV_RX", "INV_TX|INV_RX"};
#define RX (iomux_table_uart[index + 1])
#define TX (iomux_table_uart[index])
bool lpuart_set_iomux(int8_t uart) {
int index = (uart - 1) * 2;
if (TX.muxRegister != 0) {
IOMUXC_SetPinMux(TX.muxRegister, TX.muxMode, TX.inputRegister, TX.inputDaisy, TX.configRegister, 0U);
IOMUXC_SetPinConfig(TX.muxRegister, TX.muxMode, TX.inputRegister, TX.inputDaisy, TX.configRegister, 0x10B0u);
IOMUXC_SetPinMux(RX.muxRegister, RX.muxMode, RX.inputRegister, RX.inputDaisy, RX.configRegister, 0U);
IOMUXC_SetPinConfig(RX.muxRegister, RX.muxMode, RX.inputRegister, RX.inputDaisy, RX.configRegister, 0x10B0u);
return true;
} else {
return false;
}
}
uint32_t UART_SrcFreq(void) {
uint32_t freq;
// To make it simple, we assume default PLL and divider settings, and the
// only variable from application is use PLL3 source or OSC source.
if (CLOCK_GetMux(kCLOCK_UartMux) == 0) { // PLL3 div6 80M
freq = (CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6U) / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U);
} else {
freq = CLOCK_GetOscFreq() / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1U);
}
return freq;
}
void LPUART_UserCallback(LPUART_Type *base, lpuart_handle_t *handle, status_t status, void *userData) {
machine_uart_obj_t *self = userData;
if (kStatus_LPUART_TxIdle == status) {
self->tx_status = kStatus_LPUART_TxIdle;
}
if (kStatus_LPUART_RxRingBufferOverrun == status) {
; // Ringbuffer full, deassert RTS if flow control is enabled
}
}
STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, "
"rxbuf=%d, txbuf=%d, timeout=%u, timeout_char=%u, invert=%s)",
self->id, self->config.baudRate_Bps, 8 - self->config.dataBitsCount,
_parity_name[self->config.parityMode], self->config.stopBitCount + 1,
self->handle.rxRingBufferSize, self->txbuf_len, self->timeout, self->timeout_char,
_invert_name[self->invert]);
}
STATIC mp_obj_t machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop,
ARG_timeout, ARG_timeout_char, ARG_invert, ARG_rxbuf, ARG_txbuf};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_bits, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} },
{ MP_QSTR_stop, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
// Parse args
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Set baudrate if configured.
if (args[ARG_baudrate].u_int > 0) {
self->config.baudRate_Bps = args[ARG_baudrate].u_int;
}
// Set bits if configured.
if (args[ARG_bits].u_int > 0) {
self->config.dataBitsCount = 8 - args[ARG_bits].u_int;
}
// Set parity if configured.
if (args[ARG_parity].u_obj != MP_OBJ_NEW_SMALL_INT(-1)) {
if (args[ARG_parity].u_obj == mp_const_none) {
self->config.parityMode = kLPUART_ParityDisabled;
} else if (mp_obj_get_int(args[ARG_parity].u_obj) & 1) {
self->config.parityMode = kLPUART_ParityOdd;
} else {
self->config.parityMode = kLPUART_ParityEven;
}
}
// Set stop bits if configured.
if (args[ARG_stop].u_int > 0) {
self->config.stopBitCount = args[ARG_stop].u_int - 1;
}
// Set timeout if configured.
if (args[ARG_timeout].u_int >= 0) {
self->timeout = args[ARG_timeout].u_int;
}
// Set timeout_char if configured.
if (args[ARG_timeout_char].u_int >= 0) {
self->timeout_char = args[ARG_timeout_char].u_int;
}
// Set line inversion if configured.
if (args[ARG_invert].u_int >= 0) {
if (args[ARG_invert].u_int & ~UART_INVERT_MASK) {
mp_raise_ValueError(MP_ERROR_TEXT("bad inversion mask"));
}
self->invert = args[ARG_invert].u_int;
}
self->tx_status = kStatus_LPUART_TxIdle;
self->config.enableTx = true;
self->config.enableRx = true;
// Set the RX buffer size if configured.
size_t rxbuf_len = DEFAULT_BUFFER_SIZE;
if (args[ARG_rxbuf].u_int > 0) {
rxbuf_len = args[ARG_rxbuf].u_int;
if (rxbuf_len < MIN_BUFFER_SIZE) {
rxbuf_len = MIN_BUFFER_SIZE;
} else if (rxbuf_len > MAX_BUFFER_SIZE) {
mp_raise_ValueError(MP_ERROR_TEXT("rxbuf too large"));
}
}
// Set the TX buffer size if configured.
size_t txbuf_len = DEFAULT_BUFFER_SIZE;
if (args[ARG_txbuf].u_int > 0) {
txbuf_len = args[ARG_txbuf].u_int;
if (txbuf_len < MIN_BUFFER_SIZE) {
txbuf_len = MIN_BUFFER_SIZE;
} else if (txbuf_len > MAX_BUFFER_SIZE) {
mp_raise_ValueError(MP_ERROR_TEXT("txbuf too large"));
}
}
// Initialise the UART peripheral if any arguments given, or it was not initialised previously.
if (n_args > 1 || self->new) {
self->new = false;
// may be obsolete
if (self->config.baudRate_Bps == 0) {
self->config.baudRate_Bps = DEFAULT_UART_BAUDRATE;
}
// Make sure timeout_char is at least as long as a whole character (13 bits to be safe).
uint32_t min_timeout_char = 13000 / self->config.baudRate_Bps + 1;
if (self->timeout_char < min_timeout_char) {
self->timeout_char = min_timeout_char;
}
LPUART_Init(self->lpuart, &self->config, UART_SrcFreq()); // ??
LPUART_TransferCreateHandle(self->lpuart, &self->handle, LPUART_UserCallback, self);
uint8_t *buffer = m_new(uint8_t, rxbuf_len + 1);
LPUART_TransferStartRingBuffer(self->lpuart, &self->handle, buffer, rxbuf_len);
self->txbuf = m_new(uint8_t, txbuf_len); // Allocate the TX buffer.
self->txbuf_len = txbuf_len;
// The Uart supports inverting, but not the fsl API, so it has to coded directly
// And it has to be done after LPUART_Init.
if (self->invert & UART_INVERT_RX) {
LPUART_EnableRx(self->lpuart, false);
self->lpuart->STAT |= 1 << LPUART_STAT_RXINV_SHIFT;
LPUART_EnableRx(self->lpuart, true);
}
if (self->invert & UART_INVERT_TX) {
LPUART_EnableTx(self->lpuart, false);
self->lpuart->CTRL |= 1 << LPUART_CTRL_TXINV_SHIFT;
LPUART_EnableTx(self->lpuart, true);
}
// Send long break; drop that code for a shorter break duration
LPUART_EnableTx(self->lpuart, false);
self->lpuart->STAT |= 1 << LPUART_STAT_BRK13_SHIFT;
LPUART_EnableTx(self->lpuart, true);
// Allocate the TX ring buffer. Not used yet, but maybe later.
// ringbuf_alloc(&(self->write_buffer), txbuf_len + 1);
// MP_STATE_PORT(rp2_uart_tx_buffer[uart_id]) = self->write_buffer.buf;
}
return MP_OBJ_FROM_PTR(self);
}
STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
// Get UART bus.
int uart_id = mp_obj_get_int(args[0]);
if (uart_id < 1 || uart_id > MICROPY_HW_UART_NUM) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id);
}
// Create the UART object and fill it with defaults.
uint8_t uart_hw_id = uart_index_table[uart_id]; // the hw uart number 1..n
machine_uart_obj_t *self = m_new_obj(machine_uart_obj_t);
self->base.type = &machine_uart_type;
self->id = uart_id;
self->lpuart = uart_base_ptr_table[uart_hw_id];
self->invert = false;
self->timeout = 1;
self->timeout_char = 1;
self->new = true;
LPUART_GetDefaultConfig(&self->config);
// Configure board-specific pin MUX based on the hardware device number.
lpuart_set_iomux(uart_hw_id);
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
return machine_uart_init_helper(self, n_args - 1, args + 1, &kw_args);
}
// uart.init(baud, [kwargs])
STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
return machine_uart_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_uart_init_obj, 1, machine_uart_init);
STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
size_t count = LPUART_TransferGetRxRingBufferLength(self->lpuart, &self->handle);
return MP_OBJ_NEW_SMALL_INT(count);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any);
STATIC mp_obj_t machine_uart_sendbreak(mp_obj_t self_in) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
self->lpuart->CTRL |= 1 << LPUART_CTRL_SBK_SHIFT; // Set SBK bit
self->lpuart->CTRL &= ~LPUART_CTRL_SBK_MASK; // Clear SBK bit
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_sendbreak_obj, machine_uart_sendbreak);
STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_uart_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_sendbreak), MP_ROM_PTR(&machine_uart_sendbreak_obj) },
{ MP_ROM_QSTR(MP_QSTR_INV_TX), MP_ROM_INT(UART_INVERT_TX) },
{ MP_ROM_QSTR(MP_QSTR_INV_RX), MP_ROM_INT(UART_INVERT_RX) },
};
STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table);
STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint64_t t = ticks_us64() + (uint64_t)self->timeout * 1000;
uint64_t timeout_char_us = (uint64_t)self->timeout_char * 1000;
lpuart_transfer_t xfer;
uint8_t *dest = buf_in;
size_t avail;
size_t nget;
for (size_t received = 0; received < size;) {
// Wait for the first/next character.
while ((avail = LPUART_TransferGetRxRingBufferLength(self->lpuart, &self->handle)) <= 0) {
if (ticks_us64() > t) { // timed out
if (received <= 0) {
*errcode = MP_EAGAIN;
return MP_STREAM_ERROR;
} else {
return received;
}
}
MICROPY_EVENT_POLL_HOOK
}
// Get as many bytes as possible to meet the need.
nget = avail < (size - received) ? avail : size - received;
xfer.data = dest + received;
xfer.dataSize = nget;
LPUART_TransferReceiveNonBlocking(self->lpuart, &self->handle, &xfer, NULL);
received += nget;
t = ticks_us64() + timeout_char_us;
}
return size;
}
STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
lpuart_transfer_t xfer;
uint64_t t;
size_t remaining = size;
size_t offset = 0;
uint8_t fifo_size = FSL_FEATURE_LPUART_FIFO_SIZEn(0);
// First check if a previous transfer is still ongoing,
// then wait at least the number of remaining character times.
t = ticks_us64() + (uint64_t)(self->handle.txDataSize + fifo_size) * (13000000 / self->config.baudRate_Bps + 1000);
while (self->tx_status != kStatus_LPUART_TxIdle) {
if (ticks_us64() > t) { // timed out, hard error
*errcode = MP_ETIMEDOUT;
return MP_STREAM_ERROR;
}
MICROPY_EVENT_POLL_HOOK
}
// Check if the first part has to be sent semi-blocking.
if (size > self->txbuf_len) {
// Send the first block.
xfer.data = (uint8_t *)buf_in;
offset = xfer.dataSize = size - self->txbuf_len;
self->tx_status = kStatus_LPUART_TxBusy;
LPUART_TransferSendNonBlocking(self->lpuart, &self->handle, &xfer);
// Wait at least the number of character times for this chunk.
t = ticks_us64() + (uint64_t)xfer.dataSize * (13000000 / self->config.baudRate_Bps + 1000);
while (self->handle.txDataSize) {
// Wait for the first/next character to be sent.
if (ticks_us64() > t) { // timed out
if (self->handle.txDataSize >= size) {
*errcode = MP_ETIMEDOUT;
return MP_STREAM_ERROR;
} else {
return size - self->handle.txDataSize;
}
}
MICROPY_EVENT_POLL_HOOK
}
remaining = self->txbuf_len;
} else {
// The data fits into the tx buffer.
offset = 0;
remaining = size;
}
// Send the remaining data without waiting for completion.
memcpy(self->txbuf, (uint8_t *)buf_in + offset, remaining);
xfer.data = self->txbuf;
xfer.dataSize = remaining;
self->tx_status = kStatus_LPUART_TxBusy;
LPUART_TransferSendNonBlocking(self->lpuart, &self->handle, &xfer);
return size;
}
STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) {
machine_uart_obj_t *self = self_in;
mp_uint_t ret;
if (request == MP_STREAM_POLL) {
uintptr_t flags = arg;
ret = 0;
if (flags & MP_STREAM_POLL_RD) {
uint32_t count;
count = LPUART_TransferGetRxRingBufferLength(self->lpuart, &self->handle);
if (count > 0) {
ret |= MP_STREAM_POLL_RD;
}
}
if ((flags & MP_STREAM_POLL_WR)) {
ret |= MP_STREAM_POLL_WR;
}
} else {
*errcode = MP_EINVAL;
ret = MP_STREAM_ERROR;
}
return ret;
}
STATIC const mp_stream_p_t uart_stream_p = {
.read = machine_uart_read,
.write = machine_uart_write,
.ioctl = machine_uart_ioctl,
.is_text = false,
};
const mp_obj_type_t machine_uart_type = {
{ &mp_type_type },
.name = MP_QSTR_UART,
.print = machine_uart_print,
.make_new = machine_uart_make_new,
.getiter = mp_identity_getiter,
.iternext = mp_stream_unbuffered_iter,
.protocol = &uart_stream_p,
.locals_dict = (mp_obj_dict_t *)&machine_uart_locals_dict,
};

View File

@ -36,6 +36,7 @@
#include "ticks.h"
#include "tusb.h"
#include "led.h"
#include "modmachine.h"
extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end;
@ -69,7 +70,8 @@ int main(void) {
if (ret & PYEXEC_FORCED_EXIT) {
goto soft_reset_exit;
}
if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) {
// Do not execute main.py if boot.py failed
if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) {
ret = pyexec_file_if_exists("main.py");
if (ret & PYEXEC_FORCED_EXIT) {
goto soft_reset_exit;
@ -90,6 +92,7 @@ int main(void) {
soft_reset_exit:
mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
machine_pin_irq_deinit();
gc_sweep_all();
mp_deinit();
}
@ -128,8 +131,23 @@ const char mimxrt_help_text[] =
" machine.Pin(pin, m, [p]) -- get a pin and configure it for IO mode m, pull mode p\n"
" methods: init(..), value([v]), high(), low())\n"
"\n"
"Pin IO modes are: Pin.IN, Pin.OUT, Pin.OPEN_DRAIN\n"
"Pin pull modes are: Pin.PULL_UP, Pin.PULL_UP_47K, Pin.PULL_UP_22K, Pin.PULL_DOWN, Pin.PULL_HOLD\n"
" Pins are numbered board specific, either 0-n, or 'D0'-'Dn', or 'A0' - 'An',\n"
" according to the boards's pinout sheet.\n"
" Pin IO modes are: Pin.IN, Pin.OUT, Pin.OPEN_DRAIN\n"
" Pin pull modes are: Pin.PULL_UP, Pin.PULL_UP_47K, Pin.PULL_UP_22K, Pin.PULL_DOWN, Pin.PULL_HOLD\n"
" machine.ADC(pin) -- make an analog object from a pin\n"
" methods: read_u16()\n"
" machine.UART(id, baudrate=115200) -- create an UART object (id=1 - 8)\n"
" methods: init(), write(buf), any()\n"
" buf=read(n), readinto(buf), buf=readline()\n"
" The RX and TX pins are fixed and board-specific.\n"
" machine.SoftI2C() -- create an Soft I2C object\n"
" methods: readfrom(addr, buf, stop=True), writeto(addr, buf, stop=True)\n"
" readfrom_mem(addr, memaddr, arg), writeto_mem(addr, memaddr, arg)\n"
" machine.SoftSPI(baudrate=1000000) -- create an SPI object ()\n"
" methods: read(nbytes, write=0x00), write(buf), write_readinto(wr_buf, rd_buf)\n"
" machine.Timer(id, freq, callback) -- create a hardware timer object (id=0,1,2)\n"
" eg: machine.Timer(freq=1, callback=lambda t:print(t))\n"
"\n"
"Useful control commands:\n"
" CTRL-C -- interrupt a running program\n"

View File

@ -127,13 +127,6 @@ STATIC mp_obj_t mimxrt_flash_readblocks(size_t n_args, const mp_obj_t *args) {
mimxrt_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE);
// if (n_args == 4) {
// mp_printf(MP_PYTHON_PRINTER, "readblocks: nargs = %d, block = %d, offset = %d, len = %d\n",
// n_args, mp_obj_get_int(args[1]), mp_obj_get_int(args[3]), bufinfo.len);
// } else {
// mp_printf(MP_PYTHON_PRINTER, "readblocks: nargs = %d, block = %d, len = %d\n",
// n_args, mp_obj_get_int(args[1]), bufinfo.len);
// }
// Calculate read offset from block number.
uint32_t offset = mp_obj_get_int(args[1]) * SECTOR_SIZE_BYTES;
// Add optional offset

View File

@ -28,6 +28,7 @@
#include "py/runtime.h"
#include "extmod/machine_mem.h"
#include "extmod/machine_i2c.h"
#include "extmod/machine_pulse.h"
#include "extmod/machine_signal.h"
#include "extmod/machine_spi.h"
#include "led.h"
@ -48,6 +49,25 @@ STATIC mp_obj_t machine_freq(void) {
}
MP_DEFINE_CONST_FUN_OBJ_0(machine_freq_obj, machine_freq);
STATIC mp_obj_t machine_idle(void) {
MICROPY_EVENT_POLL_HOOK;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle);
STATIC mp_obj_t machine_disable_irq(void) {
uint32_t state = MICROPY_BEGIN_ATOMIC_SECTION();
return mp_obj_new_int(state);
}
MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq);
STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) {
uint32_t state = mp_obj_get_int(state_in);
MICROPY_END_ATOMIC_SECTION(state);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq);
STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) },
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) },
@ -65,6 +85,15 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) },
{ MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) },
{ MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_spi_type) },
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) },
{ MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) },
{ MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) },
{ MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) },
{ MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) },
};
STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table);

View File

@ -32,8 +32,11 @@
extern const mp_obj_type_t machine_adc_type;
extern const mp_obj_type_t machine_timer_type;
extern const mp_obj_type_t machine_rtc_type;
extern const mp_obj_type_t machine_spi_type;
extern const mp_obj_type_t machine_uart_type;
void machine_adc_init(void);
void machine_pin_irq_deinit(void);
void machine_timer_init_PIT(void);
#endif // MICROPY_INCLUDED_MIMXRT_MODMACHINE_H

View File

@ -61,21 +61,34 @@ STATIC mp_obj_t os_uname(void) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname);
STATIC mp_obj_t os_urandom(mp_obj_t num) {
mp_int_t n = mp_obj_get_int(num);
static bool initialized = false;
vstr_t vstr;
vstr_init_len(&vstr, n);
static bool initialized = false;
STATIC void trng_start(void) {
trng_config_t trngConfig;
if (!initialized) {
trng_config_t trngConfig;
TRNG_GetDefaultConfig(&trngConfig);
trngConfig.sampleMode = kTRNG_SampleModeVonNeumann;
TRNG_Init(TRNG, &trngConfig);
initialized = true;
}
}
uint32_t trng_random_u32(void) {
uint32_t rngval;
trng_start();
TRNG_GetRandomData(TRNG, (uint8_t *)&rngval, 4);
return rngval;
}
STATIC mp_obj_t os_urandom(mp_obj_t num) {
mp_int_t n = mp_obj_get_int(num);
vstr_t vstr;
vstr_init_len(&vstr, n);
trng_start();
TRNG_GetRandomData(TRNG, vstr.buf, n);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);

View File

@ -25,11 +25,90 @@
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "lib/timeutils/timeutils.h"
#include "extmod/utime_mphal.h"
#include "fsl_snvs_lp.h"
// localtime([secs])
// Convert a time expressed in seconds since the Epoch into an 8-tuple which
// contains: (year, month, mday, hour, minute, second, weekday, yearday)
// If secs is not provided or None, then the current time from the RTC is used.
STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) {
if (n_args == 0 || args[0] == mp_const_none) {
// Get current date and time.
snvs_lp_srtc_datetime_t t;
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
mp_obj_t tuple[8] = {
mp_obj_new_int(t.year),
mp_obj_new_int(t.month),
mp_obj_new_int(t.day),
mp_obj_new_int(t.hour),
mp_obj_new_int(t.minute),
mp_obj_new_int(t.second),
mp_obj_new_int(timeutils_calc_weekday(t.year, t.month, t.day)),
mp_obj_new_int(timeutils_year_day(t.year, t.month, t.day)),
};
return mp_obj_new_tuple(8, tuple);
} else {
// Convert given seconds to tuple.
mp_int_t seconds = mp_obj_get_int(args[0]);
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);
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_localtime_obj, 0, 1, time_localtime);
// mktime()
// This is inverse function of localtime. It's argument is a full 8-tuple
// which expresses a time as per localtime. It returns an integer which is
// the number of seconds since the Epoch.
STATIC mp_obj_t time_mktime(mp_obj_t tuple) {
size_t len;
mp_obj_t *elem;
mp_obj_get_array(tuple, &len, &elem);
// localtime generates a tuple of len 8. CPython uses 9, so we accept both.
if (len < 8 || len > 9) {
mp_raise_TypeError(MP_ERROR_TEXT("mktime needs a tuple of length 8 or 9"));
}
return mp_obj_new_int_from_uint(timeutils_mktime(mp_obj_get_int(elem[0]),
mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), mp_obj_get_int(elem[3]),
mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5])));
}
MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime);
// time()
// Return the number of seconds since the Epoch.
STATIC mp_obj_t time_time(void) {
snvs_lp_srtc_datetime_t t;
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
return mp_obj_new_int_from_ull(timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time);
STATIC const mp_rom_map_elem_t time_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) },
{ MP_ROM_QSTR(MP_QSTR_gmtime), MP_ROM_PTR(&time_localtime_obj) },
{ MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&time_localtime_obj) },
{ MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&time_mktime_obj) },
{ MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) },
{ MP_ROM_QSTR(MP_QSTR_time_ns), MP_ROM_PTR(&mp_utime_time_ns_obj) },
{ MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) },
{ MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) },
{ MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) },

View File

@ -30,6 +30,8 @@
#include "mpconfigboard.h"
#include "fsl_common.h"
uint32_t trng_random_u32(void);
// Memory allocation policies
#define MICROPY_GC_STACK_ENTRY_TYPE uint16_t
#define MICROPY_GC_ALLOC_THRESHOLD (0)
@ -118,9 +120,11 @@
#define MICROPY_PY_UTIME_MP_HAL (1)
#define MICROPY_PY_URANDOM (1)
#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1)
#define MICROPY_PY_URANDOM_SEED_INIT_FUNC (trng_random_u32())
#define MICROPY_PY_USELECT (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new
#define MICROPY_PY_MACHINE_PULSE (1)
#define MICROPY_PY_MACHINE_I2C (1)
#define MICROPY_PY_MACHINE_SPI (1)
#define MICROPY_PY_FRAMEBUF (1)
@ -167,7 +171,8 @@ extern const struct _mp_obj_module_t mp_module_utime;
#define MICROPY_PORT_ROOT_POINTERS \
const char *readline_hist[8]; \
struct _machine_timer_obj_t *timer_table[MICROPY_HW_PIT_NUM_CHANNELS];
struct _machine_timer_obj_t *timer_table[MICROPY_HW_PIT_NUM_CHANNELS]; \
void *machine_pin_irq_objects[MICROPY_HW_NUM_PIN_IRQS]; \
#define MP_STATE_PORT MP_STATE_VM

View File

@ -28,8 +28,10 @@
#include "py/runtime.h"
#include "py/stream.h"
#include "py/mphal.h"
#include "lib/timeutils/timeutils.h"
#include "ticks.h"
#include "tusb.h"
#include "fsl_snvs_lp.h"
#include CPU_HEADER_H
@ -94,3 +96,10 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
// USARTx->USART.DATA.bit.DATA = *str++;
// }
}
uint64_t mp_hal_time_ns(void) {
snvs_lp_srtc_datetime_t t;
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second);
return s * 1000000000ULL;
}

View File

@ -76,9 +76,5 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) {
return 0;
}
static inline uint64_t mp_hal_time_ns(void) {
// TODO: Implement this function.
return 0UL;
}
#endif // MICROPY_INCLUDED_MIMXRT_MPHALPORT_H

View File

@ -29,6 +29,7 @@
#include <stdint.h>
#include "py/obj.h"
#include "lib/utils/mpirq.h"
#include "fsl_gpio.h"
// ------------------------------------------------------------------------------------------------------------------ //
@ -118,6 +119,12 @@ typedef struct {
const machine_pin_adc_obj_t *adc_list; // pointer to list with ADC options
} machine_pin_obj_t;
typedef struct _machine_pin_irq_obj_t {
mp_irq_obj_t base;
uint32_t flags;
uint32_t trigger;
} machine_pin_irq_obj_t;
// ------------------------------------------------------------------------------------------------------------------ //
extern const mp_obj_type_t machine_pin_type;

View File

@ -149,7 +149,7 @@ STATIC void async_stop(void) {
STATIC void wait_for_event(void) {
while (!wakeup_event) {
// allow CTRL-C to stop the animation
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) {
async_stop();
return;
}

View File

@ -140,7 +140,7 @@ STATIC void wait_async_music_idle(void) {
// wait for the async music state to become idle
while (music_data->async_state != ASYNC_MUSIC_STATE_IDLE) {
// allow CTRL-C to stop the music
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) {
music_data->async_state = ASYNC_MUSIC_STATE_IDLE;
pwm_set_duty_cycle(music_data->async_pin->pin, 0); // TODO: remove pin setting.
break;

View File

@ -140,7 +140,7 @@ int switch_get(int sw) {
// TODO need an irq
void uart_rx_irq(void) {
if (c == interrupt_char) {
MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj);
MP_STATE_MAIN_THREAD(mp_pending_exception) = MP_STATE_PORT(keyboard_interrupt_obj);
}
}
*/

View File

@ -86,6 +86,7 @@ set(MICROPY_SOURCE_PORT
machine_i2c.c
machine_pin.c
machine_pwm.c
machine_rtc.c
machine_spi.c
machine_timer.c
machine_uart.c
@ -113,6 +114,7 @@ set(MICROPY_SOURCE_QSTR
${PROJECT_SOURCE_DIR}/machine_i2c.c
${PROJECT_SOURCE_DIR}/machine_pin.c
${PROJECT_SOURCE_DIR}/machine_pwm.c
${PROJECT_SOURCE_DIR}/machine_rtc.c
${PROJECT_SOURCE_DIR}/machine_spi.c
${PROJECT_SOURCE_DIR}/machine_timer.c
${PROJECT_SOURCE_DIR}/machine_uart.c
@ -154,6 +156,7 @@ set(PICO_SDK_COMPONENTS
pico_sync
pico_time
pico_unique_id
pico_util
tinyusb_common
tinyusb_device
)

123
ports/rp2/machine_rtc.c Normal file
View File

@ -0,0 +1,123 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 "Krzysztof Adamski" <k@japko.eu>
*
* 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 <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "py/nlr.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "py/mphal.h"
#include "py/mperrno.h"
#include "lib/timeutils/timeutils.h"
#include "hardware/rtc.h"
#include "pico/util/datetime.h"
#include "modmachine.h"
typedef struct _machine_rtc_obj_t {
mp_obj_base_t base;
} machine_rtc_obj_t;
// singleton RTC object
STATIC const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}};
STATIC mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 0, 0, false);
bool r = rtc_running();
if (!r) {
// This shouldn't happen as rtc_init() is already called in main so
// it's here just in case
rtc_init();
datetime_t t = { .month = 1, .day = 1 };
rtc_set_datetime(&t);
}
// return constant object
return (mp_obj_t)&machine_rtc_obj;
}
STATIC mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) {
if (n_args == 1) {
bool ret;
datetime_t t;
ret = rtc_get_datetime(&t);
if (!ret) {
mp_raise_OSError(MP_EIO);
}
mp_obj_t tuple[8] = {
mp_obj_new_int(t.year),
mp_obj_new_int(t.month),
mp_obj_new_int(t.day),
mp_obj_new_int(t.dotw),
mp_obj_new_int(t.hour),
mp_obj_new_int(t.min),
mp_obj_new_int(t.sec),
mp_obj_new_int(0)
};
return mp_obj_new_tuple(8, tuple);
} else {
mp_obj_t *items;
mp_obj_get_array_fixed_n(args[1], 8, &items);
datetime_t t = {
.year = mp_obj_get_int(items[0]),
.month = mp_obj_get_int(items[1]),
.day = mp_obj_get_int(items[2]),
.hour = mp_obj_get_int(items[4]),
.min = mp_obj_get_int(items[5]),
.sec = mp_obj_get_int(items[6]),
};
// Deliberately ignore the weekday argument and compute the proper value
t.dotw = timeutils_calc_weekday(t.year, t.month, t.day);
if (!rtc_set_datetime(&t)) {
mp_raise_OSError(MP_EINVAL);
}
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime);
STATIC const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&machine_rtc_datetime_obj) },
};
STATIC MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table);
const mp_obj_type_t machine_rtc_type = {
{ &mp_type_type },
.name = MP_QSTR_RTC,
.make_new = machine_rtc_make_new,
.locals_dict = (mp_obj_t)&machine_rtc_locals_dict,
};

View File

@ -80,7 +80,7 @@ int main(int argc, char **argv) {
.year = 2021,
.month = 1,
.day = 1,
.dotw = 5, // 0 is Sunday, so 5 is Friday
.dotw = 4, // 0 is Monday, so 4 is Friday
.hour = 0,
.min = 0,
.sec = 0,

View File

@ -164,6 +164,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) },
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) },
{ MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) },
{ MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) },
{ MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_spi_type) },
{ MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) },

View File

@ -7,6 +7,7 @@ extern const mp_obj_type_t machine_adc_type;
extern const mp_obj_type_t machine_hw_i2c_type;
extern const mp_obj_type_t machine_pin_type;
extern const mp_obj_type_t machine_pwm_type;
extern const mp_obj_type_t machine_rtc_type;
extern const mp_obj_type_t machine_spi_type;
extern const mp_obj_type_t machine_timer_type;
extern const mp_obj_type_t machine_uart_type;

View File

@ -132,6 +132,7 @@ void VCC_GND_F407VE_board_early_init(void);
extern const struct _mp_spiflash_config_t spiflash_config;
extern struct _spi_bdev_t spi_bdev;
#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1)
#define MICROPY_HW_BDEV_IOCTL(op, arg) ( \
(op) == BDEV_IOCTL_NUM_BLOCKS ? (MICROPY_HW_SPIFLASH_SIZE_BITS / 8 / FLASH_BLOCK_SIZE) : \
(op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(&spi_bdev, (op), (uint32_t)&spiflash_config) : \

View File

@ -140,6 +140,7 @@ void VCC_GND_F407ZG_board_early_init(void);
extern const struct _mp_spiflash_config_t spiflash_config;
extern struct _spi_bdev_t spi_bdev;
#define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1)
#define MICROPY_HW_BDEV_IOCTL(op, arg) ( \
(op) == BDEV_IOCTL_NUM_BLOCKS ? (MICROPY_HW_SPIFLASH_SIZE_BITS / 8 / FLASH_BLOCK_SIZE) : \
(op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(&spi_bdev, (op), (uint32_t)&spiflash_config) : \

View File

@ -46,6 +46,7 @@
#define PHY_BCR (0x0000)
#define PHY_BCR_SOFT_RESET (0x8000)
#define PHY_BCR_AUTONEG_EN (0x1000)
#define PHY_BCR_POWER_DOWN (0x0800U)
#undef PHY_BSR
#define PHY_BSR (0x0001)
@ -188,17 +189,6 @@ STATIC uint32_t eth_phy_read(uint32_t reg) {
void eth_init(eth_t *self, int mac_idx) {
mp_hal_get_mac(mac_idx, &self->netif.hwaddr[0]);
self->netif.hwaddr_len = 6;
}
void eth_set_trace(eth_t *self, uint32_t value) {
self->trace_flags = value;
}
STATIC int eth_mac_init(eth_t *self) {
// Configure MPU
uint32_t irq_state = mpu_config_start();
mpu_config_region(MPU_REGION_ETH, (uint32_t)&eth_dma, MPU_CONFIG_ETH(MPU_REGION_SIZE_16KB));
mpu_config_end(irq_state);
// Configure GPIO
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDC, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDC);
@ -211,6 +201,27 @@ STATIC int eth_mac_init(eth_t *self) {
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD0);
mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD1);
// Enable peripheral clock
#if defined(STM32H7)
__HAL_RCC_ETH1MAC_CLK_ENABLE();
__HAL_RCC_ETH1TX_CLK_ENABLE();
__HAL_RCC_ETH1RX_CLK_ENABLE();
#else
__HAL_RCC_ETH_CLK_ENABLE();
#endif
}
void eth_set_trace(eth_t *self, uint32_t value) {
self->trace_flags = value;
}
STATIC int eth_mac_init(eth_t *self) {
// Configure MPU
uint32_t irq_state = mpu_config_start();
mpu_config_region(MPU_REGION_ETH, (uint32_t)&eth_dma, MPU_CONFIG_ETH(MPU_REGION_SIZE_16KB));
mpu_config_end(irq_state);
// Enable peripheral clock
#if defined(STM32H7)
__HAL_RCC_ETH1MAC_CLK_ENABLE();
__HAL_RCC_ETH1TX_CLK_ENABLE();
@ -780,17 +791,20 @@ int eth_link_status(eth_t *self) {
return 2; // link no-ip;
}
} else {
int s = eth_phy_read(0) | eth_phy_read(0x10) << 16;
if (s == 0) {
return 0; // link down
if (eth_phy_read(PHY_BSR) & PHY_BSR_LINK_STATUS) {
return 1; // link up
} else {
return 1; // link join
return 0; // link down
}
}
}
int eth_start(eth_t *self) {
eth_lwip_deinit(self);
// Make sure Eth is Not in low power mode.
eth_low_power_mode(self, false);
int ret = eth_mac_init(self);
if (ret < 0) {
return ret;
@ -805,4 +819,29 @@ int eth_stop(eth_t *self) {
return 0;
}
void eth_low_power_mode(eth_t *self, bool enable) {
(void)self;
// Enable eth clock
#if defined(STM32H7)
__HAL_RCC_ETH1MAC_CLK_ENABLE();
#else
__HAL_RCC_ETH_CLK_ENABLE();
#endif
uint16_t bcr = eth_phy_read(PHY_BCR);
if (enable) {
// Enable low-power mode.
eth_phy_write(PHY_BCR, bcr | PHY_BCR_POWER_DOWN);
// Disable eth clock.
#if defined(STM32H7)
__HAL_RCC_ETH1MAC_CLK_DISABLE();
#else
__HAL_RCC_ETH_CLK_DISABLE();
#endif
} else {
// Disable low-power mode.
eth_phy_write(PHY_BCR, bcr & (~PHY_BCR_POWER_DOWN));
}
}
#endif // defined(MICROPY_HW_ETH_MDC)

View File

@ -35,5 +35,6 @@ struct netif *eth_netif(eth_t *self);
int eth_link_status(eth_t *self);
int eth_start(eth_t *self);
int eth_stop(eth_t *self);
void eth_low_power_mode(eth_t *self, bool enable);
#endif // MICROPY_INCLUDED_STM32_ETH_H

View File

@ -54,6 +54,7 @@
#endif
#include "boardctrl.h"
#include "mpbthciport.h"
#include "mpu.h"
#include "rfcore.h"
#include "systick.h"
@ -440,8 +441,7 @@ void stm32_main(uint32_t reset_mode) {
systick_enable_dispatch(SYSTICK_DISPATCH_LWIP, mod_network_lwip_poll_wrapper);
#endif
#if MICROPY_PY_BLUETOOTH
extern void mp_bluetooth_hci_systick(uint32_t ticks_ms);
systick_enable_dispatch(SYSTICK_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_systick);
mp_bluetooth_hci_init();
#endif
#if MICROPY_PY_NETWORK_CYW43

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Damien P. George
* Copyright (c) 2018-2021 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
@ -28,7 +28,8 @@
#include "py/mphal.h"
#include "extmod/mpbthci.h"
#include "extmod/modbluetooth.h"
#include "systick.h"
#include "mpbthciport.h"
#include "softtimer.h"
#include "pendsv.h"
#include "lib/utils/mpirq.h"
@ -38,22 +39,48 @@
uint8_t mp_bluetooth_hci_cmd_buf[4 + 256];
// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c).
// Request new data from the uart and pass to the stack, and run pending events/callouts.
extern void mp_bluetooth_hci_poll(void);
// Soft timer for scheduling a HCI poll.
STATIC soft_timer_entry_t mp_bluetooth_hci_soft_timer;
// Hook for pendsv poller to run this periodically every 128ms
#define BLUETOOTH_HCI_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & 0x7f) == 0)
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// Prevent double-enqueuing of the scheduled task.
STATIC volatile bool events_task_is_scheduled;
#endif
// This is called by soft_timer and executes at IRQ_PRI_PENDSV.
STATIC void mp_bluetooth_hci_soft_timer_callback(soft_timer_entry_t *self) {
mp_bluetooth_hci_poll_now();
}
void mp_bluetooth_hci_init(void) {
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
events_task_is_scheduled = false;
#endif
soft_timer_static_init(
&mp_bluetooth_hci_soft_timer,
SOFT_TIMER_MODE_ONE_SHOT,
0,
mp_bluetooth_hci_soft_timer_callback
);
}
STATIC void mp_bluetooth_hci_start_polling(void) {
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
events_task_is_scheduled = false;
#endif
mp_bluetooth_hci_poll_now();
}
void mp_bluetooth_hci_poll_in_ms(uint32_t ms) {
soft_timer_reinsert(&mp_bluetooth_hci_soft_timer, ms);
}
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// For synchronous mode, we run all BLE stack code inside a scheduled task.
// This task is scheduled periodically (every 128ms) via SysTick, or
// This task is scheduled periodically via a soft timer, or
// immediately on HCI UART RXIDLE.
// Prevent double-enqueuing of the scheduled task.
STATIC volatile bool events_task_is_scheduled = false;
STATIC mp_obj_t run_events_scheduled_task(mp_obj_t none_in) {
(void)none_in;
events_task_is_scheduled = false;
@ -65,23 +92,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(run_events_scheduled_task_obj, run_events_sched
// Called periodically (systick) or directly (e.g. UART RX IRQ) in order to
// request that processing happens ASAP in the scheduler.
void mp_bluetooth_hci_systick(uint32_t ticks_ms) {
if (events_task_is_scheduled) {
return;
}
if (ticks_ms == 0 || BLUETOOTH_HCI_TICK(ticks_ms)) {
void mp_bluetooth_hci_poll_now(void) {
if (!events_task_is_scheduled) {
events_task_is_scheduled = mp_sched_schedule(MP_OBJ_FROM_PTR(&run_events_scheduled_task_obj), mp_const_none);
if (!events_task_is_scheduled) {
// The schedule queue is full, set callback to try again soon.
mp_bluetooth_hci_poll_in_ms(5);
}
}
}
#else // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// Called periodically (systick) or directly (e.g. uart irq).
void mp_bluetooth_hci_systick(uint32_t ticks_ms) {
if (ticks_ms == 0 || BLUETOOTH_HCI_TICK(ticks_ms)) {
pendsv_schedule_dispatch(PENDSV_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_poll);
}
void mp_bluetooth_hci_poll_now(void) {
pendsv_schedule_dispatch(PENDSV_DISPATCH_BLUETOOTH_HCI, mp_bluetooth_hci_poll);
}
#endif
@ -104,14 +128,13 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
DEBUG_printf("mp_bluetooth_hci_uart_init (stm32 rfcore)\n");
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
events_task_is_scheduled = false;
#endif
rfcore_ble_init();
hci_uart_rx_buf_cur = 0;
hci_uart_rx_buf_len = 0;
// Start the HCI polling to process any initial events/packets.
mp_bluetooth_hci_start_polling();
return 0;
}
@ -163,7 +186,6 @@ int mp_bluetooth_hci_uart_readchar(void) {
/******************************************************************************/
// HCI over UART
#include "pendsv.h"
#include "uart.h"
pyb_uart_obj_t mp_bluetooth_hci_uart_obj;
@ -173,7 +195,7 @@ static uint8_t hci_uart_rxbuf[768];
mp_obj_t mp_uart_interrupt(mp_obj_t self_in) {
// Queue up the scheduler to run the HCI UART and event processing ASAP.
mp_bluetooth_hci_systick(0);
mp_bluetooth_hci_poll_now();
return mp_const_none;
}
@ -182,10 +204,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_uart_interrupt_obj, mp_uart_interrupt);
int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
DEBUG_printf("mp_bluetooth_hci_uart_init (stm32)\n");
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
events_task_is_scheduled = false;
#endif
// bits (8), stop (1), parity (none) and flow (rts/cts) are assumed to match MYNEWT_VAL_BLE_HCI_UART_ constants in syscfg.h.
mp_bluetooth_hci_uart_obj.base.type = &pyb_uart_type;
mp_bluetooth_hci_uart_obj.uart_id = port;
@ -208,6 +226,9 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
mp_bluetooth_hci_uart_irq_obj.ishard = true;
uart_irq_config(&mp_bluetooth_hci_uart_obj, true);
// Start the HCI polling to process any initial events/packets.
mp_bluetooth_hci_start_polling();
return 0;
}

42
ports/stm32/mpbthciport.h Normal file
View File

@ -0,0 +1,42 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 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_STM32_MPBTHCIPORT_H
#define MICROPY_INCLUDED_STM32_MPBTHCIPORT_H
// Initialise the HCI subsystem (should be called once, early on).
void mp_bluetooth_hci_init(void);
// Poll the HCI now, or after a certain timeout.
void mp_bluetooth_hci_poll_now(void);
void mp_bluetooth_hci_poll_in_ms(uint32_t ms);
// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c).
// Request new data from the uart and pass to the stack, and run pending events/callouts.
// This is a low-level function and should not be called directly, use
// mp_bluetooth_hci_poll_now/mp_bluetooth_hci_poll_in_ms instead.
void mp_bluetooth_hci_poll(void);
#endif // MICROPY_INCLUDED_STM32_MPBTHCIPORT_H

View File

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Damien P. George
* Copyright (c) 2020-2021 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
@ -31,30 +31,79 @@
#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK
#include "lib/btstack/src/btstack.h"
#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h"
#include "lib/btstack/platform/embedded/hal_cpu.h"
#include "lib/btstack/platform/embedded/hal_time_ms.h"
#include "extmod/mpbthci.h"
#include "extmod/btstack/btstack_hci_uart.h"
#include "extmod/btstack/modbluetooth_btstack.h"
#include "mpbthciport.h"
// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the
// following three functions are empty.
void mp_bluetooth_hci_poll_in_ms(uint32_t ms);
void hal_cpu_disable_irqs(void) {
static btstack_linked_list_t mp_btstack_runloop_timers;
static void mp_btstack_runloop_init(void) {
mp_btstack_runloop_timers = NULL;
}
void hal_cpu_enable_irqs(void) {
static void mp_btstack_runloop_set_timer(btstack_timer_source_t *tim, uint32_t timeout_ms) {
tim->timeout = mp_hal_ticks_ms() + timeout_ms + 1;
}
void hal_cpu_enable_irqs_and_sleep(void) {
static void mp_btstack_runloop_add_timer(btstack_timer_source_t *tim) {
btstack_linked_item_t **node = &mp_btstack_runloop_timers;
for (; *node; node = &(*node)->next) {
btstack_timer_source_t *node_tim = (btstack_timer_source_t *)*node;
if (node_tim == tim) {
// Timer is already in the list, don't add it.
return;
}
int32_t delta = btstack_time_delta(tim->timeout, node_tim->timeout);
if (delta < 0) {
// Found sorted location in list.
break;
}
}
// Insert timer into list in sorted location.
tim->item.next = *node;
*node = &tim->item;
// Reschedule the HCI poll if this timer is at the head of the list.
if (mp_btstack_runloop_timers == &tim->item) {
int32_t delta_ms = btstack_time_delta(tim->timeout, mp_hal_ticks_ms());
mp_bluetooth_hci_poll_in_ms(delta_ms);
}
}
uint32_t hal_time_ms(void) {
static bool mp_btstack_runloop_remove_timer(btstack_timer_source_t *tim) {
return btstack_linked_list_remove(&mp_btstack_runloop_timers, (btstack_linked_item_t *)tim);
}
static void mp_btstack_runloop_execute(void) {
// Should not be called.
}
static void mp_btstack_runloop_dump_timer(void) {
// Not implemented/needed.
}
static uint32_t mp_btstack_runloop_get_time_ms(void) {
return mp_hal_ticks_ms();
}
static const btstack_run_loop_t mp_btstack_runloop_stm32 = {
&mp_btstack_runloop_init,
NULL, // add_data_source,
NULL, // remove_data_source,
NULL, // enable_data_source_callbacks,
NULL, // disable_data_source_callbacks,
&mp_btstack_runloop_set_timer,
&mp_btstack_runloop_add_timer,
&mp_btstack_runloop_remove_timer,
&mp_btstack_runloop_execute,
&mp_btstack_runloop_dump_timer,
&mp_btstack_runloop_get_time_ms,
};
STATIC const hci_transport_config_uart_t hci_transport_config_uart = {
HCI_TRANSPORT_CONFIG_UART,
MICROPY_HW_BLE_UART_BAUDRATE,
@ -68,22 +117,32 @@ void mp_bluetooth_hci_poll(void) {
return;
}
// Process uart data.
// Process UART data.
if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_HALTING) {
mp_bluetooth_btstack_hci_uart_process();
}
// Call the BTstack run loop.
btstack_run_loop_embedded_execute_once();
// Process any BTstack timers.
while (mp_btstack_runloop_timers != NULL) {
btstack_timer_source_t *tim = (btstack_timer_source_t *)mp_btstack_runloop_timers;
int32_t delta_ms = btstack_time_delta(tim->timeout, mp_hal_ticks_ms());
if (delta_ms > 0) {
// Timer has not expired yet, reschedule HCI poll for this timer.
mp_bluetooth_hci_poll_in_ms(delta_ms);
break;
}
btstack_linked_list_pop(&mp_btstack_runloop_timers);
tim->process(tim);
}
}
void mp_bluetooth_btstack_port_init(void) {
static bool run_loop_init = false;
if (!run_loop_init) {
run_loop_init = true;
btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
btstack_run_loop_init(&mp_btstack_runloop_stm32);
} else {
btstack_run_loop_embedded_get_instance()->init();
mp_btstack_runloop_stm32.init();
}
// hci_dump_open(NULL, HCI_DUMP_STDOUT);

View File

@ -432,6 +432,9 @@ struct _mp_bluetooth_btstack_root_pointers_t;
/* pointers to all CAN objects (if they have been created) */ \
struct _pyb_can_obj_t *pyb_can_obj_all[MICROPY_HW_MAX_CAN]; \
\
/* USB_VCP IRQ callbacks (if they have been set) */ \
mp_obj_t pyb_usb_vcp_irq[MICROPY_HW_USB_CDC_NUM]; \
\
/* list of registered NICs */ \
mp_obj_list_t mod_network_nic_list; \
\

View File

@ -40,6 +40,7 @@
#include "extmod/modbluetooth.h"
#include "extmod/nimble/modbluetooth_nimble.h"
#include "extmod/nimble/hal/hal_uart.h"
#include "mpbthciport.h"
// Get any pending data from the UART and send it to NimBLE's HCI buffers.
// Any further processing by NimBLE will be run via its event queue.
@ -56,6 +57,12 @@ void mp_bluetooth_hci_poll(void) {
// Run any remaining events (e.g. if there was no UART data).
mp_bluetooth_nimble_os_eventq_run_all();
}
if (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
// Call this function again in 128ms to check for new events.
// TODO: improve this by only calling back when needed.
mp_bluetooth_hci_poll_in_ms(128);
}
}
// --- Port-specific helpers for the generic NimBLE bindings. -----------------

View File

@ -134,6 +134,10 @@ STATIC mp_obj_t network_lan_config(size_t n_args, const mp_obj_t *args, mp_map_t
eth_set_trace(self->eth, mp_obj_get_int(e->value));
break;
}
case MP_QSTR_low_power: {
eth_low_power_mode(self->eth, mp_obj_get_int(e->value));
break;
}
default:
mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
}

View File

@ -60,10 +60,10 @@ void pendsv_init(void) {
// the given exception object using nlr_jump in the context of the top-level
// thread.
void pendsv_kbd_intr(void) {
if (MP_STATE_VM(mp_pending_exception) == MP_OBJ_NULL) {
if (MP_STATE_MAIN_THREAD(mp_pending_exception) == MP_OBJ_NULL) {
mp_sched_keyboard_interrupt();
} else {
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
MP_STATE_MAIN_THREAD(mp_pending_exception) = MP_OBJ_NULL;
pendsv_object = &MP_STATE_VM(mp_kbd_exception);
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
}

View File

@ -32,6 +32,7 @@
#include "py/mphal.h"
#include "py/runtime.h"
#include "extmod/modbluetooth.h"
#include "mpbthciport.h"
#include "rtc.h"
#include "rfcore.h"
@ -714,8 +715,7 @@ void IPCC_C1_RX_IRQHandler(void) {
#if MICROPY_PY_BLUETOOTH
// Queue up the scheduler to process UART data and run events.
extern void mp_bluetooth_hci_systick(uint32_t ticks_ms);
mp_bluetooth_hci_systick(0);
mp_bluetooth_hci_poll_now();
#endif
}

View File

@ -276,6 +276,13 @@ void sdram_leave_low_power(void) {
(0 << 9U)); // Mode Register Definition
}
#if __GNUC__ >= 11
// Prevent array bounds warnings when accessing SDRAM_START_ADDRESS as a memory pointer.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
bool sdram_test(bool fast) {
uint8_t const pattern = 0xaa;
uint8_t const antipattern = 0x55;
@ -325,4 +332,8 @@ bool sdram_test(bool fast) {
return true;
}
#if __GNUC__ >= 11
#pragma GCC diagnostic pop
#endif
#endif // FMC_SDRAM_BANK

View File

@ -122,6 +122,7 @@ void soft_timer_gc_mark_all(void) {
}
void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t delta_ms, void (*cb)(soft_timer_entry_t *)) {
mp_pairheap_init_node(soft_timer_lt, &entry->pairheap);
entry->flags = 0;
entry->mode = mode;
entry->delta_ms = delta_ms;

View File

@ -56,4 +56,11 @@ void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t d
void soft_timer_insert(soft_timer_entry_t *entry, uint32_t initial_delta_ms);
void soft_timer_remove(soft_timer_entry_t *entry);
// The timer will be reinserted into the heap so that it is called after initial_delta_ms milliseconds.
// After that, if it's periodic, it will continue to be called every entry->delta_ms milliseconds.
static inline void soft_timer_reinsert(soft_timer_entry_t *entry, uint32_t initial_delta_ms) {
soft_timer_remove(entry);
soft_timer_insert(entry, initial_delta_ms);
}
#endif // MICROPY_INCLUDED_STM32_SOFTTIMER_H

View File

@ -37,9 +37,6 @@ enum {
#if MICROPY_PY_NETWORK && MICROPY_PY_LWIP
SYSTICK_DISPATCH_LWIP,
#endif
#if MICROPY_PY_BLUETOOTH
SYSTICK_DISPATCH_BLUETOOTH_HCI,
#endif
SYSTICK_DISPATCH_MAX
};

Some files were not shown because too many files have changed in this diff Show More