CHGame 0.3.0
Libraries for the CHGame handheld
Loading...
Searching...
No Matches
CHGfx.h File Reference

CHGfx: a maximum-throughput ST7735 driver for the CHGame board (CH32X035G8U6 @ 48 MHz, QingKe V4C, 20 KB SRAM, 62 KB flash). More...

#include <Arduino.h>
#include <stdint.h>
#include "CHGfx_gfxfont.h"

Go to the source code of this file.

Classes

class  CHGfx
 Arduino-style access to CHGfx: every method is a zero-cost inline forwarder to a gfx_* function. More...
 

Macros

#define GFX_W   128
 Screen width in pixels (default 128).
 
#define GFX_H   128
 Screen height in pixels (default 128).
 
#define GFX_FB_STRIDE   (GFX_W / 2)
 Bytes per framebuffer row: two pixels a byte, 64 at 128 wide.
 
#define GFX_FB_BYTES   (GFX_FB_STRIDE * GFX_H)
 Size of the framebuffer, gfx_fb, in bytes: 8192 at 128x128.
 
#define GFX_CHUNK_ROWS   2
 Framebuffer rows converted per DMA chunk (default 2).
 
#define GFX_CHUNK_BYTES   (GFX_W * GFX_CHUNK_ROWS * 2)
 Size of one DMA chunk buffer in bytes (worst case, 16 bpp).
 
#define CHGFX_CS_PORT   GPIOA
 GPIO port of the panel's chip select (default GPIOA, the CHGame board's LCD_CS on PA4).
 
#define CHGFX_CS_PIN   4
 Pin number of the panel's chip select on CHGFX_CS_PORT (default 4).
 
#define CHGFX_DC_PORT   GPIOB
 GPIO port of the panel's data/command line (default GPIOB, LCD_DC on PB0).
 
#define CHGFX_DC_PIN   0
 Pin number of the panel's data/command line on CHGFX_DC_PORT (default 0).
 
#define CHGFX_RST_PORT   GPIOB
 GPIO port of the panel's reset line (default GPIOB, LCD_RST on PB12).
 
#define CHGFX_RST_PIN   12
 Pin number of the panel's reset line on CHGFX_RST_PORT (default 12).
 
#define CHGFX_SDCS_PORT   GPIOB
 GPIO port of the SD card's chip select, which shares SPI1 (default GPIOB, SD_CS on PB11).
 
#define CHGFX_SDCS_PIN   11
 Pin number of the SD card's chip select on CHGFX_SDCS_PORT (default 11).
 

Typedefs

typedef void(* gfx_streamFn) (uint8_t *dst, int y0, int rows, void *user)
 A direct-streaming callback: fills rows rows starting at row y0 into dst, in the panel's native format.
 

Enumerations

enum  : uint8_t {
  GFX_16BPP = 0 ,
  GFX_12BPP = 1 ,
  GFX_18BPP = 2
}
 The panel's colour output modes, for gfx_begin() and gfx_setColorMode(). More...
 
enum  : uint8_t {
  GFX_DIV2 = 2 ,
  GFX_DIV4 = 4 ,
  GFX_DIV8 = 8 ,
  GFX_DIV16 = 16
}
 SPI baud dividers (HCLK / n) for gfx_begin() and gfx_setSpiDiv(). More...
 

Functions

static void gfx_begin (uint8_t spiDiv=GFX_DIV2, uint8_t colorMode=GFX_16BPP)
 Starts the panel: pins, SPI1, DMA, a hardware reset and the ST7735 initialisation, then a default 16-colour palette.
 
void gfx_setSpiDiv (uint8_t div)
 Changes the SPI clock divider, and sets SPI1 up afresh for the panel.
 
void gfx_setColorMode (uint8_t mode)
 Switches the panel's colour output mode.
 
uint8_t gfx_colorMode (void)
 The current colour output mode.
 
uint8_t gfx_spiDiv (void)
 The current SPI clock divider.
 
uint32_t gfx_spiHz (void)
 The current SPI clock.
 
void gfx_setPanelOffsets (uint8_t madctl, uint8_t colStart, uint8_t rowStart)
 Sets the panel's orientation (MADCTL) and the offsets of the visible window in its memory.
 
void gfx_setInverted (bool on)
 Turns the panel's colour inversion (INVON / INVOFF) on or off.
 
void gfx_setPanelFrameRate (uint8_t rtna, uint8_t fpa, uint8_t bpa)
 Sets the panel's own refresh rate (ST7735 FRMCTR1, 0xB1).
 
void gfx_setPalette (const uint16_t *rgb565, uint8_t count)
 Sets the first count palette entries.
 
void gfx_setPaletteEntry (uint8_t index, uint16_t rgb565)
 Sets one palette entry.
 
void gfx_setFade (uint8_t amount, uint16_t rgb565=0x0000)
 Fades every colour toward one colour (black by default).
 
uint8_t gfx_fade (void)
 The current fade amount.
 
uint16_t gfx_paletteOut (uint8_t index)
 The colour a palette index actually reaches the panel as, fade included.
 
static uint16_t gfx_rgb (uint8_t r, uint8_t g, uint8_t b)
 Packs an 8-bit-per-channel colour into RGB565.
 
void gfx_flush (void)
 Sends the whole framebuffer to the panel and returns when it has gone out.
 
void gfx_flushAsync (void)
 Starts sending the whole framebuffer and returns at once; the DMA interrupt does the rest.
 
bool gfx_busy (void)
 Whether an async flush is still in flight.
 
void gfx_wait (void)
 Waits until the flush in flight, if any, has finished.
 
int gfx_flushRow (void)
 The first framebuffer row the flush in flight may still read.
 
void gfx_waitRow (int y)
 Waits until rows 0..y-1 are free to draw into.
 
void gfx_stream (gfx_streamFn fn, void *user)
 Draws one full-screen frame by calling fn for every chunk of rows and sending the result straight to the panel.
 
uint8_t gfx_bytesPerPixel (void)
 Bytes per pixel a stream callback writes in the current mode.
 
static uint8_t * gfx_px565 (uint8_t *p, uint16_t rgb565)
 Writes one 16 bpp pixel for a stream callback.
 
static uint8_t * gfx_px666 (uint8_t *p, uint8_t r6, uint8_t g6, uint8_t b6)
 Writes one 18 bpp pixel for a stream callback.
 
void gfx_flushRect (int x, int y, int w, int h)
 Sends one rectangle of the framebuffer to the panel and returns when it has gone out.
 
void gfx_flushRectAsync (int x, int y, int w, int h)
 Starts sending one rectangle of the framebuffer and returns at once, as gfx_flushAsync() does for the whole frame.
 
void gfx_setClip (int x, int y, int w, int h)
 Sets the clip rectangle every drawing call stays inside.
 
void gfx_resetClip (void)
 Puts the clip rectangle back to the whole screen.
 
void gfx_getClip (int *x, int *y, int *w, int *h)
 Reads the clip rectangle, as intersected with the screen.
 
void gfx_clear (uint8_t c)
 Fills the clip rectangle (normally the whole screen) with one colour.
 
void gfx_pixel (int x, int y, uint8_t c)
 Sets one pixel.
 
uint8_t gfx_getPixel (int x, int y)
 Reads one pixel of the framebuffer (the clip is ignored).
 
void gfx_hline (int x, int y, int w, uint8_t c)
 Draws a horizontal line from (x, y), w pixels to the right.
 
void gfx_vline (int x, int y, int h, uint8_t c)
 Draws a vertical line from (x, y), h pixels down.
 
void gfx_fillRect (int x, int y, int w, int h, uint8_t c)
 Fills a rectangle.
 
void gfx_rect (int x, int y, int w, int h, uint8_t c)
 Draws a rectangle's 1-pixel outline.
 
void gfx_line (int x0, int y0, int x1, int y1, uint8_t c)
 Draws a line between two points, both ends included.
 
void gfx_circle (int cx, int cy, int r, uint8_t c)
 Draws a circle's 1-pixel outline, 2*r+1 pixels across.
 
void gfx_fillCircle (int cx, int cy, int r, uint8_t c)
 Draws a filled circle, 2*r+1 pixels across.
 
void gfx_blit (const uint8_t *spr, int x, int y, int w, int h, int transparent)
 Copies a 4 bpp bitmap into the framebuffer, optionally with one colour transparent.
 
void gfx_roundRect (int x, int y, int w, int h, int r, uint8_t c)
 Draws a rounded rectangle's 1-pixel outline.
 
void gfx_fillRoundRect (int x, int y, int w, int h, int r, uint8_t c)
 Fills a rounded rectangle (corners as gfx_roundRect()).
 
void gfx_ellipse (int cx, int cy, int rx, int ry, uint8_t c)
 Draws an axis-aligned ellipse's 1-pixel outline, 2*rx+1 by 2*ry+1 pixels, centred on (cx, cy).
 
void gfx_fillEllipse (int cx, int cy, int rx, int ry, uint8_t c)
 Draws a filled axis-aligned ellipse, 2*rx+1 by 2*ry+1 pixels, centred on (cx, cy).
 
void gfx_dither (int x, int y, int w, int h, uint8_t c, uint8_t phase)
 Paints a 50% checkerboard of one colour over a rectangle, leaving the pixels in between alone.
 
void gfx_remapRect (int x, int y, int w, int h, const uint8_t *remap)
 Recolours a rectangle in place through a 16-entry table: pixel = remap[pixel].
 
void gfx_sprite4 (const uint8_t *spr, int x, int y, const uint8_t *remap=nullptr, int scale=256)
 Draws a span sprite, optionally recoloured and scaled.
 
static int gfx_sprite4W (const uint8_t *spr)
 A span sprite's width.
 
static int gfx_sprite4H (const uint8_t *spr)
 A span sprite's height.
 
void gfx_sprite4Rot (const uint8_t *spr, int ax, int ay, int px, int py, uint8_t angle, int scale=256, const uint8_t *remap=nullptr)
 Draws a span sprite rotated and scaled about a pivot.
 
void gfx_scroll (int y, int h, int dx, int dy, int fill=-1)
 Moves a band of rows by (dx, dy) pixels inside the band.
 
void gfx_copyRow (int y, const uint8_t *src, int x0, int x1)
 Copies pixels [x0, x1) of a full-width row buffer into framebuffer row y, clipped.
 
void gfx_setFont (const GFXfont *f)
 Selects the font every text call uses from now on.
 
const GFXfont * gfx_font (void)
 The current font.
 
int gfx_fontLineHeight (void)
 Baseline-to-baseline line spacing of the current font, at scale 1.
 
int gfx_fontBaseline (void)
 The current font's ascent: how far its tallest ink reaches above the baseline, at scale 1.
 
void gfx_char (int x, int y, char ch, uint8_t c)
 Draws one character.
 
void gfx_charScaled (int x, int y, char ch, uint8_t c, uint8_t scale)
 Draws one character, scaled up by a whole number.
 
void gfx_text (int x, int y, const char *s, uint8_t c)
 Draws a string in the current font.
 
void gfx_textScaled (int x, int y, const char *s, uint8_t c, uint8_t scale)
 Draws a string in the current font, scaled up by a whole number.
 
int gfx_textWidth (const char *s)
 Advance width of a string in the current font - the number to use for centring and right-alignment.
 
int gfx_textWidthScaled (const char *s, uint8_t scale)
 Advance width of a string in the current font at a scale.
 
void gfx_textBounds (const char *s, int x, int y, uint8_t scale, int *bx, int *by, int *bw, int *bh)
 Bounding box of a string drawn at (x, y).
 
bool gfx_textFx (int x, int y, const char *s, uint8_t scale, uint8_t fill, int outline=-1, int shadow=-1, const uint8_t *ramp=nullptr, const int8_t *dy=nullptr)
 Draws outlined, shadowed or gradient-filled text - titles and banners.
 
void gfx_select (void)
 Asserts the panel's chip select.
 
void gfx_deselect (void)
 Waits for the last byte to leave SPI1, then releases the panel's chip select.
 
void gfx_setWindow (uint8_t x, uint8_t y, uint8_t w, uint8_t h)
 Sets the panel's address window and starts a memory write (CASET, RASET, RAMWR).
 
void gfx_cmd (uint8_t c)
 Sends one command byte (D/C low), then sets D/C back to data.
 
void gfx_data8 (uint8_t d)
 Sends one data byte (D/C high).
 
void gfx_writeColorLut (void)
 Programs the ST7735 RGBSET (2Dh) colour-depth conversion LUT for the current mode.
 
void gfx_setWriteColorLut (bool on)
 Chooses whether entering 12 bpp writes the colour LUT (gfx_writeColorLut()).
 
void gfx_directFillRect (uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t rgb565)
 Fills a rectangle of the panel with one colour by DMA, bypassing the framebuffer.
 
void gfx_directBlit (const void *data, uint32_t bytes, bool halfword)
 Sends a caller-owned buffer, already in panel format, by DMA and waits for it.
 
void gfx_blockingWrite (const uint8_t *data, uint32_t bytes)
 Sends bytes one at a time in a polled loop, for baseline comparisons.
 
