This commit is contained in:
Amir Gonnen 2019-11-28 21:00:51 +02:00
commit 0e1b6ab0ca
32 changed files with 962 additions and 214 deletions

View File

@ -52,6 +52,12 @@ Event Handling
The optional *trigger* parameter allows you to set a mask of events that
your program is interested in. The default is all events.
Note: the ``addr``, ``adv_data`` and ``uuid`` entries in the tuples are
references to data managed by the :mod:`ubluetooth` module (i.e. the same
instance will be re-used across multiple calls to the event handler). If
your program wants to use this data outside of the handler, then it must
copy them first, e.g. by using ``bytes(addr)`` or ``bluetooth.UUID(uuid)``.
An event handler showing all possible events::
def bt_irq(event, data):

View File

@ -2,6 +2,7 @@
from micropython import const
import struct
import bluetooth
# Advertising payloads are repeated packets of the following form:
# 1 byte data length (N + 1)
@ -46,3 +47,39 @@ def advertising_payload(limited_disc=False, br_edr=False, name=None, services=No
_append(_ADV_TYPE_APPEARANCE, struct.pack('<h', appearance))
return payload
def decode_field(payload, adv_type):
i = 0
result = []
while i + 1 < len(payload):
if payload[i + 1] == adv_type:
result.append(payload[i + 2:i + payload[i] + 1])
i += 1 + payload[i]
return result
def decode_name(payload):
n = decode_field(payload, _ADV_TYPE_NAME)
return str(n[0], 'utf-8') if n else ''
def decode_services(payload):
services = []
for u in decode_field(payload, _ADV_TYPE_UUID16_COMPLETE):
services.append(bluetooth.UUID(struct.unpack('<h', u)[0]))
for u in decode_field(payload, _ADV_TYPE_UUID32_COMPLETE):
services.append(bluetooth.UUID(struct.unpack('<d', u)[0]))
for u in decode_field(payload, _ADV_TYPE_UUID128_COMPLETE):
services.append(bluetooth.UUID(u))
return services
def demo():
payload = advertising_payload(name='micropython', services=[bluetooth.UUID(0x181A), bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')])
print(payload)
print(decode_name(payload))
print(decode_services(payload))
if __name__ == '__main__':
demo()

View File

@ -0,0 +1,222 @@
# This example finds and connects to a BLE temperature sensor (e.g. the one in ble_temperature.py).
import bluetooth
import random
import struct
import time
import micropython
from ble_advertising import decode_services, decode_name
from micropython import const
_IRQ_CENTRAL_CONNECT = const(1 << 0)
_IRQ_CENTRAL_DISCONNECT = const(1 << 1)
_IRQ_GATTS_WRITE = const(1 << 2)
_IRQ_GATTS_READ_REQUEST = const(1 << 3)
_IRQ_SCAN_RESULT = const(1 << 4)
_IRQ_SCAN_COMPLETE = const(1 << 5)
_IRQ_PERIPHERAL_CONNECT = const(1 << 6)
_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7)
_IRQ_GATTC_SERVICE_RESULT = const(1 << 8)
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9)
_IRQ_GATTC_DESCRIPTOR_RESULT = const(1 << 10)
_IRQ_GATTC_READ_RESULT = const(1 << 11)
_IRQ_GATTC_WRITE_STATUS = const(1 << 12)
_IRQ_GATTC_NOTIFY = const(1 << 13)
_IRQ_GATTC_INDICATE = const(1 << 14)
_IRQ_ALL = const(0xffff)
# org.bluetooth.service.environmental_sensing
_ENV_SENSE_UUID = bluetooth.UUID(0x181A)
# org.bluetooth.characteristic.temperature
_TEMP_UUID = bluetooth.UUID(0x2A6E)
_TEMP_CHAR = (_TEMP_UUID, bluetooth.FLAG_READ|bluetooth.FLAG_NOTIFY,)
_ENV_SENSE_SERVICE = (_ENV_SENSE_UUID, (_TEMP_CHAR,),)
# org.bluetooth.characteristic.gap.appearance.xml
_ADV_APPEARANCE_GENERIC_THERMOMETER = const(768)
class BLETemperatureCentral:
def __init__(self, ble):
self._ble = ble
self._ble.active(True)
self._ble.irq(handler=self._irq)
self._reset()
def _reset(self):
# Cached name and address from a successful scan.
self._name = None
self._addr_type = None
self._addr = None
# Cached value (if we have one)
self._value = None
# Callbacks for completion of various operations.
# These reset back to None after being invoked.
self._scan_callback = None
self._conn_callback = None
self._read_callback = None
# Persistent callback for when new data is notified from the device.
self._notify_callback = None
# Connected device.
self._conn_handle = None
self._value_handle = None
def _irq(self, event, data):
if event == _IRQ_SCAN_RESULT:
addr_type, addr, connectable, rssi, adv_data = data
if connectable and _ENV_SENSE_UUID in decode_services(adv_data):
# Found a potential device, remember it and stop scanning.
self._addr_type = addr_type
self._addr = bytes(addr) # Note: The addr buffer is owned by modbluetooth, need to copy it.
self._name = decode_name(adv_data) or '?'
self._ble.gap_scan(None)
elif event == _IRQ_SCAN_COMPLETE:
if self._scan_callback:
if self._addr:
# Found a device during the scan (and the scan was explicitly stopped).
self._scan_callback(self._addr_type, self._addr, self._name)
self._scan_callback = None
else:
# Scan timed out.
self._scan_callback(None, None, None)
elif event == _IRQ_PERIPHERAL_CONNECT:
# Connect successful.
conn_handle, addr_type, addr, = data
if addr_type == self._addr_type and addr == self._addr:
self._conn_handle = conn_handle
self._ble.gattc_discover_services(self._conn_handle)
elif event == _IRQ_PERIPHERAL_DISCONNECT:
# Disconnect (either initiated by us or the remote end).
conn_handle, _, _, = data
if conn_handle == self._conn_handle:
# If it was initiated by us, it'll already be reset.
self._reset()
elif event == _IRQ_GATTC_SERVICE_RESULT:
# Connected device returned a service.
conn_handle, start_handle, end_handle, uuid = data
if conn_handle == self._conn_handle and uuid == _ENV_SENSE_UUID:
self._ble.gattc_discover_characteristics(self._conn_handle, start_handle, end_handle)
elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
# Connected device returned a characteristic.
conn_handle, def_handle, value_handle, properties, uuid = data
if conn_handle == self._conn_handle and uuid == _TEMP_UUID:
self._value_handle = value_handle
# We've finished connecting and discovering device, fire the connect callback.
if self._conn_callback:
self._conn_callback()
elif event == _IRQ_GATTC_READ_RESULT:
# A read completed successfully.
conn_handle, value_handle, char_data = data
if conn_handle == self._conn_handle and value_handle == self._value_handle:
self._update_value(char_data)
if self._read_callback:
self._read_callback(self._value)
self._read_callback = None
elif event == _IRQ_GATTC_NOTIFY:
# The ble_temperature.py demo periodically notifies its value.
conn_handle, value_handle, notify_data = data
if conn_handle == self._conn_handle and value_handle == self._value_handle:
self._update_value(notify_data)
if self._notify_callback:
self._notify_callback(self._value)
# Returns true if we've successfully connected and discovered characteristics.
def is_connected(self):
return self._conn_handle is not None and self._value_handle is not None
# Find a device advertising the environmental sensor service.
def scan(self, callback=None):
self._addr_type = None
self._addr = None
self._scan_callback = callback
self._ble.gap_scan(2000, 30000, 30000)
# Connect to the specified device (otherwise use cached address from a scan).
def connect(self, addr_type=None, addr=None, callback=None):
self._addr_type = addr_type or self._addr_type
self._addr = addr or self._addr
self._conn_callback = callback
if self._addr_type is None or self._addr is None:
return False
self._ble.gap_connect(self._addr_type, self._addr)
return True
# Disconnect from current device.
def disconnect(self):
if not self._conn_handle:
return
self._ble.gap_disconnect(self._conn_handle)
self._reset()
# Issues an (asynchronous) read, will invoke callback with data.
def read(self, callback):
if not self.is_connected():
return
self._read_callback = callback
self._ble.gattc_read(self._conn_handle, self._value_handle)
# Sets a callback to be invoked when the device notifies us.
def on_notify(self, callback):
self._notify_callback = callback
def _update_value(self, data):
# Data is sint16 in degrees Celsius with a resolution of 0.01 degrees Celsius.
self._value = struct.unpack('<h', data)[0] / 100
return self._value
def value(self):
return self._value
def demo():
ble = bluetooth.BLE()
central = BLETemperatureCentral(ble)
not_found = False
def on_scan(addr_type, addr, name):
if addr_type is not None:
print('Found sensor:', addr_type, addr, name)
central.connect()
else:
nonlocal not_found
not_found = True
print('No sensor found.')
central.scan(callback=on_scan)
# Wait for connection...
while not central.is_connected():
time.sleep_ms(100)
if not_found:
return
print('Connected')
# Explicitly issue reads, using "print" as the callback.
while central.is_connected():
central.read(callback=print)
time.sleep_ms(2000)
# Alternative to the above, just show the most recently notified value.
# while central.is_connected():
# print(central.value())
# time.sleep_ms(2000)
print('Disconnected')
if __name__ == '__main__':
demo()

View File

@ -59,11 +59,13 @@ STATIC const mp_obj_type_t bluetooth_uuid_type;
typedef struct {
mp_obj_base_t base;
mp_obj_t irq_handler;
mp_obj_t irq_data_tuple;
uint8_t irq_addr_bytes[6];
uint8_t irq_data_bytes[MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_BYTES_LEN];
mp_obj_t irq_data_uuid;
uint16_t irq_trigger;
mp_obj_t irq_data_tuple;
uint8_t irq_data_addr_bytes[6];
uint8_t irq_data_data_bytes[MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_BYTES_LEN];
mp_obj_str_t irq_data_addr;
mp_obj_str_t irq_data_data;
mp_obj_bluetooth_uuid_t irq_data_uuid;
ringbuf_t ringbuf;
} mp_obj_bluetooth_ble_t;
@ -79,37 +81,6 @@ STATIC mp_obj_t bluetooth_handle_errno(int err) {
// UUID object
// ----------------------------------------------------------------------------
// Parse string UUIDs, which are expected to be 128-bit UUIDs.
STATIC void mp_bluetooth_parse_uuid_128bit_str(mp_obj_t obj, uint8_t *uuid) {
size_t str_len;
const char *str_data = mp_obj_str_get_data(obj, &str_len);
int uuid_i = 32;
for (int i = 0; i < str_len; i++) {
char c = str_data[i];
if (c == '-') {
continue;
}
if (!unichar_isxdigit(c)) {
mp_raise_ValueError("invalid char in UUID");
}
c = unichar_xdigit_value(c);
uuid_i--;
if (uuid_i < 0) {
mp_raise_ValueError("UUID too long");
}
if (uuid_i % 2 == 0) {
// lower nibble
uuid[uuid_i/2] |= c;
} else {
// upper nibble
uuid[uuid_i/2] = c << 4;
}
}
if (uuid_i > 0) {
mp_raise_ValueError("UUID too short");
}
}
STATIC mp_obj_t bluetooth_uuid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
mp_arg_check_num(n_args, n_kw, 1, 1, false);
@ -125,8 +96,41 @@ STATIC mp_obj_t bluetooth_uuid_make_new(const mp_obj_type_t *type, size_t n_args
self->data[0] = value & 0xff;
self->data[1] = (value >> 8) & 0xff;
} else {
self->type = MP_BLUETOOTH_UUID_TYPE_128;
mp_bluetooth_parse_uuid_128bit_str(all_args[0], self->data);
mp_buffer_info_t uuid_bufinfo = {0};
mp_get_buffer_raise(all_args[0], &uuid_bufinfo, MP_BUFFER_READ);
if (uuid_bufinfo.len == 2 || uuid_bufinfo.len == 4 || uuid_bufinfo.len == 16) {
// Bytes data -- infer UUID type from length and copy data.
self->type = uuid_bufinfo.len;
memcpy(self->data, uuid_bufinfo.buf, self->type);
} else {
// Assume UUID string (e.g. '6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
self->type = MP_BLUETOOTH_UUID_TYPE_128;
int uuid_i = 32;
for (int i = 0; i < uuid_bufinfo.len; i++) {
char c = ((char*)uuid_bufinfo.buf)[i];
if (c == '-') {
continue;
}
if (!unichar_isxdigit(c)) {
mp_raise_ValueError("invalid char in UUID");
}
c = unichar_xdigit_value(c);
uuid_i--;
if (uuid_i < 0) {
mp_raise_ValueError("UUID too long");
}
if (uuid_i % 2 == 0) {
// lower nibble
self->data[uuid_i/2] |= c;
} else {
// upper nibble
self->data[uuid_i/2] = c << 4;
}
}
if (uuid_i > 0) {
mp_raise_ValueError("UUID too short");
}
}
}
return self;
@ -143,6 +147,30 @@ STATIC mp_obj_t bluetooth_uuid_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
}
}
STATIC mp_obj_t bluetooth_uuid_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
if (!mp_obj_is_type(rhs_in, &bluetooth_uuid_type)) {
return MP_OBJ_NULL;
}
mp_obj_bluetooth_uuid_t *lhs = MP_OBJ_TO_PTR(lhs_in);
mp_obj_bluetooth_uuid_t *rhs = MP_OBJ_TO_PTR(rhs_in);
switch (op) {
case MP_BINARY_OP_EQUAL:
case MP_BINARY_OP_LESS:
case MP_BINARY_OP_LESS_EQUAL:
case MP_BINARY_OP_MORE:
case MP_BINARY_OP_MORE_EQUAL:
if (lhs->type == rhs->type) {
return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs->data, lhs->type, rhs->data, rhs->type));
} else {
return mp_binary_op(op, MP_OBJ_NEW_SMALL_INT(lhs->type), MP_OBJ_NEW_SMALL_INT(rhs->type));
}
default:
return MP_OBJ_NULL; // op not supported
}
}
STATIC void bluetooth_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
mp_obj_bluetooth_uuid_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "UUID%u(%s", self->type * 8, self->type <= 4 ? "0x" : "'");
@ -196,6 +224,7 @@ STATIC const mp_obj_type_t bluetooth_uuid_type = {
.name = MP_QSTR_UUID,
.make_new = bluetooth_uuid_make_new,
.unary_op = bluetooth_uuid_unary_op,
.binary_op = bluetooth_uuid_binary_op,
.locals_dict = NULL,
.print = bluetooth_uuid_print,
.buffer_p = { .get_buffer = bluetooth_uuid_get_buffer },
@ -207,16 +236,25 @@ STATIC const mp_obj_type_t bluetooth_uuid_type = {
STATIC mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
if (MP_STATE_VM(bluetooth) == MP_OBJ_NULL) {
mp_obj_bluetooth_ble_t *o = m_new_obj(mp_obj_bluetooth_ble_t);
mp_obj_bluetooth_ble_t *o = m_new0(mp_obj_bluetooth_ble_t, 1);
o->base.type = &bluetooth_ble_type;
o->irq_handler = mp_const_none;
o->irq_trigger = 0;
// Pre-allocate the event data tuple to prevent needing to allocate in the IRQ handler.
o->irq_data_tuple = mp_obj_new_tuple(MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN, NULL);
mp_obj_bluetooth_uuid_t *uuid = m_new_obj(mp_obj_bluetooth_uuid_t);
uuid->base.type = &bluetooth_uuid_type;
o->irq_data_uuid = MP_OBJ_FROM_PTR(uuid);
o->irq_trigger = 0;
// Pre-allocated buffers for address, payload and uuid.
o->irq_data_addr.base.type = &mp_type_bytes;
o->irq_data_addr.data = o->irq_data_addr_bytes;
o->irq_data_data.base.type = &mp_type_bytes;
o->irq_data_data.data = o->irq_data_data_bytes;
o->irq_data_uuid.base.type = &bluetooth_uuid_type;
// Allocate the ringbuf. TODO: Consider making the size user-specified.
ringbuf_alloc(&o->ringbuf, MICROPY_PY_BLUETOOTH_RINGBUF_SIZE);
MP_STATE_VM(bluetooth) = MP_OBJ_FROM_PTR(o);
}
return MP_STATE_VM(bluetooth);
@ -737,36 +775,31 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) {
mp_obj_t handler = handler = o->irq_handler;
mp_obj_tuple_t *data_tuple = MP_OBJ_TO_PTR(o->irq_data_tuple);
// Some events need to pass bytes objects to their handler, using the
// pre-allocated bytes array.
mp_obj_str_t irq_data_bytes_addr = {{&mp_type_bytes}, 0, 6, o->irq_addr_bytes};
mp_obj_str_t irq_data_bytes_data = {{&mp_type_bytes}, 0, 0, o->irq_data_bytes};
if (event == MP_BLUETOOTH_IRQ_CENTRAL_CONNECT || event == MP_BLUETOOTH_IRQ_PERIPHERAL_CONNECT || event == MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT || event == MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT) {
// conn_handle, addr_type, addr
ringbuf_extract(&o->ringbuf, data_tuple, 1, 1, &irq_data_bytes_addr, 0, 0, NULL, NULL);
ringbuf_extract(&o->ringbuf, data_tuple, 1, 1, &o->irq_data_addr, 0, 0, NULL, NULL);
} else if (event == MP_BLUETOOTH_IRQ_GATTS_WRITE) {
// conn_handle, value_handle
ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, 0, NULL, NULL);
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
} else if (event == MP_BLUETOOTH_IRQ_SCAN_RESULT) {
// addr_type, addr, connectable, rssi, adv_data
ringbuf_extract(&o->ringbuf, data_tuple, 0, 1, &irq_data_bytes_addr, 1, 1, NULL, &irq_data_bytes_data);
ringbuf_extract(&o->ringbuf, data_tuple, 0, 1, &o->irq_data_addr, 1, 1, NULL, &o->irq_data_data);
} else if (event == MP_BLUETOOTH_IRQ_SCAN_COMPLETE) {
// No params required.
data_tuple->len = 0;
} else if (event == MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT) {
// conn_handle, start_handle, end_handle, uuid
ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, 0, MP_OBJ_TO_PTR(o->irq_data_uuid), NULL);
ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, 0, &o->irq_data_uuid, NULL);
} else if (event == MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_RESULT) {
// conn_handle, def_handle, value_handle, properties, uuid
ringbuf_extract(&o->ringbuf, data_tuple, 3, 1, NULL, 0, 0, MP_OBJ_TO_PTR(o->irq_data_uuid), NULL);
ringbuf_extract(&o->ringbuf, data_tuple, 3, 1, NULL, 0, 0, &o->irq_data_uuid, NULL);
} else if (event == MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_RESULT) {
// conn_handle, handle, uuid
ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, 0, MP_OBJ_TO_PTR(o->irq_data_uuid), NULL);
ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, 0, &o->irq_data_uuid, NULL);
} else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_RESULT || event == MP_BLUETOOTH_IRQ_GATTC_NOTIFY || event == MP_BLUETOOTH_IRQ_GATTC_INDICATE) {
// conn_handle, value_handle, data
ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, 0, NULL, &irq_data_bytes_data);
ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, 0, NULL, &o->irq_data_data);
} else if (event == MP_BLUETOOTH_IRQ_GATTC_WRITE_STATUS) {
// conn_handle, value_handle, status
ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, 0, NULL, NULL);

View File

@ -117,14 +117,14 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(const ble_uuid_any_t *uuid) {
case BLE_UUID_TYPE_16:
result.type = MP_BLUETOOTH_UUID_TYPE_16;
result.data[0] = uuid->u16.value & 0xff;
result.data[1] = (uuid->u16.value << 8) & 0xff;
result.data[1] = (uuid->u16.value >> 8) & 0xff;
break;
case BLE_UUID_TYPE_32:
result.type = MP_BLUETOOTH_UUID_TYPE_32;
result.data[0] = uuid->u32.value & 0xff;
result.data[1] = (uuid->u32.value << 8) & 0xff;
result.data[2] = (uuid->u32.value << 16) & 0xff;
result.data[3] = (uuid->u32.value << 24) & 0xff;
result.data[1] = (uuid->u32.value >> 8) & 0xff;
result.data[2] = (uuid->u32.value >> 16) & 0xff;
result.data[3] = (uuid->u32.value >> 24) & 0xff;
break;
case BLE_UUID_TYPE_128:
result.type = MP_BLUETOOTH_UUID_TYPE_128;
@ -659,6 +659,9 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_
}
int mp_bluetooth_gap_scan_stop(void) {
if (!ble_gap_disc_active()) {
return 0;
}
int err = ble_gap_disc_cancel();
if (err == 0) {
mp_bluetooth_gap_on_scan_complete();

View File

@ -33,7 +33,7 @@
enum { LFS_MAKE_ARG_bdev, LFS_MAKE_ARG_readsize, LFS_MAKE_ARG_progsize, LFS_MAKE_ARG_lookahead };
static const mp_arg_t lfs_make_allowed_args[] = {
{ MP_QSTR_, MP_ARG_OBJ },
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_readsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} },
{ MP_QSTR_progsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} },
{ MP_QSTR_lookahead, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} },

