rp2: Integrate CYW43xx WiFi driver.

This includes:
- Configuration file for the cyw43-driver.
- Integration of cyw43-driver into the build, using lwIP.
- Enhancements to machine.Pin to support extension IO pins provided by the
  CYW43xx.
- More mp-hal pin helper functions.
- mp_hal_get_mac_ascii MAC address helper function.
- Addition of rp2.country() function to set the country code.

A board can enable this driver by setting MICROPY_PY_NETWORK_CYW43 in their
cmake snippet.

Work done in collaboration with Graham Sanderson and Peter Harper.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George 2022-06-30 16:01:02 +10:00
parent c001cfa603
commit 50e46552c0
14 changed files with 600 additions and 67 deletions

View File

@ -16,6 +16,10 @@ endif()
# Use the local tinyusb instead of the one in pico-sdk
set(PICO_TINYUSB_PATH ${MICROPY_DIR}/lib/tinyusb)
# Use the local cyw43_driver instead of the one in pico-sdk
set(PICO_CYW43_DRIVER_PATH ${MICROPY_DIR}/lib/cyw43-driver)
# Use the local lwip instead of the one in pico-sdk
set(PICO_LWIP_PATH ${MICROPY_DIR}/lib/lwip)
# Set the location of this port's directory.
set(MICROPY_PORT_DIR ${CMAKE_SOURCE_DIR})
@ -73,6 +77,7 @@ set(MICROPY_SOURCE_LIB
${MICROPY_DIR}/lib/mbedtls_errors/mp_mbedtls_errors.c
${MICROPY_DIR}/lib/oofatfs/ff.c
${MICROPY_DIR}/lib/oofatfs/ffunicode.c
${MICROPY_DIR}/shared/netutils/dhcpserver.c
${MICROPY_DIR}/shared/netutils/netutils.c
${MICROPY_DIR}/shared/netutils/trace.c
${MICROPY_DIR}/shared/readline/readline.c
@ -216,6 +221,30 @@ if(MICROPY_BLUETOOTH_NIMBLE)
list(APPEND MICROPY_INC_CORE ${NIMBLE_INCLUDE})
endif()
if (MICROPY_PY_NETWORK_CYW43)
string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/cyw43-driver)
target_compile_definitions(${MICROPY_TARGET} PRIVATE
MICROPY_PY_NETWORK_CYW43=1
MICROPY_PY_NETWORK_CYW43_USE_LIB_DRIVER=1
MICROPY_PY_SOCKET_DEFAULT_TIMEOUT_MS=30000 # default socket timeout
)
if (CMAKE_BUILD_TYPE MATCHES Debug)
target_compile_definitions(${MICROPY_TARGET} PRIVATE
CYW43_USE_STATS=1
)
endif()
list(APPEND MICROPY_SOURCE_EXTMOD
${MICROPY_DIR}/extmod/network_cyw43.c
)
target_link_libraries(${MICROPY_TARGET}
cyw43_driver_picow
cmsis_core
)
endif()
if (MICROPY_PY_NETWORK_NINAW10)
target_compile_definitions(${MICROPY_TARGET} PRIVATE
MICROPY_PY_NETWORK_NINAW10=1

View File

@ -29,9 +29,11 @@ all:
clean:
$(RM) -rf $(BUILD)
GIT_SUBMODULES += lib/mbedtls lib/pico-sdk lib/tinyusb
GIT_SUBMODULES += lib/mbedtls lib/tinyusb
submodules:
# lib/pico-sdk is required for the cmake build to function (as used for boards other than PICO below)
$(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="lib/pico-sdk" submodules
ifeq ($(BOARD),PICO)
# Run the standard submodules target with minimum required submodules above
$(MAKE) -f ../../py/mkrules.mk GIT_SUBMODULES="$(GIT_SUBMODULES)" submodules

View File

@ -37,6 +37,13 @@ You can also build the standard CMake way. The final firmware is found in
the top-level of the CMake build directory (`build` by default) and is
called `firmware.uf2`.
If you are using a different board other than a Rasoberry Pi Pico, then you should
pass the board name to the build; e.g. for Raspberry Pi Pico W:
$ make BOARD=PICO_W submodules
$ make BOARD=PICO_W clean
$ make BOARD=PICO_W
## Deploying firmware to the device
Firmware can be deployed to the device by putting it into bootloader mode

View File

@ -0,0 +1,98 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2022 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_RP2_CYW43_CONFIGPORT_H
#define MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H
// The board-level config will be included here, so it can set some CYW43 values.
#include "py/mpconfig.h"
#include "py/mperrno.h"
#include "py/mphal.h"
#include "pendsv.h"
#define CYW43_IOCTL_TIMEOUT_US (1000000)
#define CYW43_SLEEP_MAX (10)
#define CYW43_NETUTILS (1)
#define CYW43_USE_OTP_MAC (1)
#define CYW43_EPERM MP_EPERM // Operation not permitted
#define CYW43_EIO MP_EIO // I/O error
#define CYW43_EINVAL MP_EINVAL // Invalid argument
#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out
#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
#define CYW43_THREAD_LOCK_CHECK
#define CYW43_SDPCM_SEND_COMMON_WAIT __WFI();
#define CYW43_DO_IOCTL_WAIT __WFI();
#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a)
#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT
#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT
#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE
#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP
#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN
#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0
// set in SDK board header
#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT
#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
#define cyw43_hal_ticks_us mp_hal_ticks_us
#define cyw43_hal_ticks_ms mp_hal_ticks_ms
#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t
#define cyw43_hal_pin_config mp_hal_pin_config
#define cyw43_hal_pin_read mp_hal_pin_read
#define cyw43_hal_pin_low mp_hal_pin_low
#define cyw43_hal_pin_high mp_hal_pin_high
#define cyw43_hal_get_mac mp_hal_get_mac
#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii
#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac
#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func)
void cyw43_post_poll_hook(void);
static inline void cyw43_delay_us(uint32_t us) {
uint32_t start = mp_hal_ticks_us();
while (mp_hal_ticks_us() - start < us) {
}
}
static inline void cyw43_delay_ms(uint32_t ms) {
uint32_t us = ms * 1000;
int32_t start = mp_hal_ticks_us();
while (mp_hal_ticks_us() - start < us) {
__WFI();
}
}
#endif // MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H

View File

@ -58,9 +58,18 @@
// Open drain behaviour is simulated.
#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1 << (id)))
#ifndef MICROPY_HW_PIN_RESERVED
#define MICROPY_HW_PIN_RESERVED(i) (0)
#endif
typedef struct _machine_pin_obj_t {
mp_obj_base_t base;
uint32_t id;
uint8_t id;
#if MICROPY_HW_PIN_CYW43_COUNT
bool is_cyw43;
bool is_output;
bool last_output_value;
#endif
} machine_pin_obj_t;
typedef struct _machine_pin_irq_obj_t {
@ -104,9 +113,60 @@ STATIC const machine_pin_obj_t machine_pin_obj[NUM_BANK0_GPIOS] = {
{{&machine_pin_type}, 29},
};
#if MICROPY_HW_PIN_CYW43_COUNT
#include "lib/cyw43-driver/src/cyw43.h"
#define CYW43_PIN_NAME_PREFIX "WL_GPIO"
STATIC machine_pin_obj_t cyw43_pin_obj[MICROPY_HW_PIN_CYW43_COUNT];
#endif
#define LED_PIN_NAME "LED"
#ifndef MICROPY_HW_PIN_ENABLE_LED_PIN
#if defined(MICROPY_HW_PIN_CYW43_LED_PIN_NUM) || defined(PICO_DEFAULT_LED_PIN)
#define MICROPY_HW_PIN_ENABLE_LED_PIN 1
#endif
#endif
#ifdef MICROPY_HW_PIN_ENABLE_LED_PIN
#ifdef MICROPY_HW_PIN_CYW43_LED_PIN_NUM
STATIC machine_pin_obj_t *led_pin_obj = &cyw43_pin_obj[MICROPY_HW_PIN_CYW43_LED_PIN_NUM];
#elif defined(MICROPY_HW_PIN_LED_PIN_NUM)
STATIC machine_pin_obj_t *led_pin_obj = &machine_pin_obj[MICROPY_HW_PIN_LED_PIN_NUM];
#elif defined(PICO_DEFAULT_LED_PIN)
STATIC const machine_pin_obj_t *led_pin_obj = &machine_pin_obj[PICO_DEFAULT_LED_PIN];
#else
#error MICROPY_HW_PIN_ENABLE_LED_PIN defined but there is no LED pin
#endif
#endif
// Mask with "1" indicating that the corresponding pin is in simulated open-drain mode.
uint32_t machine_pin_open_drain_mask;
#if MICROPY_HW_PIN_CYW43_COUNT
STATIC inline bool is_cyw43_pin(__unused const machine_pin_obj_t *self) {
return self->is_cyw43;
}
#else
#define is_cyw43_pin(x) false
#endif
#if MICROPY_HW_PIN_CYW43_COUNT
STATIC inline void update_cyw43_value(__unused machine_pin_obj_t *self, bool value) {
if (value != self->last_output_value || !self->is_output) {
cyw43_gpio_set(&cyw43_state, self->id, value);
}
self->last_output_value = value;
}
#endif
#if MICROPY_HW_PIN_CYW43_COUNT
STATIC inline bool get_cyw43_value(__unused machine_pin_obj_t *self) {
bool value = false;
cyw43_gpio_get(&cyw43_state, self->id, &value);
return value;
}
#endif
STATIC void gpio_irq(void) {
for (int i = 0; i < 4; ++i) {
uint32_t intr = iobank0_hw->intr[i];
@ -129,15 +189,24 @@ STATIC void gpio_irq(void) {
void machine_pin_init(void) {
memset(MP_STATE_PORT(machine_pin_irq_obj), 0, sizeof(MP_STATE_PORT(machine_pin_irq_obj)));
irq_set_exclusive_handler(IO_IRQ_BANK0, gpio_irq);
irq_add_shared_handler(IO_IRQ_BANK0, gpio_irq, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(IO_IRQ_BANK0, true);
#if MICROPY_HW_PIN_CYW43_COUNT
for (uint i = 0; i < count_of(cyw43_pin_obj); i++) {
cyw43_pin_obj[i].id = i;
cyw43_pin_obj[i].base.type = &machine_pin_type;
cyw43_pin_obj[i].is_cyw43 = true;
}
#endif
}
void machine_pin_deinit(void) {
for (int i = 0; i < NUM_BANK0_GPIOS; ++i) {
if (MICROPY_HW_PIN_RESERVED(i)) {
continue;
}
gpio_set_irq_enabled(i, GPIO_IRQ_ALL, false);
}
irq_set_enabled(IO_IRQ_BANK0, false);
irq_remove_handler(IO_IRQ_BANK0, gpio_irq);
}
@ -145,45 +214,112 @@ STATIC void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin
machine_pin_obj_t *self = self_in;
uint funcsel = GPIO_GET_FUNCSEL(self->id);
qstr mode_qst;
if (funcsel == GPIO_FUNC_SIO) {
if (GPIO_IS_OPEN_DRAIN(self->id)) {
mode_qst = MP_QSTR_OPEN_DRAIN;
} else if (GPIO_IS_OUT(self->id)) {
mode_qst = MP_QSTR_OUT;
if (!is_cyw43_pin(self)) {
if (funcsel == GPIO_FUNC_SIO) {
if (GPIO_IS_OPEN_DRAIN(self->id)) {
mode_qst = MP_QSTR_OPEN_DRAIN;
} else if (GPIO_IS_OUT(self->id)) {
mode_qst = MP_QSTR_OUT;
} else {
mode_qst = MP_QSTR_IN;
}
} else {
mode_qst = MP_QSTR_IN;
mode_qst = MP_QSTR_ALT;
}
mp_printf(print, "Pin(%u, mode=%q", self->id, mode_qst);
bool pull_up = false;
if (GPIO_IS_PULL_UP(self->id)) {
mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP);
pull_up = true;
}
if (GPIO_IS_PULL_DOWN(self->id)) {
if (pull_up) {
mp_printf(print, "|%q", MP_QSTR_PULL_DOWN);
} else {
mp_printf(print, ", pull=%q", MP_QSTR_PULL_DOWN);
}
}
if (funcsel != GPIO_FUNC_SIO) {
mp_printf(print, ", alt=%u", funcsel);
}
} else {
mode_qst = MP_QSTR_ALT;
}
mp_printf(print, "Pin(%u, mode=%q", self->id, mode_qst);
bool pull_up = false;
if (GPIO_IS_PULL_UP(self->id)) {
mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP);
pull_up = true;
}
if (GPIO_IS_PULL_DOWN(self->id)) {
if (pull_up) {
mp_printf(print, "|%q", MP_QSTR_PULL_DOWN);
} else {
mp_printf(print, ", pull=%q", MP_QSTR_PULL_DOWN);
}
}
if (funcsel != GPIO_FUNC_SIO) {
mp_printf(print, ", alt=%u", funcsel);
#if MICROPY_HW_PIN_CYW43_COUNT
mode_qst = self->is_output ? MP_QSTR_OUT : MP_QSTR_IN;
mp_printf(print, "Pin(%s%u, mode=%q", CYW43_PIN_NAME_PREFIX, self->id, mode_qst);
#endif
}
mp_printf(print, ")");
}
enum {
ARG_mode, ARG_pull, ARG_value, ARG_alt
};
static const mp_arg_t allowed_args[] = {
{MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
{MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
{MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
{MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = GPIO_FUNC_SIO}},
};
#if MICROPY_HW_PIN_CYW43_COUNT
// pin.init(mode, pull=None, *, value=None, alt=FUNC_SIO)
STATIC mp_obj_t machine_pin_cyw43_obj_init_helper(machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_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);
if (args[ARG_pull].u_obj != mp_const_none) {
int pull = mp_obj_get_int(args[ARG_pull].u_obj);
if (pull) {
mp_raise_ValueError("Pulls are not supported for this pin");
}
}
if (args[ARG_alt].u_int != GPIO_FUNC_SIO) {
mp_raise_ValueError("Alternate functions are not supported for this pin");
}
int value = -1;
if (args[ARG_value].u_obj != mp_const_none) {
value = mp_obj_is_true(args[ARG_value].u_obj);
}
if (args[ARG_mode].u_obj != mp_const_none) {
mp_int_t mode = mp_obj_get_int(args[ARG_mode].u_obj);
if (mode == GPIO_MODE_IN) {
if (self->is_output) {
// todo need to disable output
}
self->is_output = false;
} else if (mode == GPIO_MODE_OUT) {
if (!self->is_output) {
// todo need to enable output
// for now we just set the value
if (value == -1) {
value = self->last_output_value;
}
self->last_output_value = !self->last_output_value; // defeat shortcircuit
update_cyw43_value(self, value);
self->is_output = true;
}
} else {
mp_raise_ValueError("only Pin.OUT and Pin.IN are supported for this pin");
}
}
if (value != -1) {
if (self->is_output) {
update_cyw43_value(self, value);
} else {
// figure if you pass a value to IN it should still remember it (this is what regular GPIO does)
self->last_output_value = value;
}
}
return mp_const_none;
}
#endif
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) {
enum { ARG_mode, ARG_pull, ARG_value, ARG_alt };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
{ MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
{ MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}},
{ MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = GPIO_FUNC_SIO}},
};
// parse args
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -216,7 +352,6 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_
pull = mp_obj_get_int(args[ARG_pull].u_obj);
}
gpio_set_pulls(self->id, pull & GPIO_PULL_UP, pull & GPIO_PULL_DOWN);
return mp_const_none;
}
@ -224,21 +359,57 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_
mp_obj_t mp_pin_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 the wanted pin object
int wanted_pin = mp_obj_get_int(args[0]);
if (!(0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_obj))) {
mp_raise_ValueError("invalid pin");
}
const machine_pin_obj_t *self = &machine_pin_obj[wanted_pin];
const machine_pin_obj_t *self = NULL;
if (mp_obj_is_str(args[0])) {
const char *name = mp_obj_str_get_str(args[0]);
#if MICROPY_HW_PIN_ENABLE_LED_PIN
if (!strcmp(name, LED_PIN_NAME)) {
self = led_pin_obj;
}
#endif
#if MICROPY_HW_PIN_CYW43_COUNT
static_assert(MICROPY_HW_PIN_CYW43_COUNT < 10, ""); // makes parsing name easy!
if (!self && !strncmp(name, CYW43_PIN_NAME_PREFIX, strlen(CYW43_PIN_NAME_PREFIX)) && strlen(name) == strlen(CYW43_PIN_NAME_PREFIX) + 1) {
int num = name[strlen(CYW43_PIN_NAME_PREFIX)] - '0';
if (num < MICROPY_HW_PIN_CYW43_COUNT) {
self = &cyw43_pin_obj[num];
}
}
#endif
if (!self) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Unknown named pin \"%s\""), name);
}
}
if (!self) {
// get the wanted pin object
int wanted_pin = mp_obj_get_int(args[0]);
if (!(0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_obj))) {
mp_raise_ValueError("invalid pin");
}
self = &machine_pin_obj[wanted_pin];
}
// note we have different init args based on the type of pin. so Pin("LED", Pin.OUT) may not always make sense
if (!is_cyw43_pin(self)) {
if (n_args > 1 || n_kw > 0) {
// pin mode given, so configure this GPIO
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args);
}
return MP_OBJ_FROM_PTR(self);
}
#if MICROPY_HW_PIN_CYW43_COUNT
if (n_args > 1 || n_kw > 0) {
// pin mode given, so configure this GPIO
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args);
// The regular Pins are const, but the CYW43 pins are mutable.
machine_pin_obj_t *mutable_self = (machine_pin_obj_t *)self;
machine_pin_cyw43_obj_init_helper(mutable_self, n_args - 1, args + 1, &kw_args);
}
return MP_OBJ_FROM_PTR(self);
#endif
}
// fast method for getting/setting pin value
@ -247,23 +418,39 @@ STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c
machine_pin_obj_t *self = self_in;
if (n_args == 0) {
// get pin
return MP_OBJ_NEW_SMALL_INT(gpio_get(self->id));
if (!is_cyw43_pin(self)) {
return MP_OBJ_NEW_SMALL_INT(gpio_get(self->id));
}
#if MICROPY_HW_PIN_CYW43_COUNT
return MP_OBJ_NEW_SMALL_INT(get_cyw43_value(self));
#endif
} else {
// set pin
bool value = mp_obj_is_true(args[0]);
if (GPIO_IS_OPEN_DRAIN(self->id)) {
MP_STATIC_ASSERT(GPIO_IN == 0 && GPIO_OUT == 1);
gpio_set_dir(self->id, 1 - value);
} else {
gpio_put(self->id, value);
if (!is_cyw43_pin(self)) {
if (GPIO_IS_OPEN_DRAIN(self->id)) {
MP_STATIC_ASSERT(GPIO_IN == 0 && GPIO_OUT == 1);
gpio_set_dir(self->id, 1 - value);
} else {
gpio_put(self->id, value);
}
return mp_const_none;
}
return mp_const_none;
#if MICROPY_HW_PIN_CYW43_COUNT
update_cyw43_value(self, value);
#endif
}
return mp_const_none;
}
// pin.init(mode, pull)
STATIC mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
if (!is_cyw43_pin(args[0])) {
return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
#if MICROPY_HW_PIN_CYW43_COUNT
return machine_pin_cyw43_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
#endif
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init);
@ -276,40 +463,59 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_
// pin.low()
STATIC mp_obj_t machine_pin_low(mp_obj_t self_in) {
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (GPIO_IS_OPEN_DRAIN(self->id)) {
gpio_set_dir(self->id, GPIO_OUT);
} else {
gpio_clr_mask(1u << self->id);
if (!is_cyw43_pin(self)) {
if (GPIO_IS_OPEN_DRAIN(self->id)) {
gpio_set_dir(self->id, GPIO_OUT);
} else {
gpio_clr_mask(1u << self->id);
}
return mp_const_none;
}
#if MICROPY_HW_PIN_CYW43_COUNT
update_cyw43_value(self, 0);
return mp_const_none;
#endif
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low);
// pin.high()
STATIC mp_obj_t machine_pin_high(mp_obj_t self_in) {
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (GPIO_IS_OPEN_DRAIN(self->id)) {
gpio_set_dir(self->id, GPIO_IN);
} else {
gpio_set_mask(1u << self->id);
if (!is_cyw43_pin(self)) {
if (GPIO_IS_OPEN_DRAIN(self->id)) {
gpio_set_dir(self->id, GPIO_IN);
} else {
gpio_set_mask(1u << self->id);
}
return mp_const_none;
}
#if MICROPY_HW_PIN_CYW43_COUNT
update_cyw43_value(self, 1);
return mp_const_none;
#endif
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high);
// pin.toggle()
STATIC mp_obj_t machine_pin_toggle(mp_obj_t self_in) {
machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (GPIO_IS_OPEN_DRAIN(self->id)) {
if (GPIO_IS_OUT(self->id)) {
gpio_set_dir(self->id, GPIO_IN);
if (!is_cyw43_pin(self)) {
if (GPIO_IS_OPEN_DRAIN(self->id)) {
if (GPIO_IS_OUT(self->id)) {
gpio_set_dir(self->id, GPIO_IN);
} else {
gpio_set_dir(self->id, GPIO_OUT);
}
} else {
gpio_set_dir(self->id, GPIO_OUT);
gpio_xor_mask(1u << self->id);
}
} else {
gpio_xor_mask(1u << self->id);
return mp_const_none;
}
#if MICROPY_HW_PIN_CYW43_COUNT
update_cyw43_value(self, self->last_output_value ^ 1);
return mp_const_none;
#endif
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle);
@ -357,6 +563,10 @@ STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_
{ MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
};
machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
if (is_cyw43_pin(self)) {
mp_raise_ValueError(MP_ERROR_TEXT("expecting a regular GPIO Pin"));
}
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);
@ -400,16 +610,31 @@ STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, i
(void)errcode;
machine_pin_obj_t *self = self_in;
if (!is_cyw43_pin(self)) {
switch (request) {
case MP_PIN_READ: {
return gpio_get(self->id);
}
case MP_PIN_WRITE: {
gpio_put(self->id, arg);
return 0;
}
}
return -1;
}
#if MICROPY_HW_PIN_CYW43_COUNT
switch (request) {
case MP_PIN_READ: {
return gpio_get(self->id);
return get_cyw43_value(self);
}
case MP_PIN_WRITE: {
gpio_put(self->id, arg);
update_cyw43_value(self, arg);
return 0;
}
}
return -1;
#endif
}
STATIC const mp_pin_p_t pin_pin_p = {
@ -457,5 +682,8 @@ mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) {
mp_raise_ValueError(MP_ERROR_TEXT("expecting a Pin"));
}
machine_pin_obj_t *pin = MP_OBJ_TO_PTR(obj);
if (is_cyw43_pin(pin)) {
mp_raise_ValueError(MP_ERROR_TEXT("expecting a regular GPIO Pin"));
}
return pin->id;
}

View File

@ -46,12 +46,16 @@
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "pico/unique_id.h"
#include "hardware/rtc.h"
#include "hardware/structs/rosc.h"
#if MICROPY_PY_LWIP
#include "lwip/init.h"
#include "lwip/apps/mdns.h"
#endif
#if MICROPY_PY_NETWORK_CYW43
#include "lib/cyw43-driver/src/cyw43.h"
#endif
#ifndef MICROPY_GC_HEAP_SIZE
#if MICROPY_PY_LWIP
@ -90,6 +94,10 @@ int main(int argc, char **argv) {
mp_thread_init();
#endif
#ifndef NDEBUG
stdio_init_all();
#endif
// Start and initialise the RTC
datetime_t t = {
.year = 2021,
@ -118,6 +126,27 @@ int main(int argc, char **argv) {
#endif
#endif
#if MICROPY_PY_NETWORK_CYW43
{
cyw43_init(&cyw43_state);
cyw43_irq_init();
cyw43_post_poll_hook(); // enable the irq
uint8_t buf[8];
memcpy(&buf[0], "PICO", 4);
// MAC isn't loaded from OTP yet, so use unique id to generate the default AP ssid.
const char hexchr[16] = "0123456789ABCDEF";
pico_unique_board_id_t pid;
pico_get_unique_board_id(&pid);
buf[4] = hexchr[pid.id[7] >> 4];
buf[5] = hexchr[pid.id[6] & 0xf];
buf[6] = hexchr[pid.id[5] >> 4];
buf[7] = hexchr[pid.id[4] & 0xf];
cyw43_wifi_ap_set_ssid(&cyw43_state, 8, buf);
cyw43_wifi_ap_set_password(&cyw43_state, 8, (const uint8_t *)"picoW123");
}
#endif
for (;;) {
// Initialise MicroPython runtime.

View File

@ -28,6 +28,28 @@
#include "drivers/dht/dht.h"
#include "modrp2.h"
#if MICROPY_PY_NETWORK_CYW43
#include "lib/cyw43-driver/src/cyw43_country.h"
extern uint32_t cyw43_country_code;
STATIC mp_obj_t rp2_country(size_t n_args, const mp_obj_t *args) {
if (n_args == 0) {
char code[2] = {cyw43_country_code, cyw43_country_code >> 8};
return mp_obj_new_str(code, 2);
} else {
size_t len;
const char *str = mp_obj_str_get_data(args[0], &len);
if (len != 2) {
mp_raise_ValueError(NULL);
}
cyw43_country_code = CYW43_COUNTRY(str[0], str[1], 0);
return mp_const_none;
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_country_obj, 0, 1, rp2_country);
#endif
STATIC const mp_rom_map_elem_t rp2_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rp2) },
{ MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&rp2_flash_type) },
@ -35,6 +57,10 @@ STATIC const mp_rom_map_elem_t rp2_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_StateMachine), MP_ROM_PTR(&rp2_state_machine_type) },
{ MP_ROM_QSTR(MP_QSTR_dht_readinto), MP_ROM_PTR(&dht_readinto_obj) },
#if MICROPY_PY_NETWORK_CYW43
{ MP_ROM_QSTR(MP_QSTR_country), MP_ROM_PTR(&rp2_country_obj) },
#endif
};
STATIC MP_DEFINE_CONST_DICT(rp2_module_globals, rp2_module_globals_table);

View File

@ -154,6 +154,20 @@ struct _mp_bluetooth_nimble_malloc_t;
#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_NIMBLE
#endif
#if MICROPY_PY_NETWORK_CYW43
extern const struct _mp_obj_type_t mp_network_cyw43_type;
#define MICROPY_HW_NIC_CYW43 \
{ MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, \
{ MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(CYW43_LINK_DOWN) }, \
{ MP_ROM_QSTR(MP_QSTR_STAT_CONNECTING), MP_ROM_INT(CYW43_LINK_JOIN) }, \
{ MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(CYW43_LINK_BADAUTH) }, \
{ MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(CYW43_LINK_NONET) }, \
{ MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(CYW43_LINK_FAIL) }, \
{ MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(CYW43_LINK_UP) },
#else
#define MICROPY_HW_NIC_CYW43
#endif
#if MICROPY_PY_NETWORK_NINAW10
// This Network interface requires the extended socket state.
#ifndef MICROPY_PY_USOCKET_EXTENDED_STATE
@ -184,6 +198,7 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_wiznet5k;
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
MICROPY_HW_NIC_CYW43 \
MICROPY_HW_NIC_NINAW10 \
MICROPY_HW_NIC_WIZNET5K \
MICROPY_BOARD_NETWORK_INTERFACES \
@ -258,3 +273,10 @@ typedef intptr_t mp_off_t;
extern uint32_t rosc_random_u32(void);
extern void lwip_lock_acquire(void);
extern void lwip_lock_release(void);
extern uint32_t cyw43_country_code;
extern void cyw43_irq_init(void);
extern void cyw43_post_poll_hook(void);
#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
#define MICROPY_CYW43_COUNTRY cyw43_country_code

View File

@ -35,6 +35,10 @@
#include "hardware/rtc.h"
#include "pico/unique_id.h"
#if MICROPY_PY_NETWORK_CYW43
#include "lib/cyw43-driver/src/cyw43.h"
#endif
#if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_ENABLE_USBDEV
#ifndef MICROPY_HW_STDIN_BUFFER_LEN
@ -168,6 +172,9 @@ uint64_t mp_hal_time_ns(void) {
// Generate a random locally administered MAC address (LAA)
void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]) {
#ifndef NDEBUG
printf("Warning: No MAC in OTP, generating MAC from board id\n");
#endif
pico_unique_board_id_t pid;
pico_get_unique_board_id(&pid);
buf[0] = 0x02; // LAA range
@ -180,5 +187,21 @@ void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]) {
// A board can override this if needed
MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) {
#if MICROPY_PY_NETWORK_CYW43
// The mac should come from cyw43 otp when CYW43_USE_OTP_MAC is defined
// This is loaded into the state after the driver is initialised
// cyw43_hal_generate_laa_mac is only called by the driver to generate a mac if otp is not set
memcpy(buf, cyw43_state.mac, 6);
#else
mp_hal_generate_laa_mac(idx, buf);
#endif
}
void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest) {
static const char hexchr[16] = "0123456789ABCDEF";
uint8_t mac[6];
mp_hal_get_mac(idx, mac);
for (; chr_len; ++chr_off, --chr_len) {
*dest++ = hexchr[mac[chr_off >> 1] >> (4 * (1 - (chr_off & 1))) & 0xf];
}
}

View File

@ -30,6 +30,7 @@
#include "pico/time.h"
#include "hardware/clocks.h"
#include "hardware/structs/systick.h"
#include "RP2040.h" // cmsis, for __WFI
#define SYSTICK_MAX (0xffffff)
@ -74,6 +75,11 @@ static inline mp_uint_t mp_hal_get_cpu_freq(void) {
#define MP_HAL_PIN_FMT "%u"
#define mp_hal_pin_obj_t uint
#define MP_HAL_PIN_MODE_INPUT (GPIO_IN)
#define MP_HAL_PIN_MODE_OUTPUT (GPIO_OUT)
#define MP_HAL_PIN_PULL_NONE (0)
#define MP_HAL_PIN_PULL_UP (1)
#define MP_HAL_PIN_PULL_DOWN (2)
extern uint32_t machine_pin_open_drain_mask;
@ -102,6 +108,12 @@ static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) {
machine_pin_open_drain_mask |= 1 << pin;
}
static inline void mp_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt) {
assert((mode == MP_HAL_PIN_MODE_INPUT || mode == MP_HAL_PIN_MODE_OUTPUT) && alt == 0);
gpio_set_dir(pin, mode);
gpio_set_pulls(pin, pull == MP_HAL_PIN_PULL_UP, pull == MP_HAL_PIN_PULL_DOWN);
}
static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) {
return gpio_get(pin);
}
@ -143,6 +155,7 @@ enum {
};
void mp_hal_get_mac(int idx, uint8_t buf[6]);
void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest);
void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]);
#endif // MICROPY_INCLUDED_RP2_MPHALPORT_H

View File

@ -38,6 +38,41 @@
static alarm_id_t lwip_alarm_id = -1;
#if MICROPY_PY_NETWORK_CYW43
#include "lib/cyw43-driver/src/cyw43.h"
#include "lib/cyw43-driver/src/cyw43_country.h"
#include "lib/cyw43-driver/src/cyw43_stats.h"
#include "hardware/irq.h"
#define CYW43_IRQ_LEVEL GPIO_IRQ_LEVEL_HIGH
#define CYW43_SHARED_IRQ_HANDLER_PRIORITY PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY
uint32_t cyw43_country_code = CYW43_COUNTRY_WORLDWIDE;
static void gpio_irq_handler(void) {
uint32_t events = gpio_get_irq_event_mask(CYW43_PIN_WL_HOST_WAKE);
if (events & CYW43_IRQ_LEVEL) {
// As we use a high level interrupt, it will go off forever until it's serviced.
// So disable the interrupt until this is done. It's re-enabled again by
// CYW43_POST_POLL_HOOK which is called at the end of cyw43_poll_func.
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, CYW43_IRQ_LEVEL, false);
pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, cyw43_poll);
CYW43_STAT_INC(IRQ_COUNT);
}
}
void cyw43_irq_init(void) {
gpio_add_raw_irq_handler_with_order_priority(IO_IRQ_BANK0, gpio_irq_handler, CYW43_SHARED_IRQ_HANDLER_PRIORITY);
irq_set_enabled(IO_IRQ_BANK0, true);
NVIC_SetPriority(PendSV_IRQn, PICO_LOWEST_IRQ_PRIORITY);
}
void cyw43_post_poll_hook(void) {
gpio_set_irq_enabled(CYW43_PIN_WL_HOST_WAKE, CYW43_IRQ_LEVEL, true);
}
#endif
#if MICROPY_PY_NETWORK_WIZNET5K
void wiznet5k_poll(void);
void wiznet5k_deinit(void);

View File

@ -28,6 +28,10 @@
#include "pendsv.h"
#include "RP2040.h"
#if MICROPY_PY_NETWORK_CYW43
#include "lib/cyw43-driver/src/cyw43_stats.h"
#endif
static pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS];
static int pendsv_lock;
@ -54,12 +58,21 @@ void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) {
pendsv_dispatch_table[slot] = f;
if (pendsv_lock == 0) {
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
} else {
#if MICROPY_PY_NETWORK_CYW43
CYW43_STAT_INC(PENDSV_DISABLED_COUNT);
#endif
}
}
// PendSV interrupt handler to perform background processing.
void PendSV_Handler(void) {
assert(pendsv_lock == 0);
#if MICROPY_PY_NETWORK_CYW43
CYW43_STAT_INC(PENDSV_RUN_COUNT);
#endif
for (size_t i = 0; i < PENDSV_DISPATCH_NUM_SLOTS; ++i) {
if (pendsv_dispatch_table[i] != NULL) {
pendsv_dispatch_t f = pendsv_dispatch_table[i];

View File

@ -32,6 +32,9 @@ enum {
#if MICROPY_PY_LWIP
PENDSV_DISPATCH_LWIP,
#endif
#if MICROPY_PY_NETWORK_CYW43
PENDSV_DISPATCH_CYW43,
#endif
#if MICROPY_PY_NETWORK_WIZNET5K
PENDSV_DISPATCH_WIZNET,
#endif

View File

@ -103,8 +103,13 @@ STATIC void pio1_irq0(void) {
void rp2_pio_init(void) {
// Reset all PIO instruction memory.
#if MICROPY_PY_NETWORK_CYW43
// TODO: cannot reset PIO memory when CYW43 driver is enabled and active
// because it uses a PIO for the bus interface.
#else
pio_clear_instruction_memory(pio0);
pio_clear_instruction_memory(pio1);
#endif
// Set up interrupts.
memset(MP_STATE_PORT(rp2_pio_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_pio_irq_obj)));