diff --git a/drivers/uc8151/uc8151.cpp b/drivers/uc8151/uc8151.cpp index 5b7fd099..b61fedca 100644 --- a/drivers/uc8151/uc8151.cpp +++ b/drivers/uc8151/uc8151.cpp @@ -461,6 +461,10 @@ namespace pimoroni { *p |= b; // set bit value } + uint8_t* UC8151::get_frame_buffer() { + return frame_buffer; + } + void UC8151::invert(bool inv) { inverted = inv; command(CDI, {(uint8_t)(inverted ? 0b01'01'1100 : 0b01'00'1100)}); // vcom and data interval @@ -477,15 +481,15 @@ namespace pimoroni { uint32_t UC8151::update_time() { switch(_update_speed) { case 0: - return 5500; + return 4500; case 1: - return 2600; + return 2000; case 2: - return 1000; + return 800; case 3: - return 300; + return 250; default: - return 5500; + return 4500; } } diff --git a/drivers/uc8151/uc8151.hpp b/drivers/uc8151/uc8151.hpp index 051c33f3..07007d8d 100644 --- a/drivers/uc8151/uc8151.hpp +++ b/drivers/uc8151/uc8151.hpp @@ -208,6 +208,7 @@ namespace pimoroni { void off(); void pixel(int x, int y, int v); + uint8_t* get_frame_buffer(); }; } diff --git a/libraries/badger2040/badger2040.cpp b/libraries/badger2040/badger2040.cpp index 695a56d7..c257b9d1 100644 --- a/libraries/badger2040/badger2040.cpp +++ b/libraries/badger2040/badger2040.cpp @@ -69,14 +69,6 @@ namespace pimoroni { // TODO: set default image? } - void Badger2040::clear() { - for(uint32_t y = 0; y < 128; y++) { - for(uint32_t x = 0; x < 296; x++) { - pixel(x, y); - } - } - } - void Badger2040::halt() { gpio_put(ENABLE_3V3, 0); @@ -100,15 +92,54 @@ namespace pimoroni { 15, 7, 13, 5 }; + if (p == 0) { + return 1; + } + if (p == 15) { + return 0; + } + // calculate dither matrix offset uint32_t dmo = (x & 0b11) | ((y & 0b11) << 2); - if(p == 0) { - return 1; - }else if(p == 15) { + return p <= _odm[dmo] ? 1 : 0; + } + + // Return dither values for an entire byte in the column + uint8_t _dither_column_value(int32_t x, uint8_t p) { + if (p == 0) { + return 0xff; + } + if (p == 15) { return 0; - }else{ - return p <= _odm[dmo] ? 1 : 0; + } + + uint8_t val = 0; + for (int32_t y = 0; y < 4; ++y) { + val |= _dither_value(x, y, p) << (7 - y); + } + val |= val >> 4; + return val; + } + + + void Badger2040::clear() { + const uint32_t column_len = 128 / 8; + const uint32_t buf_len = column_len * 296; + uint8_t* buf = uc8151.get_frame_buffer(); + + if (_pen == 0) { + memset(buf, 0xff, buf_len); + } + else if (_pen == 15) { + memset(buf, 0, buf_len); + } + else { + for(uint32_t x = 0; x < 296; x++) { + uint8_t val = _dither_column_value(x, _pen); + memset(buf, val, column_len); + buf += column_len; + } } } @@ -130,14 +161,38 @@ namespace pimoroni { image(data, sheet_width, icon_size * index, 0, icon_size, icon_size, dx, dy); } - // Display an image that fills the screen (286*128) + // Display an image that fills the screen (296*128) void Badger2040::image(const uint8_t* data) { - image(data, 296, 0, 0, 296, 128, 0, 0); + uint8_t* ptr = uc8151.get_frame_buffer(); + + for (uint32_t x = 0; x < 296; ++x) { + // extract bitmask for this pixel + uint32_t bm = 0b10000000 >> (x & 0b111); + + for (uint32_t y = 0; y < 128; y += 8) { + uint8_t val = 0; + for (uint32_t cy = 0; cy < 8; ++cy) { + // work out byte offset in source data + uint32_t o = ((y + cy) * (296 >> 3)) + (x >> 3); + + // Set bit in val if set in source data + if (data[o] & bm) { + val |= 0b10000000 >> cy; + } + } + *ptr++ = val; + } + } } // Display an image smaller than the screen (sw*sh) at dx, dy void Badger2040::image(const uint8_t *data, int w, int h, int x, int y) { - image(data, w, 0, 0, w, h, x, y); + if (x == 0 && y == 0 && w == 296 && h == 128) { + image(data); + } + else { + image(data, w, 0, 0, w, h, x, y); + } } void Badger2040::image(const uint8_t *data, int stride, int sx, int sy, int dw, int dh, int dx, int dy) { @@ -156,9 +211,44 @@ namespace pimoroni { } void Badger2040::rectangle(int32_t x, int32_t y, int32_t w, int32_t h) { - for(int cy = y; cy < y + h; cy++) { + // Adjust for thickness + uint32_t ht = _thickness / 2; + x -= ht; + if (x < 0) { + w += x; + x = 0; + } + y -= ht; + if (y < 0) { + h += y; + y = 0; + } + w += _thickness - 1; + h += _thickness - 1; + + if (h >= 8) { + // Directly write to the frame buffer when clearing a large area + uint8_t* buf = uc8151.get_frame_buffer(); + for(int cx = x; cx < x + w; cx++) { - pixel(cx, cy); + uint8_t* buf_ptr = &buf[cx * 16 + y / 8]; + uint8_t first_mask = 0xff >> (y & 7); + uint8_t last_mask = 0xff >> ((y + h) & 7); + uint32_t val = _dither_column_value(cx, _pen); + *buf_ptr &= ~first_mask; + *buf_ptr++ |= (val & first_mask); + for (int32_t c = h - (8 - (y & 7)); c >= 8; c -= 8) { + *buf_ptr++ = val; + } + *buf_ptr &= last_mask; + *buf_ptr |= (val & (~last_mask)); + } + } + else { + for(int cx = x; cx < x + w; cx++) { + for(int cy = y; cy < y + h; cy++) { + uc8151.pixel(cx, cy, _dither_value(cx, cy, _pen)); + } } } } diff --git a/micropython/examples/badger2040/clock.py b/micropython/examples/badger2040/clock.py index 973e1d48..0865780b 100644 --- a/micropython/examples/badger2040/clock.py +++ b/micropython/examples/badger2040/clock.py @@ -2,6 +2,8 @@ import time import machine import badger2040 +# We're going to keep the badger on, so slow down the system clock if on battery +badger2040.system_speed(badger2040.SYSTEM_SLOW) rtc = machine.RTC() display = badger2040.Badger2040() diff --git a/micropython/examples/badger2040/conway.py b/micropython/examples/badger2040/conway.py index 7bdb23d7..3f8c67de 100644 --- a/micropython/examples/badger2040/conway.py +++ b/micropython/examples/badger2040/conway.py @@ -6,6 +6,9 @@ import machine import badger2040 +# Overclock the RP2040 to run the sim faster +badger2040.system_speed(badger2040.SYSTEM_TURBO) + # ------------------------------ # Program setup # ------------------------------ @@ -16,6 +19,7 @@ INITIAL_DENSITY = 0.3 # Density of cells at start # Create a new Badger and set it to update TURBO screen = badger2040.Badger2040() +screen.led(128) screen.update_speed(badger2040.UPDATE_TURBO) restart = False # should sim be restarted diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index 2741f45c..8c043dc9 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -1,14 +1,13 @@ import gc import time import math -import machine import badger2040 from badger2040 import WIDTH import launchericons import badger_os -# Reduce clock speed to 48MHz, that's fast enough! -machine.freq(48000000) +# Reduce clock speed to 48MHz +badger2040.system_speed(badger2040.SYSTEM_NORMAL) changed = False exited_to_launcher = False diff --git a/micropython/modules/badger2040/README.md b/micropython/modules/badger2040/README.md index 5b2f9f26..c082f015 100644 --- a/micropython/modules/badger2040/README.md +++ b/micropython/modules/badger2040/README.md @@ -67,7 +67,19 @@ Badger 2040 features five buttons on its front, labelled A, B, C, ↑ (up), ↓ * `BUTTON_DOWN` = `11` * `BUTTON_USER` = `23` -Note, due to the BOOT/USR button performing both BOOT and USER functions, the output of reading it with `pressed()` will be inverted from the others. +## System speed + +The system clock speed of the RP2040 can be controlled, allowing power to be saved if on battery, or faster computations to be performed. Use `badger2040.system_speed(speed)` where `speed` is one of the following constants: + +* `SYSTEM_VERY_SLOW` = `0` _4 MHz if on battery, 48 MHz if connected to USB_ +* `SYSTEM_SLOW` = `1` _12 MHz if on battery, 48 MHz if connected to USB_ +* `SYSTEM_NORMAL` = `2` _48 MHz_ +* `SYSTEM_FAST` = `3` _133 MHz_ +* `SYSTEM_TURBO` = `4` _250 MHz_ + +On USB, the system will not run slower than 48MHz, as that is the minimum clock speed required to keep the USB connection stable. + +Note that `SYSTEM_TURBO` overclocks the RP2040 to 250MHz, and applies a small over voltage to ensure this is stable. We've found that every RP2040 we've tested is happy to run at this speed without any issues. ## Other Functions @@ -95,7 +107,6 @@ icon(data, icon_index, sheet_size, icon_size) clear() update() partial_update(x, y, w, h) -update_speed(speed) invert(inverted) ``` diff --git a/micropython/modules/badger2040/badger2040.c b/micropython/modules/badger2040/badger2040.c index aa81c828..9c1df9e4 100644 --- a/micropython/modules/badger2040/badger2040.c +++ b/micropython/modules/badger2040/badger2040.c @@ -35,6 +35,7 @@ MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_command_obj, Badger2040_command); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_pressed_to_wake_obj, Badger2040_pressed_to_wake); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_halt_obj, Badger2040_halt); MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_woken_by_button_obj, Badger2040_woken_by_button); +MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_system_speed_obj, Badger2040_system_speed); /***** Binding of Methods *****/ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { @@ -90,6 +91,7 @@ STATIC const mp_rom_map_elem_t badger2040_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_pressed_to_wake), MP_ROM_PTR(&Badger2040_pressed_to_wake_obj) }, { MP_ROM_QSTR(MP_QSTR_woken_by_button), MP_ROM_PTR(&Badger2040_woken_by_button_obj) }, + { MP_ROM_QSTR(MP_QSTR_system_speed), MP_ROM_PTR(&Badger2040_system_speed_obj) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_NORMAL), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_MEDIUM), MP_ROM_INT(1) }, @@ -97,6 +99,12 @@ STATIC const mp_rom_map_elem_t badger2040_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_UPDATE_TURBO), MP_ROM_INT(3) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_SUPER_EXTRA_TURBO), MP_ROM_INT(3) }, // ho ho placebo! + { MP_ROM_QSTR(MP_QSTR_SYSTEM_VERY_SLOW), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_SYSTEM_SLOW), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_SYSTEM_NORMAL), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_SYSTEM_FAST), MP_ROM_INT(3) }, + { MP_ROM_QSTR(MP_QSTR_SYSTEM_TURBO), MP_ROM_INT(4) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(296) }, { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(128) }, diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index d928b193..1f444b6e 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -139,7 +139,7 @@ MICROPY_EVENT_POLL_HOOK // Ensure blocking for the minimum amount of time // in cases where "is_busy" is unreliable. - while(self->badger2040->is_busy() || absolute_time_diff_us(t_end, get_absolute_time()) > 0) { + while(self->badger2040->is_busy() || absolute_time_diff_us(get_absolute_time(), t_end) > 0) { #ifdef MICROPY_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK #endif @@ -517,4 +517,75 @@ mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args, mp_ma return mp_obj_new_int(self->badger2040->measure_glyph(c, scale)); } +#include "hardware/vreg.h" +#include "hardware/clocks.h" +#include "hardware/pll.h" + +mp_obj_t Badger2040_system_speed(mp_obj_t speed) { + uint32_t sys_freq; + uint32_t selected_speed = mp_obj_get_int(speed); + + if (gpio_get(pimoroni::Badger2040::VBUS_DETECT) && selected_speed < 2) { + // If on USB never go slower than normal speed. + selected_speed = 2; + } + + switch (selected_speed) + { + case 4: // TURBO: 250 MHZ, 1.2V + vreg_set_voltage(VREG_VOLTAGE_1_20); + set_sys_clock_khz(250000, true); + return mp_const_none; + case 3: // FAST: 133 MHZ + vreg_set_voltage(VREG_VOLTAGE_1_10); + set_sys_clock_khz(133000, true); + return mp_const_none; + + default: + case 2: // NORMAL: 48 MHZ + vreg_set_voltage(VREG_VOLTAGE_1_10); + set_sys_clock_48mhz(); + return mp_const_none; + + case 1: // SLOW: 12 MHZ, 1.0V + sys_freq = 12 * MHZ; + break; + + case 0: // VERY_SLOW: 4 MHZ, 1.0V + sys_freq = 4 * MHZ; + break; + } + + + // Set the configured clock speed, by dividing the USB PLL + clock_configure(clk_sys, + CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + 48 * MHZ, + sys_freq); + + clock_configure(clk_peri, + 0, + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, + sys_freq, + sys_freq); + + clock_configure(clk_adc, + 0, + CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, + 48 * MHZ, + sys_freq); + + // No longer using the SYS PLL so disable it + pll_deinit(pll_sys); + + // Not using USB so stop the clock + clock_stop(clk_usb); + + // Drop the core voltage + vreg_set_voltage(VREG_VOLTAGE_1_00); + + return mp_const_none; +} + } diff --git a/micropython/modules/badger2040/badger2040.h b/micropython/modules/badger2040/badger2040.h index 0b6fac44..58ebc851 100644 --- a/micropython/modules/badger2040/badger2040.h +++ b/micropython/modules/badger2040/badger2040.h @@ -43,3 +43,5 @@ extern mp_obj_t Badger2040_command(mp_obj_t self_in, mp_obj_t reg, mp_obj_t data extern mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button); extern mp_obj_t Badger2040_woken_by_button(); + +extern mp_obj_t Badger2040_system_speed(mp_obj_t speed); diff --git a/micropython/modules/badger2040/micropython.cmake b/micropython/modules/badger2040/micropython.cmake index 4ae063de..770e4660 100644 --- a/micropython/modules/badger2040/micropython.cmake +++ b/micropython/modules/badger2040/micropython.cmake @@ -20,4 +20,7 @@ target_compile_definitions(usermod_${MOD_NAME} INTERFACE MODULE_BADGER2040_ENABLED=1 ) -target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) +target_link_libraries(usermod INTERFACE usermod_${MOD_NAME} + hardware_vreg + hardware_pll + hardware_resets)