Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bricks/_common/sources.mk
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ PYBRICKS_PYBRICKS_SRC_C = $(addprefix pybricks/,\
iodevices/pb_type_uart_device.c \
iodevices/pb_type_iodevices_xbox_controller.c \
media/pb_module_media.c \
media/pb_type_image.c \
nxtdevices/pb_module_nxtdevices.c \
nxtdevices/pb_type_nxtdevices_colorsensor.c \
nxtdevices/pb_type_nxtdevices_energymeter.c \
Expand Down Expand Up @@ -198,6 +199,7 @@ PBIO_SRC_C = $(addprefix lib/pbio/,\
src/drivebase.c \
src/error.c \
src/geometry.c \
src/image/image.c \
src/imu.c \
src/int_math.c \
src/integrator.c \
Expand Down
1 change: 1 addition & 0 deletions bricks/cityhub/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define PYBRICKS_PY_IODEVICES (1)
#define PYBRICKS_PY_IODEVICES_XBOX_CONTROLLER (0)
#define PYBRICKS_PY_MEDIA (0)
#define PYBRICKS_PY_MEDIA_IMAGE (0)
#define PYBRICKS_PY_NXTDEVICES (0)
#define PYBRICKS_PY_PARAMETERS (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON (1)
Expand Down
1 change: 1 addition & 0 deletions bricks/essentialhub/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#define PYBRICKS_PY_IODEVICES (1)
#define PYBRICKS_PY_IODEVICES_XBOX_CONTROLLER (1)
#define PYBRICKS_PY_MEDIA (0)
#define PYBRICKS_PY_MEDIA_IMAGE (0)
#define PYBRICKS_PY_NXTDEVICES (0)
#define PYBRICKS_PY_PARAMETERS (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON (1)
Expand Down
3 changes: 2 additions & 1 deletion bricks/ev3/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
#define PYBRICKS_PY_HUBS (1)
#define PYBRICKS_PY_IODEVICES (1)
#define PYBRICKS_PY_IODEVICES_XBOX_CONTROLLER (0)
#define PYBRICKS_PY_MEDIA (0)
#define PYBRICKS_PY_MEDIA (1)
#define PYBRICKS_PY_MEDIA_IMAGE (1)
#define PYBRICKS_PY_NXTDEVICES (1)
#define PYBRICKS_PY_PARAMETERS (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON (1)
Expand Down
1 change: 1 addition & 0 deletions bricks/movehub/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#define PYBRICKS_PY_HUBS (1)
#define PYBRICKS_PY_IODEVICES (0)
#define PYBRICKS_PY_MEDIA (0)
#define PYBRICKS_PY_MEDIA_IMAGE (0)
#define PYBRICKS_PY_PARAMETERS (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON_REMOTE_ONLY (1)
Expand Down
1 change: 1 addition & 0 deletions bricks/nxt/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#define PYBRICKS_PY_HUBS (1)
#define PYBRICKS_PY_IODEVICES (0)
#define PYBRICKS_PY_MEDIA (0)
#define PYBRICKS_PY_MEDIA_IMAGE (0)
#define PYBRICKS_PY_NXTDEVICES (0)
#define PYBRICKS_PY_PARAMETERS (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON (1)
Expand Down
1 change: 1 addition & 0 deletions bricks/primehub/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#define PYBRICKS_PY_IODEVICES (1)
#define PYBRICKS_PY_IODEVICES_XBOX_CONTROLLER (1)
#define PYBRICKS_PY_MEDIA (1)
#define PYBRICKS_PY_MEDIA_IMAGE (0)
#define PYBRICKS_PY_NXTDEVICES (0)
#define PYBRICKS_PY_PARAMETERS (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON (1)
Expand Down
1 change: 1 addition & 0 deletions bricks/technichub/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define PYBRICKS_PY_IODEVICES (1)
#define PYBRICKS_PY_IODEVICES_XBOX_CONTROLLER (1)
#define PYBRICKS_PY_MEDIA (0)
#define PYBRICKS_PY_MEDIA_IMAGE (0)
#define PYBRICKS_PY_NXTDEVICES (0)
#define PYBRICKS_PY_PARAMETERS (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON (1)
Expand Down
1 change: 1 addition & 0 deletions bricks/virtualhub/mpconfigvariant.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#define PYBRICKS_PY_HUBS (1)
#define PYBRICKS_PY_IODEVICES (0)
#define PYBRICKS_PY_MEDIA (1)
#define PYBRICKS_PY_MEDIA_IMAGE (0)
#define PYBRICKS_PY_NXTDEVICES (0)
#define PYBRICKS_PY_PARAMETERS (1)
#define PYBRICKS_PY_PARAMETERS_BUTTON (1)
Expand Down
1 change: 1 addition & 0 deletions lib/pbio/doc/doxygen.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2128,6 +2128,7 @@ PREDEFINED = \
PBIO_CONFIG_DCMOTOR=1 \
PBIO_CONFIG_DCMOTOR_NUM_DEV=4 \
PBIO_CONFIG_DRIVEBASE_SPIKE=1 \
PBIO_CONFIG_IMAGE=1 \
PBIO_CONFIG_LIGHT_MATRIX=1 \
PBIO_CONFIG_LIGHT=1 \
PBIO_CONFIG_MOTOR_PROCESS=1 \
Expand Down
108 changes: 67 additions & 41 deletions lib/pbio/drv/display/display_ev3.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
#include <stdio.h>
#include <string.h>

#include <contiki.h>

#include "../core.h"

#include <pbdrv/display.h>
#include <pbdrv/gpio.h>
#include <pbio/error.h>
#include <pbio/os.h>
#include <pbio/util.h>

#include <tiam1808/edma.h>
Expand Down Expand Up @@ -111,13 +111,11 @@ static const pbdrv_gpio_t pin_lcd_reset = PBDRV_GPIO_EV3_PIN(12, 31, 28, 5, 0);

static volatile spi_status_t spi_status = SPI_STATUS_ERROR;

PROCESS(pbdrv_display_ev3_init_process, "st7586s");

/**
* Number of column triplets. Each triplet is 3 columns of pixels, as detailed
* below in the description of the display buffer.
*/
#define ST7586S_NUM_COL_TRIPLETS (60)
#define ST7586S_NUM_COL_TRIPLETS ((PBDRV_CONFIG_DISPLAY_NUM_COLS + 2) / 3)

/**
* Number of rows. This is the same as the number of display rows.
Expand All @@ -134,7 +132,7 @@ PROCESS(pbdrv_display_ev3_init_process, "st7586s");
*
* Non-atomic updated by the application are allowed.
*/
static uint8_t pbdrv_display_user_frame[PBDRV_CONFIG_DISPLAY_NUM_ROWS][PBDRV_CONFIG_DISPLAY_NUM_COLS] __attribute__((section(".noinit"), used));
static uint8_t pbdrv_display_user_frame[PBDRV_CONFIG_DISPLAY_NUM_ROWS][ST7586S_NUM_COL_TRIPLETS * 3] __attribute__((section(".noinit"), used));

/**
* Flag to indicate that the user frame has been updated and needs to be
Expand Down Expand Up @@ -227,7 +225,7 @@ static const uint16_t pbdrv_display_pybricks_logo[] = {
15542, 15608, 15648, 15680, 15720, 15786, 15826, 15858, 15898, 15964, 16004, 16036, 16075,
16143, 16182, 16215, 16253, 16321, 16359, 16393, 16431, 16499, 16537, 16572, 16608, 16678,
16714, 16751, 16785, 16857, 16891, 16930, 16962, 17036, 17068, 17109, 17139, 17215, 17245,
17289, 17314, 17396, 17421
17289, 17314, 17396, 17421, 22784
};

/**
Expand All @@ -246,6 +244,10 @@ static void pbdrv_display_load_indexed_bitmap(const uint16_t *indexed_bitmap) {
}
pbdrv_display_user_frame[r][c] = set ? 3 : 0;
}
// Fill unused columns out of screen.
for (size_t c = PBDRV_CONFIG_DISPLAY_NUM_COLS; c < ST7586S_NUM_COL_TRIPLETS * 3; c++) {
pbdrv_display_user_frame[r][c] = 0;
}
}
}

Expand All @@ -258,9 +260,8 @@ void pbdrv_display_st7586s_encode_user_frame(void) {
// Iterating ST7586S column-triplets, which are 3 columns each.
for (size_t triplet = 0; triplet < ST7586S_NUM_COL_TRIPLETS; triplet++) {
uint8_t p0 = pbdrv_display_user_frame[row][triplet * 3];
// The last triplet has no second and third pixel.
uint8_t p1 = triplet == ST7586S_NUM_COL_TRIPLETS - 1 ? 0 : pbdrv_display_user_frame[row][triplet * 3 + 1];
uint8_t p2 = triplet == ST7586S_NUM_COL_TRIPLETS - 1 ? 0 : pbdrv_display_user_frame[row][triplet * 3 + 2];
uint8_t p1 = pbdrv_display_user_frame[row][triplet * 3 + 1];
uint8_t p2 = pbdrv_display_user_frame[row][triplet * 3 + 2];
st7586s_send_buf[row * ST7586S_NUM_COL_TRIPLETS + triplet] = encode_triplet(p0, p1, p2);
}
}
Expand Down Expand Up @@ -349,7 +350,7 @@ static const pbdrv_display_st7586s_action_t init_script[] = {
void pbdrv_display_ev3_spi1_tx_complete(uint32_t status) {
SPIIntDisable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
spi_status = SPI_STATUS_COMPLETE;
process_poll(&pbdrv_display_ev3_init_process);
pbio_os_request_poll();
}

/**
Expand Down Expand Up @@ -411,7 +412,7 @@ void pbdrv_display_st7586s_write_data_begin(uint8_t *data, uint32_t size) {
*
* Pinmux and common EDMA handlers are already set up in platform.c.
*/
void pbdrv_display_init(void) {
static void pbdrv_display_ev3_spi_init(void) {

// GPIO Mux. CS is in GPIO mode (manual control).
pbdrv_gpio_alt(&pin_spi1_mosi, SYSCFG_PINMUX5_PINMUX5_23_20_SPI1_SIMO0);
Expand Down Expand Up @@ -444,32 +445,27 @@ void pbdrv_display_init(void) {

// Enable the SPI controller.
SPIEnable(SOC_SPI_1_REGS);

// Start SPI process and ask pbdrv to wait until it is initialized.
pbdrv_init_busy_up();
process_start(&pbdrv_display_ev3_init_process);
}

static pbio_os_process_t pbdrv_display_ev3_process;

/**
* Display driver process. Initializes the display and updates the display
* with the user frame buffer at a regular interval if the user data was
* updated.
* with the user frame buffer if the user data was updated.
*/
PROCESS_THREAD(pbdrv_display_ev3_init_process, ev, data) {
static pbio_error_t pbdrv_display_ev3_process_thread(pbio_os_state_t *state, void *context) {

static struct etimer etimer;
static pbio_os_timer_t timer;
static uint32_t script_index;
static uint8_t payload;

PROCESS_BEGIN();
PBIO_OS_ASYNC_BEGIN(state);

#if ST7586S_DO_RESET_AND_INIT
pbdrv_gpio_out_low(&pin_lcd_reset);
etimer_set(&etimer, 10);
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(&etimer));
PBIO_OS_AWAIT_MS(state, &timer, 10);
pbdrv_gpio_out_high(&pin_lcd_reset);
etimer_set(&etimer, 120);
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(&etimer));
PBIO_OS_AWAIT_MS(state, &timer, 120);
#endif // ST7586S_DO_RESET_AND_INIT

// For every action in the init script, either send a command or data, or
Expand All @@ -479,8 +475,7 @@ PROCESS_THREAD(pbdrv_display_ev3_init_process, ev, data) {

if (action->type == ST7586S_ACTION_DELAY) {
// Simple delay.
etimer_set(&etimer, action->payload);
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(&etimer));
PBIO_OS_AWAIT_MS(state, &timer, action->payload);
} else {
// Send command or data.
payload = action->payload;
Expand All @@ -490,7 +485,7 @@ PROCESS_THREAD(pbdrv_display_ev3_init_process, ev, data) {
pbdrv_gpio_out_low(&pin_lcd_a0);
}
pbdrv_display_st7586s_write_data_begin(&payload, sizeof(payload));
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_POLL && spi_status == SPI_STATUS_COMPLETE);
PBIO_OS_AWAIT_UNTIL(state, spi_status == SPI_STATUS_COMPLETE);
pbdrv_gpio_out_high(&pin_lcd_cs);
}
}
Expand All @@ -502,27 +497,58 @@ PROCESS_THREAD(pbdrv_display_ev3_init_process, ev, data) {
pbdrv_display_load_indexed_bitmap(pbdrv_display_pybricks_logo);
pbdrv_display_st7586s_encode_user_frame();
pbdrv_display_st7586s_write_data_begin(st7586s_send_buf, sizeof(st7586s_send_buf));
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_POLL && spi_status == SPI_STATUS_COMPLETE);
PBIO_OS_AWAIT_UNTIL(state, spi_status == SPI_STATUS_COMPLETE);
pbdrv_gpio_out_high(&pin_lcd_cs);

// Done initializing.
pbdrv_init_busy_down();

// Regularly update the display with the user frame buffer, if changed.
etimer_set(&etimer, 40);
// Update the display with the user frame buffer, if changed.
for (;;) {
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(&etimer));
if (pbdrv_display_user_frame_update_requested) {
pbdrv_display_user_frame_update_requested = false;
pbdrv_display_st7586s_encode_user_frame();
pbdrv_display_st7586s_write_data_begin(st7586s_send_buf, sizeof(st7586s_send_buf));
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_POLL && spi_status == SPI_STATUS_COMPLETE);
pbdrv_gpio_out_high(&pin_lcd_cs);
}
etimer_reset(&etimer);
PBIO_OS_AWAIT_UNTIL(state, pbdrv_display_user_frame_update_requested);
pbdrv_display_user_frame_update_requested = false;
pbdrv_display_st7586s_encode_user_frame();
pbdrv_display_st7586s_write_data_begin(st7586s_send_buf, sizeof(st7586s_send_buf));
PBIO_OS_AWAIT_UNTIL(state, spi_status == SPI_STATUS_COMPLETE);
pbdrv_gpio_out_high(&pin_lcd_cs);
}

PROCESS_END();
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
}

/**
* Image corresponding to the display.
*/
static pbio_image_t display_image;

/**
* Initialize the display driver.
*/
void pbdrv_display_init(void) {
// Initialize SPI.
pbdrv_display_ev3_spi_init();

// Initialize image.
pbio_image_init(&display_image, (uint8_t *)pbdrv_display_user_frame,
PBDRV_CONFIG_DISPLAY_NUM_COLS, PBDRV_CONFIG_DISPLAY_NUM_ROWS,
ST7586S_NUM_COL_TRIPLETS * 3);

// Start display process and ask pbdrv to wait until it is initialized.
pbdrv_init_busy_up();
pbio_os_process_start(&pbdrv_display_ev3_process, pbdrv_display_ev3_process_thread, NULL);
}

pbio_image_t *pbdrv_display_get_image(void) {
return &display_image;
}

uint8_t pbdrv_display_get_max_value(void) {
return 3;
}

void pbdrv_display_update(void) {
pbdrv_display_user_frame_update_requested = true;
pbio_os_request_poll();
}

#endif // PBDRV_CONFIG_DISPLAY_EV3
51 changes: 51 additions & 0 deletions lib/pbio/include/pbdrv/display.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 The Pybricks Authors

/**
* @addtogroup DisplayDriver Driver: Display
* @{
*/

#ifndef _PBDRV_DISPLAY_H_
#define _PBDRV_DISPLAY_H_

#include <pbdrv/config.h>
#include <pbio/image.h>

#if PBDRV_CONFIG_DISPLAY

/**
* Get an image container representing the display.
* @return Image container, or NULL if no display.
*/
pbio_image_t *pbdrv_display_get_image(void);

/**
* Get the maximum value of a pixel.
* @return Maximum value, corresponding to black on a LCD screen.
*/
uint8_t pbdrv_display_get_max_value(void);

/**
* Update the display to show current content of image container.
*/
void pbdrv_display_update(void);

#else // PBDRV_CONFIG_DISPLAY

static inline pbio_image_t *pbdrv_display_get_image(void) {
return NULL;
}

static inline uint8_t pbdrv_display_get_max_value(void) {
return 0;
}

static inline void pbdrv_display_update(void) {
}

#endif // PBDRV_CONFIG_DISPLAY

#endif // _PBDRV_DISPLAY_H_

/** @} */
Loading