espidf的iic波特律动oled

参考

ESP32 波特律动oled.csdn

香橙派zero3配个iic屏显示ip.csdn

蚂蚁S9矿板PS 配个iic屏显示ip.csdn

bsp

iic

bsp_iic.h

c 复制代码
#ifndef BSP_IIC_H
#define BSP_IIC_H

#include "stdbool.h"
#include "driver/i2c_master.h"
#ifdef __cplusplus
extern "C" {
#endif
void bsp_iic_init(void);
i2c_master_dev_handle_t  bsp_iic_oled_dev(void);
#ifdef __cplusplus
}
#endif
#endif

bsp_iic.c

c 复制代码
#include "bsp_iic.h"
#include "driver/i2c_master.h"
//SSD1306
#define I2C_MASTER_NUM     I2C_NUM_0
#define I2C_MASTER_SDA_IO  21
#define I2C_MASTER_SCL_IO  22
#define I2C_MASTER_FREQ_HZ 400000


static i2c_master_dev_handle_t i2c_dev_handle = NULL;

void bsp_iic_init(void)
{

    // Initialize I2C bus
    i2c_master_bus_config_t i2c_bus_cfg = {
            .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,
            .flags.enable_internal_pullup = true,
    };

    i2c_master_bus_handle_t bus_handle;
    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &bus_handle));
    // Add I2C device
    i2c_device_config_t i2c_dev_cfg = {
            .device_address = 0x3C,
            .scl_speed_hz = I2C_MASTER_FREQ_HZ,
    };

    ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &i2c_dev_cfg, &i2c_dev_handle));


}

i2c_master_dev_handle_t  bsp_iic_oled_dev(void){
    return i2c_dev_handle;
}

oled

oled.h font.h font.cpp 三个文件 和

ESP32 波特律动oled.csdn 中的一样

oled.cpp

c 复制代码
#include <math.h>
#include <stdlib.h>
#include "oled.h"
#include "../iic/bsp_iic.h"

// OLED参数
#define OLED_PAGE 4                 // OLED页数
#define OLED_ROW (8 * OLED_PAGE)    // OLED行数
#define OLED_COLUMN 128             // OLED列数

// 显存
uint8_t OLED_GRAM[OLED_PAGE][OLED_COLUMN];

// ========================== 底层通信函数 ==========================

static esp_err_t oled_write(uint8_t* data, size_t len) {
    i2c_master_dev_handle_t i2c_dev_handle = bsp_iic_oled_dev();
    if (i2c_dev_handle == NULL) {
        return ESP_ERR_INVALID_STATE;
    }

    return i2c_master_transmit(i2c_dev_handle, data, len, -1);
}

static esp_err_t oled_write_data(uint8_t* data, size_t len) {
    uint8_t* buf = (uint8_t*)malloc(len + 1);
    if (buf == NULL) {
        return ESP_ERR_NO_MEM;
    }

    buf[0] = 0x40;
    memcpy(buf + 1, data, len);
    esp_err_t ret = oled_write(buf, len + 1);
    free(buf);

    return ret;
}

/**
 * @brief 向OLED发送数据的函数
 * @param data 要发送的数据
 * @param len 要发送的数据长度
 * @return None
 * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他平台时应根据实际情况修改此函数
 */
void OLED_Send(uint8_t* data, uint8_t len) {
    (void)oled_write(data, len);
}

/**
 * @brief 向OLED发送指令
 */
void OLED_SendCmd(uint8_t cmd) {
    uint8_t sendBuffer[2] = { 0x00, cmd };
    (void)oled_write(sendBuffer, sizeof(sendBuffer));
}

// ========================== OLED驱动函数 ==========================

/**
 * @brief 初始化OLED (SSD1306)
 * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他驱动芯片时应根据实际情况修改此函数
 */
