myh007
发表于 2025-7-16 07:17:34
ok
大明狐
发表于 2025-7-19 17:08:38
mechray 发表于 2025-7-8 21:09
1.ssd1306的寻址方式分为页寻址、行寻址和列寻址,由寄存器22设定,默认为页寻址。
2.根据官方例程的初始 ...
这里面仍然有几个误解
首先,设置显示的起始位置的指令,
page用(0xB0+page号)来设置
列用(0x00+列地址的低四位)和(0x10+列地址的高四位)来设置
这一点是没错的。
不论用的是水平地址模式、页地址模式、还是垂直地址模式,0xB0、0x00、0x01都是有效的。
====================================
而你提到的0x22,则是另外作用的指令。
在常见的SSD1306、SSD1309、SSD1315这些芯片的屏幕里,有两个独属的“设置显示区域”的指令:0x21和0x22。
它俩都是三字节指令,可以在屏幕上画出一个显示区域进行显示,区域之外不参与显示(相当于把屏幕的128×64的面积改小了)。
格式是:
OLED_WR_Byte( 0x21, CMD ); // 设置列范围
OLED_WR_Byte( 起始列地址, CMD ); // 范围 0~127
OLED_WR_Byte( 结束列地址, CMD ); // 范围 0~127
OLED_WR_Byte( 0x22, CMD ); // 设置page范围
OLED_WR_Byte( 起始page号, CMD ); // 范围 0~7
OLED_WR_Byte( 结束page号, CMD ); // 范围 0~7
当起始值和结束值相同的时候,就是定位具体某一page或者某一列。
如果不进行0x21和0x22的配置,上电默认起始列是0,结束列是127,起始page是0,结束page是7,也就是全屏。
设置了区域之后,发送的数据会在这个区域里进行显示,三种地址模式均有效。
关于你说配置了0x21和0x22就不用0xB0、0x00、0x01,如果你的显示程序能始终保持全屏操作,或者整行操作,那么没问题。
但是0x22和0xB0一起用,肯定会出问题,因为用过0x22之后,显示区域的0点位置就已经变了,0xB0是按照新的0点进行定位的。
如果显示局部内容,比如一个字符,也可以用这两个指令,画出字符大小的区域,然后发送字符数据。
而0xB0、0x00、0x01在这个新的区域里仍然有效。
之所以说“专属”是因为SH1106之类屏幕不支持这个指令,只支持0xB0、0x00、0x01。
所以,这是两组不同功能的指令,不能混为一谈。
你用0x22重新定义了起始page,原先page里的内容仍然在屏幕芯片的显存里,所以仍然可以保持显示。
这一点上不能说你的用法是错误的,都能得到想要的显示结果,只能说是把两种定位方法混用了,多走了个曲线达到目的地。
比如
OLED_WR_Byte( 0xB3, CMD ); // 设置起始行
和
OLED_WR_Byte( 0x22, CMD ); // 设置page范围
OLED_WR_Byte( 0x03, CMD ); // 范围 0~7
OLED_WR_Byte( 0x03, CMD ); // 范围 0~7
OLED_WR_Byte( 0xB0, CMD ); // 设置起始行
定位效果是一样的,都是定位到了屏幕的第四个page上,只不过后者只能在第四行里进行显示,而前者如果在水平地址模式下,还可以继续向下面几行显示。
如果常用TFT彩屏,就会发现,TFT彩屏没有具体定位某个点的指令,用的就是类似0x21、0x22这种设置区域的方式。
================================================
下面是之前学习这两个指令的时候的笔记视频
849
以及设置区域之后,在水平地址模式下用DMA连续发送数据的效果的视频
850
===========================================
关于例程使用的时钟频率,因为整个程序本身就是按照24MHz进行配置的,还关系到很多其他功能,而且比24M所以略高一点可能不会有问题。但是硬件I2C最好还是根据实际需要进行配置。
前面我给的那个硬件I2C和DMA的视频连接,就是从0开始配置的过程。视频里用STC8H8K64U和AI8051U两种芯片,都按照40MHz进行配置的,都可以顺利运行。
分频数的问题,只要配置正确,对于SSD1306的屏幕,分频数用2也能正常显示,而SH1106之类屏幕,以及AT24C之类存储芯片,则必须用3以上才可以。
这一点在硬件I2C点亮OLED的帖子里也有提到:https://www.stcaimcu.com/thread-18224-1-1.html
===================================================
例程里地址模式的注释写错这一点,因为之前跟中景园版的驱动作者聊过,他自己说的写的时候就很随意,甚至还有一些笔误。比如显示汉字的函数OLED_ShowCHinese,H大写就是手误,不过后来就这么发布了,无伤大雅。
但是那个试验箱例程里,确实应该更严谨一些。因为错误的注释,会在参考学习的时候造成困扰。
所以我的视频里,是对每条指令都写了中文的注释,以及汉化了数据手册里所有指令部分。重写注释的过程也是学习的过程。
汉化过程中还发现,就连SSD1306的原版数据手册里,都有一些错误。
神农鼎
发表于 2025-7-20 08:14:28
https://www.bilibili.com/video/BV1vsutzGERV
神农鼎
发表于 2025-7-20 08:15:47
https://www.bilibili.com/video/BV1GSu4zTErB
神农鼎
发表于 2025-7-20 08:17:52
https://www.bilibili.com/video/BV1GSu4zTErB
神农鼎
发表于 2025-7-20 08:19:05
https://www.bilibili.com/video/BV1XdN4ztEvp
神农鼎
发表于 2025-7-20 08:20:44
https://www.bilibili.com/video/BV1tXNCzeEUx
神农鼎
发表于 2025-7-20 08:22:04
https://www.bilibili.com/video/BV169K8zJE1t
mechray
发表于 2025-7-22 13:11:14
大明狐 发表于 2025-7-19 17:08
这里面仍然有几个误解
感谢大佬的详细解释,我也看了讲解视频,非常精彩。
1.关于0xb0命令在水平地址模式下的作用,我在水平地址下逐一调试0xb0-0xb7,发现没有任何变化。而0x21和0x22设置显示范围是能达到预期显示效果的。所以我仍然比较倾向于相信数据手册的说法,即0xb0仅针对页地址模式使用。不知大佬是否实际验证过水平地址模式下调整0xb0命令是否有效。
2. 关于传输速度,根据数据手册,ssd1306时钟周期最小需要2.5us,即最高速度对应的是i2c的高速模式400kbit/s。所以即使cpu时钟为24mhz,AIcube下设置分频2或3的情况下,速度也是超过的。但24mhz下,居然能正常显示。另外,如果是40mhz,即使通过分频将i2c速度降到400kbit/s以下,仍然不能正常显示。这是我还不解的地方。
大明狐
发表于 2025-7-22 19:15:08
mechray 发表于 2025-7-22 13:11
感谢大佬的详细解释,我也看了讲解视频,非常精彩。
1.关于0xb0命令在水平地址模式下的作用,我在水平地 ...
1. 关于0xB0指令,虽然SSD1306、SSD1309和SSD1315的数据手册里都说的是“Under Page Address Mode”,但是实际上在任何模式下都是有用的。
网上所有的例程也都使用0xB0进行定位的,因为都没有用0x21和0x22重设范围,默认都是128×64的范围。
比如为了兼容SSD和SH系列的屏幕,清屏函数会用0xB0~0xB7的方式,分八次给每一行填充128个0;
再比如显示字符和图片的函数,高度大于一行的,也都是用“0xB0+行号”的方式,在每一行里写入对应的数据。
为了省事儿,我的所有程序都是默认用水平地址模式,都能正常定位。
https://www.stcaimcu.com/thread-6840-1-1.html
=======================================================
下面是刚刚重建的程序。
初始化函数里,配置的是水平地址模式
然后直接用指令方式显示内容
用“0xB0+行号”的方式定位要显示的行,然后发送要显示的内容数据。
显示结果一切顺利
====================================================
2. 关于速度问题,数据手册上的只是理论值,就像上次说的,手册里的数据也不都准确,表格里的参数仅仅只是个参考。
还是上面那个程序,用的是模拟I2C。发送间隔只用了两个nop。
单片机是AI8051U,40MHz运行。两个nop,加上调用延时函数,还有IO口的电平转换操作消耗的时钟,肯定也远小于表格里的2.5μs,结果就是上面照片里的依旧可以正常显示。
============================================================
所以光看手册没啥用,不如把你的代码发出来看一下,是不是代码里的原因。