diff --git a/sys/arm/rpi-pico/README.md b/sys/arm/rpi-pico/README.md new file mode 100644 index 00000000..e945ed43 --- /dev/null +++ b/sys/arm/rpi-pico/README.md @@ -0,0 +1,32 @@ +## Raspberry Pi Pico / RP2040 HAL Example + +This directory contains an example **HAL implementation for Raspberry Pi Pico–class boards** +using the official Raspberry Pi Pico SDK. + +This example is provided in response to the request made in +[u8g2 issue #2159](https://github.com/olikraus/u8g2/issues/2159), where example HAL +implementations were explicitly welcomed. + +Although the display controller used here differs from the one discussed in that issue, +the overall structure and integration approach are the same and can be adapted to other +RP2040-based systems. + +### Scope and Intent + +- Demonstrates how to integrate u8g2 with the Raspberry Pi Pico SDK +- Provides a minimal HAL layer for GPIO, delay, and communication callbacks +- Intended as a **reference implementation**, not a fully generic or complete port +- Meant to be copied and adapted for specific projects + +### Tested Hardware + +- Raspberry Pi Pico (RP2040) +- Raspberry Pi Pico 2 (RP2350) + +The same HAL implementation was tested on both platforms and works without modification. + +### Notes + +- No existing u8g2 HAL implementations are modified +- The example is self-contained and does not affect other platforms +- Display interface and controller specifics are intentionally kept minimal diff --git a/sys/arm/rpi-pico/examples/pico_u8g2_st7305_Hello-world.c b/sys/arm/rpi-pico/examples/pico_u8g2_st7305_Hello-world.c new file mode 100644 index 00000000..83b040f4 --- /dev/null +++ b/sys/arm/rpi-pico/examples/pico_u8g2_st7305_Hello-world.c @@ -0,0 +1,40 @@ +#include +#include "pico/stdlib.h" +#include "u8g2.h" +#include "u8g2_pico_hal.h" + +u8g2_t u8g2; + +/* Static lifetime config */ +static const u8g2_pico_config_t display_cfg = { + .interface = U8G2_PICO_IF_HW_SPI, + .hw_spi = { + .spi = spi0, + .baudrate = 4000000, + .pin_sck = 2, + .pin_mosi = 3, + .pin_cs = 5, + .pin_dc = 4, + .pin_rst = 11, + }, +}; + +int main(void) +{ + stdio_init_all(); + + u8g2_pico_init( + &u8g2, + u8g2_Setup_st7305_168x384_f, + &display_cfg + ); + + u8g2_ClearBuffer(&u8g2); + u8g2_SetFont(&u8g2, u8g2_font_ncenB24_tr); + u8g2_DrawStr(&u8g2, 100, 100, "Hello World"); + u8g2_SendBuffer(&u8g2); + sleep_ms(100); + while (true) { + sleep_ms(1000); + } +} diff --git a/sys/arm/rpi-pico/hal/u8g2_pico_hal.c b/sys/arm/rpi-pico/hal/u8g2_pico_hal.c new file mode 100644 index 00000000..37eda91b --- /dev/null +++ b/sys/arm/rpi-pico/hal/u8g2_pico_hal.c @@ -0,0 +1,181 @@ +/* + * RP2040/RP2350 HAL example for u8g2 + * Added in response to https://github.com/olikraus/u8g2/issues/2159 + * This is a reference implementation intended for adaptation. + */ + + + + +#include "u8g2_pico_hal.h" +#include "pico/stdlib.h" +#include "hardware/gpio.h" +#include "hardware/spi.h" + +static u8g2_pico_spi_hw_config_t g_hw_cfg; + +/* ------------------------------------------------------------ + * Forward declarations of u8g2 callbacks + * ------------------------------------------------------------ */ + +static uint8_t u8x8_byte_pico_hw_spi( + u8x8_t *u8x8, + uint8_t msg, + uint8_t arg_int, + void *arg_ptr); + +static uint8_t u8x8_gpio_and_delay_pico( + u8x8_t *u8x8, + uint8_t msg, + uint8_t arg_int, + void *arg_ptr); + +/* ------------------------------------------------------------ + * Public HAL API + * ------------------------------------------------------------ */ + +void u8g2_pico_init( + u8g2_t *u8g2, + u8g2_pico_setup_fn setup_fn, + const u8g2_pico_config_t *cfg) +{ + if (cfg->interface != U8G2_PICO_IF_HW_SPI) + return; + + g_hw_cfg = cfg->hw_spi; + + setup_fn( + u8g2, + U8G2_R3, + u8x8_byte_pico_hw_spi, + u8x8_gpio_and_delay_pico + ); + + + u8g2->u8x8.user_ptr = &g_hw_cfg; + + u8g2_InitDisplay(u8g2); + u8g2_SetPowerSave(u8g2, 0); +} + +static uint8_t u8x8_gpio_and_delay_pico( + u8x8_t *u8x8, + uint8_t msg, + uint8_t arg_int, + void *arg_ptr) +{ + + const u8g2_pico_spi_hw_config_t *cfg = + (const u8g2_pico_spi_hw_config_t *)u8x8->user_ptr; + + switch (msg) + { + + case U8X8_MSG_GPIO_AND_DELAY_INIT: + spi_init(cfg->spi, cfg->baudrate); + spi_set_format(cfg->spi, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); + + gpio_set_function(cfg->pin_sck, GPIO_FUNC_SPI); + gpio_set_function(cfg->pin_mosi, GPIO_FUNC_SPI); + + gpio_init(cfg->pin_cs); + gpio_init(cfg->pin_dc); + gpio_init(cfg->pin_rst); + + gpio_set_dir(cfg->pin_cs, GPIO_OUT); + gpio_set_dir(cfg->pin_dc, GPIO_OUT); + gpio_set_dir(cfg->pin_rst, GPIO_OUT); + + gpio_put(cfg->pin_cs, 1); + gpio_put(cfg->pin_rst, 1); + break; + + case U8X8_MSG_DELAY_NANO: // delay arg_int * 1 nano second + sleep_us(arg_int); // 1000 times slower, though generally fine in practice given rp2040 has no `sleep_ns()` + break; + case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds + sleep_us(arg_int); + break; + case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds + sleep_us(arg_int * 10); + break; + case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second + sleep_ms(arg_int); + break; + case U8X8_MSG_GPIO_CS: // CS (chip select) pin: Output level in arg_int + gpio_put(cfg->pin_cs, arg_int); + break; + case U8X8_MSG_GPIO_DC: // DC (data/cmd, A0, register select) pin: Output level + gpio_put(cfg->pin_dc, arg_int); + break; + case U8X8_MSG_GPIO_RESET: // Reset pin: Output level in arg_int + gpio_put(cfg->pin_rst, arg_int); // printf("U8X8_MSG_GPIO_RESET %d\n", arg_int); + break; + default: + u8x8_SetGPIOResult(u8x8, 1); // default return value + break; + } + + return 1; +} + +static uint8_t u8x8_byte_pico_hw_spi( + u8x8_t *u8x8, + uint8_t msg, + uint8_t arg_int, + void *arg_ptr) +{ + const u8g2_pico_spi_hw_config_t *cfg = + (const u8g2_pico_spi_hw_config_t *)u8x8->user_ptr; + + switch (msg) + { + case U8X8_MSG_BYTE_INIT: + // Ensure CS is inactive + u8x8_gpio_SetCS(u8x8, + u8x8->display_info->chip_disable_level); + break; + + case U8X8_MSG_BYTE_START_TRANSFER: + // Assert CS + u8x8_gpio_SetCS(u8x8, + u8x8->display_info->chip_enable_level); + + // Respect display timing + u8x8->gpio_and_delay_cb( + u8x8, + U8X8_MSG_DELAY_NANO, + u8x8->display_info->post_chip_enable_wait_ns, + NULL); + break; + + case U8X8_MSG_BYTE_SEND: + spi_write_blocking( + cfg->spi, + (const uint8_t *)arg_ptr, + arg_int); + break; + + case U8X8_MSG_BYTE_END_TRANSFER: + // Respect display timing + u8x8->gpio_and_delay_cb( + u8x8, + U8X8_MSG_DELAY_NANO, + u8x8->display_info->pre_chip_disable_wait_ns, + NULL); + + // Deassert CS + u8x8_gpio_SetCS(u8x8, + u8x8->display_info->chip_disable_level); + break; + + case U8X8_MSG_BYTE_SET_DC: + u8x8_gpio_SetDC(u8x8, arg_int); + break; + + default: + return 0; + } + + return 1; +} diff --git a/sys/arm/rpi-pico/hal/u8g2_pico_hal.h b/sys/arm/rpi-pico/hal/u8g2_pico_hal.h new file mode 100644 index 00000000..70fabbd3 --- /dev/null +++ b/sys/arm/rpi-pico/hal/u8g2_pico_hal.h @@ -0,0 +1,100 @@ + +/* + * RP2040/RP2350 HAL example for u8g2 + * Added in response to https://github.com/olikraus/u8g2/issues/2159 + * This is a reference implementation intended for adaptation. + */ + + +#ifndef U8G2_PICO_HAL_H +#define U8G2_PICO_HAL_H + +#include +#include "u8g2.h" +#include "hardware/spi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ============================================================ + * Interface selection + * ============================================================ */ + +typedef enum { + U8G2_PICO_IF_HW_SPI = 0, + U8G2_PICO_IF_SW_SPI /* reserved for future */ +} u8g2_pico_interface_t; + + +/* ============================================================ + * Hardware SPI configuration + * ============================================================ */ + +typedef struct { + spi_inst_t *spi; /* spi0 or spi1 */ + uint32_t baudrate; + + uint8_t pin_sck; + uint8_t pin_mosi; + uint8_t pin_cs; + uint8_t pin_dc; + uint8_t pin_rst; +} u8g2_pico_spi_hw_config_t; + + +/* ============================================================ + * Unified configuration object + * ============================================================ */ + +typedef struct { + u8g2_pico_interface_t interface; + u8g2_pico_spi_hw_config_t hw_spi; +} u8g2_pico_config_t; + + +/* ============================================================ + * Display setup function type + * + * This matches u8g2_Setup_xxx() signatures exactly. + * ============================================================ */ + +typedef void (*u8g2_pico_setup_fn)( + u8g2_t *u8g2, + const u8g2_cb_t *rotation, + u8x8_msg_cb byte_cb, + u8x8_msg_cb gpio_cb +); + + +/* ============================================================ + * Public HAL API + * ============================================================ */ + +/* + * Initialize U8g2 on RP2040 using the Pico SDK. + * + * Responsibilities: + * - configure SPI and GPIO + * - install U8g2 callbacks + * - initialize and wake the display + * + * After this call: + * - u8g2 is fully ready for drawing + * - no further hardware setup is required + * + * @param u8g2 Pointer to u8g2 instance + * @param setup_fn Display-specific u8g2_Setup_xxx() function + * @param cfg Pico hardware configuration + */ +void u8g2_pico_init( + u8g2_t *u8g2, + u8g2_pico_setup_fn setup_fn, + const u8g2_pico_config_t *cfg +); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* U8G2_PICO_HAL_H */