View File

@ -73,7 +73,7 @@ STATIC void MP_VFS_LFSx(init_config)(MP_OBJ_VFS_LFSx *self, mp_obj_t bdev, size_
config->erase = MP_VFS_LFSx(dev_erase);
config->sync = MP_VFS_LFSx(dev_sync);
MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_INIT, 0, false); // initialise block device
MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_INIT, 1, false); // initialise block device
int bs = MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_BLOCK_SIZE, 0, true); // get block size
int bc = MP_VFS_LFSx(dev_ioctl)(config, MP_BLOCKDEV_IOCTL_BLOCK_COUNT, 0, true); // get block count
self->blockdev.block_size = bs;

View File

@ -33,55 +33,55 @@ SWDIO,PA13
SWCLK,PA14
USER_B1,PC13
LED_GREEN,PA5
PA0,PA0
PA1,PA1
PA2,PA2
PA3,PA3
PA4,PA4
PA5,PA5
PA6,PA6
PA7,PA7
PA8,PA8
PA9,PA9
PA10,PA10
PA11,PA11
PA12,PA12
PA13,PA13
PA14,PA14
PA15,PA15
PB0,PB0
PB1,PB1
PB2,PB2
PB3,PB3
PB4,PB4
PB5,PB5
PB6,PB6
PB7,PB7
PB8,PB8
PB9,PB9
PB10,PB10
PB11,PB11
PB12,PB12
PB13,PB13
PB14,PB14
PB15,PB15
PC0,PC0
PC1,PC1
PC2,PC2
PC3,PC3
PC4,PC4
PC5,PC5
PC6,PC6
PC7,PC7
PC8,PC8
PC9,PC9
PC10,PC10
PC11,PC11
PC12,PC12
PC13,PC13
PC14,PC14
PC15,PC15
PD2,PD2
PF0,PF0
PF1,PF1
PF11,PF11
,PA0
,PA1
,PA2
,PA3
,PA4
,PA5
,PA6
,PA7
,PA8
,PA9
,PA10
,PA11
,PA12
,PA13
,PA14
,PA15
,PB0
,PB1
,PB2
,PB3
,PB4
,PB5
,PB6
,PB7
,PB8
,PB9
,PB10
,PB11
,PB12
,PB13
,PB14
,PB15
,PC0
,PC1
,PC2
,PC3
,PC4
,PC5
,PC6
,PC7
,PC8
,PC9
,PC10
,PC11
,PC12
,PC13
,PC14
,PC15
,PD2
,PF0
,PF1
,PF11

1 D0 PA3
33 SWCLK PA14
34 USER_B1 PC13
35 LED_GREEN PA5
36 PA0 PA0
37 PA1 PA1
38 PA2 PA2
39 PA3 PA3
40 PA4 PA4
41 PA5 PA5
42 PA6 PA6
43 PA7 PA7
44 PA8 PA8
45 PA9 PA9
46 PA10 PA10
47 PA11 PA11
48 PA12 PA12
49 PA13 PA13
50 PA14 PA14
51 PA15 PA15
52 PB0 PB0
53 PB1 PB1
54 PB2 PB2
55 PB3 PB3
56 PB4 PB4
57 PB5 PB5
58 PB6 PB6
59 PB7 PB7
60 PB8 PB8
61 PB9 PB9
62 PB10 PB10
63 PB11 PB11
64 PB12 PB12
65 PB13 PB13
66 PB14 PB14
67 PB15 PB15
68 PC0 PC0
69 PC1 PC1
70 PC2 PC2
71 PC3 PC3
72 PC4 PC4
73 PC5 PC5
74 PC6 PC6
75 PC7 PC7
76 PC8 PC8
77 PC9 PC9
78 PC10 PC10
79 PC11 PC11
80 PC12 PC12
81 PC13 PC13
82 PC14 PC14
83 PC15 PC15
84 PD2 PD2
85 PF0 PF0
86 PF1 PF1
87 PF11 PF11

View File

@ -84,6 +84,7 @@ extern struct _spi_bdev_t spi_bdev;
)
#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(&spi_bdev, (dest), (bl), (n))
#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(&spi_bdev, (src), (bl), (n))
#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) // for extended block protocol
// SPI flash #2, to be memory mapped
#define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (24)

View File

@ -16,3 +16,4 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_NETWORK_CYW43 = 1
MICROPY_PY_USSL = 1
MICROPY_SSL_MBEDTLS = 1
MICROPY_VFS_LFS2 = 1

View File

@ -11,3 +11,6 @@ LD_FILES = boards/stm32f405.ld boards/common_ifs.ld
TEXT0_ADDR = 0x08000000
TEXT1_ADDR = 0x08020000
endif
# MicroPython settings
MICROPY_VFS_LFS2 = 1

View File

@ -11,3 +11,6 @@ LD_FILES = boards/stm32f405.ld boards/common_ifs.ld
TEXT0_ADDR = 0x08000000
TEXT1_ADDR = 0x08020000
endif
# MicroPython settings
MICROPY_VFS_LFS2 = 1

View File

@ -25,6 +25,11 @@
*/
#include "py/runtime.h"
#include "py/mperrno.h"
#include "extmod/vfs_fat.h"
#include "systick.h"
#include "led.h"
#include "storage.h"
#include "factoryreset.h"
#if MICROPY_HW_ENABLE_STORAGE
@ -96,4 +101,31 @@ MP_WEAK void factory_reset_make_files(FATFS *fatfs) {
}
}
MP_WEAK int factory_reset_create_filesystem(void) {
// LED on to indicate creation of local filesystem
led_state(PYB_LED_GREEN, 1);
uint32_t start_tick = HAL_GetTick();
fs_user_mount_t vfs;
pyb_flash_init_vfs(&vfs);
uint8_t working_buf[FF_MAX_SS];
FRESULT res = f_mkfs(&vfs.fatfs, FM_FAT, 0, working_buf, sizeof(working_buf));
if (res != FR_OK) {
mp_printf(&mp_plat_print, "MPY: can't create flash filesystem\n");
return -MP_ENODEV;
}
// Set label
f_setlabel(&vfs.fatfs, MICROPY_HW_FLASH_FS_LABEL);
// Populate the filesystem with factory files
factory_reset_make_files(&vfs.fatfs);
// Keep LED on for at least 200ms
systick_wait_at_least(start_tick, 200);
led_state(PYB_LED_GREEN, 0);
return 0; // success
}
#endif // MICROPY_HW_ENABLE_STORAGE

View File

@ -29,5 +29,6 @@
#include "lib/oofatfs/ff.h"
void factory_reset_make_files(FATFS *fatfs);
int factory_reset_create_filesystem(void);
#endif // MICROPY_INCLUDED_STM32_FACTORYRESET_H

View File

@ -292,4 +292,44 @@ bool flash_bdev_writeblock(const uint8_t *src, uint32_t block) {
return true;
}
int flash_bdev_readblocks_ext(uint8_t *dest, uint32_t block, uint32_t offset, uint32_t len) {
// Get data from flash memory, possibly via cache
while (len) {
uint32_t l = MIN(len, FLASH_BLOCK_SIZE - offset);
uint32_t flash_addr = convert_block_to_flash_addr(block);
if (flash_addr == -1) {
// bad block number
return -1;
}
uint8_t *src = flash_cache_get_addr_for_read(flash_addr + offset);
memcpy(dest, src, l);
dest += l;
block += 1;
offset = 0;
len -= l;
}
return 0;
}
int flash_bdev_writeblocks_ext(const uint8_t *src, uint32_t block, uint32_t offset, uint32_t len) {
// Copy to cache
while (len) {
uint32_t l = MIN(len, FLASH_BLOCK_SIZE - offset);
uint32_t flash_addr = convert_block_to_flash_addr(block);
if (flash_addr == -1) {
// bad block number
return -1;
}
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
uint8_t *dest = flash_cache_get_addr_for_write(flash_addr + offset);
memcpy(dest, src, l);
restore_irq_pri(basepri);
src += l;
block += 1;
offset = 0;
len -= l;
}
return 0;
}
#endif // MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE

View File

@ -30,12 +30,18 @@
#include "py/runtime.h"
#include "py/stackctrl.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "py/mphal.h"
#include "lib/mp-readline/readline.h"
#include "lib/utils/pyexec.h"
#include "lib/oofatfs/ff.h"
#include "lib/littlefs/lfs1.h"
#include "lib/littlefs/lfs1_util.h"
#include "lib/littlefs/lfs2.h"
#include "lib/littlefs/lfs2_util.h"
#include "extmod/vfs.h"
#include "extmod/vfs_fat.h"
#include "extmod/vfs_lfs.h"
#if MICROPY_PY_LWIP
#include "lwip/init.h"
@ -81,10 +87,6 @@
STATIC pyb_thread_t pyb_thread_main;
#endif
#if MICROPY_HW_ENABLE_STORAGE
STATIC fs_user_mount_t fs_user_mount_flash;
#endif
#if defined(MICROPY_HW_UART_REPL)
#ifndef MICROPY_HW_UART_REPL_RXBUF
#define MICROPY_HW_UART_REPL_RXBUF (260)
@ -159,65 +161,91 @@ STATIC mp_obj_t pyb_main(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
MP_DEFINE_CONST_FUN_OBJ_KW(pyb_main_obj, 1, pyb_main);
#if MICROPY_HW_ENABLE_STORAGE
STATIC int vfs_mount_and_chdir(mp_obj_t bdev, mp_obj_t mount_point) {
nlr_buf_t nlr;
mp_int_t ret = -MP_EIO;
if (nlr_push(&nlr) == 0) {
mp_obj_t args[] = { bdev, mount_point };
mp_vfs_mount(2, args, (mp_map_t*)&mp_const_empty_map);
mp_vfs_chdir(mount_point);
ret = 0; // success
nlr_pop();
} else {
mp_obj_base_t *exc = nlr.ret_val;
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_OSError))) {
mp_obj_t v = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc));
mp_obj_get_int_maybe(v, &ret); // get errno value
ret = -ret;
}
}
return ret;
}
// avoid inlining to avoid stack usage within main()
MP_NOINLINE STATIC bool init_flash_fs(uint reset_mode) {
// init the vfs object
fs_user_mount_t *vfs_fat = &fs_user_mount_flash;
vfs_fat->blockdev.flags = 0;
pyb_flash_init_vfs(vfs_fat);
if (reset_mode == 3) {
// Asked by user to reset filesystem
factory_reset_create_filesystem();
}
// try to mount the flash
FRESULT res = f_mount(&vfs_fat->fatfs);
// Default block device to entire flash storage
mp_obj_t bdev = MP_OBJ_FROM_PTR(&pyb_flash_obj);
if (reset_mode == 3 || res == FR_NO_FILESYSTEM) {
// no filesystem, or asked to reset it, so create a fresh one
#if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2
// LED on to indicate creation of LFS
led_state(PYB_LED_GREEN, 1);
uint32_t start_tick = HAL_GetTick();
// Try to detect the block device used for the main filesystem, based on the first block
uint8_t working_buf[FF_MAX_SS];
res = f_mkfs(&vfs_fat->fatfs, FM_FAT, 0, working_buf, sizeof(working_buf));
if (res == FR_OK) {
// success creating fresh LFS
} else {
printf("MPY: can't create flash filesystem\n");
return false;
uint8_t buf[FLASH_BLOCK_SIZE];
storage_read_blocks(buf, FLASH_PART1_START_BLOCK, 1);
mp_int_t len = -1;
#if MICROPY_VFS_LFS1
if (memcmp(&buf[40], "littlefs", 8) == 0) {
// LFS1
lfs1_superblock_t *superblock = (void*)&buf[12];
uint32_t block_size = lfs1_fromle32(superblock->d.block_size);
uint32_t block_count = lfs1_fromle32(superblock->d.block_count);
len = block_count * block_size;
}
#endif
#if MICROPY_VFS_LFS2
if (memcmp(&buf[8], "littlefs", 8) == 0) {
// LFS2
lfs2_superblock_t *superblock = (void*)&buf[20];
uint32_t block_size = lfs2_fromle32(superblock->block_size);
uint32_t block_count = lfs2_fromle32(superblock->block_count);
len = block_count * block_size;
}
#endif
if (len != -1) {
// Detected a littlefs filesystem so create correct block device for it
mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR_len), MP_OBJ_NEW_SMALL_INT(len) };
bdev = pyb_flash_type.make_new(&pyb_flash_type, 0, 1, args);
}
#endif
// Try to mount the flash on "/flash" and chdir to it for the boot-up directory.
mp_obj_t mount_point = MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash);
int ret = vfs_mount_and_chdir(bdev, mount_point);
if (ret == -MP_ENODEV && bdev == MP_OBJ_FROM_PTR(&pyb_flash_obj) && reset_mode != 3) {
// No filesystem, bdev is still the default (so didn't detect a possibly corrupt littlefs),
// and didn't already create a filesystem, so try to create a fresh one now.
ret = factory_reset_create_filesystem();
if (ret == 0) {
ret = vfs_mount_and_chdir(bdev, mount_point);
}
}
// set label
f_setlabel(&vfs_fat->fatfs, MICROPY_HW_FLASH_FS_LABEL);
// populate the filesystem with factory files
factory_reset_make_files(&vfs_fat->fatfs);
// keep LED on for at least 200ms
systick_wait_at_least(start_tick, 200);
led_state(PYB_LED_GREEN, 0);
} else if (res == FR_OK) {
// mount sucessful
} else {
fail:
if (ret != 0) {
printf("MPY: can't mount flash\n");
return false;
}
// mount the flash device (there should be no other devices mounted at this point)
// we allocate this structure on the heap because vfs->next is a root pointer
mp_vfs_mount_t *vfs = m_new_obj_maybe(mp_vfs_mount_t);
if (vfs == NULL) {
goto fail;
}
vfs->str = "/flash";
vfs->len = 6;
vfs->obj = MP_OBJ_FROM_PTR(vfs_fat);
vfs->next = NULL;
MP_STATE_VM(vfs_mount_table) = vfs;
// The current directory is used as the boot up directory.
// It is set to the internal flash filesystem by default.
MP_STATE_PORT(vfs_cur) = vfs;
return true;
}
#endif
@ -616,7 +644,7 @@ soft_reset:
// if an SD card is present then mount it on /sd/
if (sdcard_is_present()) {
// if there is a file in the flash called "SKIPSD", then we don't mount the SD card
if (!mounted_flash || f_stat(&fs_user_mount_flash.fatfs, "/SKIPSD", NULL) != FR_OK) {
if (!mounted_flash || mp_vfs_import_stat("SKIPSD") == MP_IMPORT_STAT_FILE) {
mounted_sdcard = init_sdcard_fs();
}
}

View File

@ -36,6 +36,7 @@
#include "extmod/misc.h"
#include "extmod/vfs.h"
#include "extmod/vfs_fat.h"
#include "extmod/vfs_lfs.h"
#include "genhdr/mpversion.h"
#include "rng.h"
#include "usb.h"
@ -174,6 +175,12 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = {
#if MICROPY_VFS_FAT
{ MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) },
#endif
#if MICROPY_VFS_LFS1
{ MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) },
#endif
#if MICROPY_VFS_LFS2
{ MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table);

View File

