找回密码
 立即注册
查看: 81|回复: 7

我的SSD1306会隔行扫描

[复制链接]

67

主题

220

回帖

2511

积分

金牌会员

积分
2511
发表于 2026-9-10 22:40:39 | 显示全部楼层 |阅读模式
问题描述:

SSD1306模块,手册上说是128*64分辨率。全屏写入也是写了1024个字节。
在水平寻址模式下,显示16*16大小的字符时,发现垂直方向上有16个列完全没有问题。但是水平方向上就像是隔行扫描,垂直方向上跨了2个page,但是看起来字符就是16*8大小的。可是的确写入了32个字节给SSD1306.那问题处在哪里呢?
请求指导!谢谢先 :-)

回复

使用道具 举报 送花

83

主题

8214

回帖

1万

积分

超级版主

积分
17626
发表于 2026-9-10 23:30:18 | 显示全部楼层
I2C接口的话,速度不要太快,不要超过1MHz。
其余的就是看设置地址是否正确。
你上传一个照片看看。
回复

使用道具 举报 送花

67

主题

220

回帖

2511

积分

金牌会员

积分
2511
 楼主| 发表于 2026-9-10 23:44:01 | 显示全部楼层
梁*** 发表于 2026-9-10 23:30
I2C接口的话,速度不要太快,不要超过1MHz。
其余的就是看设置地址是否正确。
你上传一个照片看看。 ...

感谢大佬的关注!
屏幕先行全屏写入0xFF点亮屏幕。然后再写入大小为16*16的字符“O”。指定显示区域为列地址0x30 ~ 0x3F,page范围为2 ~ 3.
IIC时钟频率 200 kHz。

IMG_20260910_233715.jpg

点评

这个屏不是128x64的,你仔细核对点阵数和地址安排。  详情 回复 发表于 2026-9-11 10:22
回复

使用道具 举报 送花

47

主题

589

回帖

5283

积分

荣誉版主

积分
5283
发表于 2026-9-11 10:06:41 | 显示全部楼层
首先确认一下,用的OLED屏幕是哪个尺寸的?
“手册上说是128*64分辨率”指的是SSD1306控制芯片最大支持128*64点阵分辨率,而实际用在屏幕上时,会根据不同规格的屏幕,使用全部或者一部分。
比如3楼照片里的这种 分辨率是 128*32 点阵的,就只用到了一半的行数。



之所以16*16的字符会显示成照片里那样的16*8,是因为你给128*32的屏幕用了128*64的配置程序

先说一下扫描顺序
OLED模块的厂家通常用的是奇数行和偶数行分开控制的接线方式
也就是将行(ROW)分成了两组:
(偶数行)    ROW0, ROW2, ROW4, …… ROW60, ROW62

(奇数行)    ROW1, ROW3, ROW5, …… ROW61, ROW63


截图202609110917564252.jpg


而你用的这块 128*32点阵 的屏幕模块,内部同样用的是这种接线方式

但是由于只有32行,所以只用到了其中的偶数行那组(ROW0, ROW2, ROW4, …… ROW60, ROW62)
因此,当你用 128*64点阵 的驱动程序进行显示的时候

实际只有偶数行的内容能够显示出来,等于图像被隔行抽走了一半
于是就出现了 16*16 的字符变成了 16*8 的样子
截图202609161716572173.jpg
所以要解决这个问题,只需要对初始化程序进行很简单的一个修改就可以

首先找到初始化函数 OLED_Init 函数,
然后找到控制指令 0xDA (如果没有,就手动添加上)
截图202609110932407461.jpg
指令有两行,第一行 0xDA 是列引脚配置的指令,第二行是它的参数
通过SSD13106的数据手册里的说明,参数的第4位就是奇偶行控制的选择
截图202609110931016699.jpg
如果参数是 0x12(A[4]=1),就是奇偶行都控制,是给 128*64 的屏幕用的;
如果参数是 0x02(A[4]=0),就是只控制偶数行,才是给 128*32 的屏幕用的。

所以,如果有这个设置的指令,只需要按照上面截图修改这一处配置参数就可以了
如果没有这两行指令,必须手动添加上,因为如果不配置,屏幕会使用默认配置 0x12

再举个更直观的例子,如果反过来,给 128*64 的屏幕用了 128*32的驱动
所有的内容就会只是用偶数行进行显示,于是画面就会被拉长,中间还有空行
比如16*16 的字符,就会变成 16*32 的样子了
截图202609110947576143.jpg

===========================================

修改完这个指令之后,还有个地方需要修改,就是对全屏进行操作的函数。
比如清屏函数 OLED_Clear() ,在128*64 的驱动程序里,清屏函数是对八个page(64行
进行操作的,也需要对应修改成四个page(32行

如果清屏函数用的是水平地址模式的方式,连续发送1024个0 进行清屏的,就改成 连续发送512个0

void OLED_Clear(void)
{
    unsigned int i;
    OLED_address( 0, 0 );
    for( i=0; i<1024; i++ )   ←这里需要修改成 512
    {
        OLED_WR_Byte( 0x00, OLED_DATA );
    }
}


如果用的是页地址模式的方式,分八次将每一个page内容清空,就改成分四次

void OLED_Clear(void)
{
    unsigned char i, n;
    for( i=0; i<8; i++ )    ←这里需要修改成 4
    {
        OLED_address( 0, i );
        for( n=0; n<128; n++ )
         {
            OLED_WR_Byte( 0x00, OLED_DATA );
        }
    }
}

修改之后,可以减少单片机的的工作量,不改也没啥,按你说的“全屏写入也是写了1024个字节”,按照水平地址模式,不过只是屏幕每次会被清空两遍(page4~page7会从page0开始重新寻址),浪费算力和发数据的时间。
填充全屏的函数,比如 OLED_Fill( dat ); 亦是如此。


除此之外,如果你的驱动程序使用的是缓存方式进行显示
还需要将缓存数组大小也从1024改成512个字节
    GRAM[512] 或者 GRAM[4][128]
更新显示的函数,跟清屏函数一样,刷新范围也改成四个page,否则超出屏幕的部分(从 page4 开始的内容)会从 page0 开始重新显示,将原本内容覆盖掉


===========================================

另外,初始化函数里还有一处指令 0xA8 ,是设置屏幕行数使用率的,它下面一行的参数如果是 0x3F(十进制63),就是扫描全部64行,如果只有32行的话,可以改成 0x1F (十进制31)。
这一处指令可改可不改,对视觉效果影响不大。建议修改,给SSD1306芯片减点儿工作量。













能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

83

主题

8214

回帖

1万

积分

超级版主

积分
17626
发表于 2026-9-11 10:22:38 | 显示全部楼层
BeHa*** 发表于 2026-9-10 23:44
感谢大佬的关注!
屏幕先行全屏写入0xFF点亮屏幕。然后再写入大小为16*16的字符“O”。指定显示区域为列 ...

这个屏不是128x64的,你仔细核对点阵数和地址安排。
回复

使用道具 举报 送花

67

主题

220

回帖

2511

积分

金牌会员

积分
2511
 楼主| 发表于 2026-9-11 14:27:10 | 显示全部楼层
大*** 发表于 2026-9-11 10:06
首先确认一下,用的OLED屏幕是那个尺寸的?
“手册上说是128*64分辨率”指的是SSD1306控制芯片最大支持128* ...

感谢大佬讲清楚前因后果,娓娓道来,为我点拨迷雾!
实际,这个问题困扰了我至少有2年了,我一直以为这种模块就是这样显示的!
得亏这回问了,得亏机缘巧合得到您的指点!
不然要一直当蒙鼓人了 :-(


改好后屏幕的显示

改好后屏幕的显示

点评

显示正常了就好。。。 另外,ASCII字符可以用8*16点阵进行取模,这样就不会有后面的一个字符的空白了。 也可以再论坛上找现成的OLED驱动程序,里面会有取模好的 8*16 的字库可以直接用。 showchar函数也需要对应修  详情 回复 发表于 2026-9-11 16:41
回复

使用道具 举报 送花

47

主题

589

回帖

5283

积分

荣誉版主

积分
5283
发表于 2026-9-11 16:41:09 | 显示全部楼层
BeHa*** 发表于 2026-9-11 14:27
感谢大佬讲清楚前因后果,娓娓道来,为我点拨迷雾!
实际,这个问题困扰了我至少有2年了,我一直以为这种 ...

显示正常了就好。。。

另外,ASCII字符可以用8*16点阵进行取模,这样就不会有后面的一个字符的空白了。
也可以再论坛上找现成的OLED驱动程序,里面会有取模好的 8*16 的字库可以直接用。
showchar函数也需要对应修改,改成横向读取八个点。
具体改法,因为不知道你用的读取程序是按照竖向取模的列行式还是横向取模的逐行式,所以就需要你自己进行调整了。这个可以参考现成的驱动程序。

STC的取模工具给这种字符取模挺轻松的
截图202609111648334144.jpg
能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

67

主题

220

回帖

2511

积分

金牌会员

积分
2511
 楼主| 发表于 2026-9-11 17:53:29 | 显示全部楼层
大*** 发表于 2026-9-11 16:41
显示正常了就好。。。

另外,ASCII字符可以用8*16点阵进行取模,这样就不会有后面的一个字符的空白了。

大佬眼光犀利!一下子又指出关键问题之一!
按照16*16大小显示会造成一半的空间无有效内容,进而缩小屏幕实际能显示字符的空间。
后续我会改成8*16大小的字符。非常感谢您的指导!
回复

使用道具 举报 送花

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

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-9-19 21:06 , Processed in 0.071760 second(s), 81 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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