Skip to content

⚠️ 产品声明 / Product Disclaimer

非量产产品,仅供工程验证,不承诺符合 RoHS。 Non-mass-production product; for engineering verification only. RoHS compliance is not guaranteed.

Overview

The official base project already includes the SSD1306 OLED driver (components/SSD1306, with Chinese font support) and initializes it in the ws2812_modeTask task — right now all it shows is a welcome page ("Welcome to the Jiuzhang Dev Board"). The official project reads temperature and humidity every second (sht3x_read_task) but only prints them to the serial port; nothing goes to the screen. This chapter puts that data on the screen: define global variables → display them from a status-page function → refresh every second, so the screen shows the ambient temperature and humidity in real time.

The OLED uses SPI1 (PA5 SCK / PA7 MOSI); chip select and DC use GPIO (PA4 / PA1). This project's components/SSD1306/axk_ssd1306.c is already adapted, and comes with GT20L16S Chinese font support (accessed through CS2 = PB0).


🎯Page GoalTurn the official welcome page into a real-time temperature/humidity status page, and master the standard OLED display pattern: "global variables + display function + periodic refresh".
🧰Prerequisites① Finished [emMCP Porting](./emmcp-porting-win) (previous chapter) ② An SPI-version SSD1306 OLED (128×64).
🔗RelatedOnce the screen shows temperature and humidity, move on to [Creating MCP Tools](./create-mcp-tool-win); the Chapter 8 temperature/humidity query tool reuses this chapter's `g_temp`/`g_hum` global variables.
Confirm the Official Project Already Has the OLED Module

The official base project already includes the OLED driver components/SSD1306/axk_ssd1306.c (with Chinese font support) — no need to copy anything. The matching module in the SCBB library is named OLED096 (same SSD1306 controller chip); for reference if you start from a blank project:

dir AiPi-SCBB\OLED096
# axk_oled096.c  axk_oled096.h

The SPI BSP the driver depends on (already implemented in Bsp/spi/stm32f10x_bsp_spi.c):

void bsp_spi_init(void);                 // initialize the SPI1 master
void bsp_spi_cs1_reset(void);            // pull OLED_CS1 (PA4) low → select the OLED
void bsp_spi_cs1_set(void);              // pull it high → release
void bsp_spi_cs2_reset(void);            // CS2 (PB0) selects the font chip
void bsp_spi_transmit_dma(const uint8_t *data, uint16_t size);  // DMA transfer

Add the SSD1306 directory (including the gt20l16s subdirectory) to components/CMakeLists.txt.

Wiring
OLED Module Connect to Board
DIN (data) PA7 (SPI1_MOSI)
CLK (clock) PA5 (SPI1_SCK)
CS (chip select) PA4
DC (data/command) PA1
RES (reset) 3.3V, or controlled by the driver (this project pulls it high from a driver GPIO)
VCC 3.3V
GND GND
Confirm the Official Initialization

The official base project already initializes the OLED in the ws2812_modeTask task and shows a welcome page:

axk_ssd1306_init();
axk_ssd1306_set_color_turn(0);
axk_ssd1306_set_display_turn(0);
axk_ssd1306_clear_screen();
axk_ssd1306_show_utf8_str(32, 0, "欢迎使用");
axk_ssd1306_show_utf8_str(24, 3, "九章开发板");

Core APIs (axk_ssd1306.h):

axk_ssd1306_init();
axk_ssd1306_set_color_turn(0);      // normal color (1 inverts it)
axk_ssd1306_set_display_turn(0);    // normal orientation (1 flips it 180°)
axk_ssd1306_clear_screen();

There are only three core APIs (axk_ssd1306.h):

void axk_ssd1306_clear_screen(void);                          // clear the screen
void axk_ssd1306_show_utf8_str(x, y, str);                    // show a string (UTF-8, uses the font chip automatically)
// x: 0~15 (8 pixel columns per character), y: 0~7 (8 pixels per row) → 128x64 = 8 rows
Show Real-Time Temperature and Humidity (Replacing the Welcome Page)

What this step does: The official project reads temperature and humidity every second (in the sht3x_read_task task), but only prints them to the serial port — it never shows them on screen. This chapter puts them on the OLED: promote the data to global variables → display it from a status-page function → refresh every second, so the screen shows the ambient temperature and humidity in real time.

File path: C:\Users\YourName\DOCS_TEST1/Core/Src/freertos.c — 7 changes in total

Change 1: Add the stdio.h header (needed by the snprintf function)

Location: add it after /* USER CODE BEGIN Includes */:

/* USER CODE BEGIN Includes */
#include    // needed by snprintf
/* USER CODE END Includes */

Change 2: Define the global temperature/humidity variables

Location: add it after /* USER CODE BEGIN Variables */:

/* USER CODE BEGIN Variables */
double g_temp = 0.0;   // temperature, for the screen
double g_hum  = 0.0;   // humidity, for the screen
/* USER CODE END Variables */

Change 3: Add the function prototype (ui_show_status is defined after it is called, so it must be declared first)

Location: add it after /* USER CODE BEGIN FunctionPrototypes */:

/* USER CODE BEGIN FunctionPrototypes */
void ui_show_status(void);   // status-page prototype
/* USER CODE END FunctionPrototypes */

Change 4: Store the readings in the global variables

Location: in the sht3x_read_task function, after res = axk_sht3x_read(0x2c06, &temperature, &humidity); and after the if (res != 0) { ... continue; } block (this point is only reached when the read succeeded) — insert two lines:

    res = axk_sht3x_read(0x2c06, &temperature, &humidity);   ← existing code
if (res != 0) { log_error("sht3x read error: %d", res); continue; }   ← existing code
g_temp = temperature;   // save to the globals, for the screen
g_hum  = humidity;