uint32_t gfx_convertRows_ram (uint8_t *dst, uint16_t row, uint16_t rows)
 Converts framebuffer rows into the panel's format, with the converter in SRAM.
 
uint32_t gfx_convertRows_flash (uint8_t *dst, uint16_t row, uint16_t rows)
 Converts framebuffer rows into the panel's format, with the converter in flash (for comparison with gfx_convertRows_ram()).
 
uint32_t gfx_convertSpan_ram (uint8_t *dst, const uint8_t *src, uint32_t srcBytes)
 Converts a run of framebuffer bytes into the panel's format (the SRAM converter every flush uses).
 
uint8_t * gfx_chunkScratch (void)
 1 KB of word-aligned scratch memory: the flush's chunk buffers.
 
uint32_t gfx_frameBytes (void)
 Bytes the panel receives for a full frame in the current mode.
 
uint8_t gfx_nearest (uint16_t rgb565)
 Nearest palette index for a full RGB565 colour.
 

Variables

uint8_t gfx_fb [GFX_FB_BYTES]
 The framebuffer: GFX_FB_BYTES of 4 bpp pixels, two per byte.
 
uint16_t gfx_pal [16]
 The 16 palette colours as set, in RGB565.
 
CHGfx Gfx
 The one CHGfx instance.
 

Detailed Description

CHGfx: a maximum-throughput ST7735 driver for the CHGame board (CH32X035G8U6 @ 48 MHz, QingKe V4C, 20 KB SRAM, 62 KB flash).

Why this exists. Adafruit_GFX + Adafruit_ST7735 on this part is slow for three separate reasons, and they compound:

  1. Every drawPixel() sends CASET + RASET + RAMWR (11 bytes of command traffic) to paint 2 bytes of pixel. ~85% of the bus is overhead.
  2. The Arduino SPI class transfers one byte at a time in a polled loop (cores/.../libraries/SPI/src/utility/spi_com.c spins on TXE for every single byte). The CPU is the bottleneck, not the wire.
  3. There is no framebuffer, so overdraw goes straight out over SPI.

What this does instead.

  • Register-level SPI1 at HCLK/2 = 24 MHz. That is the CH32X035 hard ceiling: "Maximum clock frequency supports up to half of FHCLK" (Reference Manual ch.16.1), and HCLK cannot exceed 48 MHz because this part has no PLL, only the 48 MHz HSI RC (RM ch.3.3).
  • DMA1 channel 3 - the SPI1_TX request line (RM 9.2.3) - pushes whole chunks with zero CPU involvement.
  • 16-bit SPI data frames (DFF=1) so DMA does half as many bus cycles.
  • A 4 bpp (16-colour) framebuffer. 128*128 RGB565 is 32 KB and this chip has 20 KB, so a full-colour framebuffer is physically impossible. 4 bpp is 8 KB and leaves room to live.
  • A 256-entry uint32 lookup table expands one framebuffer byte (= 2 pixels) into one 32-bit store. Conversion costs ~3 cycles per pixel against a 32-cycle-per-pixel SPI budget, so the wire stays saturated. Free for the WIRE, not for the CPU: an async flush still spends its conversion time inside the DMA interrupt - see "What a flush costs the CPU" in Presenting the framebuffer.
  • Optional 12 bpp (RGB444) output - ST7735 COLMOD 0x03. Three bytes per two pixels instead of four: 25% less traffic, and with a 16-colour palette you lose nothing you were actually using.
  • Ping-pong chunk buffers driven by the DMA transfer-complete ISR, so gfx_flushAsync() returns immediately and game logic overlaps the ~11 ms it takes to shift a frame out.
  • The conversion inner loops are linked into .data so they execute from SRAM. Flash on this part runs at 3 wait states above 24 MHz (RM 20.3.1, FLASH_ACTLR LATENCY = 10b); SRAM runs at zero.

Ceiling. 24 MHz SPI = 3.0 MB/s.

Output Bytes a frame Frame time Frame rate
16 bpp, 128*128*2 32768 10.9 ms 91 fps
12 bpp, 128*128*1.5 24576 8.2 ms 122 fps

Nothing on this chip beats that: there is no parallel LCD interface, no second SPI on these pins, and no PLL to go past 48 MHz HCLK.

Everything is a plain C function (gfx_*); the CHGfx class and its one instance, Gfx, are inline forwarders for the Gfx.clear(0) spelling. The topics are listed under CHGfx library.