hippy
Posts: 10886
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

How To : Add C Extensions to Pico MicroPython

Tue Jan 26, 2021 2:32 pm

This is a Fast-Track How To for adding C Extensions to the MicroPython build for Pico.

Adding a new C Extension module is basically a six step process -
  • Create a new MicroPython port from the 'rp2' default
  • Add a C Extension module
  • Update the module list to reference your module
  • Update the build to include your module
  • Rebuild MicroPython
  • Flash the Pico with the new build
That is all a lot easier than it sounds. The first step only needs to be done once.

Create a new MicroPython port

If you have installed the Pico MicroPython SDK to '~/pico/micropython' you should have a '~/pico/micropython/ports/rp2' directory which is the default MicroPython build for the Pico.

Copy that directory and everything in it to '~/pico/micropython/ports/rp2hack' so we can progress without touching the default build -

Code: Select all

cd ~/pico/micropython/ports
cp -r ./rp2 ./rp2hack
cd ./rp2hack
rm -r build
That final step removes the existing 'build' directory. We will be building directly into the 'rp2hack' directory for now.

I need to figure out how to make using the build directory work.

Create the new C Extension

Within the 'rp2hack' directory create a new C Extension module 'modhack.c' -

Code: Select all

nano modhack.c
Adding the following -

Code: Select all

#include "py/runtime.h"

STATIC mp_obj_t hack_test(void) {
    return MP_OBJ_NEW_SMALL_INT(1234);
}
MP_DEFINE_CONST_FUN_OBJ_0(hack_test_obj, hack_test);

STATIC const mp_rom_map_elem_t hack_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__),            MP_ROM_QSTR(MP_QSTR_hack) },
    { MP_ROM_QSTR(MP_QSTR_test),                MP_ROM_PTR(&hack_test_obj) },
};
STATIC MP_DEFINE_CONST_DICT(hack_module_globals, hack_module_globals_table);

const mp_obj_module_t mp_module_hack = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&hack_module_globals,
};
Update the MicroPython configuration

In the 'rp2hack' directory, edit the 'mpconfigport.h' file to reference your new module -

Code: Select all

nano mpconfigport.h
Locate the two lines which reference "module_machine" and before the first, line 126, add -

Code: Select all

extern const struct _mp_obj_module_t mp_module_hack;
And before the second, line 134, add -

Code: Select all

    { MP_OBJ_NEW_QSTR(MP_QSTR_hack), (mp_obj_t)&mp_module_hack }, \
It is recommended to alter the board name so you can easily tell you are running this modified MicroPython build in Thonny, line 35 -

Code: Select all

#define MICROPY_HW_BOARD_NAME           "Raspberry Pi Pico (Hack)"
Update the file list

In the 'rp2hack' directory, edit the 'CMakeLists.txt' file so your new module will be included in your MicroPython build -

Code: Select all

nano CMakeLists.txt
Locate the lines which reference 'modmachine.c' and before the first, line 60, add -

Code: Select all

    modhack.c
And before the second, line 86, add -

Code: Select all

    ${PROJECT_SOURCE_DIR}/modhack.c
Rebuild MicroPython

In the 'rp2hack' directory -

Code: Select all

cmake .
make
The "cmake ." takes just a couple of seconds but the "make" takes a couple of minutes on a Pi 3B (non-plus). It will be quicker on a Pi 3B+, 4B or Pi 400.

If you have 'picotool' installed -

Code: Select all

picotool info -a firmware.uf2

Code: Select all

File firmware.uf2:

Program Information
 name:            MicroPython
 version:         de1239b6a
 features:        USB REPL
                  thread support
 frozen modules:  _boot, rp2, onewire, ds18x20, uasyncio, uasyncio/core,
                  uasyncio/event, uasyncio/funcs, uasyncio/lock,
                  uasyncio/stream
 binary start:    0x10000000
 binary end:      0x10038bf8
 embedded drive:  0x100a0000-0x10200000 (1408K): MicroPython

Fixed Pin Information
 none

Build Information
 sdk version:       1.0.0
 pico_board:        pico
 build date:        Jan 26 2021
 build attributes:  MinSizeRel
The "build date" at the end of the report should match the current date.

Re-flashing the Pico

Use the usual 'BOOTSEL procedure' to install your new MicroPython build to your Pico.

