高级会员

- 积分
- 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显示的稳定性和准确性。希望上述优化建议能够帮助你解决驱动中的问题,顺利实现字符串的正确显示。
|
|