Skip to content

Commit 8afe185

Browse files
authored
Merge pull request #9256 from bradanlane/bls_rp2040_explorer_badge
added explorer badge RP2040 board
2 parents ba22fc8 + bed97ed commit 8afe185

File tree

5 files changed

+544
-0
lines changed

5 files changed

+544
-0
lines changed
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2024 Bradán Lane STUDIO
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
/*
26+
The Explorer Badge(s) have more than one specific hardware configuration.
27+
This is a result of small changes in parts availability. The changes are
28+
insignificant to the end user but require changes to the initialization.
29+
The hardware revisions use a voltage divider connected to pin GP29 to
30+
indicate which hardware configuration exists. The code generates a
31+
"version ID" or VID which is used by the code to perform the correct
32+
initialization. The VID is also exposed to the end user in case there is
33+
a need - either for documentation, support requests, or tutorial materials.
34+
*/
35+
36+
#include "mpconfigboard.h"
37+
38+
#include "supervisor/board.h"
39+
#include "shared-bindings/board/__init__.h"
40+
41+
#include "shared-bindings/busio/SPI.h"
42+
#include "shared-bindings/fourwire/FourWire.h"
43+
#include "shared-bindings/microcontroller/Pin.h"
44+
#include "shared-module/displayio/__init__.h"
45+
#include "supervisor/shared/board.h"
46+
47+
#include "src/rp2_common/hardware_gpio/include/hardware/gpio.h"
48+
49+
#include "src/rp2_common/hardware_adc/include/hardware/adc.h"
50+
#define ADC_FIRST_PIN_NUMBER 26
51+
#define ADC_PIN_COUNT 4
52+
extern void common_hal_mcu_delay_us(uint32_t);
53+
54+
#define HEIGHT 200
55+
#define WIDTH 200
56+
57+
#define DELAY_FLAG 0x80
58+
59+
#define EPD_RAM_BW 0x10
60+
#define EPD_RAM_RED 0x13
61+
62+
#define DISPLAY_EN_PIN 8
63+
64+
// These commands are the combination of SSD1608 and SSD1681 and not all commands are supported for each controller
65+
#define SSD_DRIVER_CONTROL 0x01
66+
#define SSD_GATE_VOLTAGE 0x03
67+
#define SSD_SOURCE_VOLTAGE 0x04
68+
#define SSD_DISPLAY_CONTROL 0x07
69+
#define SSD_PROGOTP_INITIAL 0x08
70+
#define SSD_WRITEREG_INITIAL 0x09
71+
#define SSD_READREG_INITIAL 0x0A
72+
#define SSD_NON_OVERLAP 0x0B
73+
#define SSD_BOOST_SOFT_START 0x0C
74+
#define SSD_GATE_SCAN_START 0x0F
75+
#define SSD_DEEP_SLEEP 0x10
76+
#define SSD_DATA_MODE 0x11
77+
#define SSD_SW_RESET 0x12
78+
#define SSD_HV_DETECT 0x14
79+
#define SSD_VCI_DETECT 0x15
80+
#define SSD_TEMP_CONTROL_1681 0x18
81+
#define SSD_TEMP_CONTROL_1608 0x1C
82+
#define SSD_TEMP_WRITE 0x1A
83+
#define SSD_TEMP_READ 0x1B
84+
#define SSD_TEMP_EXTERN 0x1C
85+
#define SSD_MASTER_ACTIVATE 0x20
86+
#define SSD_DISP_CTRL1 0x21
87+
#define SSD_DISP_CTRL2 0x22
88+
#define SSD_WRITE_RAM_BLK 0x24
89+
#define SSD_READ_RAM_BLK 0x25
90+
#define SSD_WRITE_RAM_RED 0x26
91+
#define SSD_READ_RAM_RED 0x27
92+
#define SSD_VCOM_SENSE 0x28
93+
#define SSD_VCOM_DURRATION 0x29
94+
#define SSD_PROG_VCOM 0x2A
95+
#define SSD_CTRL_VCOM 0x2B
96+
#define SSD_WRITE_VCOM 0x2C
97+
#define SSD_READ_OTP 0x2D
98+
#define SSD_READ_ID 0x2E
99+
#define SSD_READ_STATUS 0x2F
100+
#define SSD_WRITE_LUT 0x32
101+
#define SSD_WRITE_DUMMY 0x3A
102+
#define SSD_WRITE_GATELINE_1608 0x3B
103+
#define SSD_WRITE_BORDER 0x3C
104+
#define SSD_SET_RAMXPOS 0x44
105+
#define SSD_SET_RAMYPOS 0x45
106+
#define SSD_SET_RAMXCOUNT 0x4E
107+
#define SSD_SET_RAMYCOUNT 0x4F
108+
#define SSD_NOP 0xFF
109+
110+
const uint8_t _start_sequence_ssd1681[] = {
111+
SSD_SW_RESET, DELAY_FLAG + 0, 20, // soft reset and wait 20ms
112+
SSD_DATA_MODE, 1, 0x03, // Data entry sequence
113+
SSD_WRITE_BORDER, 1, 0x05, // border color
114+
SSD_TEMP_CONTROL_1681, 1, 0x80, // Temperature control
115+
SSD_SET_RAMXCOUNT, 1, 0x00,
116+
SSD_SET_RAMYCOUNT, 2, 0x00, 0x00,
117+
SSD_DRIVER_CONTROL, 3, ((WIDTH - 1) & 0xFF), (((WIDTH >> 8) - 1) & 0xFF), 0x00, // set display size
118+
SSD_DISP_CTRL2, 1, 0xf7, // Set DISP only full refreshes
119+
};
120+
const uint8_t _stop_sequence_ssd1681[] = {
121+
SSD_DEEP_SLEEP, DELAY_FLAG + 1, 0x01, 0x64 // Enter deep sleep
122+
};
123+
const uint8_t _refresh_sequence_ssd1681[] = {
124+
SSD_MASTER_ACTIVATE, 0,
125+
};
126+
127+
128+
const uint8_t _start_sequence_ssd1608[] = {
129+
SSD_SW_RESET, DELAY_FLAG + 0, 20, // soft reset and wait 20ms
130+
SSD_DRIVER_CONTROL, 3, ((WIDTH - 1) & 0xFF), (((WIDTH - 1) >> 8) & 0xFF), 0x00, // set display size
131+
SSD_WRITE_GATELINE_1608, 1, 0x0B, // gate line width
132+
SSD_DATA_MODE, 1, 0x03, // Data entry sequence
133+
SSD_WRITE_VCOM, 1, 0x70,
134+
SSD_WRITE_LUT, 30, 0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, 0x66, 0x69,
135+
0x69, 0x59, 0x58, 0x99, 0x99, 0x88, 0x00, 0x00, 0x00, 0x00,
136+
0xf8, 0xb4, 0x13, 0x51, 0x35, 0x51, 0x51, 0x19, 0x01, 0x00,
137+
SSD_DISP_CTRL2, 1, 0xC7, // Set DISP only full refreshes
138+
};
139+
const uint8_t _stop_sequence_ssd1608[] = {
140+
SSD_DEEP_SLEEP, DELAY_FLAG + 1, 0x01, 0x64 // Enter deep sleep
141+
};
142+
const uint8_t _refresh_sequence_ssd1608[] = {
143+
SSD_MASTER_ACTIVATE, 0,
144+
};
145+
146+
147+
extern uint16_t vid_setting; // declared in pins.c
148+
// _set_vid() uses the GPIO29 analog pin (which is connected to a voltage divider) to computer a revision code for the board
149+
// this allows multiple similar boards to chare the same CP build
150+
static int _set_vid(void) {
151+
vid_setting = 9999;
152+
153+
#define DCK01_VID_PIN 29
154+
uint16_t value;
155+
adc_init();
156+
adc_gpio_init(DCK01_VID_PIN);
157+
adc_select_input(DCK01_VID_PIN - ADC_FIRST_PIN_NUMBER); // the VID pin is 29 and the first ADC pin is 26
158+
common_hal_mcu_delay_us(100);
159+
uint32_t accum = 0;
160+
for (int i = 0; i < 10; i++) {
161+
accum += adc_read();
162+
}
163+
value = accum / 10; // average the readings
164+
vid_setting = value;
165+
/*
166+
Voltage Divider with 3.3V: (1241 * V)
167+
10K/ 15K = 1.98V = 2458
168+
15K/ 10K = 1.32V = 1638
169+
15K/4.7K = 0.79V = 980
170+
15K/ 2K = 1.32V = 482
171+
15K/ 1K = 1.32V = 256
172+
Note: extreme values (using 100K or greater) will not create a strong enough current for the ADC to read accurately
173+
Note: we do not get a usable value when the voltage divider is missing
174+
*/
175+
176+
// TODO change to min/max to tighten up the ranges (requires sampling of the initial boards)
177+
if (value > 2800) {
178+
vid_setting = 9;
179+
} else if (value > 2000) {
180+
vid_setting = 5;
181+
} else if (value > 1200) {
182+
vid_setting = 4;
183+
} else if (value > 600) {
184+
vid_setting = 3;
185+
} else if (value > 300) {
186+
vid_setting = 2;
187+
} else if (value > 150) {
188+
vid_setting = 1;
189+
} else {
190+
vid_setting = 0;
191+
}
192+
193+
return vid_setting;
194+
}
195+
196+
197+
// Note: board_reset_pin_number() is new to CP9 and allows a board to handle pin resets on a per-pin basis
198+
// we use it to prevent the DISPLAY_EN pin from automatically changing state
199+
bool board_reset_pin_number(uint8_t pin_number) {
200+
static bool _display_pin_inited = false;
201+
202+
if (pin_number == DISPLAY_EN_PIN) {
203+
// init the pin the first time; do nothing after that
204+
if (!_display_pin_inited) {
205+
_display_pin_inited = true;
206+
// doing this (rather than gpio_init) in this specific order ensures no
207+
// glitch if pin was already configured as a high output. gpio_init() temporarily
208+
// configures the pin as an input, so the power enable value would potentially
209+
// glitch.
210+
gpio_put(pin_number, 1);
211+
gpio_set_dir(pin_number, GPIO_OUT);
212+
hw_write_masked(&padsbank0_hw->io[pin_number], PADS_BANK0_GPIO0_DRIVE_VALUE_12MA << PADS_BANK0_GPIO0_DRIVE_LSB, PADS_BANK0_GPIO0_DRIVE_BITS);
213+
gpio_set_function(pin_number, GPIO_FUNC_SIO);
214+
}
215+
return true;
216+
}
217+
return false;
218+
}
219+
220+
void board_init(void) {
221+
board_reset_pin_number(DISPLAY_EN_PIN);
222+
_set_vid(); // sets vid_setting global
223+
224+
fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus;
225+
busio_spi_obj_t *spi = &bus->inline_bus;
226+
common_hal_busio_spi_construct(spi, &pin_GPIO14, &pin_GPIO15, NULL, false);
227+
common_hal_busio_spi_never_reset(spi);
228+
229+
bus->base.type = &fourwire_fourwire_type;
230+
common_hal_fourwire_fourwire_construct(bus,
231+
spi,
232+
&pin_GPIO11, // DEFAULT_SPI_BUS_DC, // EPD_DC Command or data
233+
&pin_GPIO13, // DEFAULT_SPI_BUS_CS, // EPD_CS Chip select
234+
&pin_GPIO10, // DEFAULT_SPI_BUS_RESET, // EPD_RST Reset
235+
1000000, // Baudrate
236+
0, // Polarity
237+
0); // Phase
238+
239+
// board_vid is a computed flag to let us know what hardware is active
240+
// currently, we only know two codes '1' and '2' and these indicate what ePaper display is installed
241+
242+
epaperdisplay_epaperdisplay_obj_t *display = NULL;
243+
244+
// Set up the DisplayIO epaper object
245+
display = &allocate_display()->epaper_display;
246+
display->base.type = &epaperdisplay_epaperdisplay_type;
247+
248+
// VID codes: 1 = tricolor ePaper (BWR), 2 = monochrome ePaper (BW), other codes are TBD
249+
if (vid_setting == 1) {
250+
common_hal_epaperdisplay_epaperdisplay_construct(
251+
display,
252+
bus,
253+
_start_sequence_ssd1681, sizeof(_start_sequence_ssd1681),
254+
1.0, // start up time
255+
_stop_sequence_ssd1681, sizeof(_stop_sequence_ssd1681),
256+
WIDTH, // width
257+
HEIGHT, // height
258+
WIDTH, // ram_width
259+
HEIGHT + 0x60, // ram_height RAM is actually only 200 bits high but we use 296 to match the 9 bits
260+
0, // colstart
261+
0, // rowstart
262+
270, // rotation
263+
SSD_SET_RAMXPOS, // set_column_window_command
264+
SSD_SET_RAMYPOS, // set_row_window_command
265+
SSD_SET_RAMXCOUNT, // set_current_column_command
266+
SSD_SET_RAMYCOUNT, // set_current_row_command
267+
SSD_WRITE_RAM_BLK, // write_black_ram_command
268+
false, // black_bits_inverted
269+
SSD_WRITE_RAM_RED, // write_color_ram_command
270+
false, // color_bits_inverted
271+
0xFF0000, // highlight_color (RED for tri-color display)
272+
_refresh_sequence_ssd1681, sizeof(_refresh_sequence_ssd1681), // refresh_display_command
273+
15.0, // refresh_time
274+
&pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin
275+
true, // busy_state
276+
20.0, // seconds_per_frame (does not seem the user can change this)
277+
true, // always_toggle_chip_select
278+
false, // not grayscale
279+
false, // not acep
280+
false, // not two_byte_sequence_length
281+
true); // address_little_endian
282+
} else if (vid_setting == 2) {
283+
common_hal_epaperdisplay_epaperdisplay_construct(
284+
display,
285+
bus,
286+
_start_sequence_ssd1608, sizeof(_start_sequence_ssd1608),
287+
1.0, // start up time
288+
_stop_sequence_ssd1608, sizeof(_stop_sequence_ssd1608),
289+
WIDTH, // width
290+
HEIGHT, // height
291+
WIDTH, // ram_width
292+
HEIGHT /* + 0x60 */, // ram_height RAM is actually only 200 bits high but we use 296 to match the 9 bits
293+
0, // colstart
294+
0, // rowstart
295+
0, // rotation
296+
SSD_SET_RAMXPOS, // set_column_window_command
297+
SSD_SET_RAMYPOS, // set_row_window_command
298+
SSD_SET_RAMXCOUNT, // set_current_column_command
299+
SSD_SET_RAMYCOUNT, // set_current_row_command
300+
SSD_WRITE_RAM_BLK, // write_black_ram_command
301+
false, // black_bits_inverted
302+
NO_COMMAND, // write_color_ram_command
303+
false, // color_bits_inverted
304+
0x000000, // highlight_color (RED for tri-color display)
305+
_refresh_sequence_ssd1608, sizeof(_refresh_sequence_ssd1608), // refresh_display_command
306+
1.0, // refresh_time
307+
&pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin
308+
true, // busy_state
309+
5.0, // seconds_per_frame (does not seem the user can change this)
310+
true, // always_toggle_chip_select
311+
false, // not grayscale
312+
false, // not acep
313+
false, // not two_byte_sequence_length
314+
true); // address_little_endian
315+
} else {
316+
// what should happen if this firmware is installed on some other board?
317+
// currently, we mark the display as None
318+
display->base.type = &mp_type_NoneType;
319+
}
320+
321+
}
322+
323+
void board_deinit(void) {
324+
if ((vid_setting == 1) || (vid_setting == 2)) {
325+
// we initialized an ePaper display so we can de-init it
326+
epaperdisplay_epaperdisplay_obj_t *display = &displays[0].epaper_display;
327+
if (display->base.type == &epaperdisplay_epaperdisplay_type) {
328+
while (common_hal_epaperdisplay_epaperdisplay_get_busy(display)) {
329+
// RUN_BACKGROUND_TASKS;
330+
}
331+
}
332+
common_hal_displayio_release_displays();
333+
}
334+
}
335+
336+
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.

0 commit comments

Comments
 (0)