You need to install '~/pico/micropython/ports/rp2hack/firmware.uf2' to the Pico's flash drive.

Testing your build

When you connect your Pico and launch Thonny, when the REPL is entered, you should see something like -

Code: Select all

MicroPython de1239b6a on 2021-01-26; Raspberry Pi Pico (Hack) with RP2040
Type "help()" for more information.
>>>
Note the "(Hack)" we added to the board name earlier. This confirms your Pico is running your build of MicroPython and not the supplied default.

You can now check your C Extension can be accessed and that it works -

Code: Select all

>>> import hack
>>> hack.test()
1234
Bingo! Job done.

History

2021-01-26 - Initial publication of this How To

kilograham
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1022
Joined: Fri Apr 12, 2019 11:00 am
Location: austin tx

Re: How To : Add C Extensions to Pico MicroPython

Tue Jan 26, 2021 2:58 pm

Check out the pico_user_c_modules branch, which lets you add C modules to Pico Micropython without modifying the core CMakeLists.txt

hippy
Posts: 10886
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: How To : Add C Extensions to Pico MicroPython

Tue Jan 26, 2021 3:13 pm

kilograham wrote:
Tue Jan 26, 2021 2:58 pm
Check out the pico_user_c_modules branch, which lets you add C modules to Pico Micropython without modifying the core CMakeLists.txt
This I presume - https://github.com/raspberrypi/micropyt ... _c_modules

Any details on how to use it, what to do, any How To for that ?

kilograham
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1022
Joined: Fri Apr 12, 2019 11:00 am
Location: austin tx

Re: How To : Add C Extensions to Pico MicroPython

Tue Jan 26, 2021 3:52 pm

ah sorry; there is a (terse) example in (one of?) the commit message(s)

Also this repo is using it https://github.com/pimoroni/pimoroni-pico

hippy
Posts: 10886
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: How To : Add C Extensions to Pico MicroPython

Fri Feb 05, 2021 2:07 am

Here's a MicroPython module which allows the BOOTSEL button to be read ..

modbootsel.c

Code: Select all

// *************************************************************************
// *                                                                       *
// *    MicroPython 'bootsel' module add-in                                *
// *                                                                       *
// *************************************************************************

#include "py/runtime.h"

#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/sync.h"
#include "hardware/structs/ioqspi.h"
#include "hardware/structs/sio.h"

// *************************************************************************
// *                                                                       *
// *    C functionality                                                    *
// *                                                                       *
// *************************************************************************

bool __no_inline_not_in_flash_func(get_bootsel_button)() {
    const uint CS_PIN_INDEX = 1;
    uint32_t flags = save_and_disable_interrupts();
    hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
                    GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
                    IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
    for (volatile int i = 0; i < 1000; ++i);
    bool button_state = !(sio_hw->gpio_hi_in & (1u << CS_PIN_INDEX));
    hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
                    GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
                    IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
    restore_interrupts(flags);
    return button_state;
}

// *************************************************************************
// *                                                                       *
// *    MicroPython interface                                              *
// *                                                                       *
// *************************************************************************

STATIC mp_obj_t bootsel_button(void) {
    return MP_OBJ_NEW_SMALL_INT(get_bootsel_button());
}
MP_DEFINE_CONST_FUN_OBJ_0(bootsel_button_obj, bootsel_button);

// *************************************************************************
// *                                                                       *
// *    MicroPython module definition                                      *
// *                                                                       *
// *************************************************************************

STATIC const mp_rom_map_elem_t bootsel_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__),     MP_ROM_QSTR(MP_QSTR_bootsel) },
    { MP_ROM_QSTR(MP_QSTR_button),       MP_ROM_PTR(&bootsel_button_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bootsel_module_globals, bootsel_module_globals_table);

// .-----------------------------------------------------------------------.
// *    MicroPython integration                                            |
// `-----------------------------------------------------------------------'

const mp_obj_module_t mp_module_bootsel = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&bootsel_module_globals,
};

// *************************************************************************
// *                                                                       *
// *    End of 'bootsel' module                                            *
// *                                                                       *
// *************************************************************************

Code: Select all