Change 5: Replace the welcome-page display code

Location: inside the ws2812_modeTask function, the two official welcome-page lines:

axk_ssd1306_init();                                       ← existing code
axk_ssd1306_show_utf8_str(32, 0, "欢迎使用");   ← delete
axk_ssd1306_show_utf8_str(24, 3, "九章开发板");   ← delete
ui_show_status();   // call the status-page function   ← replace with
axk_ws2812_init(&ws2812);                               ← existing code

Change 6: Add the status-page function

Location: add it after /* USER CODE BEGIN Application */ (the official project already implements the receive callback in this section — just add it after that):

/* USER CODE BEGIN Application */
void ui_show_status(void) {
  char buf[32];
  uint8_t ti = (uint8_t)g_temp, td = ((uint8_t)(g_temp * 10)) % 10;
  uint8_t hi = (uint8_t)g_hum, hd = ((uint8_t)(g_hum * 10)) % 10;

  /* line 1: system name */
  axk_ssd1306_show_utf8_str(0, 0, "九章开发板");

  /* line 3: real-time temperature (%2d.%d integer concatenation) */
  snprintf(buf, sizeof(buf), "    T:%2d.%d C    ", ti, td);
  axk_ssd1306_show_utf8_str(0, 2, buf);

  /* line 5: real-time humidity */
  snprintf(buf, sizeof(buf), "    H:%2d.%d %%   ", hi, hd);
  axk_ssd1306_show_utf8_str(0, 4, buf);
}
/* USER CODE END Application */

Change 7: Refresh the screen periodically

Location: inside the loop of the sht3x_read_task function, add it after (the two assignment lines added in Change 4):

    g_temp = temperature;                                     ← the two lines added in Change 4
g_hum  = humidity;
ui_show_status();   // refresh the screen every second (after a successful read)

⚠️ Don’t skip this step: it is the key to a screen that keeps updating. Without it, the screen only shows the values once at boot (the 0.0 values from startup) and the numbers never change again.

Verify the Result and Troubleshoot
  1. Build and flash using the graphical method in Build and Flash the Project; after power-up the screen should show:

    九章开发板
        T:29.2 C
        H:52.4 %
    
  2. The temperature and humidity values refresh every second (cup your hand over the sensor and watch the numbers change) — this proves the “global variables → display function → periodic refresh” chain works. The Chapter 8 temperature/humidity query tool reuses g_temp/g_hum directly.

OLED display result

Troubleshooting:

Symptom Check
Screen stays dark Check the SPI wiring (PA5/PA7/PA4/PA1), the power supply (3.3V), and whether the RES pin is pulled high
Garbled / corrupted image Confirm the DMA for SPI1_TX (DMA1_Channel3) is configured; check the CubeMX .ioc
Chinese text renders incorrectly The font chip GT20L16S needs CS2 (PB0) connected properly or held high

FAQ & Troubleshooting

🔧 The screen stays completely dark
Cause: ① Power/wiring ② RES left floating ③ Wrong CS selected
Fix: ① VCC 3.3V and GND to a common ground ② RES must be pulled high (many modules leave it floating; connecting it to 3.3V is enough — this project pulls it high from a driver GPIO) ③ Make sure you connected the OLED's CS (PA4), not the font chip's CS2 (PB0)

🔧 The screen lights up but only half of it shows / the image is garbled
Cause: Wrong SPI line order, or the DMA is not configured
Fix: Verify DIN→PA7 and CLK→PA5; confirm the DMA for SPI1_TX (DMA1_Channel3) is configured in CubeMX

🔧 Displayed content is incomplete or garbled
Cause: ① Data sent too fast ② Font-chip access conflict
Fix: ① Check the SPI prescaler (128 is enough) ② The Chinese font chip GT20L16S needs CS2 (PB0) connected properly or held high, so the font chip can be read correctly when displaying Chinese

🔧 Chinese text shows as boxes or question marks
Cause: The font chip is not connected, or the glyphs are missing
Fix: Confirm the GT20L16S wiring (shared SPI bus + CS2 = PB0); this font chip has the common GB2312 Chinese characters built in

🔧 The screen is white at power-up and stays white
Cause: Initialization failed, or the reset pin is held low
Fix: Check that axk_ssd1306_init() is called (it should be inside the task); do not connect the RES pin to GND

🔧 Content appears in the wrong position
Cause: Misunderstanding of x/y
Fix: axk_ssd1306_show_utf8_str(x, y, str): x is the column (0~15, 8 pixels per character) and y is the row (0~7) — these are not pixel coordinates

🔧 The screen occasionally flickers or tears
Cause: The refresh collides with the DMA transfer
Fix: Do not refresh the display data and call bsp_spi_transmit_dma concurrently; this project runs them in separate tasks, staggered with osDelay

🔧 The temperature and humidity values never update
Cause: The periodic refresh logic never runs
Fix: Confirm that sht3x_read_task calls the refresh function inside its loop (this project refreshes once per second)

🔧 Pressing the button does not turn the page
Cause: Button pin / debounce
Fix: This project uses PB4 (page up) / PB8 (page down); the button must be held to trigger (btn_pressed applies 20 ms debounce)

Black Screen Troubleshooting Order (Highest Probability First)

  1. Is RES pulled high → 2. Are VCC/GND wired correctly → 3. Are DIN/CLK wired correctly (PA7/PA5) → 4. Is the correct CS selected (PA4) → 5. Is the SPI DMA configured → 6. Is the init function being called. Nine out of ten black screens come down to wiring problems in items 1~3.

Released under the MIT License. Build Time 2026-09-11 14:52:23