@ -162,7 +162,9 @@ void nimble_hci_uart_tx_strn(const char *str, uint len) {
if (mp_hal_pin_read(pyb_pin_BT_DEV_WAKE) == 1) {
//printf("BT WAKE for TX\n");
mp_hal_pin_low(pyb_pin_BT_DEV_WAKE); // wake up
mp_hal_delay_ms(5); // can't go lower than this
// Use delay_us rather than delay_ms to prevent running the scheduler (which
// might result in more BLE operations).
mp_hal_delay_us(5000); // can't go lower than this
}
#endif

View File

@ -26,22 +26,18 @@
// qstrs specific to this port
Q(boot.py)
Q(main.py)
// Entries for sys.path
Q(/flash)
Q(/flash/lib)
Q(/sd)
Q(/sd/lib)
// For uos.sep
Q(/)
#if MICROPY_HW_ENABLE_USB
// for usb modes
Q(MSC+HID)
Q(VCP+MSC)
Q(VCP+HID)
Q(CDC+MSC)
Q(CDC+HID)
Q(/)
// The following qstrings not referenced from anywhere in the sources
Q(CDC)
Q(flash)
#endif

View File

@ -55,6 +55,13 @@ int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
restore_irq_pri(basepri);
}
return 0;
case BDEV_IOCTL_BLOCK_ERASE: {
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
mp_spiflash_erase_block(&bdev->spiflash, arg * MP_SPIFLASH_ERASE_BLOCK_SIZE);
restore_irq_pri(basepri);
return 0;
}
}
return -MP_EINVAL;
}
@ -79,4 +86,20 @@ int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_nu
return ret;
}
int spi_bdev_readblocks_raw(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes) {
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
mp_spiflash_read(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE + block_offset, num_bytes, dest);
restore_irq_pri(basepri);
return 0;
}
int spi_bdev_writeblocks_raw(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes) {
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
int ret = mp_spiflash_write(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE + block_offset, num_bytes, src);
restore_irq_pri(basepri);
return ret;
}
#endif

View File

@ -28,6 +28,7 @@
#include <string.h>
#include "py/runtime.h"
#include "py/mperrno.h"
#include "extmod/vfs_fat.h"
#include "systick.h"
@ -40,8 +41,6 @@
#define STORAGE_SYSTICK_MASK (0x1ff) // 512ms
#define STORAGE_IDLE_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & STORAGE_SYSTICK_MASK) == 0)
#define FLASH_PART1_START_BLOCK (0x100)
#if defined(MICROPY_HW_BDEV2_IOCTL)
#define FLASH_PART2_START_BLOCK (FLASH_PART1_START_BLOCK + MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_NUM_BLOCKS, 0))
#endif
@ -186,7 +185,7 @@ bool storage_write_block(const uint8_t *src, uint32_t block) {
}
}
mp_uint_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
int storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
#if defined(MICROPY_HW_BDEV_READBLOCKS)
if (FLASH_PART1_START_BLOCK <= block_num && block_num + num_blocks <= FLASH_PART1_START_BLOCK + MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_NUM_BLOCKS, 0)) {
return MICROPY_HW_BDEV_READBLOCKS(dest, block_num - FLASH_PART1_START_BLOCK, num_blocks);
@ -201,13 +200,13 @@ mp_uint_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_bl
for (size_t i = 0; i < num_blocks; i++) {
if (!storage_read_block(dest + i * FLASH_BLOCK_SIZE, block_num + i)) {
return 1; // error
return -MP_EIO; // error
}
}
return 0; // success
}
mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
int storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
#if defined(MICROPY_HW_BDEV_WRITEBLOCKS)
if (FLASH_PART1_START_BLOCK <= block_num && block_num + num_blocks <= FLASH_PART1_START_BLOCK + MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_NUM_BLOCKS, 0)) {
return MICROPY_HW_BDEV_WRITEBLOCKS(src, block_num - FLASH_PART1_START_BLOCK, num_blocks);
@ -222,7 +221,7 @@ mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t
for (size_t i = 0; i < num_blocks; i++) {
if (!storage_write_block(src + i * FLASH_BLOCK_SIZE, block_num + i)) {
return 1; // error
return -MP_EIO; // error
}
}
return 0; // success
@ -233,41 +232,197 @@ mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t
//
// Expose the flash as an object with the block protocol.
// there is a singleton Flash object
STATIC const mp_obj_base_t pyb_flash_obj = {&pyb_flash_type};
#ifdef MICROPY_HW_BDEV_SPIFLASH_EXTENDED
// Board defined an external SPI flash for use with extended block protocol
#define SPIFLASH (MICROPY_HW_BDEV_SPIFLASH_EXTENDED)
#define PYB_FLASH_NATIVE_BLOCK_SIZE (MP_SPIFLASH_ERASE_BLOCK_SIZE)
#define MICROPY_HW_BDEV_READBLOCKS_EXT(dest, bl, off, len) (spi_bdev_readblocks_raw(SPIFLASH, (dest), (bl), (off), (len)))
#define MICROPY_HW_BDEV_WRITEBLOCKS_EXT(dest, bl, off, len) (spi_bdev_writeblocks_raw(SPIFLASH, (dest), (bl), (off), (len)))
STATIC mp_obj_t pyb_flash_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);
#elif (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) && MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE
// Board uses littlefs and internal flash, so enable extended block protocol on internal flash
#define PYB_FLASH_NATIVE_BLOCK_SIZE (FLASH_BLOCK_SIZE)
#define MICROPY_HW_BDEV_READBLOCKS_EXT(dest, bl, off, len) (flash_bdev_readblocks_ext((dest), (bl), (off), (len)))
#define MICROPY_HW_BDEV_WRITEBLOCKS_EXT(dest, bl, off, len) (flash_bdev_writeblocks_ext((dest), (bl), (off), (len)))
#endif
// return singleton object
return MP_OBJ_FROM_PTR(&pyb_flash_obj);
#ifndef PYB_FLASH_NATIVE_BLOCK_SIZE
#define PYB_FLASH_NATIVE_BLOCK_SIZE (FLASH_BLOCK_SIZE)
#endif
typedef struct _pyb_flash_obj_t {
mp_obj_base_t base;
uint32_t start; // in bytes
uint32_t len; // in bytes
#if defined(SPIFLASH)
bool use_native_block_size;
#endif
} pyb_flash_obj_t;
// This Flash object represents the entire available flash, with emulated partition table at start
const pyb_flash_obj_t pyb_flash_obj = {
{ &pyb_flash_type },
-(FLASH_PART1_START_BLOCK * FLASH_BLOCK_SIZE), // to offset FLASH_PART1_START_BLOCK
0, // actual size handled in ioctl, MP_BLOCKDEV_IOCTL_BLOCK_COUNT case
};
STATIC void pyb_flash_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
pyb_flash_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self == &pyb_flash_obj) {
mp_printf(print, "Flash()");
} else {
mp_printf(print, "Flash(start=%u, len=%u)", self->start, self->len);
}
}
STATIC mp_obj_t pyb_flash_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
STATIC mp_obj_t pyb_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
// Parse arguments
enum { ARG_start, ARG_len };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
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);
if (args[ARG_start].u_int == -1 && args[ARG_len].u_int == -1) {
// Default singleton object that accesses entire flash, including virtual partition table
return MP_OBJ_FROM_PTR(&pyb_flash_obj);
}
pyb_flash_obj_t *self = m_new_obj(pyb_flash_obj_t);
self->base.type = &pyb_flash_type;
#if defined(SPIFLASH)
self->use_native_block_size = false;
#endif
uint32_t bl_len = (storage_get_block_count() - FLASH_PART1_START_BLOCK) * FLASH_BLOCK_SIZE;
mp_int_t start = args[ARG_start].u_int;
if (start == -1) {
start = 0;
} else if (!(0 <= start && start < bl_len && start % PYB_FLASH_NATIVE_BLOCK_SIZE == 0)) {
mp_raise_ValueError(NULL);
}
mp_int_t len = args[ARG_len].u_int;
if (len == -1) {
len = bl_len - start;
} else if (!(0 < len && start + len <= bl_len && len % PYB_FLASH_NATIVE_BLOCK_SIZE == 0)) {
mp_raise_ValueError(NULL);
}
self->start = start;
self->len = len;
return MP_OBJ_FROM_PTR(self);
}
STATIC mp_obj_t pyb_flash_readblocks(size_t n_args, const mp_obj_t *args) {
pyb_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
uint32_t block_num = mp_obj_get_int(args[1]);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE);
mp_uint_t ret = storage_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE);
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE);
mp_uint_t ret = -MP_EIO;
if (n_args == 3) {
// Cast self->start to signed in case it's pyb_flash_obj with negative start
block_num += FLASH_PART1_START_BLOCK + (int32_t)self->start / FLASH_BLOCK_SIZE;
ret = storage_read_blocks(bufinfo.buf, block_num, bufinfo.len / FLASH_BLOCK_SIZE);
}
#if defined(MICROPY_HW_BDEV_READBLOCKS_EXT)
else if (self != &pyb_flash_obj) {
// Extended block read on a sub-section of the flash storage
uint32_t offset = mp_obj_get_int(args[3]);
block_num += self->start / PYB_FLASH_NATIVE_BLOCK_SIZE;
ret = MICROPY_HW_BDEV_READBLOCKS_EXT(bufinfo.buf, block_num, offset, bufinfo.len);
}
#endif
return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(pyb_flash_readblocks_obj, pyb_flash_readblocks);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_flash_readblocks_obj, 3, 4, pyb_flash_readblocks);
STATIC mp_obj_t pyb_flash_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
STATIC mp_obj_t pyb_flash_writeblocks(size_t n_args, const mp_obj_t *args) {
pyb_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
uint32_t block_num = mp_obj_get_int(args[1]);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
mp_uint_t ret = storage_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE);
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
mp_uint_t ret = -MP_EIO;
if (n_args == 3) {
// Cast self->start to signed in case it's pyb_flash_obj with negative start
block_num += FLASH_PART1_START_BLOCK + (int32_t)self->start / FLASH_BLOCK_SIZE;
ret = storage_write_blocks(bufinfo.buf, block_num, bufinfo.len / FLASH_BLOCK_SIZE);
}
#if defined(MICROPY_HW_BDEV_WRITEBLOCKS_EXT)
else if (self != &pyb_flash_obj) {
// Extended block write on a sub-section of the flash storage
uint32_t offset = mp_obj_get_int(args[3]);
block_num += self->start / PYB_FLASH_NATIVE_BLOCK_SIZE;
ret = MICROPY_HW_BDEV_WRITEBLOCKS_EXT(bufinfo.buf, block_num, offset, bufinfo.len);
}
#endif
return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(pyb_flash_writeblocks_obj, pyb_flash_writeblocks);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_flash_writeblocks_obj, 3, 4, pyb_flash_writeblocks);
STATIC mp_obj_t pyb_flash_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) {
STATIC mp_obj_t pyb_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) {
pyb_flash_obj_t *self = self_in;
mp_int_t cmd = mp_obj_get_int(cmd_in);
switch (cmd) {
case MP_BLOCKDEV_IOCTL_INIT: storage_init(); return MP_OBJ_NEW_SMALL_INT(0);
case MP_BLOCKDEV_IOCTL_INIT: {
mp_int_t ret = 0;
storage_init();
if (mp_obj_get_int(arg_in) == 1) {
// Will be using extended block protocol
if (self == &pyb_flash_obj) {
ret = -1;
#if defined(SPIFLASH)
} else {
// Switch to use native block size of SPI flash
self->use_native_block_size = true;
#endif
}
}
return MP_OBJ_NEW_SMALL_INT(ret);
}
case MP_BLOCKDEV_IOCTL_DEINIT: storage_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly
case MP_BLOCKDEV_IOCTL_SYNC: storage_flush(); return MP_OBJ_NEW_SMALL_INT(0);
case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: return MP_OBJ_NEW_SMALL_INT(storage_get_block_count());
case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: return MP_OBJ_NEW_SMALL_INT(storage_get_block_size());
case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: {
mp_int_t n;
if (self == &pyb_flash_obj) {
// Get true size
n = storage_get_block_count();
#if defined(SPIFLASH)
} else if (self->use_native_block_size) {
n = self->len / PYB_FLASH_NATIVE_BLOCK_SIZE;
#endif
} else {
n = self->len / FLASH_BLOCK_SIZE;
}
return MP_OBJ_NEW_SMALL_INT(n);
}
case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: {
mp_int_t n = FLASH_BLOCK_SIZE;
#if defined(SPIFLASH)
if (self->use_native_block_size) {
n = PYB_FLASH_NATIVE_BLOCK_SIZE;
}
#endif
return MP_OBJ_NEW_SMALL_INT(n);
}
case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: {
int ret = 0;
#if defined(SPIFLASH)
if (self->use_native_block_size) {
mp_int_t block_num = self->start / PYB_FLASH_NATIVE_BLOCK_SIZE + mp_obj_get_int(arg_in);
ret = spi_bdev_ioctl(SPIFLASH, BDEV_IOCTL_BLOCK_ERASE, block_num);
}
#endif
return MP_OBJ_NEW_SMALL_INT(ret);
}
default: return mp_const_none;
}
}
@ -284,6 +439,7 @@ STATIC MP_DEFINE_CONST_DICT(pyb_flash_locals_dict, pyb_flash_locals_dict_table);
const mp_obj_type_t pyb_flash_type = {
{ &mp_type_type },
.name = MP_QSTR_Flash,
.print = pyb_flash_print,
.make_new = pyb_flash_make_new,
.locals_dict = (mp_obj_dict_t*)&pyb_flash_locals_dict,
};