void OLED_Init() {
    OLED_SendCmd(0xAE); /*display off*/
    OLED_SendCmd(0x00); /*set lower column address*/
    OLED_SendCmd(0x10); /*set higher column address*/
    OLED_SendCmd(0x00); /*set display start line*/
    OLED_SendCmd(0xB0); /*set page address*/
    OLED_SendCmd(0x81); /*contract control*/
    OLED_SendCmd(0xff); /*128*/
    OLED_SendCmd(0xA1); /*set segment remap*/
    OLED_SendCmd(0xA6); /*normal / reverse*/
    OLED_SendCmd(0xA8); /*multiplex ratio*/
    OLED_SendCmd(0x1F); /*duty = 1/32*/
    OLED_SendCmd(0xC8); /*Com scan direction*/
    OLED_SendCmd(0xD3); /*set display offset*/
    OLED_SendCmd(0x00);
    OLED_SendCmd(0xD5); /*set osc division*/
    OLED_SendCmd(0x80);
    OLED_SendCmd(0xD9); /*set pre-charge period*/
    OLED_SendCmd(0x1f);
    OLED_SendCmd(0xDA); /*set COM pins*/
    OLED_SendCmd(0x00);
    OLED_SendCmd(0xdb); /*set vcomh*/
    OLED_SendCmd(0x40);
    OLED_SendCmd(0x8d); /*set charge pump enable*/
    OLED_SendCmd(0x14);

    OLED_NewFrame();
    OLED_ShowFrame();

    OLED_SendCmd(0xAF); /*开启显示 display ON*/
}

/**
 * @brief 开启OLED显示
 */
void OLED_DisPlay_On() {
    OLED_SendCmd(0x8D); // 电荷泵使能
    OLED_SendCmd(0x14); // 开启电荷泵
    OLED_SendCmd(0xAF); // 点亮屏幕
}

/**
 * @brief 关闭OLED显示
 */
void OLED_DisPlay_Off() {
    OLED_SendCmd(0x8D); // 电荷泵使能
    OLED_SendCmd(0x10); // 关闭电荷泵
    OLED_SendCmd(0xAE); // 关闭屏幕
}

/**
 * @brief 设置颜色模式 黑底白字或白底黑字
 * @param ColorMode 颜色模式COLOR_NORMAL/COLOR_REVERSED
 * @note 此函数直接设置屏幕的颜色模式
 */
void OLED_SetColorMode(OLED_ColorMode mode) {
    if (mode == OLED_COLOR_NORMAL) {
        OLED_SendCmd(0xA6); // 正常显示
    }
    if (mode == OLED_COLOR_REVERSED) {
        OLED_SendCmd(0xA7); // 反色显示
    }
}

// ========================== 显存操作函数 ==========================

/**
 * @brief 清空显存 绘制新的一帧
 */
void OLED_NewFrame() {
    memset(OLED_GRAM, 0, sizeof(OLED_GRAM));
}

/**
 * @brief 将当前显存显示到屏幕上
 * @note 此函数是移植本驱动时的重要函数 将本驱动库移植到其他驱动芯片时应根据实际情况修改此函数
 */
void OLED_ShowFrame() {
    for (uint8_t i = 0; i < OLED_PAGE; i++) {
        OLED_SendCmd(0xB0 + i); // 设置页地址
        OLED_SendCmd(0x00); // 设置列地址低4位
        OLED_SendCmd(0x10); // 设置列地址高4位
        (void)oled_write_data(OLED_GRAM[i], OLED_COLUMN);
    }
}

/**
 * @brief 设置一个像素点
 * @param x 横坐标
 * @param y 纵坐标
 * @param color 颜色
 */
void OLED_SetPixel(uint8_t x, uint8_t y, OLED_ColorMode color) {
    if (x >= OLED_COLUMN || y >= OLED_ROW)
        return;
    if (!color) {
        OLED_GRAM[y / 8][x] |= 0x01 << (y % 8);
    }
    else {
        OLED_GRAM[y / 8][x] &= ~(0x01 << (y % 8));
    }
}

/**
 * @brief 设置显存中一字节数据的某几位
 * @param page 页地址
 * @param column 列地址
 * @param data 数据
 * @param start 起始位
 * @param end 结束位
 * @param color 颜色
 * @note 此函数将显存中的某一字节的第start位到第end位设置为与data相同
 * @note start和end的范围为0-7, start必须小于等于end
 * @note 此函数与OLED_SetByte_Fine的区别在于此函数只能设置显存中的某一真实字节
 */
void OLED_SetByte_Fine(uint8_t page, uint8_t column, uint8_t data, uint8_t start, uint8_t end, OLED_ColorMode color) {
    static uint8_t temp;
    if (page >= OLED_PAGE || column >= OLED_COLUMN)
        return;
    if (color)
        data = ~data;

    temp = data | (0xff << (end + 1)) | (0xff >> (8 - start));
    OLED_GRAM[page][column] &= temp;
    temp = data & ~(0xff << (end + 1)) & ~(0xff >> (8 - start));
    OLED_GRAM[page][column] |= temp;
    // 使用OLED_SetPixel实现
    // for (uint8_t i = start; i <= end; i++) {
    //   OLED_SetPixel(column, page * 8 + i, !((data >> i) & 0x01));
    // }
}

/**
 * @brief 设置显存中的一字节数据
 * @param page 页地址
 * @param column 列地址
 * @param data 数据
 * @param color 颜色
 * @note 此函数将显存中的某一字节设置为data的值
 */
void OLED_SetByte(uint8_t page, uint8_t column, uint8_t data, OLED_ColorMode color) {
    if (page >= OLED_PAGE || column >= OLED_COLUMN)
        return;
    if (color)
        data = ~data;
    OLED_GRAM[page][column] = data;
}

/**
 * @brief 设置显存中的一字节数据的某几位
 * @param x 横坐标
 * @param y 纵坐标
 * @param data 数据
 * @param len 位数
 * @param color 颜色
 * @note 此函数将显存中从(x,y)开始向下数len位设置为与data相同
 * @note len的范围为1-8
 * @note 此函数与OLED_SetByte_Fine的区别在于此函数的横坐标和纵坐标是以像素为单位的, 可能出现跨两个真实字节的情况(跨页)
 */
void OLED_SetBits_Fine(uint8_t x, uint8_t y, uint8_t data, uint8_t len, OLED_ColorMode color) {
    uint8_t page = y / 8;
    uint8_t bit = y % 8;
    if (bit + len > 8) {
        OLED_SetByte_Fine(page, x, data << bit, bit, 7, color);
        OLED_SetByte_Fine(page + 1, x, data >> (8 - bit), 0, len + bit - 1 - 8, color);
    }
    else {
        OLED_SetByte_Fine(page, x, data << bit, bit, bit + len - 1, color);
    }
    // 使用OLED_SetPixel实现
    // for (uint8_t i = 0; i < len; i++) {
    //   OLED_SetPixel(x, y + i, !((data >> i) & 0x01));
    // }
}

/**
 * @brief 设置显存中一字节长度的数据
 * @param x 横坐标
 * @param y 纵坐标
 * @param data 数据
 * @param color 颜色
 * @note 此函数将显存中从(x,y)开始向下数8位设置为与data相同
 * @note 此函数与OLED_SetByte的区别在于此函数的横坐标和纵坐标是以像素为单位的, 可能出现跨两个真实字节的情况(跨页)
 */
void OLED_SetBits(uint8_t x, uint8_t y, uint8_t data, OLED_ColorMode color) {
    uint8_t page = y / 8;
    uint8_t bit = y % 8;
    OLED_SetByte_Fine(page, x, data << bit, bit, 7, color);
    if (bit) {
        OLED_SetByte_Fine(page + 1, x, data >> (8 - bit), 0, bit - 1, color);
    }
}

/**
 * @brief 设置一块显存区域
 * @param x 起始横坐标
 * @param y 起始纵坐标
 * @param data 数据的起始地址
 * @param w 宽度
 * @param h 高度
 * @param color 颜色
 * @note 此函数将显存中从(x,y)开始的w*h个像素设置为data中的数据
 * @note data的数据应该采用列行式排列
 */
void OLED_SetBlock(uint8_t x, uint8_t y, const uint8_t* data, uint8_t w, uint8_t h, OLED_ColorMode color) {
    uint8_t fullRow = h / 8; // 完整的行数
    uint8_t partBit = h % 8; // 不完整的字节中的有效位数
    for (uint8_t i = 0; i < w; i++) {
        for (uint8_t j = 0; j < fullRow; j++) {
            OLED_SetBits(x + i, y + j * 8, data[i + j * w], color);
        }
    }
    if (partBit) {
        uint16_t fullNum = w * fullRow; // 完整的字节数
        for (uint8_t i = 0; i < w; i++) {
            OLED_SetBits_Fine(x + i, y + (fullRow * 8), data[fullNum + i], partBit, color);
        }
    }
    // 使用OLED_SetPixel实现
    // for (uint8_t i = 0; i < w; i++) {
    //   for (uint8_t j = 0; j < h; j++) {
    //     for (uint8_t k = 0; k < 8; k++) {
    //       if (j * 8 + k >= h) break; // 防止越界(不完整的字节
    //       OLED_SetPixel(x + i, y + j * 8 + k, !((data[i + j * w] >> k) & 0x01));
    //     }
    //   }
    // }
}

// ========================== 图形绘制函数 ==========================
/**
 * @brief 绘制一条线段
 * @param x1 起始点横坐标
 * @param y1 起始点纵坐标
 * @param x2 终止点横坐标
 * @param y2 终止点纵坐标
 * @param color 颜色
 * @note 此函数使用Bresenham算法绘制线段
 */
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, OLED_ColorMode color) {
    static uint8_t temp = 0;
    if (x1 == x2) {
        if (y1 > y2) {
            temp = y1;
            y1 = y2;
            y2 = temp;
        }
        for (uint8_t y = y1; y <= y2; y++) {
            OLED_SetPixel(x1, y, color);
        }
    }
    else if (y1 == y2) {
        if (x1 > x2) {
            temp = x1;
            x1 = x2;
            x2 = temp;
        }
        for (uint8_t x = x1; x <= x2; x++) {
            OLED_SetPixel(x, y1, color);
        }
    }
    else {
        // Bresenham直线算法
        int16_t dx = x2 - x1;
        int16_t dy = y2 - y1;
        int16_t ux = ((dx > 0) << 1) - 1;
        int16_t uy = ((dy > 0) << 1) - 1;
        int16_t x = x1, y = y1, eps = 0;
        dx = abs(dx);
        dy = abs(dy);
        if (dx > dy) {
            for (x = x1; x != x2; x += ux) {
                OLED_SetPixel(x, y, color);
                eps += dy;
                if ((eps << 1) >= dx) {
                    y += uy;
                    eps -= dx;
                }
            }
        }
        else {
            for (y = y1; y != y2; y += uy) {
                OLED_SetPixel(x, y, color);
                eps += dx;
                if ((eps << 1) >= dy) {
                    x += ux;
                    eps -= dy;
                }
            }
        }
    }
}

/**
 * @brief 绘制一个矩形
 * @param x 起始点横坐标
 * @param y 起始点纵坐标
 * @param w 矩形宽度
 * @param h 矩形高度
 * @param color 颜色
 */
void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color) {
    OLED_DrawLine(x, y, x + w, y, color);
    OLED_DrawLine(x, y + h, x + w, y + h, color);
    OLED_DrawLine(x, y, x, y + h, color);
    OLED_DrawLine(x + w, y, x + w, y + h, color);
}

/**
 * @brief 绘制一个填充矩形
 * @param x 起始点横坐标
 * @param y 起始点纵坐标
 * @param w 矩形宽度
 * @param h 矩形高度
 * @param color 颜色
 */
void OLED_DrawFilledRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color) {
    for (uint8_t i = 0; i < h; i++) {
        OLED_DrawLine(x, y + i, x + w, y + i, color);
    }
}

/**
 * @brief 绘制一个三角形
 * @param x1 第一个点横坐标
 * @param y1 第一个点纵坐标
 * @param x2 第二个点横坐标
 * @param y2 第二个点纵坐标
 * @param x3 第三个点横坐标
 * @param y3 第三个点纵坐标
 * @param color 颜色
 */
void OLED_DrawTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color) {
    OLED_DrawLine(x1, y1, x2, y2, color);
    OLED_DrawLine(x2, y2, x3, y3, color);
    OLED_DrawLine(x3, y3, x1, y1, color);
}

/**
 * @brief 绘制一个填充三角形
 * @param x1 第一个点横坐标
 * @param y1 第一个点纵坐标
 * @param x2 第二个点横坐标
 * @param y2 第二个点纵坐标
 * @param x3 第三个点横坐标
 * @param y3 第三个点纵坐标
 * @param color 颜色
 */
void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3,
                             OLED_ColorMode color) {
    uint8_t a = 0, b = 0, y = 0, last = 0;
    if (y1 > y2) {
        a = y2;
        b = y1;
    }
    else {
        a = y1;
        b = y2;
    }
    y = a;
    for (; y <= b; y++) {
        if (y <= y3) {
            OLED_DrawLine(x1 + (y - y1) * (x2 - x1) / (y2 - y1), y, x1 + (y - y1) * (x3 - x1) / (y3 - y1), y, color);
        }
        else {
            last = y - 1;
            break;
        }
    }
    for (; y <= b; y++) {
        OLED_DrawLine(x2 + (y - y2) * (x3 - x2) / (y3 - y2), y, x1 + (y - last) * (x3 - x1) / (y3 - last), y, color);
    }
}

/**
 * @brief 绘制一个圆
 * @param x 圆心横坐标
 * @param y 圆心纵坐标
 * @param r 圆半径
 * @param color 颜色
 * @note 此函数使用Bresenham算法绘制圆
 */
void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color) {
    int16_t a = 0, b = r, di = 3 - (r << 1);
    while (a <= b) {
        OLED_SetPixel(x - b, y - a, color);
        OLED_SetPixel(x + b, y - a, color);
        OLED_SetPixel(x - a, y + b, color);
        OLED_SetPixel(x - b, y - a, color);
        OLED_SetPixel(x - a, y - b, color);
        OLED_SetPixel(x + b, y + a, color);
        OLED_SetPixel(x + a, y - b, color);
        OLED_SetPixel(x + a, y + b, color);
        OLED_SetPixel(x - b, y + a, color);
        a++;
        if (di < 0) {
            di += 4 * a + 6;
        }
        else {
            di += 10 + 4 * (a - b);
            b--;
        }
        OLED_SetPixel(x + a, y + b, color);
    }
}

/**
 * @brief 绘制一个填充圆
 * @param x 圆心横坐标
 * @param y 圆心纵坐标
 * @param r 圆半径
 * @param color 颜色
 * @note 此函数使用Bresenham算法绘制圆
 */
void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r, OLED_ColorMode color) {
    int16_t a = 0, b = r, di = 3 - (r << 1);
    while (a <= b) {
        for (int16_t i = x - b; i <= x + b; i++) {
            OLED_SetPixel(i, y + a, color);
            OLED_SetPixel(i, y - a, color);
        }
        for (int16_t i = x - a; i <= x + a; i++) {
            OLED_SetPixel(i, y + b, color);
            OLED_SetPixel(i, y - b, color);
        }
        a++;
        if (di < 0) {
            di += 4 * a + 6;
        }
        else {
            di += 10 + 4 * (a - b);
            b--;
        }
    }
}

/**
 * @brief 绘制一个椭圆
 * @param x 椭圆中心横坐标
 * @param y 椭圆中心纵坐标
 * @param a 椭圆长轴
 * @param b 椭圆短轴
 */
void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b, OLED_ColorMode color) {
    int xpos = 0, ypos = b;
    int a2 = a * a, b2 = b * b;
    int d = b2 + a2 * (0.25 - b);
    while (a2 * ypos > b2 * xpos) {
        OLED_SetPixel(x + xpos, y + ypos, color);
        OLED_SetPixel(x - xpos, y + ypos, color);
        OLED_SetPixel(x + xpos, y - ypos, color);
        OLED_SetPixel(x - xpos, y - ypos, color);
        if (d < 0) {
            d = d + b2 * ((xpos << 1) + 3);
            xpos += 1;
        }
        else {
            d = d + b2 * ((xpos << 1) + 3) + a2 * (-(ypos << 1) + 2);
            xpos += 1, ypos -= 1;
        }
    }
    d = b2 * (xpos + 0.5) * (xpos + 0.5) + a2 * (ypos - 1) * (ypos - 1) - a2 * b2;
    while (ypos > 0) {
        OLED_SetPixel(x + xpos, y + ypos, color);
        OLED_SetPixel(x - xpos, y + ypos, color);
        OLED_SetPixel(x + xpos, y - ypos, color);
        OLED_SetPixel(x - xpos, y - ypos, color);
        if (d < 0) {
            d = d + b2 * ((xpos << 1) + 2) + a2 * (-(ypos << 1) + 3);
            xpos += 1, ypos -= 1;
        }
        else {
            d = d + a2 * (-(ypos << 1) + 3);
            ypos -= 1;
        }
    }
}

/**
 * @brief 绘制一张图片
 * @param x 起始点横坐标
 * @param y 起始点纵坐标
 * @param img 图片
 * @param color 颜色
 */
void OLED_DrawImage(uint8_t x, uint8_t y, const Image* img, OLED_ColorMode color) {
    OLED_SetBlock(x, y, img->data, img->w, img->h, color);
}

// ================================ 文字绘制 ================================

/**
 * @brief 绘制一个ASCII字符
 * @param x 起始点横坐标
 * @param y 起始点纵坐标
 * @param ch 字符
 * @param font 字体
 * @param color 颜色
 */
void OLED_PrintASCIIChar(uint8_t x, uint8_t y, char ch, const ASCIIFont* font, OLED_ColorMode color) {
    OLED_SetBlock(x, y, font->chars + (ch - ' ') * (((font->h + 7) / 8) * font->w), font->w, font->h, color);
}

/**
 * @brief 绘制一个ASCII字符串
 * @param x 起始点横坐标
 * @param y 起始点纵坐标
 * @param str 字符串
 * @param font 字体
 * @param color 颜色
 */
void OLED_PrintASCIIString(uint8_t x, uint8_t y, char* str, const ASCIIFont* font, OLED_ColorMode color) {
    uint8_t x0 = x;
    while (*str) {
        OLED_PrintASCIIChar(x0, y, *str, font, color);
        x0 += font->w;
        str++;
    }
}

/**
 * @brief 获取UTF-8编码的字符长度
 */
uint8_t _OLED_GetUTF8Len(char* string) {
    if ((string[0] & 0x80) == 0x00) {
        return 1;
    }
    else if ((string[0] & 0xE0) == 0xC0) {
        return 2;
    }
    else if ((string[0] & 0xF0) == 0xE0) {
        return 3;
    }
    else if ((string[0] & 0xF8) == 0xF0) {
        return 4;
    }
    return 0;
}

/**
 * @brief 绘制字符串
 * @param x 起始点横坐标
 * @param y 起始点纵坐标
 * @param str 字符串
 * @param font 字体
 * @param color 颜色
 *
 * @note 为保证字符串中的中文会被自动识别并绘制, 需:
 * 1. 编译器字符集设置为UTF-8
 * 2. 使用波特律动LED取模工具生成字模(https://led.baud-dance.com)
 */