MicroPython v1.14-8-g1f800cac3 on 2021-02-05; Raspberry Pi Pico (BOOTSEL-HACK) with RP2040
Type "help()" for more information.
>>> import bootsel
>>> bootsel.button()
0
>>> bootsel.button()
1
>>>

zmarties
Posts: 66
Joined: Fri Jan 22, 2021 6:53 pm

Re: How To : Add C Extensions to Pico MicroPython

Fri Feb 05, 2021 11:04 am

Please open this as a PR on the MicroPython repo - I'd really love this to be part of the standard distribution.

fanoush
Posts: 835
Joined: Mon Feb 27, 2012 2:37 pm

Re: How To : Add C Extensions to Pico MicroPython

Fri Feb 05, 2021 11:24 am

hippy wrote:
Fri Feb 05, 2021 2:07 am
Here's a MicroPython module which allows the BOOTSEL button to be read ..

modbootsel.c

Code: Select all

...
    for (volatile int i = 0; i < 1000; ++i);
...
Isn't there some delay/sleep method? This may break at different clock speeds.

zmarties
Posts: 66
Joined: Fri Jan 22, 2021 6:53 pm

Re: How To : Add C Extensions to Pico MicroPython

Fri Feb 05, 2021 11:33 am

You can't use a sleep here - all interrupts are disabled.

It's true that this loop should probably take clock speed in to account.

fanoush
Posts: 835
Joined: Mon Feb 27, 2012 2:37 pm

Re: How To : Add C Extensions to Pico MicroPython

Fri Feb 05, 2021 12:52 pm

Oh, didn't think about what the code does. So the bootsel button is also wired to QSPI interface and acts as CS pin? The interface that is connected to SPI flash and is used all the time? That's why you need to disable interrupts to read its state? So how come I can press it (=pulling it low or high) without breaking something?

hippy
Posts: 10886
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: How To : Add C Extensions to Pico MicroPython

Fri Feb 05, 2021 3:15 pm

fanoush wrote:
Fri Feb 05, 2021 12:52 pm
So how come I can press it (=pulling it low or high) without breaking something?
QSPI_SS goes to CS/ on the chip which is active low when the Flash is being accessed, so pushing the button which also pulls it low has no consequence.

If QSPI_SS is high, then Edited: even if the button push makes CS/ low, but the chip is not being accessed so no harm done.
Last edited by hippy on Fri Feb 05, 2021 5:06 pm, edited 1 time in total.

fanoush
Posts: 835
Joined: Mon Feb 27, 2012 2:37 pm

Re: How To : Add C Extensions to Pico MicroPython

Fri Feb 05, 2021 3:54 pm

So if you would keep button pressed you will keep CS low and SPI flash selected all the time? That can be dangerous because some SPI commands are ended by pulling CS high - like normal read command 0x03 https://www.winbond.com/resource-files/ ... df#page=28

So as long as you push CS low all written bytes (=next commands) are ignored and you just get data out as the previous read continues. Not sure however how the QSPI traffic works - the quad ones over all 4 wires.

hippy
Posts: 10886
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: How To : Add C Extensions to Pico MicroPython

Fri Feb 05, 2021 5:02 pm

fanoush wrote:
Fri Feb 05, 2021 3:54 pm
So if you would keep button pressed you will keep CS low and SPI flash selected all the time?
There is an in-line resistor on the BOOTSEL button so, when QSPI_SS is high, it should still overcome the load to 0V to drive CS/ high ...

Code: Select all

.--------.                              .-------.
| RP2040 |---> QSPI_SS ---.---> CS/ --->| Flash |
`--------'               .|.            `-------'
                         |_|
                          |
                          O |-.
                          O |-'
                         _|_
I poorly phrased how it is in my previous post. I have edited that.

kilograham
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1022
Joined: Fri Apr 12, 2019 11:00 am
Location: austin tx

Re: How To : Add C Extensions to Pico MicroPython

Sat Feb 06, 2021 12:02 am

Isn't there some delay/sleep method? This may break at different clock speeds.
You can't use a sleep here - all interrupts are disabled.
busy_wait_us() can be used in place of sleep_us() with the obvious distinction.

hippy
Posts: 10886
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: How To : Add C Extensions to Pico MicroPython

Wed Feb 17, 2021 7:46 pm

modesetclock.c

Code: Select all

// *************************************************************************
// *                                                                       *
// *    MicroPython 'setclock' module                                      *
// *                                                                       *
// *************************************************************************

#include "py/runtime.h"

#include "pico/stdlib.h"

// *************************************************************************
// *                                                                       *
// *    C functionality                                                    *
// *                                                                       *
// *************************************************************************

void setclock_khz(uint32_t khz) {
    set_sys_clock_khz(khz, true);
}

void setclock_mhz(uint32_t mhz) {
    set_sys_clock_khz(mhz * 1000, true);
}

void setclock_default(void) {
    set_sys_clock_khz(133000, true);
}

// *************************************************************************
// *                                                                       *
// *    MicroPython interface                                              *
// *                                                                       *
// *************************************************************************

STATIC mp_obj_t setclock_khz_extension(mp_obj_t khz_obj) {
    uint32_t khz = mp_obj_get_int(khz_obj);
    setclock_khz(khz);
    return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(setclock_khz_obj, setclock_khz_extension);

STATIC mp_obj_t setclock_mhz_extension(mp_obj_t mhz_obj) {
    uint32_t mhz = mp_obj_get_int(mhz_obj);
    setclock_mhz(mhz);
    return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(setclock_mhz_obj, setclock_mhz_extension);

STATIC mp_obj_t setclock_default_extension(void) {
    setclock_default();
    return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_0(setclock_default_obj, setclock_default_extension);

// *************************************************************************
// *                                                                       *
// *    MicroPython module definition                                      *
// *                                                                       *
// *************************************************************************

STATIC const mp_rom_map_elem_t setclock_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__),     MP_ROM_QSTR(MP_QSTR_setclock) },
    { MP_ROM_QSTR(MP_QSTR_khz),          MP_ROM_PTR(&setclock_khz_obj) },
    { MP_ROM_QSTR(MP_QSTR_mhz),          MP_ROM_PTR(&setclock_mhz_obj) },
    { MP_ROM_QSTR(MP_QSTR_default),      MP_ROM_PTR(&setclock_default_obj) },
};
STATIC MP_DEFINE_CONST_DICT(setclock_module_globals, setclock_module_globals_table);

// .-----------------------------------------------------------------------.
// *    MicroPython integration                                            |
// `-----------------------------------------------------------------------'

const mp_obj_module_t mp_module_setclock = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&setclock_module_globals,
};

// *************************************************************************
// *                                                                       *
// *    End of 'setclock' module                                           *
// *                                                                       *
// *************************************************************************

plugwash
Forum Moderator
Forum Moderator
Posts: 3753
Joined: Wed Dec 28, 2011 11:45 pm

Re: How To : Add C Extensions to Pico MicroPython

Thu Feb 18, 2021 1:25 am

kilograham wrote:
Sat Feb 06, 2021 12:02 am
busy_wait_us() can be used in place of sleep_us() with the obvious distinction.
Can it be safely called from code that runs with XIP non-funcational?

SteveRPi
Posts: 14
Joined: Wed Oct 01, 2014 10:35 pm
Location: Canberra

Re: How To : Add C Extensions to Pico MicroPython

Thu Feb 18, 2021 10:10 pm

@kilograham
Check out the pico_user_c_modules branch, which lets you add C modules to Pico Micropython without modifying the core CMakeLists.txt
@hippy
Any details on how to use it, what to do, any How To for that ?
@kilograham
Is there a simple explanation of this method?

syrus8k
Posts: 1
Joined: Sat Feb 20, 2021 10:32 pm

Re: How To : Add C Extensions to Pico MicroPython

Sat Feb 20, 2021 10:36 pm

Would I be able to use this method to expose the sleep & dormant power management modes? It sort of looks like I might be able to, but.. should I? Is there a newer firmware that already exposes this? I notice machine.RTC() is not available which is in the 'standard' micropython release.

I would probably just put some calls that in turn call the C functions within this file:
https://github.com/raspberrypi/pico-ext ... ep/sleep.c

hippy
Posts: 10886
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: How To : Add C Extensions to Pico MicroPython

Sun Feb 21, 2021 7:34 pm

syrus8k wrote:
Sat Feb 20, 2021 10:36 pm
Would I be able to use this method to expose the sleep & dormant power management modes?
Best way to find out is probably to try it.

Return to “MicroPython”