View File

@ -29,13 +29,15 @@
#include "drivers/memory/spiflash.h"
#define FLASH_BLOCK_SIZE (512)
#define FLASH_PART1_START_BLOCK (0x100)
// Try to match Python-level VFS block protocol where possible for these constants
enum {
BDEV_IOCTL_INIT = 1,
BDEV_IOCTL_SYNC = 3,
BDEV_IOCTL_NUM_BLOCKS = 4,
BDEV_IOCTL_IRQ_HANDLER = 6,
BDEV_IOCTL_BLOCK_ERASE = 6,
BDEV_IOCTL_IRQ_HANDLER = 7,
};
void storage_init(void);
@ -45,13 +47,15 @@ void storage_flush(void);
bool storage_read_block(uint8_t *dest, uint32_t block);
bool storage_write_block(const uint8_t *src, uint32_t block);
// these return 0 on success, non-zero on error
mp_uint_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks);
mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks);
// these return 0 on success, negative errno on error
int storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks);
int storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks);
int32_t flash_bdev_ioctl(uint32_t op, uint32_t arg);
bool flash_bdev_readblock(uint8_t *dest, uint32_t block);
bool flash_bdev_writeblock(const uint8_t *src, uint32_t block);
int flash_bdev_readblocks_ext(uint8_t *dest, uint32_t block, uint32_t offset, uint32_t len);
int flash_bdev_writeblocks_ext(const uint8_t *src, uint32_t block, uint32_t offset, uint32_t len);
typedef struct _spi_bdev_t {
mp_spiflash_t spiflash;
@ -62,7 +66,12 @@ int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg);
int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t num_blocks);
int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t num_blocks);
// These raw functions bypass the cache and go directly to SPI flash
int spi_bdev_readblocks_raw(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes);
int spi_bdev_writeblocks_raw(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes);
extern const struct _mp_obj_type_t pyb_flash_type;
extern const struct _pyb_flash_obj_t pyb_flash_obj;
struct _fs_user_mount_t;
void pyb_flash_init_vfs(struct _fs_user_mount_t *vfs);

View File

@ -334,6 +334,10 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
mod_len = new_mod_l;
}
if (mod_len == 0) {
mp_raise_ValueError(NULL);
}
// check if module already exists
qstr module_name_qstr = mp_obj_str_get_qstr(module_name);
mp_obj_t module_obj = mp_module_get(module_name_qstr);