/**
 * @brief 绘制字符串
 * @param x 起始点横坐标
 * @param y 起始点纵坐标
 * @param str 字符串
 * @param font 字体
 * @param color 颜色
 *
 * @note 为保证字符串中的中文会被自动识别并绘制, 需:
 * 1. 编译器字符集设置为UTF-8
 * 2. 使用波特律动LED取模工具生成字模(https://led.baud-dance.com)
 */
void OLED_PrintString(uint8_t x, uint8_t y, char* str, const Font* font, OLED_ColorMode color) {
    uint16_t i = 0; // 字符串索引
    uint8_t oneLen = (((font->h + 7) / 8) * font->w) + 4; // 一个字模占多少字节
    while (str[i]) {
        uint8_t found = 0; // 是否找到字模
        uint8_t utf8Len = _OLED_GetUTF8Len(str + i); // UTF-8编码长度
        if (utf8Len == 0)
            break; // 有问题的UTF-8编码

        // 寻找字符  TODO 优化查找算法, 二分查找或者hash
        for (uint8_t j = 0; j < font->len; j++) {
            uint8_t* head = (uint8_t*)(font->chars) + (j * oneLen); // 字模头指针
            if (memcmp(str + i, head, utf8Len) == 0) {
                OLED_SetBlock(x, y, head + 4, font->w, font->h, color);
                // 移动光标
                x += font->w;
                i += utf8Len;
                found = 1;
                break;
            }
        }

        // 若未找到字模,且为ASCII字符, 则缺省显示ASCII字符
        if (found == 0) {
            if (utf8Len == 1) {
                OLED_PrintASCIIChar(x, y, str[i], font->ascii, color);
                // 移动光标
                x += font->ascii->w;
                i += utf8Len;
            }
            else {
                OLED_PrintASCIIChar(x, y, ' ', font->ascii, color);
                x += font->ascii->w;
                i += utf8Len;
            }
        }
    }
}

App.cpp

c 复制代码
#define OS_GLOBALS
#include "App.h"
#include "Main_constant.h"
#include "bsp_iic.h"
#include "oled.h"

bool App::Init() {
    bsp_iic_init();
    OLED_Init();
    return true;
}

int i = 0;
char msg[50] = { 0 };

void App::Loop()
{
    OLED_NewFrame();
    i++;
    sprintf(msg, "time:%d", i);
    OLED_PrintString(0, 0, msg, &font16x16, OLED_COLOR_NORMAL);
    OLED_PrintString(0, 17, "波特律hello", &font16x16, OLED_COLOR_REVERSED);
    OLED_ShowFrame();
}
相关推荐
MartinYeung537 分钟前
[论文学习]智能合约不变式防御网络犯罪
网络·学习·智能合约
xx~t40 分钟前
嵌入式学习——进程与线程2
linux·c语言·学习·进程与线程
我爱cope1 小时前
【计算机网络 | 网络层10:OSPF 与 BGP:自治系统内部和互联网之间如何路由?】
网络·学习·计算机网络·智能路由器
白猫不黑1 小时前
网络空间安全/信息安全专业学习规划(超级详细版)
运维·学习·安全·web安全·网络安全·信息安全
HyperAI超神经1 小时前
MiniMax H3 突破视频生成边界,音画内容一体生成;Ornith-1.5-35B-A3B 探索推理模型自进化训练新模式
人工智能·深度学习·学习·音视频·多模态·视频生成·推理模型
小小帅呀1 小时前
学习 VLA 第6天:SWIN VIT算法原理以及复现
学习·算法
平原20181 小时前
AI岗位需求增长244%背后:从任务重组到可验证学习闭环
大数据·人工智能·学习
ouynagda1 小时前
Linux信号与进程间通信学习笔记
linux·笔记·学习
️学习的小王1 小时前
AI Agent Skills进阶:调试、排错、实战开发与工程化落地
人工智能·经验分享·笔记·学习