Skip to content

U8G2 not working on RPi Pico W #2613

@dwn-dragon

Description

@dwn-dragon

u8g2 doesn't give out any kind of output on the display (both shapes and text) using hardware i2c.
I tested it with both a SSD1306 and a SSD1309 (and both of them work when used with u8x8 with same callbacks).
During debugging I've found that between START_TRANSFER and END_TRANSFER, u8g2 tries to send more than 32 bytes:

[debug][byte][0x3c] msg: START_TRANSFER
[debug][byte][0x3c] msg: SET_DC
[debug][byte][0x3c] msg: BYTE_SEND, Len: 1
[debug][byte][0x3c] msg: SET_DC
[debug][byte][0x3c] msg: BYTE_SEND, Len: 1
[debug][byte][0x3c] msg: SET_DC
[debug][byte][0x3c] msg: BYTE_SEND, Len: 1
[debug][byte][0x3c] msg: SET_DC
[debug][byte][0x3c] msg: BYTE_SEND, Len: 1
[debug][byte][0x3c] msg: SET_DC
[debug][byte][0x3c] msg: BYTE_SEND, Len: 128
[debug][byte][0x3c] msg: END_TRANSFER, Len: 132

Main.cpp

#include <stdio.h>
#include <iostream>

#include <pico/stdlib.h>
#include <pico/cyw43_arch.h>
#include <pico/binary_info.h>

#include <u8g2.h>
#include "u8x8_hal_i2c.h"

//*
#define USE_U8G2
/**/

#ifdef USE_U8G2
u8g2_t display;
#else
u8x8_t display;
#endif

int main() {
    stdio_init_all();

    // Initialise the Wi-Fi chip
    if (cyw43_arch_init()) {
        printf("Wi-Fi init failed\n");
        return -1;
    }

	u8x8_hal_i2c_init(&i2c0_inst, 100 * 1000, 4, 5, true);

#ifdef USE_U8G2
	u8g2_Setup_ssd1306_128x64_noname_f(&display, U8G2_R0, u8x8_hal_i2c_byte, u8x8_hal_i2c_gpio_delay);
	u8g2_SetI2CAddress(&display, (0x3c << 1));

 	u8g2_InitDisplay(&display);
	u8g2_ClearDisplay(&display);
	u8g2_SetPowerSave(&display, 0);

	u8g2_SetFont(&display, u8g2_font_5x7_tf);
#else
	u8x8_Setup(&display, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_i2c, u8x8_hal_i2c_byte, u8x8_hal_i2c_gpio_delay);
//	u8x8_SetI2CAddress(&display, (0x3c << 1));

	u8x8_InitDisplay(&display);
	u8x8_ClearDisplay(&display);
	u8x8_SetPowerSave(&display, 0);

	u8x8_SetFont(&display, u8x8_font_5x8_f);
#endif

	cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true);

	while (true) {
#ifdef USE_U8G2
		u8g2_ClearDisplay(&display);
		u8g2_ClearBuffer(&display);

		for (size_t i = 0; i < 64; i++)
			u8g2_DrawStr(&display, 0, i, "Hello world!");
		u8g2_DrawBox(&display, 64, 32, 20, 20);

		u8g2_SendBuffer(&display);
#else
		u8x8_DrawString(&display, 0, 3, "Hello world!");
#endif

		sleep_ms(1000);
	}

	return 0;
}

u8x8_hal_i2c.h

#pragma once

#ifndef U8X8_PICO_HAL_INCLUDE
#define U8X8_PICO_HAL_INCLUDE

#include <u8x8.h>

#include <hardware/i2c.h>
#include <hardware/gpio.h>

#ifndef ENV_DISABLE_PICOTOOL
#include <pico/binary_info.h>
#endif

/**
 * @brief Sets up the needed data in order to initialize the i2c interface and all the needed pins. It's not necessary, by default i2c0_inst is used with pins 4 and 5 plus pullup enabled. The pins can be passed in any order (SDA, SCL)/(SCL, SDA).
 * @param I2CInst i2c interface to use
 * @param Baudrate i2c speed
 * @param Pin0 First i2c pin
 * @param Pin1 Second i2c pin
 * @param PullUp Enables the pull up resistors on the SDA and SCL lines.
 */
void u8x8_hal_i2c_init(i2c_inst_t* I2CInst, uint Baudrate, uint Pin0, uint Pin1, bool PullUp);

/**
 * @brief Callback used to interface u8x8 (and u8g2) with the Pico i2c interface as the `byte_cb`.
 * @param u8x8 u8x8 instance
 * @param msg message type
 * @param arg_int argument 1
 * @param arg_ptr argument 2
 * @return `1` if the message has been handled, `0` otherwise.
 */
uint8_t u8x8_hal_i2c_byte(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
/**
 * @brief Callback used to interface u8x8 (and u8g2) with the Pico i2c interface as the `gpio_and_delay_cb`.
 * @param u8x8 u8x8 instance
 * @param msg message type
 * @param arg_int argument 1
 * @param arg_ptr argument 2
 * @return `1` if the message has been handled, `0` otherwise.
 */
uint8_t u8x8_hal_i2c_gpio_delay(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);

#endif	//	Include guard

u8x8_hal_i2c.cpp

#include "u8x8_hal_i2c.h"

#include <iostream>

//*
#define BYTE_DEBUG
/**/
/*
#define GPIO_DEBUG
/**/

//	i2c data
i2c_inst_t* Inst = PICO_DEFAULT_I2C_INSTANCE();
uint Baudrate = 100 * 1000;
//	i2c pins
uint Pins[2] = { PICO_DEFAULT_I2C_SCL_PIN, PICO_DEFAULT_I2C_SDA_PIN };
bool PullUp = true;

//	Buffer for sending data
uint8_t Buffer[256];
size_t Index;

//	debug
#ifdef GPIO_DEBUG
uint8_t called = 0;
uint8_t debug_buff[256];
#endif

void u8x8_hal_i2c_init(i2c_inst_t* inst, uint br, uint p0, uint p1, bool pu) {
	Inst = inst, Baudrate = br;
	Pins[0] = p0, Pins[1] = p1, PullUp = pu;
}

uint8_t u8x8_hal_i2c_byte(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
	uint8_t addr = u8x8_GetI2CAddress(u8x8) >> 1;
	
#ifdef GPIO_DEBUG
	std::cout << '\t' << (uint)called << '\n';
	for (uint8_t i = 0; i < called; i++)
		std::cout << (uint)debug_buff[i] << '\n';
#endif

#ifdef BYTE_DEBUG
	std::cout << "[debug][byte][0x" << std::hex << (uint16_t)addr << std::dec << "] msg: ";
#endif

	switch(msg)
	{
	case U8X8_MSG_BYTE_INIT:
#ifdef BYTE_DEBUG
		std::cout << "INIT\n";
#endif
		//	starts i2c interface
		i2c_init(Inst, Baudrate);

		break;

	case U8X8_MSG_BYTE_START_TRANSFER:
#ifdef BYTE_DEBUG
		std::cout << "START_TRANSFER\n";
#endif
		Index = 0;
		break;

	case U8X8_MSG_BYTE_SEND: {
#ifdef BYTE_DEBUG
		std::cout << "BYTE_SEND, Len: " << (uint16_t)arg_int << "\n";
#endif
		auto data = static_cast<uint8_t*>(arg_ptr);
		for (size_t i = 0; i < arg_int; i++) {
			Buffer[Index] = data[i];
			Index += 1;
		}

		break;
	}

	case U8X8_MSG_BYTE_END_TRANSFER:
#ifdef BYTE_DEBUG
		std::cout << "END_TRANSFER, Len: " << Index << "\n";
#endif
		i2c_write_blocking(Inst, addr, Buffer, Index, false);
		break;

	case U8X8_MSG_BYTE_SET_DC:
#ifdef BYTE_DEBUG
		std::cout << "SET_DC\n";
#endif
		break;

	default:
#ifdef BYTE_DEBUG
		std::cout << "UNKNOWN (" << (uint16_t)msg << ")\n";
#endif
		return 0;
	}

	return 1;
}

uint8_t u8x8_hal_i2c_gpio_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {	
	uint8_t addr = u8x8_GetI2CAddress(u8x8) >> 1;
#ifdef GPIO_DEBUG
	debug_buff[called] = msg;
	called += 1;
	std::cout << "[debug][byte][0x" << std::hex << (uint16_t)addr << std::dec << "] msg: ";
#endif

	switch(msg)
	{
	case U8X8_MSG_GPIO_AND_DELAY_INIT:
#ifdef GPIO_DEBUG
		std::cout << "GPIO_AND_DELAY_INIT\n";
#endif
		//	sets gpio pins as i2c
		gpio_set_function(Pins[0], GPIO_FUNC_I2C);
		gpio_set_function(Pins[1], GPIO_FUNC_I2C);
		//	enables pull up if needed
		if (PullUp)
			gpio_pull_up(Pins[0]), gpio_pull_up(Pins[1]);
#ifndef ENV_DISABLE_PICOTOOL
		//	notifies picotool about i2c pins
		bi_decl(bi_2pins_with_func(Pins[0], Pins[1], GPIO_FUNC_I2C));
#endif
		break;

	case U8X8_MSG_DELAY_MILLI:
#ifdef GPIO_DEBUG
		std::cout << "DELAY_MILLI\n";
#endif
		sleep_ms(arg_int);
		break;

	case U8X8_MSG_DELAY_NANO:
#ifdef GPIO_DEBUG
		std::cout << "DELAY_NANO\n";
#endif
		sleep_us(arg_int==0?0:1);
		break;

	case U8X8_MSG_DELAY_10MICRO:
	case U8X8_MSG_DELAY_100NANO:
		/* not used at the moment */
		break;
	
	default:
#ifdef GPIO_DEBUG
		std::cout << "UNKNOWN(" << (uint16_t)msg << ")\n";
#endif
		if (msg < U8X8_MSG_GPIO(0))
			return 0;

		u8x8_SetGPIOResult(u8x8, 1);
		break;
	}

	return 1;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions