Skip to content

Commit 1eb34d0

Browse files
authored
Merge pull request #10929 from bablokb/badger2350
Add support for Pimoroni Badger2350
2 parents f9e6afc + 5b2e839 commit 1eb34d0

File tree

7 files changed

+444
-0
lines changed

7 files changed

+444
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once
8+
9+
#include "py/obj.h"
10+
#include "shared-bindings/digitalio/DigitalInOut.h"
11+
12+
extern digitalio_digitalinout_obj_t i2c_power_en_pin_obj;
13+
extern const mp_obj_fun_builtin_fixed_t set_update_speed_obj;
14+
extern const mp_obj_fun_builtin_fixed_t get_reset_state_obj;
15+
extern const mp_obj_fun_builtin_fixed_t on_reset_pressed_obj;
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2024 Bob Abeles
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include "py/obj.h"
8+
9+
#include "mpconfigboard.h"
10+
#include "shared-bindings/busio/SPI.h"
11+
#include "shared-bindings/fourwire/FourWire.h"
12+
#include "shared-bindings/microcontroller/Pin.h"
13+
#include "shared-bindings/digitalio/DigitalInOut.h"
14+
#include "shared-bindings/microcontroller/Pin.h"
15+
#include "shared-module/displayio/__init__.h"
16+
#include "supervisor/shared/board.h"
17+
#include "supervisor/board.h"
18+
#include "badger2350-shared.h"
19+
20+
#include "hardware/gpio.h"
21+
#include "hardware/structs/iobank0.h"
22+
23+
digitalio_digitalinout_obj_t i2c_power_en_pin_obj;
24+
static volatile uint32_t reset_button_state = 0;
25+
26+
// Forward declaration to satisfy -Wmissing-prototypes
27+
static void preinit_button_state(void) __attribute__((constructor(101)));
28+
29+
30+
// pin definitions
31+
// Button pin definitions for Badger2350
32+
#define SW_A_PIN 7
33+
#define SW_B_PIN 9
34+
#define SW_C_PIN 10
35+
#define SW_DOWN_PIN 6
36+
#define SW_UP_PIN 11
37+
38+
static const uint8_t _sw_pin_nrs[] = {
39+
SW_A_PIN, SW_B_PIN, SW_C_PIN, SW_DOWN_PIN, SW_UP_PIN
40+
};
41+
42+
// Mask of all front button pins
43+
#define SW_MASK ((1 << SW_A_PIN) | (1 << SW_B_PIN) | (1 << SW_C_PIN) | \
44+
(1 << SW_DOWN_PIN) | (1 << SW_UP_PIN))
45+
46+
// This function runs BEFORE main() via constructor attribute!
47+
// This is the key to fast button state detection.
48+
// Priority 101 = runs very early
49+
50+
static void preinit_button_state(void) {
51+
// Configure button pins as inputs with pull-downs using direct register access
52+
// This is faster than SDK functions and works before full init
53+
54+
for (size_t i = 0; i < sizeof(_sw_pin_nrs); i++) {
55+
uint8_t pin_nr = _sw_pin_nrs[i];
56+
// Set as input
57+
sio_hw->gpio_oe_clr = 1u << pin_nr;
58+
// enable pull-ups
59+
pads_bank0_hw->io[pin_nr] = PADS_BANK0_GPIO0_IE_BITS |
60+
PADS_BANK0_GPIO0_PUE_BITS;
61+
// Set GPIO function
62+
iobank0_hw->io[pin_nr].ctrl = 5; // SIO function
63+
}
64+
65+
// Small delay for pins to settle (just a few cycles)
66+
for (volatile int i = 0; i < 100; i++) {
67+
__asm volatile ("nop");
68+
}
69+
70+
// Capture button states NOW - before anything else runs
71+
reset_button_state = ~sio_hw->gpio_in & SW_MASK;
72+
}
73+
74+
static mp_obj_t _get_reset_state(void) {
75+
return mp_obj_new_int(reset_button_state);
76+
}
77+
MP_DEFINE_CONST_FUN_OBJ_0(get_reset_state_obj, _get_reset_state);
78+
79+
static mp_obj_t _on_reset_pressed(mp_obj_t pin_in) {
80+
mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(pin_in);
81+
return mp_obj_new_bool(
82+
(reset_button_state & (1 << pin->number)) != 0);
83+
}
84+
MP_DEFINE_CONST_FUN_OBJ_1(on_reset_pressed_obj, _on_reset_pressed);
85+
86+
// The display uses an SSD1680 control chip.
87+
uint8_t _start_sequence[] = {
88+
0x12, 0x80, 0x00, 0x14, // soft reset and wait 20ms
89+
0x11, 0x00, 0x01, 0x03, // Ram data entry mode
90+
0x3c, 0x00, 0x01, 0x03, // border color
91+
0x2c, 0x00, 0x01, 0x28, // Set vcom voltage
92+
0x03, 0x00, 0x01, 0x17, // Set gate voltage
93+
0x04, 0x00, 0x03, 0x41, 0xae, 0x32, // Set source voltage
94+
0x4e, 0x00, 0x01, 0x00, // ram x count
95+
0x4f, 0x00, 0x02, 0x00, 0x00, // ram y count
96+
0x01, 0x00, 0x03, 0x07, 0x01, 0x00, // set display size
97+
0x32, 0x00, 0x99, // Update waveforms
98+
99+
// offset 44
100+
0x40, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L0
101+
0xA0, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L1
102+
0xA8, 0x65, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L2
103+
0xAA, 0x65, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L3
104+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L4
105+
106+
// offset 104
107+
0x02, 0x00, 0x00, 0x05, 0x0A, 0x00, 0x03, // Group0 (with default speed==0)
108+
// offset 111
109+
0x19, 0x19, 0x00, 0x02, 0x00, 0x00, 0x03, // Group1 (with default speed==0)
110+
// offset 118
111+
0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x03, // Group2 (with default speed==0)
112+
113+
// offset 125
114+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group3
115+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group4
116+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group5
117+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group6
118+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group7
119+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group8
120+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group9
121+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group10
122+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group11
123+
0x44, 0x42, 0x22, 0x22, 0x23, 0x32, 0x00, // Config
124+
0x00, 0x00, // FR, XON
125+
126+
0x22, 0x00, 0x01, 0xc7, // display update mode
127+
128+
};
129+
130+
const uint8_t _stop_sequence[] = {
131+
0x10, 0x00, 0x01, 0x01 // DSM deep sleep mode 1
132+
};
133+
134+
const uint8_t _refresh_sequence[] = {
135+
0x20, 0x00, 0x00 // ADUS
136+
};
137+
138+
// Change update speed. This changes the repeat-count in the LUTs
139+
// Pimoroni uses: 0 == slow ... 3 == fast
140+
// and calculates the LUT repeat count as 3-speed
141+
142+
#define SPEED_OFFSET_1 110
143+
#define SPEED_OFFSET_2 117
144+
#define SPEED_OFFSET_3 124
145+
146+
static mp_obj_t _set_update_speed(mp_obj_t speed_in) {
147+
mp_int_t speed = mp_obj_get_int(speed_in);
148+
uint8_t count = (uint8_t)3 - (uint8_t)(speed & 3);
149+
_start_sequence[SPEED_OFFSET_1] = count;
150+
_start_sequence[SPEED_OFFSET_2] = count;
151+
_start_sequence[SPEED_OFFSET_3] = count;
152+
return mp_const_none;
153+
}
154+
155+
MP_DEFINE_CONST_FUN_OBJ_1(set_update_speed_obj, _set_update_speed);
156+
157+
void board_init(void) {
158+
// Drive the I2C_POWER_EN pin high
159+
i2c_power_en_pin_obj.base.type = &digitalio_digitalinout_type;
160+
common_hal_digitalio_digitalinout_construct(
161+
&i2c_power_en_pin_obj, &pin_GPIO27);
162+
common_hal_digitalio_digitalinout_switch_to_output(
163+
&i2c_power_en_pin_obj, true, DRIVE_MODE_PUSH_PULL);
164+
common_hal_digitalio_digitalinout_never_reset(&i2c_power_en_pin_obj);
165+
166+
fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus;
167+
busio_spi_obj_t *spi = &bus->inline_bus;
168+
common_hal_busio_spi_construct(spi, &pin_GPIO18, &pin_GPIO19, NULL, false);
169+
common_hal_busio_spi_never_reset(spi);
170+
171+
bus->base.type = &fourwire_fourwire_type;
172+
common_hal_fourwire_fourwire_construct(bus,
173+
spi,
174+
MP_OBJ_FROM_PTR(&pin_GPIO20), // EPD_DC Command or data
175+
MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select
176+
MP_OBJ_FROM_PTR(&pin_GPIO21), // EPD_RST Reset
177+
12000000, // Baudrate
178+
0, // Polarity
179+
0); // Phase
180+
181+
// create and configure display
182+
epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display;
183+
display->base.type = &epaperdisplay_epaperdisplay_type;
184+
185+
epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS;
186+
args.bus = bus;
187+
args.start_sequence = _start_sequence;
188+
args.start_sequence_len = sizeof(_start_sequence);
189+
args.stop_sequence = _stop_sequence;
190+
args.stop_sequence_len = sizeof(_stop_sequence);
191+
args.width = 264;
192+
args.height = 176;
193+
args.ram_width = 250;
194+
args.ram_height = 296;
195+
args.rotation = 270;
196+
args.set_column_window_command = 0x44;
197+
args.set_row_window_command = 0x45;
198+
args.set_current_column_command = 0x4e;
199+
args.set_current_row_command = 0x4f;
200+
args.write_black_ram_command = 0x24;
201+
args.write_color_ram_command = 0x26;
202+
args.color_bits_inverted = true;
203+
args.refresh_sequence = _refresh_sequence;
204+
args.refresh_sequence_len = sizeof(_refresh_sequence);
205+
args.refresh_time = 1.0;
206+
args.busy_pin = &pin_GPIO16;
207+
args.busy_state = true;
208+
args.seconds_per_frame = 3.0;
209+
args.grayscale = true;
210+
args.two_byte_sequence_length = true;
211+
args.address_little_endian = true;
212+
common_hal_epaperdisplay_epaperdisplay_construct(display, &args);
213+
}
214+
215+
void board_deinit(void) {
216+
epaperdisplay_epaperdisplay_obj_t *display = &displays[0].epaper_display;
217+
if (display->base.type == &epaperdisplay_epaperdisplay_type) {
218+
while (common_hal_epaperdisplay_epaperdisplay_get_busy(display)) {
219+
RUN_BACKGROUND_TASKS;
220+
}
221+
}
222+
common_hal_displayio_release_displays();
223+
}
224+
225+
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
firmware_size = 1532k;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2026 Bernhard Bablok
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#define MICROPY_HW_BOARD_NAME "Pimoroni Badger 2350"
8+
#define MICROPY_HW_MCU_NAME "rp2350a"
9+
10+
#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1)
11+
#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1)
12+
13+
#define MICROPY_HW_LED_STATUS (&pin_GPIO0)
14+
15+
#define DEFAULT_I2C_BUS_SDA (&pin_GPIO4)
16+
#define DEFAULT_I2C_BUS_SCL (&pin_GPIO5)
17+
18+
#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18)
19+
#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19)
20+
21+
#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO8)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
USB_VID = 0x2E8A
2+
USB_PID = 0x1100
3+
USB_PRODUCT = "Pimoroni Badger 2350"
4+
USB_MANUFACTURER = "Pimoroni"
5+
6+
CHIP_VARIANT = RP2350
7+
CHIP_PACKAGE = A
8+
CHIP_FAMILY = rp2
9+
10+
EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ"
11+
12+
CIRCUITPY__EVE = 1
13+
14+
CIRCUITPY_CYW43 = 1
15+
CIRCUITPY_SSL = 1
16+
CIRCUITPY_HASHLIB = 1
17+
CIRCUITPY_WEB_WORKFLOW = 1
18+
CIRCUITPY_MDNS = 1
19+
CIRCUITPY_SOCKETPOOL = 1
20+
CIRCUITPY_WIFI = 1
21+
22+
# PIO clock divider set to 2 (default), consider changing if TM2 gSPI
23+
# becomes unreliable.
24+
CFLAGS += \
25+
-DCYW43_PIN_WL_DYNAMIC=0 \
26+
-DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \
27+
-DCYW43_DEFAULT_PIN_WL_REG_ON=23 \
28+
-DCYW43_DEFAULT_PIN_WL_CLOCK=29 \
29+
-DCYW43_DEFAULT_PIN_WL_DATA_IN=24 \
30+
-DCYW43_DEFAULT_PIN_WL_DATA_OUT=24 \
31+
-DCYW43_DEFAULT_PIN_WL_CS=25 \
32+
-DCYW43_WL_GPIO_COUNT=3 \
33+
-DCYW43_PIO_CLOCK_DIV_INT=2 \
34+
-DCYW43_PIO_CLOCK_DIV_FRAC=0
35+
# Must be accompanied by a linker script change
36+
CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)'
37+
38+
FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-pcf85063a
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2024 Bob Abeles
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once
8+
9+
// Put board-specific pico-sdk definitions here. This file must exist.

0 commit comments

Comments
 (0)