当前位置: 首页 > article >正文

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础组件实现)

目录

基础组件实现

如何将图像和文字显示到OLED上

如何绘制图像

如何绘制文字

如何获取字体?

如何正确的访问字体

如何抽象字体

如何绘制字符串

绘制方案

文本绘制

更加方便的绘制

字体附录

ascii 6x8字体

ascii 8 x 16字体


基础组件实现

我们现在离手搓一个动态的多级菜单越来越近了。终于!我们来到了最基础的组件实现,我们现在搓的东西的代码库放到了:MCU_Libs/OLED/library/Graphic/widgets/base at main · Charliechen114514/MCU_Libs (github.com)当中,也就是手搓图像显示和文字显示。如果你对这篇博客所属的集合有任何疑问,可以到从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架-CSDN博客阅读。

如何将图像和文字显示到OLED上

三个字:画出来!带上一个KeysKing大跌手搓的取码地址:波特律动LED字模生成器 (baud-dance.com),实际上,你的悟性足够高,已经可以离开这篇博客自己继续手搓了。

好吧,你继续往下看了,那我就详细的好好说明。

如何绘制图像

绘制图像之前,我们还要遵循老步骤,思考一下如何设计我们的抽象。

我们如何描述一个给定的图像呢?我们可能着急于描述这个图像表达了什么,也就是图像的资源,在OLED中,我们习惯于阐述为一个字节的数组,这个数组描述了我们的图像,只要把它传递上去,一个图像就显示出来我们可以看了。

但是还是有问题:你这个图像放到哪里呢?画的要多大呢?这就是我们需要设计一个结构体抽象的原因了。请看VCR:

typedef struct __CCGraphic_Image{
    CCGraphic_Point point;
    CCGraphic_Size  image_size;
    uint8_t*        sources_register;
}CCGraphic_Image;

关于CCGraphic_Size,并不复杂,可以到MCU_Libs/OLED/library/Graphic/widgets/common/CCGraphic_Size at main · Charliechen114514/MCU_Libs (github.com)中看到源码,实际上就是宽和高的一个封装,没什么大不了的。

此外,我们对图像的操作就是绘制了

void CCGraphicWidget_init_image(
    CCGraphic_Image*    image,
    CCGraphic_Point     tl_point,
    CCGraphic_Size      image_size,
    uint8_t*      sources_register
);
​
void CCGraphicWidget_draw_image(
    CCDeviceHandler*    handler,
    CCGraphic_Image*    image
);

出乎你意料的是。绘制图像远远比你想象的简单的多

#include "Graphic/widgets/base/CCGraphic_Image/CCGraphic_Image.h"
#include "Graphic/CCGraphic_device_adapter.h"
void CCGraphicWidget_init_image(
    CCGraphic_Image*    image,
    CCGraphic_Point     tl_point,
    CCGraphic_Size      image_size,
    uint8_t*      sources_register
)
{
    image->image_size = image_size;
    image->point = tl_point;
    image->sources_register = sources_register;
}
​
void CCGraphicWidget_draw_image(
    CCDeviceHandler*    handler,
    CCGraphic_Image*    image
)
{
    if(!image->sources_register) return;
    handler->operations.draw_area_device_function(
        handler, image->point.x, image->point.y,
        image->image_size.width, image->image_size.height, image->sources_register
    );
}

我们直接使用设备draw_area的方法,将图像资源传递上去了。

CCGraphicWidget_draw_image -> draw_area_device_function(draw_area_device_oled) -> oled_helper_draw_area

你看,干净利落!完事。

如何绘制文字

现在这个事情就需要深思熟虑了,设计到文字,就必然需要考虑字体大小,以及解析字符串的问题。笔者这里没有实现UTF-8字符的打印实现,但是笔者提示你,仍然是画出来字符。让我们看看该咋做。

typedef struct __CCGraphic_TextHandle{
    char*               sources_borrowed;       // 这个就是所持有的字体资源指针
    CCGraphic_Point     tl_point;               // 这个是所占有的左上角的绘制起点
    CCGraphic_Point     indexed_point;          // 这个是现在的绘制指针,表明现在我们绘制到了那个地方
    CCGraphic_Size      TexthandleSize;         // 整个Text所在的BoundingRect大小
    Ascii_Font_Size     font_size;              // 字体多大?
}CCGraphic_AsciiTextItem;
如何获取字体?

关于ASCII字体的获取,笔者放到了附录里,值得一提的是,江科大的OLED_Data.h中对字体数组的初始化时不严谨的,不规范的,正确的初始化方案已经放到了附录,不再赘述。

如何正确的访问字体

C语言中,有一个著名的关键字叫extern,他随了汇编语言的关键字extern,在所属权层面上表达的同static完全相反,即这个资源的定义需要到其他文件中寻找。所以,当我们想要引用字体(这个字体被存放到了其他的C源文件中)的时候,只需要手动的extern一下,而且确保资源被正确的编译进来就OK了。

extern const uint8_t ascii6x8_sources[][6];
如何抽象字体

很简单,虽然说正常而言只需要抽象一个TextFont结构体即可,但是笔者认为这里更多看重的是方法,而且,没有必要对用户暴露一个Font结构体,选择结构体更加不如暴露的是一个枚举和公开的方法。

#ifndef CCGraphic_TextConfig_H
#define CCGraphic_TextConfig_H
#include "Graphic/config/CCGraphic_config.h"
#include "Graphic/CCGraphic_common.h"
#include "Graphic/widgets/common/CCGraphic_Size/CCGraphic_Size.h"
/*
    current version we only support
    6x8 and 8x16. to register more, u should
    provide the source and implement the functions
*/
typedef enum {
#if ENABLE_ASCII_6x8_SOURCES
    ASCII_6x8,
#endif
​
#if ENABLE_ASCII_8x16_SOURCES
    ASCII_8x16,
#endif
    NO_ASCII_SIZE 
}Ascii_Font_Size;
​
typedef enum {
    Unicode_16x16
}Unicode_Font_Size;
​
#define UNSUPPORTIVE_FONT_SOURCE    ((void*)0)
​
/**
 * @brief Selects the font data array based on the specified font size.
 *
 * This function receives an `Ascii_Font_Size` value 
 * and returns a pointer to the corresponding font data array. 
 * The function helps in selecting
 * the appropriate font data for display purposes, allowing for different
 * font sizes (e.g., 8x16, 6x8, etc.).
 *
 * @param s The font size to be selected 
 *          (from the `Ascii_Font_Size` enum).
 * @param ch the character wanna display
 * @return  A pointer to the font data array corresponding to the selected font size.
 *          If an invalid font size is passed, 
 *          the function returns UNSUPPORTIVE_FONT_SOURCE.
 */
uint8_t*        __select_from_ascii_font_size(const Ascii_Font_Size s, const char ch);
​
​
CCGraphic_Size  __fetch_font_size(const Ascii_Font_Size s);
​
#endif
#include "Graphic/widgets/base/CCGraphic_TextItem/CCGraphic_TextConfig.h"
​
extern const uint8_t ascii8x16_sources[][16];
extern const uint8_t ascii6x8_sources[][6];
​
uint8_t* __select_from_ascii_font_size(
    const Ascii_Font_Size s, const char ch)
{
    switch(s)
    {
#if ENABLE_ASCII_6x8_SOURCES
        case ASCII_6x8:
            return (uint8_t*)(ascii6x8_sources[ch - ' ']);
#endif
#if ENABLE_ASCII_8x16_SOURCES       
        case ASCII_8x16:
            return (uint8_t*)(ascii8x16_sources[ch - ' ']);
#endif
        /* 
            To programmers, if new ascii like sources is
            registered, please implement follows
        */
        default:
            return UNSUPPORTIVE_FONT_SOURCE;
    }
}
​
CCGraphic_Size  __fetch_font_size(const Ascii_Font_Size s)
{
    CCGraphic_Size size = {0, 0};
    switch(s)
    {
#if ENABLE_ASCII_6x8_SOURCES
        case ASCII_6x8:
            size.height     =   8;
            size.width      =   6;
            break;
#endif
​
#if ENABLE_ASCII_8x16_SOURCES  
        case ASCII_8x16:
            size.height     =   16;
            size.width      =   8;
            break;
#endif
        default:
            break;
    }
    return size;
}

题外话:使用编译宏控制资源编译:GCC是一个智能的编译器,对于任何没有使用到的资源,概不参与编译,所以,对于使用GCC的编译器,只需要确保自己不额外使用其他资源,就不会将冗余的C符号纳入编译。

但还是那句话,为了确保语义更加清晰,仍然使用控制宏对资源进行编译控制和符号控制,让自己的代码语义更加的明确,是一件事半功倍的举措

如何绘制字符串

绘制字符串是一个复杂的活。但是在那之前,把杂活做了。

#include "Graphic/widgets/base/CCGraphic_TextItem/CCGraphic_TextItem.h"
#include "Graphic/widgets/base/CCGraphic_TextItem/CCGraphic_TextConfig.h"
#include "Graphic/widgets/base/CCGraphic_Image/CCGraphic_Image.h"
#include "Graphic/CCGraphic_device_adapter.h"
#include <string.h>
​
/**
 * 初始化一个ASCII文本项。
 * @param item 指向CCGraphic_AsciiTextItem的指针。
 * @param tl_point 文本项的左上角起始坐标。
 * @param textHandleSize 文本项的尺寸信息(宽度和高度)。
 * @param text_size 字体大小枚举类型。
 */
void CCGraphicWidget_init_AsciiTextItem(
    CCGraphic_AsciiTextItem* item,
    CCGraphic_Point tl_point,
    CCGraphic_Size textHandleSize,
    Ascii_Font_Size text_size
)
{
    item->font_size = text_size;
    item->sources_borrowed = "";  // 初始化为空字符串,表示未设置内容。
    item->tl_point = tl_point;
    item->indexed_point = tl_point;
    item->TexthandleSize = textHandleSize;
}
​
/**
 * 设置ASCII文本项的内容。
 * @param item 指向CCGraphic_AsciiTextItem的指针。
 * @param text 待设置的文本内容字符串。
 */
void CCGraphicWidget_AsciiTextItem_setAsciiText(
    CCGraphic_AsciiTextItem* item,
    char* text
)
{
    item->sources_borrowed = text;
}
​
/**
 * 设置ASCII文本项的索引点。
 * @param item 指向CCGraphic_AsciiTextItem的指针。
 * @param p 索引点的指针。
 */
void CCGraphicWidget_AsciiTextItem_setIndexedPoint(
    CCGraphic_AsciiTextItem* item,
    CCGraphic_Point* p
)
{
    item->indexed_point = *p;
}
​
/**
 * 重新定位ASCII文本项。
 * @param item 指向CCGraphic_AsciiTextItem的指针。
 * @param tl_point 新的左上角起始坐标。
 * @param textHandleSize 新的尺寸信息(宽度和高度)。
 */
void CCGraphicWidget_AsciiTextItem_relocate(
    CCGraphic_AsciiTextItem* item,
    CCGraphic_Point tl_point,
    CCGraphic_Size textHandleSize
)
{
    // 这个函数的一个重要的目的就是重定位文本框,为之后的文本显示做铺垫。
    item->tl_point = tl_point;
    item->TexthandleSize = textHandleSize;
}

绘制一个字符串本身就是绘制一串字符,掌握整个原理,事情就会变得非常简单,我们线讨论如何绘制字符本身

/**
 * 绘制ASCII字符到设备。
 * @param device_handle 设备句柄。
 * @param borrowing_image 临时用于绘制的图像对象。
 * @param ch 要绘制的字符。
 * @param size 字体大小枚举类型。
 */
static void __pvt_draw_char_each(
    CCDeviceHandler* device_handle, 
    CCGraphic_Image* borrowing_image, 
    const char ch, Ascii_Font_Size size
)
{
    borrowing_image->image_size = __fetch_font_size(size);
    uint8_t* ascii = __select_from_ascii_font_size(size, ch);
    borrowing_image->sources_register = ascii;
    CCGraphicWidget_draw_image(device_handle, borrowing_image);
#if CCGraphic_TextDebug
    device_handle->operations.update_device_function(device_handle);
#endif
}

我们将一个字符的字体绘制文件放置到Image中,所以我强调:字符是画出来的

设计缺陷:注意到,我这里并没有设置绘制的位置,这是因为这件事情在上层做好了,所以我也在参变量中警示自己:整个变量是部分初始化的。

绘制方案

我们绘制的时候,更多会去在乎:是在之前的文本基础上继续绘制呢?还是换一行继续绘制,还是直接清空文本重新绘制?为了防止反复的刷新,笔者设计了三个函数完成整个工作。

首先,设置游标点:

CCGraphic_Point     indexed_point;          // 这个是现在的绘制指针,表明现在我们绘制到了那个地方

整个在Text的结构体中,不由用户直接设置。

下面,就是依赖设置:

/**
 * 判断当前字符是否需要换行。
 * @param device_handle 设备句柄。
 * @param brpoint 右下角边界点。
 * @param cur_draw_p 当前绘制点的指针。
 * @param s 字体大小枚举类型。
 * @return 如果需要换行,返回非零值;否则返回零。
 */
static uint8_t inline __pvt_should_be_next_line(
    CCDeviceHandler* device_handle,
    CCGraphic_Point* brpoint,
    CCGraphic_Point* cur_draw_p, Ascii_Font_Size s 
)
{
    return cur_draw_p->x + 
        (int16_t)(1.5 * __fetch_font_size(s).width) >= brpoint->x;
}
/**
 * 计算有效的右下角点。
 * @param device_handle 设备句柄。
 * @param size 文本项的尺寸信息。
 * @param tl 文本项的左上角起始点。
 * @return 计算后的右下角点。
 */
static CCGraphic_Point inline __pvt_fetch_valid_final_point(
    CCDeviceHandler* device_handle,
    CCGraphic_Size* size, CCGraphic_Point* tl
) 
{
    CCGraphic_Point br;
    int16_t device_width = 0;
    device_handle->operations.property_function(
        device_handle, &device_width, CommonProperty_WIDTH
    );
    int16_t device_height = 0;
    device_handle->operations.property_function(
        device_handle, &device_height, CommonProperty_HEIGHT
    );
    // 上面我们获取了设备的宽高,现在我们开获取最大的合法右下角的点
    br.x = tl->x + size->width;
    br.y = tl->y + size->height;
    if(device_width < br.x) { br.x = device_width; }
    if(device_height < br.y) { br.y = device_height; }
    return br;
}
文本绘制

绘制文本的本质是绘图。这一点务必注意。下面的整个函数实现了自动的文本换行!

/**
 * 绘制ASCII文本项。
 * @param device_handle 设备句柄,用于控制绘制设备。
 * @param item 要绘制的ASCII文本项,包含文本内容、位置及尺寸信息。
 */
void CCGraphicWidget_drawAsciiTextItem(
    CCDeviceHandler* device_handle,
    CCGraphic_AsciiTextItem* item)
{
    // 如果文本内容为空,直接返回,不进行绘制。
    if(strcmp(item->sources_borrowed, "") == 0) {
        return;
    }
​
    // 定义用于绘制的图像结构体。
    CCGraphic_Image handle_draw_image;
​
    // 初始化绘制的起始点为当前索引位置。
    CCGraphic_Point draw_tl_point = item->indexed_point;
​
    // 获取当前文本字体的尺寸(宽度和高度)。
    const Ascii_Font_Size font_size = item->font_size;
    const CCGraphic_Size size = __fetch_font_size(font_size);
    const SizeBaseType font_width = size.width;
    const SizeBaseType font_height = size.height;
​
    // 计算文本绘制区域的有效右下角点(即绘制边界)。
    CCGraphic_Point br = __pvt_fetch_valid_final_point(
        device_handle, &(item->TexthandleSize), &(item->tl_point) 
    );
​
    // 定义x方向和y方向的字符偏移量,用于逐字符定位绘制。
    uint8_t offseterx = 0;
    uint8_t offsetery = 0;
​
    // 遍历文本中的每个字符并绘制。
    for(uint8_t i = 0; item->sources_borrowed[i] != '\0'; i++) {
        // 计算当前字符的绘制位置。
        draw_tl_point.x = item->indexed_point.x + offseterx * font_width;
        draw_tl_point.y = item->indexed_point.y + offsetery * font_height;
​
        // 设置图像绘制的左上角点。
        handle_draw_image.point = draw_tl_point;
​
        // 绘制当前字符到目标设备上。
        __pvt_draw_char_each(
            device_handle, 
            &handle_draw_image, 
            item->sources_borrowed[i], 
            item->font_size
        );
​
        // 判断是否需要换行绘制。
        if(__pvt_should_be_next_line(device_handle, &br, &draw_tl_point, font_size)) {
            // 如果需要换行,将x偏移量归零,并增加y方向的行数。
            offseterx = 0;
            offsetery++;
            // 重置x方向的起点位置为文本的左上角点。
            item->indexed_point.x = item->tl_point.x;
        } else {
            // 否则继续绘制当前行的下一个字符。
            offseterx++;
        }
    }
​
    // 更新文本项的索引点位置为最后一个字符的右侧位置。
    item->indexed_point = draw_tl_point;
    item->indexed_point.x += font_width;
}
更加方便的绘制

当然,还可以为了之后的组件方便生成一个返回绘制点的方便函数:

/**
 * 绘制ASCII文本项,并返回绘制后的点。
 * @param device_handle 设备句柄,用于控制绘制设备。
 * @param item 要绘制的ASCII文本项,包含文本内容、位置及尺寸信息。
 * @param method 文本追加方式,指示绘制后是否换行或连续追加。
 * @return 绘制后的坐标点,表示下一个绘制位置。
 */
CCGraphic_Point CCGraphicWidget_drawAsciiTextItem_with_finPoint(
    CCDeviceHandler* device_handle,
    CCGraphic_AsciiTextItem* item,
    AppendMethod method
)
{
    // 如果文本内容为空,直接返回文本的初始左上角点。
    if(strcmp(item->sources_borrowed, "") == 0) {
        return item->tl_point;
    }
​
    // 定义绘制图像和绘制位置。
    CCGraphic_Image handle_draw_image;
    CCGraphic_Point draw_tl_point = item->indexed_point;
​
    // 获取字体尺寸。
    const Ascii_Font_Size font_size = item->font_size;
    const CCGraphic_Size size = __fetch_font_size(font_size);
    const SizeBaseType font_width = size.width;
    const SizeBaseType font_height = size.height;
​
    // 获取有效绘制区域的右下角点。
    CCGraphic_Point br = __pvt_fetch_valid_final_point(
        device_handle, &(item->TexthandleSize), &(item->tl_point) 
    );
​
    // x方向和y方向的偏移量,用于字符定位。
    uint8_t offseterx = 0;
    uint8_t offsetery = 0;
​
    // 遍历文本中的每个字符。
    for(uint8_t i = 0; item->sources_borrowed[i] != '\0'; i++) {
        // 计算当前字符的绘制位置。
        draw_tl_point.x = item->indexed_point.x + offseterx * font_width;
        draw_tl_point.y = item->indexed_point.y + offsetery * font_height;
​
        // 设置图像的绘制点。
        handle_draw_image.point = draw_tl_point;
​
        // 绘制当前字符。
        __pvt_draw_char_each(
            device_handle, 
            &handle_draw_image, 
            item->sources_borrowed[i], 
            item->font_size
        );
​
        // 判断是否需要换行绘制。
        if(__pvt_should_be_next_line(device_handle, &br, &draw_tl_point, font_size)) {
            offseterx = 0; // x方向偏移归零
            offsetery++;   // y方向增加一行
            item->indexed_point.x = item->tl_point.x; // 重置x起点
        } else {
            offseterx++; // 继续绘制当前行的下一个字符
        }
    }
​
    // 更新文本项的索引点为最后一个字符位置。
    item->indexed_point = draw_tl_point;
    item->indexed_point.x += font_width;
​
    // 根据文本追加方式调整返回的最终坐标点。
    switch(method) {
        case CCGraphic_AsciiTextItem_AppendNextLine:
            // 追加到下一行开始位置。
            draw_tl_point.x = item->tl_point.x;
            draw_tl_point.y += font_height;
            break;
        case CCGraphic_AsciiTextItem_AppendContinously:
            // 继续追加到同一行的下一个位置。
            draw_tl_point.x += font_width;
            break;
        default:
            break;
    }
​
    // 返回绘制完成后的坐标点。
    return draw_tl_point;
}
​
​
/**
 * 获取当前文本项的附加点(追加位置)。
 * @param item ASCII文本项。
 * @return 当前索引位置坐标点。
 */
CCGraphic_Point CCGraphicWidget_AsciiTextItem_on_append_point(CCGraphic_AsciiTextItem* item)
{
    return item->indexed_point;
}
​
/**
 * 获取文本项换行后的新行起点。
 * @param item ASCII文本项。
 * @return 新行的起始坐标点。
 */
CCGraphic_Point CCGraphicWidget_AsciiTextItem_on_newLine_point(CCGraphic_AsciiTextItem* item)
{
    CCGraphic_Point draw_tl_point;
    draw_tl_point.x = item->tl_point.x;
    const CCGraphic_Size size = __fetch_font_size(item->font_size);
    draw_tl_point.y = item->indexed_point.y + size.height;
    return draw_tl_point;    
}

为什么要给函数标记为inline

对于现代的编译器,inline只是起到了一种劝说的作用,他将调用转换为直接插入函数的汇编代码,节约了流水线刷新和代码跳转,这样来看,是一个不错的关键字,但是,一个过于庞大的函数标记为inline是一个无效的举措(几乎没有节约开销,所以编译器有的时候不会理睬,对于GCC,尝试使用force_inline标记符强制内联),现代的inline更加像是一种允许重复定义的关键字(因为他直接将汇编代码插入到了调用者上,符号直接被替换消失了)

字体附录

或者,你可以访问Github地址:MCU_Libs/OLED/library/Graphic/resources/default at main · Charliechen114514/MCU_Libs (github.com)

ascii 6x8字体

#include "Graphic/CCGraphic_common.h"
#include "Graphic/config/CCGraphic_config.h"
//  This is an array of font data for a 
//  6x8 OLED display using 6x8 pixel font representation.
//  Each character in this font set is defined by an 
//  6x8 pixel matrix (8 pixels wide, 16 pixels high).
​
/* 
    sources should be externed copy this for 
    the usage in application level
*/
​
// ---------------------------------------------
// extern const uint8_t ascii6x8_sources[][6];
// ---------------------------------------------
#if ENABLE_ASCII_6x8_SOURCES
const uint8_t ascii6x8_sources[][6] = 
{
    {0x00,0x00,0x00,0x00,0x00,0x00}, // 0
    {0x00,0x00,0x00,0x2F,0x00,0x00}, // ! 1
    {0x00,0x00,0x07,0x00,0x07,0x00}, // " 2
    {0x00,0x14,0x7F,0x14,0x7F,0x14}, // # 3
    {0x00,0x24,0x2A,0x7F,0x2A,0x12}, // $ 4
    {0x00,0x23,0x13,0x08,0x64,0x62}, // % 5
    {0x00,0x36,0x49,0x55,0x22,0x50}, // & 6
    {0x00,0x00,0x00,0x07,0x00,0x00}, // ' 7
    {0x00,0x00,0x1C,0x22,0x41,0x00}, // ( 8
    {0x00,0x00,0x41,0x22,0x1C,0x00}, // ) 9
    {0x00,0x14,0x08,0x3E,0x08,0x14}, // * 10
    {0x00,0x08,0x08,0x3E,0x08,0x08}, // + 11
    {0x00,0x00,0x00,0xA0,0x60,0x00}, // , 12
    {0x00,0x08,0x08,0x08,0x08,0x08}, // - 13
    {0x00,0x00,0x60,0x60,0x00,0x00}, // . 14
    {0x00,0x20,0x10,0x08,0x04,0x02}, // / 15
    {0x00,0x3E,0x51,0x49,0x45,0x3E}, // 0 16
    {0x00,0x00,0x42,0x7F,0x40,0x00}, // 1 17
    {0x00,0x42,0x61,0x51,0x49,0x46}, // 2 18
    {0x00,0x21,0x41,0x45,0x4B,0x31}, // 3 19
    {0x00,0x18,0x14,0x12,0x7F,0x10}, // 4 20
    {0x00,0x27,0x45,0x45,0x45,0x39}, // 5 21
    {0x00,0x3C,0x4A,0x49,0x49,0x30}, // 6 22
    {0x00,0x01,0x71,0x09,0x05,0x03}, // 7 23
    {0x00,0x36,0x49,0x49,0x49,0x36}, // 8 24
    {0x00,0x06,0x49,0x49,0x29,0x1E}, // 9 25
    {0x00,0x00,0x36,0x36,0x00,0x00}, // : 26
    {0x00,0x00,0x56,0x36,0x00,0x00}, // ; 27
    {0x00,0x08,0x14,0x22,0x41,0x00}, // < 28
    {0x00,0x14,0x14,0x14,0x14,0x14}, // = 29
    {0x00,0x00,0x41,0x22,0x14,0x08}, // > 30
    {0x00,0x02,0x01,0x51,0x09,0x06}, // ? 31
    {0x00,0x3E,0x49,0x55,0x59,0x2E}, // @ 32
    {0x00,0x7C,0x12,0x11,0x12,0x7C}, // A 33
    {0x00,0x7F,0x49,0x49,0x49,0x36}, // B 34
    {0x00,0x3E,0x41,0x41,0x41,0x22}, // C 35
    {0x00,0x7F,0x41,0x41,0x22,0x1C}, // D 36
    {0x00,0x7F,0x49,0x49,0x49,0x41}, // E 37
    {0x00,0x7F,0x09,0x09,0x09,0x01}, // F 38
    {0x00,0x3E,0x41,0x49,0x49,0x7A}, // G 39
    {0x00,0x7F,0x08,0x08,0x08,0x7F}, // H 40
    {0x00,0x00,0x41,0x7F,0x41,0x00}, // I 41
    {0x00,0x20,0x40,0x41,0x3F,0x01}, // J 42
    {0x00,0x7F,0x08,0x14,0x22,0x41}, // K 43
    {0x00,0x7F,0x40,0x40,0x40,0x40}, // L 44
    {0x00,0x7F,0x02,0x0C,0x02,0x7F}, // M 45
    {0x00,0x7F,0x04,0x08,0x10,0x7F}, // N 46
    {0x00,0x3E,0x41,0x41,0x41,0x3E}, // O 47
    {0x00,0x7F,0x09,0x09,0x09,0x06}, // P 48
    {0x00,0x3E,0x41,0x51,0x21,0x5E}, // Q 49
    {0x00,0x7F,0x09,0x19,0x29,0x46}, // R 50
    {0x00,0x46,0x49,0x49,0x49,0x31}, // S 51
    {0x00,0x01,0x01,0x7F,0x01,0x01}, // T 52
    {0x00,0x3F,0x40,0x40,0x40,0x3F}, // U 53
    {0x00,0x1F,0x20,0x40,0x20,0x1F}, // V 54
    {0x00,0x3F,0x40,0x38,0x40,0x3F}, // W 55
    {0x00,0x63,0x14,0x08,0x14,0x63}, // X 56
    {0x00,0x07,0x08,0x70,0x08,0x07}, // Y 57
    {0x00,0x61,0x51,0x49,0x45,0x43}, // Z 58
    {0x00,0x00,0x7F,0x41,0x41,0x00}, // [ 59
    {0x00,0x02,0x04,0x08,0x10,0x20}, // \ 60
    {0x00,0x00,0x41,0x41,0x7F,0x00}, // ] 61
    {0x00,0x04,0x02,0x01,0x02,0x04}, // ^ 62
    {0x00,0x40,0x40,0x40,0x40,0x40}, // _ 63
    {0x00,0x00,0x01,0x02,0x04,0x00}, // ` 64
    {0x00,0x20,0x54,0x54,0x54,0x78}, // a 65
    {0x00,0x7F,0x48,0x44,0x44,0x38}, // b 66
    {0x00,0x38,0x44,0x44,0x44,0x20}, // c 67
    {0x00,0x38,0x44,0x44,0x48,0x7F}, // d 68
    {0x00,0x38,0x54,0x54,0x54,0x18}, // e 69
    {0x00,0x08,0x7E,0x09,0x01,0x02}, // f 70
    {0x00,0x18,0xA4,0xA4,0xA4,0x7C}, // g 71
    {0x00,0x7F,0x08,0x04,0x04,0x78}, // h 72
    {0x00,0x00,0x44,0x7D,0x40,0x00}, // i 73
    {0x00,0x40,0x80,0x84,0x7D,0x00}, // j 74
    {0x00,0x7F,0x10,0x28,0x44,0x00}, // k 75
    {0x00,0x00,0x41,0x7F,0x40,0x00}, // l 76
    {0x00,0x7C,0x04,0x18,0x04,0x78}, // m 77
    {0x00,0x7C,0x08,0x04,0x04,0x78}, // n 78
    {0x00,0x38,0x44,0x44,0x44,0x38}, // o 79
    {0x00,0xFC,0x24,0x24,0x24,0x18}, // p 80
    {0x00,0x18,0x24,0x24,0x18,0xFC}, // q 81
    {0x00,0x7C,0x08,0x04,0x04,0x08}, // r 82
    {0x00,0x48,0x54,0x54,0x54,0x20}, // s 83
    {0x00,0x04,0x3F,0x44,0x40,0x20}, // t 84
    {0x00,0x3C,0x40,0x40,0x20,0x7C}, // u 85
    {0x00,0x1C,0x20,0x40,0x20,0x1C}, // v 86
    {0x00,0x3C,0x40,0x30,0x40,0x3C}, // w 87
    {0x00,0x44,0x28,0x10,0x28,0x44}, // x 88
    {0x00,0x1C,0xA0,0xA0,0xA0,0x7C}, // y 89
    {0x00,0x44,0x64,0x54,0x4C,0x44}, // z 90
    {0x00,0x00,0x08,0x7F,0x41,0x00}, // { 91
    {0x00,0x00,0x00,0x7F,0x00,0x00}, // | 92
    {0x00,0x00,0x41,0x7F,0x08,0x00}, // } 93
    {0x00,0x08,0x04,0x08,0x10,0x08}, // ~ 94
};
#endif

ascii 8 x 16字体

#include "Graphic/CCGraphic_common.h"
#include "Graphic/config/CCGraphic_config.h"
//  This is an array of font data for a 
//  8x16 OLED display using 8x16 pixel font representation.
//  Each character in this font set is defined by an 
//  8x16 pixel matrix (8 pixels wide, 16 pixels high).
​
/* 
    sources should be externed copy this for 
    the usage in application level
*/
​
// ---------------------------------------------
// extern const uint8_t ascii8x16_sources[][16];
// ---------------------------------------------
#if ENABLE_ASCII_8x16_SOURCES
const uint8_t ascii8x16_sources[][16] =
{
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//   0
    {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},// ! 1
    {0x00,0x16,0x0E,0x00,0x16,0x0E,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// " 2
    {0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,
    0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00},// # 3
    {0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,
    0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00},// $ 4
    {0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,
    0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00},// % 5
    {0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,
    0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10},// & 6
    {0x00,0x00,0x00,0x16,0x0E,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// ' 7
    {0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,
    0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},// ( 8
    {0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,
    0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},// ) 9
    {0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,
    0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},// * 10
    {0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,
    0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00},// + 11
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0xB0,0x70,0x00,0x00,0x00,0x00,0x00},// , 12
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01},// - 13
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},// . 14
    {0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,
    0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00},// / 15
    {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
    0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},// 0 16
    {0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,
    0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},// 1 17
    {0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,
    0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},// 2 18
    {0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,
    0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},// 3 19
    {0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,
    0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},// 4 20
    {0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,
    0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},// 5 21
    {0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,
    0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},// 6 22
    {0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,
    0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},// 7 23
    {0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,
    0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},// 8 24
    {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
    0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00},// 9 25
    {0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
    0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},// : 26
    {0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
    0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00},// ; 27
    {0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,
    0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},// < 28
    {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,
    0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00},// = 29
    {0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
    0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},// > 30
    {0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,
    0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00},// ? 31
    {0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,
    0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00},// @ 32
    {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,
    0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},// A 33
    {0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,
    0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},// B 34
    {0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,
    0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},// C 35
    {0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,
    0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},// D 36
    {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
    0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},// E 37
    {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
    0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},// F 38
    {0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,
    0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},// G 39
    {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
    0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},// H 40
    {0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,
    0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},// I 41
    {0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,
    0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},// J 42
    {0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,
    0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},// K 43
    {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,
    0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},// L 44
    {0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,
    0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00},// M 45
    {0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,
    0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},// N 46
    {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
    0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},// O 47
    {0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,
    0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},// P 48
    {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
    0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00},// Q 49
    {0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,
    0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},// R 50
    {0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,
    0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},// S 51
    {0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,
    0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},// T 52
    {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
    0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},// U 53
    {0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,
    0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},// V 54
    {0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,
    0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00},// W 55
    {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,
    0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},// X 56
    {0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,
    0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},// Y 57
    {0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,
    0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},// Z 58
    {0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,
    0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},// [ 59
    {0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},// \ 60
    {0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,
    0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},// ] 61
    {0x00,0x20,0x10,0x08,0x04,0x08,0x10,0x20,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// ^ 62
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},// _ 63
    {0x00,0x02,0x04,0x08,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// ` 64
    {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
    0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20},// a 65
    {0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,
    0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},// b 66
    {0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,
    0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},// c 67
    {0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,
    0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20},// d 68
    {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
    0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00},// e 69
    {0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,
    0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},// f 70
    {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
    0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},// g 71
    {0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,
    0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},// h 72
    {0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,
    0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},// i 73
    {0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,
    0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},// j 74
    {0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,
    0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00},// k 75
    {0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,
    0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},// l 76
    {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
    0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},// m 77
    {0x00,0x80,0x80,0x00,0x80,0x80,0x00,0x00,
    0x00,0x20,0x3F,0x21,0x00,0x20,0x3F,0x20},// n 78
    {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
    0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},// o 79
    {0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,
    0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00},// p 80
    {0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,
    0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80},// q 81
    {0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
    0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},// r 82
    {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
    0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},// s 83
    {0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,
    0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00},// t 84
    {0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,
    0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},// u 85
    {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
    0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00},// v 86
    {0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,
    0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00},// w 87
    {0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
    0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00},// x 88
    {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
    0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00},// y 89
    {0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
    0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},// z 90
    {0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,
    0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40},// { 91
    {0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},// | 92
    {0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,
    0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00},// } 93
    {0x00,0x80,0x40,0x40,0x80,0x00,0x00,0x80,
    0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00},// ~ 94
};
#endif

目录导览

总览

协议层封装

OLED设备封装

绘图设备抽象

基础图形库封装

基础组件实现

动态菜单组件实现


http://www.kler.cn/a/527514.html

相关文章:

  • C++ 堆栈分配的区别
  • 列表(列表是什么)
  • C# 9.0记录类型:解锁开发效率的魔法密码
  • 网站快速收录:利用新闻源的优势
  • 从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(动态菜单组件实现)
  • 回顾:Maven的环境搭建
  • 51单片机开发——I2C通信接口
  • Keepalived高可用集群企业应用实例二
  • Qt事件处理:理解处理器、过滤器与事件系统
  • 【机器学习】Google开源大模型Gemma2:原理、微调训练及推理部署实战
  • R 字符串:深入理解与高效应用
  • 推荐一款好用的翻译类浏览器扩展插件
  • 11.QT控件:输入类控件
  • 实验八 JSP访问数据库
  • 【llm对话系统】大模型 Llama 源码分析之并行训练方案
  • 各种CNN 卷积特征图可视化理解方法(链接)
  • 网站标签页图标如何添加
  • SpringBoot 数据访问(MyBatis)
  • Java实战:图像浏览器
  • (三)QT——信号与槽机制——计数器程序
  • 51单片机CLD1602显示万年历+闹钟+农历+整点报时
  • 【B站保姆级视频教程:Jetson配置YOLOv11环境(五)Miniconda安装与配置】
  • 大厂面试题备份20250130
  • 【deepseek-r1】ollama如何更改安装位置以及自定义模型下载位置
  • Flink Forward Asia 2024 - 总结和展望(附PPT下载链接)
  • 游戏引擎 Unity - Unity 下载与安装