Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

g070-oled

Monochrome OLED screens rendered on an STM32 through u8g2.

The home screen on a 128x32 OLED driven by a NUCLEO-G070RB

Each screen is a function of u8g2 drawing calls under src/screens/, called between u8g2_ClearBuffer() and u8g2_SendBuffer(). Screens can be written by hand or generated; src/screens/README.md covers the generator settings that produce compatible output.

No vendor driver layer sits under it. The peripherals are driven straight from their registers, so there is no CubeMX project to regenerate and nothing to keep in sync with a .ioc file.

Status

The board brings up, finds the display on the I2C bus and draws to it through u8g2. The bus runs slowly to work around a missing pull-up on the module; see I2C.

Hardware

Part Detail
MCU board NUCLEO-G070RB (STM32G070RB, 128 KB flash, 36 KB SRAM)
Display PGO12832A-W, 0.91" OLED, 128x32, SSD1306 controller, I2C

Wiring

Display Nucleo pin MCU pin Alternate function
SDA CN5 pin 9, D14 PB9 AF6
SCL CN5 pin 10, D15 PB8 AF6
VCC CN6 pin 4, +3V3
GND CN6 pin 6, GND

The display is powered from 3V3 rather than 5V so the I2C lines stay at the same level as the G070 pins.

I2C

The module pulls SDA up with a 10k to VCC and leaves SCL alone. The 100n next to it sits across VCC and GND and decouples the supply. So the clock line is held up by the ~40k internal pull-up on PB8 and nothing else, and against the bus capacitance its rise time lands far outside what standard mode allows.

The bus therefore runs at 10 kHz, slow enough for the levels to settle. I2C_TIMINGR is 0xF0423131, derived with the RM0454 formulas for a 16 MHz I2CCLK: PRESC=15 (a 1 us tick), SCLL=SCLH=0x31 (50 us each), SDADEL=2, SCLDEL=4. A 512-byte frame takes roughly half a second at this rate.

Soldering 4.7k from SCL to 3V3, and a second one on SDA to stiffen the 10k already there, removes the constraint, after which I2C_TIMINGR can go back to 0x30420F13 for 100 kHz: PRESC=3 (a 250 ns tick), SCLL=0x13, SCLH=0x0F, SDADEL=2, SCLDEL=4.

The panel answers at 7-bit address 0x3C. Addresses are passed around as 7-bit values; the shift into the SADD field happens inside i2c.c.

Toolchain

arm-none-eabi-gcc
make
st-flash          # optional, only for `make flash`

Newlib is not required. The firmware builds -nostdlib -ffreestanding, so a bare arm-none-eabi-gcc without a C library is enough.

Build

make          # build/g070-oled.{elf,bin,hex}
make clean

Debug info is kept in every build and the ELF is never stripped, because mcuhex resolves live variables from its symbols.

Flash

Either flash over SWD from the browser with mcuhex, pointing it at build/g070-oled.bin and build/g070-oled.elf, or use the on-board ST-Link:

make flash

What the firmware does

At startup it probes the panel at 0x3C, waits for the controller to accept a no-op, and runs the u8g2 init sequence. A board that finds nothing blinks LD4 at 5 Hz and keeps trying, so reseating a jumper is enough to bring the display up without a reset.

Once the panel is up, LD4 on PA5 toggles every 500 ms as a heartbeat and each pass redraws the screen.

Two counters are exposed as globals rather than printed, which keeps a UART and two more pins out of the build. Watch them in mcuhex:

Symbol Meaning Healthy value
g_i2c_nacks writes the panel did not acknowledge flat once the screen is drawing
g_i2c_timeouts transfers that never finished 0

Watching the counters over SWD

The capture is from a debugging build, so it lists counters that are no longer in the tree.

Both climbing means the panel is not answering: check the jumpers first, since a pin that is not quite seated looks exactly like a missing module.

Layout

Makefile                 build rules
docs/                    photos used by this file
ld/                      linker script
src/                     firmware
src/screens/             one function per screen, plus its own notes
src/shim/                the libc pieces u8g2 needs under -nostdlib
lib/u8g2/                vendored u8g2 subset
tools/extract-font.sh    pulls one font out of the upstream font file
vendor/cmsis/            ST device headers and ARM core headers

vendor/cmsis holds copies rather than a submodule, so git clone && make is one step. The sources are cmsis_device_g0 and CMSIS_5.

lib/u8g2 is a subset of u8g2 for the same reason. A full clone is 415 MB, most of it the 38 MB u8g2_fonts.c. What is here is the core plus the ssd1306 128x32 driver, at 1.2 MB. tools/extract-font.sh appends another font to lib/u8g2/fonts.c when a screen needs one:

tools/extract-font.sh u8g2_font_6x10_tf >> lib/u8g2/fonts.c

About

Bare-metal STM32G070RB firmware driving a 128x32 SSD1306 OLED through u8g2, without HAL or CubeMX

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages