Files
esp-idf/examples/peripherals/i2c/i2c_u8g2/main/i2c_u8g2_main.c

267 lines
8.8 KiB
C

/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/**
* @file i2c_u8g2_main.c
* @brief I2C U8G2 Display Demo using ESP-IDF I2C Master Driver
*
* This example demonstrates how to use the U8G2 graphics library with
* SSD1306 display over I2C interface using the new ESP-IDF I2C master driver.
*
* The demo showcases various U8G2 features including text display, geometric shapes,
* pixel manipulation, progress bars, animations, and bitmap display.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "u8g2.h"
#include "u8g2_demo.h"
static const char *TAG = "U8G2";
/* I2C Configuration (configurable via menuconfig) */
#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C master port number */
#define I2C_MASTER_SDA_IO CONFIG_I2C_MASTER_SDA /*!< GPIO number used for I2C master data */
#define I2C_MASTER_SCL_IO CONFIG_I2C_MASTER_SCL /*!< GPIO number used for I2C master clock */
#define I2C_FREQ_HZ CONFIG_I2C_MASTER_FREQUENCY /*!< I2C master clock frequency */
#define I2C_TIMEOUT_MS 1000 /*!< I2C master timeout */
/* Display Configuration (configurable via menuconfig) */
#define I2C_DISPLAY_ADDRESS CONFIG_I2C_DISPLAY_ADDRESS /*!< Display I2C address */
static i2c_master_bus_handle_t i2c_bus_handle = NULL; /*!< I2C master bus handle */
static i2c_master_dev_handle_t display_dev_handle = NULL; /*!< Display device handle */
/**
* @brief U8X8 I2C communication callback function
*
* This function handles all I2C communication between U8G2 library and display controller.
* Please refer to https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform for more details.
*
* @param[in] u8x8 Pointer to u8x8 structure (not used)
* @param[in] msg Message type from U8G2 library
* @param[in] arg_int Integer argument (varies by message type)
* @param[in] arg_ptr Pointer argument (varies by message type)
*
* @return
* - 1: Success
* - 0: Failure
*/
static uint8_t u8x8_byte_i2c_cb(u8x8_t *u8x8, uint8_t msg,
uint8_t arg_int, void *arg_ptr)
{
static uint8_t buffer[132]; /*!< Enhanced buffer: control byte + 128 data bytes + margin */
static uint8_t buf_idx; /*!< Current buffer index */
switch (msg) {
case U8X8_MSG_BYTE_INIT:
/* Add display device to the I2C bus */
i2c_device_config_t dev_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = I2C_DISPLAY_ADDRESS,
.scl_speed_hz = I2C_FREQ_HZ,
.scl_wait_us = 0, /* Use default value */
.flags.disable_ack_check = false,
};
esp_err_t ret = i2c_master_bus_add_device(i2c_bus_handle, &dev_config, &display_dev_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C master driver initialized failed");
return 0;
}
ESP_LOGI(TAG, "I2C master driver initialized successfully");
break;
case U8X8_MSG_BYTE_START_TRANSFER:
/* Start transfer, reset buffer */
buf_idx = 0;
break;
case U8X8_MSG_BYTE_SET_DC:
/* DC (Data/Command) control - handled by SSD1306 protocol */
break;
case U8X8_MSG_BYTE_SEND:
/* Add data bytes to buffer */
for (size_t i = 0; i < arg_int; ++i) {
buffer[buf_idx++] = *((uint8_t*)arg_ptr + i);
}
break;
case U8X8_MSG_BYTE_END_TRANSFER:
/* Transmit data using new I2C driver */
if (buf_idx > 0 && display_dev_handle != NULL) {
esp_err_t ret = i2c_master_transmit(display_dev_handle, buffer, buf_idx, I2C_TIMEOUT_MS);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C master transmission failed");
return 0;
}
/* Debug output: show transmitted data */
ESP_LOGD(TAG, "Sent %d bytes to 0x%02X: control_byte=0x%02X",
buf_idx, I2C_DISPLAY_ADDRESS, buffer[0]);
}
break;
default:
return 0;
}
return 1;
}
/**
* @brief U8X8 GPIO control and delay callback function for ESP32
*
* This function handles GPIO operations and timing delays required by U8G2 library.
* Please refer to https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform for more details.
*
* @param[in] u8x8 Pointer to u8x8 structure (not used)
* @param[in] msg Message type from U8G2 library
* @param[in] arg_int Integer argument (varies by message type)
* @param[in] arg_ptr Pointer argument (not used)
*
* @return
* - 1: Success
* - 0: Failure
*/
static uint8_t u8x8_gpio_delay_cb(u8x8_t *u8x8, uint8_t msg,
uint8_t arg_int, void *arg_ptr)
{
switch (msg) {
case U8X8_MSG_GPIO_AND_DELAY_INIT:
ESP_LOGI(TAG, "GPIO and delay initialization completed");
break;
case U8X8_MSG_DELAY_MILLI:
/* Millisecond delay */
vTaskDelay(pdMS_TO_TICKS(arg_int));
break;
case U8X8_MSG_DELAY_10MICRO:
/* 10 microsecond delay */
esp_rom_delay_us(arg_int * 10);
break;
case U8X8_MSG_DELAY_100NANO:
/* 100 nanosecond delay - use minimal delay on ESP32 */
__asm__ __volatile__("nop");
break;
case U8X8_MSG_DELAY_I2C:
/* I2C timing delay: 5us for 100KHz, 1.25us for 400KHz */
esp_rom_delay_us(5 / arg_int);
break;
case U8X8_MSG_GPIO_RESET:
/* GPIO reset control (optional for most display controllers) */
break;
default:
/* Other GPIO messages not handled */
return 0;
}
return 1;
}
/**
* @brief Display the current demo cycle number on the display screen.
*
* @param u8g2 Pointer to the U8G2 display structure.
* @param demo_cycle The current demo cycle number to display.
*/
static void show_demo_cycle(u8g2_t* u8g2, int demo_cycle)
{
u8g2_ClearBuffer(u8g2);
u8g2_SetFont(u8g2, u8g2_font_ncenB08_tr);
u8g2_DrawStr(u8g2, 25, 25, "Demo Cycle");
u8g2_SetFont(u8g2, u8g2_font_ncenB14_tr);
char cycle_str[16];
snprintf(cycle_str, sizeof(cycle_str), "%d", demo_cycle);
u8g2_DrawStr(u8g2, 55, 45, cycle_str);
u8g2_SendBuffer(u8g2);
vTaskDelay(pdMS_TO_TICKS(2000)); /* delay for showing static display */
}
/**
* @brief Main application entry point
*
* This function initializes the U8G2 library, configures the display controller,
* and runs a continuous demo loop showcasing various U8G2 features.
*
* The demo includes:
* - Text display with different fonts
* - Geometric shapes (rectangles, circles, triangles, lines)
* - Pixel manipulation
* - Animated progress bar
* - Bouncing ball animation
* - Bitmap display
*/
void app_main(void)
{
u8g2_t u8g2;
ESP_LOGI(TAG, "Starting U8G2 display demo program (menuconfig based configuration)");
ESP_LOGI(TAG, "I2C Configuration: SDA=GPIO%d, SCL=GPIO%d, Freq=%dHz, Timeout=%dms",
I2C_MASTER_SDA_IO, I2C_MASTER_SCL_IO, I2C_FREQ_HZ, I2C_TIMEOUT_MS);
ESP_LOGI(TAG, "Display Configuration: Address=0x%02X",
I2C_DISPLAY_ADDRESS);
i2c_master_bus_config_t bus_config = {
.i2c_port = I2C_MASTER_NUM,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.intr_priority = 0,
.trans_queue_depth = 0, /* 0 = synchronous mode */
.flags.enable_internal_pullup = true,
};
/* Create I2C master bus */
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &i2c_bus_handle));
/*
* Initialize U8G2 for your display type.
* Change the u8g2_Setup_xxx function below to match your display controller,
* e.g. SSD1306, SH1106, SSD1327, etc.
* See: https://github.com/olikraus/u8g2/wiki/u8g2setupc
*/
u8g2_Setup_ssd1306_i2c_128x64_noname_f(
&u8g2, U8G2_R0,
u8x8_byte_i2c_cb, /* I2C communication callback */
u8x8_gpio_delay_cb /* GPIO and delay callback */
);
/* Initialize display hardware */
ESP_LOGI(TAG, "Initializing display...");
u8g2_InitDisplay(&u8g2);
ESP_LOGI(TAG, "Setting power mode...");
u8g2_SetPowerSave(&u8g2, 0); /* Wake up display */
ESP_LOGI(TAG, "Display initialization completed");
/* Main demo loop - runs continuously */
int demo_cycle = 0;
while (1) {
ESP_LOGI(TAG, "Demo cycle: %d", ++demo_cycle);
/* Execute demo sequence */
demo_text_display(&u8g2);
demo_shapes(&u8g2);
demo_pixels(&u8g2);
demo_progress_bar(&u8g2);
demo_animation(&u8g2);
demo_bitmap(&u8g2);
show_demo_cycle(&u8g2, demo_cycle);
}
}