View File

@ -184,8 +184,6 @@ void mp_lexer_to_next(mp_lexer_t *lex);
// platform specific import function; must be implemented for a specific port
// TODO tidy up, rename, or put elsewhere
//mp_lexer_t *mp_import_open_file(qstr mod_name);
typedef enum {
MP_IMPORT_STAT_NO_EXIST,
MP_IMPORT_STAT_DIR,

View File

@ -691,7 +691,6 @@ mp_float_t mp_obj_get_float(mp_obj_t self_in);
bool mp_obj_get_float_maybe(mp_obj_t arg, mp_float_t *value);
void mp_obj_get_complex(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag);
#endif
//qstr mp_obj_get_qstr(mp_obj_t arg);
void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items); // *items may point inside a GC block
void mp_obj_get_array_fixed_n(mp_obj_t o, size_t len, mp_obj_t **items); // *items may point inside a GC block
size_t mp_get_index(const mp_obj_type_t *type, size_t len, mp_obj_t index, bool is_slice);

View File

@ -70,9 +70,9 @@ STATIC mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *er
STATIC void stringio_copy_on_write(mp_obj_stringio_t *o) {
const void *buf = o->vstr->buf;
o->vstr->buf = m_new(char, o->vstr->len);
memcpy(o->vstr->buf, buf, o->vstr->len);
o->vstr->fixed_buf = false;
o->ref_obj = MP_OBJ_NULL;
memcpy(o->vstr->buf, buf, o->vstr->len);
}
STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {

View File

@ -31,6 +31,7 @@
#include "py/mpstate.h"
#include "py/qstr.h"
#include "py/gc.h"
#include "py/runtime.h"
// NOTE: we are using linear arrays to store and search for qstr's (unique strings, interned strings)
// ultimately we will replace this with a static hash table of some kind
@ -192,12 +193,17 @@ qstr qstr_from_str(const char *str) {
}
qstr qstr_from_strn(const char *str, size_t len) {
assert(len < (1 << (8 * MICROPY_QSTR_BYTES_IN_LEN)));
QSTR_ENTER();
qstr q = qstr_find_strn(str, len);
if (q == 0) {
// qstr does not exist in interned pool so need to add it
// check that len is not too big
if (len >= (1 << (8 * MICROPY_QSTR_BYTES_IN_LEN))) {
QSTR_EXIT();
mp_raise_msg(&mp_type_RuntimeError, "name too long");
}
// compute number of bytes needed to intern this string
size_t n_bytes = MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + len + 1;

View File

@ -151,7 +151,6 @@ mp_obj_t mp_import_from(mp_obj_t module, qstr name);
void mp_import_all(mp_obj_t module);
NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, const char *msg);
//NORETURN void nlr_raise_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...);
NORETURN void mp_raise_ValueError(const char *msg);
NORETURN void mp_raise_TypeError(const char *msg);
NORETURN void mp_raise_NotImplementedError(const char *msg);

View File

@ -9,6 +9,12 @@ try:
except TypeError:
print('TypeError')
# module name should not be empty
try:
__import__("")
except ValueError:
print('ValueError')
# level argument should be non-negative
try:
__import__('xyz', None, None, None, -1)

View File

@ -0,0 +1,85 @@
# Test interning qstrs that go over the limit of the maximum qstr length
# (which is 255 bytes for the default configuration)
def make_id(n, base='a'):
return ''.join(chr(ord(base) + i % 26) for i in range(n))
# identifiers in parser
for l in range(254, 259):
g = {}
var = make_id(l)
try:
exec(var + '=1', g)
except RuntimeError:
print('RuntimeError', l)
continue
print(var in g)
# calling a function with kwarg
def f(**k):
print(k)
for l in range(254, 259):
try:
exec('f({}=1)'.format(make_id(l)))
except RuntimeError:
print('RuntimeError', l)
# type construction
for l in range(254, 259):
id = make_id(l)
try:
print(type(id, (), {}).__name__)
except RuntimeError:
print('RuntimeError', l)
# hasattr, setattr, getattr
class A:
pass
for l in range(254, 259):
id = make_id(l)
a = A()
try:
setattr(a, id, 123)
except RuntimeError:
print('RuntimeError', l)
try:
print(hasattr(a, id), getattr(a, id))
except RuntimeError:
print('RuntimeError', l)
# format with keys
for l in range(254, 259):
id = make_id(l)
try:
print(('{' + id + '}').format(**{id: l}))
except RuntimeError:
print('RuntimeError', l)
# modulo format with keys
for l in range(254, 259):
id = make_id(l)
try:
print(('%(' + id + ')d') % {id: l})
except RuntimeError:
print('RuntimeError', l)
# import module
# (different OS's have different results so only print those that are consistent)
for l in range(150, 259):
try:
__import__(make_id(l))
except ImportError:
if l < 152:
print('ok', l)
except RuntimeError:
if l > 255:
print('RuntimeError', l)
# import package
for l in range(125, 130):
try:
exec('import ' + make_id(l) + '.' + make_id(l, 'A'))
except ImportError:
print('ok', l)
except RuntimeError:
print('RuntimeError', l)

View File

@ -0,0 +1,43 @@
True
True
RuntimeError 256
RuntimeError 257
RuntimeError 258
{'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst': 1}
{'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu': 1}
RuntimeError 256
RuntimeError 257
RuntimeError 258
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu
RuntimeError 256
RuntimeError 257
RuntimeError 258
True 123
True 123
RuntimeError 256
RuntimeError 256
RuntimeError 257
RuntimeError 257
RuntimeError 258
RuntimeError 258
254
255
RuntimeError 256
RuntimeError 257
RuntimeError 258
254
255
RuntimeError 256
RuntimeError 257
RuntimeError 258
ok 150
ok 151
RuntimeError 256
RuntimeError 257
RuntimeError 258
ok 125
ok 126
ok 127
RuntimeError 128
RuntimeError 129

View File

@ -428,9 +428,11 @@ class RawCodeNative(RawCode):
else:
self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",%progbits @ ")))'
# Allow single-byte alignment by default for x86/x64/xtensa, but on ARM we need halfword- or word- alignment.
if config.native_arch == MP_NATIVE_ARCH_ARMV6:
# ARMV6 -- four byte align.
# Allow single-byte alignment by default for x86/x64.
# ARM needs word alignment, ARM Thumb needs halfword, due to instruction size.
# Xtensa needs word alignment due to the 32-bit constant table embedded in the code.
if config.native_arch in (MP_NATIVE_ARCH_ARMV6, MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN):
# ARMV6 or Xtensa -- four byte align.
self.fun_data_attributes += ' __attribute__ ((aligned (4)))'
elif MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP:
# ARMVxxM -- two byte align.
@ -452,8 +454,11 @@ class RawCodeNative(RawCode):
is_obj = kind == 2
if is_obj:
qst = '((uintptr_t)MP_OBJ_NEW_QSTR(%s))' % qst
if config.native_arch in (MP_NATIVE_ARCH_X86, MP_NATIVE_ARCH_X64):
print(' %s & 0xff, %s >> 8, 0, 0,' % (qst, qst))
if config.native_arch in (
MP_NATIVE_ARCH_X86, MP_NATIVE_ARCH_X64,
MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN
):
print(' %s & 0xff, (%s >> 8) & 0xff, (%s >> 16) & 0xff, %s >> 24,' % (qst, qst, qst, qst))
return 4
elif MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP:
if is_obj: