OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 697|回复: 2

中景园0.96oled驱动问题,主要是显示字符串的参数问题上

[复制链接]

54

主题

250

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1012
金钱
1012
注册时间
2017-8-19
在线时间
162 小时
发表于 2026-9-9 11:36:03 | 显示全部楼层 |阅读模式
1金钱
//画点
//x:0~127
//y:0~63
void OLED_DrawPoint(u8 x,u8 y)
{
        u8 page,hang,n;
        page=y/8;                //确定第几页
        hang=y%8;                //确定第几行       
        //页地址确定、hang和x就可以一起确定是哪个点
       
        //剩下的就是把数据写到该点上
        n=1<<hang;                        //1左移hang位
        OLED_GRAM[x]

回复

使用道具 举报

54

主题

250

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1012
金钱
1012
注册时间
2017-8-19
在线时间
162 小时
 楼主| 发表于 2026-9-9 11:37:58 | 显示全部楼层
我的理解void OLED_DrawPoint(u8 x,u8 y)和void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1)的形参y,都是指y坐标。

但是为什么void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1)的形参y,反而是页?如果是y,那他这个函数明显不对,如果是y坐标,那么y+2这个语句又不对。但是这个函数又是正常运行的,不知道是什么情况
回复

使用道具 举报

0

主题

157

帖子

0

精华

高级会员

Rank: 4

积分
738
金钱
738
注册时间
2026-1-29
在线时间
58 小时
发表于 2026-9-11 13:33:21 | 显示全部楼层
你提供的中景园0.96英寸OLED驱动代码总体上是正确的,但在显示字符串(OLED_ShowString)和字符(OLED_ShowChar)的实现中存在一些问题,特别是在 坐标计算 和 换行逻辑 方面。下面我将详细分析这些问题,并提供相应的解决方案和优化建议。

一、关键问题分析
1. OLED_ShowChar 函数的坐标计算问题
问题描述:

宽度与高度不匹配:在 OLED_ShowChar 中,字体的宽度和高度计算存在不一致。
坐标增量错误:在逐列绘制字符时,x 和 y 的增加方式可能导致显示重叠或偏移。
具体问题点:

字体宽度计算:

size2 = (size1/8 + ((size1%8)?1:0)) * (size1/2);

      
这个公式对于固定宽度的字体可能适用,但对于不同比例的字体(如12x6、16x8、24x12)处理不够灵活,尤其是高度不为字体宽度一半的情况。

列与行的遍历:

for(m=0; m<8; m++) {
    if(temp & 0x80) {
        OLED_DrawPoint(x, y);
    } else {
        OLED_ClearPoint(x, y);
    }
    temp <<= 1;
    y++;
    if((y - y0) == size1) {
        y = y0;
        x++;
        break;
    }
}

      
这里假设每个字符的高度是 size1,而每次内循环固定移动 y 8次,当 size1 不为8的倍数时,会导致坐标计算错误,例如:

当 size1 = 12:y 每次增加1,经过8次后,y - y0 = 8,与 size1 = 12 不匹配,导致坐标错误,未能正确绘制字符。
2. OLED_ShowString 函数的换行逻辑问题
问题描述:

换行处理不准确:在字符显示超出屏幕宽度时,y += 2 的换行方式不正确,无法适配不同字体高度,容易导致字符覆盖或显示混乱。
边界检测不完善:x > 128 - size1 的条件计算可能不准确,尤其在不同字体宽度下。
具体问题点:

if(x > 128 - size1) {
    x = 0;
    y += 2;
}

      
字体宽度未准确反映:size1 是字体高度,而非宽度,直接用 size1 来判断水平边界不准确。
换行步长错误:换行时直接 y += 2,无法适配不同字体高度(如12、16、24),应为字体高度的某个倍数。
二、解决方案与优化建议
1. 明确字体的宽度和高度
在字符绘制中,字体的 宽度 和 高度 需要明确区分,避免混淆。通常,对于逐列取模的字符,字体的 宽度 是固定的,而 高度 是变量。以常见的12x6、16x8、24x12为例:

字体大小 (size1)        宽度 (x 方向)        高度 (y 方向)
12        6        12
16        8        16
24        12        24
因此,应该根据 size1 计算出对应的宽度和高度,并在字符绘制和字符串显示中正确使用这些值。

2. 修改 OLED_ShowChar 函数
调整字体宽度和高度的计算,并正确遍历每一列和每一行。

优化后的 OLED_ShowChar 函数:

// 在指定位置显示一个字符,包括部分字符
// x: 0~127
// y: 0~63
// chr: 要显示的字符
// size1: 字体高度 (12/16/24)
void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 size1)
{
    u8 i, m, temp, size2, chr1;
    u8 width, height;
   
    // 根据字体高度,计算字体的宽度
    switch(size1)
    {
        case 12:
            width = 6;
            height = 12;
            break;
        case 16:
            width = 8;
            height = 16;
            break;
        case 24:
            width = 12;
            height = 24;
            break;
        default:
            width = 8; // 默认情况,适用16x8
            height = 16;
            break;
    }
   
    size2 = ((width + 7) / 8) * height; // 计算一个字符的点阵所占字节数
    chr1 = chr - ' '; // 计算偏移后的值(ASCII 表从 ' ' 开始)
   
    for(i = 0; i < size2; i++)
    {
        temp = asc2_1608[chr1][i]; // 获取一个字节的点阵数据
        for(m = 0; m < 8; m++) // 遍历8位
        {
            if(temp & 0x80)
                OLED_DrawPoint(x, y); // 点亮
            else
                OLED_ClearPoint(x, y); // 清除
            temp <<= 1;
            y++;
            if(y >= y_start + height) // 当前列的字符高度已满
            {
                y = y_start; // 重置 y
                x++; // 移动到下一列
                break;
            }
        }
        if(x >= x_start + width) // 当前字符的宽度已满
            break;
    }
}

      
注意:

y_start 和 x_start:需要在函数开始时保存传入的 x 和 y 作为起始坐标,用于后续的坐标重置。
边界检查:在完成字符宽度后及时跳出循环,避免超出预期区域。
3. 修改 OLED_ShowString 函数
调整字符串显示中的坐标计算和换行逻辑,使其适应不同字体的宽度和高度。

优化后的 OLED_ShowString 函数:

// 显示字符串
// x, y: 起点坐标
// size1: 字体高度 (12/16/24)
// *chr: 字符串起始地址
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 size1)
{
    u8 width, height;
   
    // 根据字体高度,计算字体的宽度和高度
    switch(size1)
    {
        case 12:
            width = 6;
            height = 12;
            break;
        case 16:
            width = 8;
            height = 16;
            break;
        case 24:
            width = 12;
            height = 24;
            break;
        default:
            width = 8; // 默认情况,适用16x8
            height = 16;
            break;
    }

    while((*chr >= ' ') && (*chr <= '~')) // 判断是否是有效字符
    {
        OLED_ShowChar(x, y, *chr, size1); // 显示字符
        x += width; // 下一个字符的起始 x 坐标
        if(x + width > 128) // 超出屏幕宽度
        {
            x = 0; // 换行,x 置为起点
            y += height; // y 增加一个字符的高度
            if(y + height > 64) // 超出屏幕高度
            {
                y = 0; // 超过后重置 y
                break; // 停止显示,避免溢出
            }
        }
        chr++; // 指向下一个字符
    }
}

      
优化点说明:

字体宽度计算:根据 size1 确定字体的 width 和 height,并用于字符间距和换行计算。
换行逻辑:当 x + width > 128 时,换行并 y += height,确保每行的高度足够容纳字符。
边界检测:在 y 超出屏幕高度时,停止显示,避免覆盖或溢出。
4. 修改 OLED_DrawPoint 函数
确保点绘制和清除函数 OLED_DrawPoint 和 OLED_ClearPoint 正确操作显存 OLED_GRAM。

建议检查 OLED_ClearPoint 函数:

// 清除点
void OLED_ClearPoint(u8 x, u8 y)
{
    u8 page, hang, n;
    page = y / 8;        // 确定第几页
    hang = y % 8;        // 确定第几行

    n = 1 << hang;       // 1左移hang位
    OLED_GRAM[x] &= ~n;  // 清除数据
    // 看情况是否使用refresh函数
    // OLED_Refresh();
}

      
确保 OLED_ClearPoint 操作与 OLED_DrawPoint 对称,正确清除对应的点。

三、整体优化后的代码整合
结合以上优化建议,以下是整合后的代码:

// 画点
// x: 0~127
// y: 0~63
void OLED_DrawPoint(u8 x, u8 y)
{
    u8 page, hang, n;
    page = y / 8;            // 确定第几页
    hang = y % 8;            // 确定第几行

    n = 1 << hang;
    OLED_GRAM[x] |= n;       // 写入数据
    // OLED_Refresh();
}

// 清除点
// x: 0~127
// y: 0~63
void OLED_ClearPoint(u8 x, u8 y)
{
    u8 page, hang, n;
    page = y / 8;            // 确定第几页
    hang = y % 8;            // 确定第几行

    n = 1 << hang;
    OLED_GRAM[x] &= ~n;      // 清除数据
    // OLED_Refresh();
}

// 在指定位置显示一个字符,包括部分字符
// x: 0~127
// y: 0~63
// chr: 要显示的字符
// size1: 字体高度 (12/16/24)
void OLED_ShowChar(u8 x_start, u8 y_start, u8 chr, u8 size1)
{
    u8 i, m, temp, size2, chr1;
    u8 x = x_start, y = y_start;
    u8 width, height;
   
    // 根据字体高度,计算字体的宽度和高度
    switch(size1)
    {
        case 12:
            width = 6;
            height = 12;
            break;
        case 16:
            width = 8;
            height = 16;
            break;
        case 24:
            width = 12;
            height = 24;
            break;
        default:
            width = 8; // 默认情况,适用16x8
            height = 16;
            break;
    }
   
    size2 = ((width + 7) / 8) * height; // 计算一个字符的点阵所占字节数
    chr1 = chr - ' '; // 计算偏移后的值
   
    for(i = 0; i < size2; i++)
    {
        temp = asc2_1608[chr1][i]; // 获取一个字节的点阵数据
        for(m = 0; m < 8; m++) // 遍历8位
        {
            if(temp & 0x80)
                OLED_DrawPoint(x, y); // 点亮
            else
                OLED_ClearPoint(x, y); // 清除
            temp <<= 1;
            y++;
            if(y >= y_start + height) // 当前列的字符高度已满
            {
                y = y_start; // 重置 y
                x++; // 移动到下一列
                break;
            }
        }
        if(x >= x_start + width) // 当前字符的宽度已满
            break;
    }
}

// 显示字符串
// x, y: 起点坐标
// size1: 字体高度 (12/16/24)
// *chr: 字符串起始地址
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 size1)
{
    u8 width, height;
   
    // 根据字体高度,计算字体的宽度和高度
    switch(size1)
    {
        case 12:
            width = 6;
            height = 12;
            break;
        case 16:
            width = 8;
            height = 16;
            break;
        case 24:
            width = 12;
            height = 24;
            break;
        default:
            width = 8; // 默认情况,适用16x8
            height = 16;
            break;
    }

    while((*chr >= ' ') && (*chr <= '~')) // 判断是否是有效字符
    {
        OLED_ShowChar(x, y, *chr, size1); // 显示字符
        x += width; // 下一个字符的起始 x 坐标
        if(x + width > 128) // 超出屏幕宽度
        {
            x = 0; // 换行,x 置为起点
            y += height; // y 增加一个字符的高度
            if(y + height > 64) // 超出屏幕高度
            {
                y = 0; // 超过后重置 y
                break; // 停止显示,避免溢出
            }
        }
        chr++; // 指向下一个字符
    }
}

      
四、其他建议
使用标准字符集:
确保 asc2_1608 数组中的字体数据与实际字体大小匹配,并根据字体大小正确选择不同的字体数组。例如,如果字体高度为24,应有对应的24x12字体数据。

显存刷新:
在完成字符或字符串绘制后,调用 OLED_Refresh() 函数将显存内容刷新到OLED屏幕上,确保显示内容实时更新。

参数验证:
在函数开始处添加参数边界检查,防止传入的 x 和 y 超出OLED屏幕范围,提升代码的健壮性。

调试手段:
添加简单的调试接口,如显示特定坐标的像素状态,方便排查显示问题。

优化性能:
对于高频刷新场景,优化显存更新策略,减少不必要的刷新次数,提升显示性能。

五、总结
通过明确字体的宽度和高度,修正 OLED_ShowChar 和 OLED_ShowString 函数中的坐标计算和换行逻辑,能够有效解决字符串显示的参数问题。同时,确保字符绘制与显存操作的一致性,提升OLED显示的稳定性和准确性。希望上述优化建议能够帮助你解决驱动中的问题,顺利实现字符串的正确显示。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-9-30 17:11

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表