今天又做了一些实验,我想我对于0xB0在水平模式下的作用应该是不存在误解了,昨天的推论应该是正确的。为了避免传输速度可能造成显示效果上的影响,我将mcu频率调到了24MHz,并关闭了DMA传输。
我做了一张32X32的图片如下(即行高为4行),初始放在屏幕左上角。接着在刷新函数中添加0xB*命令如下。首先设为0xB4。如果0xB*在水平地址模式下是一个理想的、扮演定位起始点的命令,那么理论上0xB4会使得整个图像从第5行开始写入,依次写满第5-8行,最终效果为图像垂直偏移到屏幕下半部分。
- void OLED_Refresh(void)
- {
- uint8_t cmd[3];
- cmd[0] = 0xB4; //设置页起始地址
- cmd[1] = 0x00; //设置低列起始地址
- cmd[2] = 0x10; //设置高列起始地址
- OLED_WR_CMD(cmd, 3);
- OLED_WR_DAT(OledCache, 1024);
- }
复制代码
但是实际情况是什么呢。图片显示效果如下,就是除了第一行不见了,什么也没发生。其实原因我昨天也已经分析了。OLED_WR_DAT(OledCache, 1024)这条语句会写入1024个字节的整屏数据。cmd[0] = 0xB4这条语句会让前128个数据写进屏幕的第5行。写完之后剩下1024-128=896个数据怎么办呢。因为屏幕初始化的时候,寻址模式设置的是水平地址模式,且0x22的默认范围是第1行到第7行,所以程序就会继续从第1行屏幕开头开始写入剩余的896个数据。写的时候会顺便覆盖掉一开始通过0xb4命令写入的那行数据。又因为总体少了128个数据,所以最后一行是空的。导致最终的现象是图像整体上移一行。如果将0xB4改为0xB5和0xB6,得到的现象是一模一样的,原因分析也是一样。
那如果改为0xB7,则情况略有不同,显示效果如下。原因是,一开始的128个数据会写入屏幕最后一行(第8行),剩余的896个数据会依此从第1行写到第7行。因为数据写到第7行就写完了,所以最后一行的数据不会被覆盖掉,即最终的现象是图像整体上移一行,最后一行是原本图像第一行的内容。
那要实现通过0xb0命令来控制图像整体下移的方法是什么呢。只有通过以下命令,一行行定位后写入数据才能达成。而这种方式其实就是页地址模式下的操作,它和水平地址的关系已经不大了。只是在水平模式的初始化设置下,实现了页地址模式的写入操作而已。
- uint8_t page;
- for(page=0;page<8;page++)
- {
- cmd[0]=0xB4+page;
- OLED_WR_CMD(cmd, 3);
- OLED_WR_DAT(OledCache+page*128, 128);
- }
复制代码
所以,结论还是0xB0在水平模式下能用,但也仅仅是能用而已,并无法发挥水平地址模式下数据可以连续写入的优势,应该算不得是水平模式的最佳搭档。如果要正确使用,本质上就是在页地址模式下画图。所以我猜这也就是数据手册不推荐它用在水平地址模式下的原因吧。
如果要实现图像偏移,其实我提供的代码中的实现方式挺好的(不是我写的,是4楼的代码,我拷贝下来学习而已)。它把需要显示的图像数组拷贝到1024大小的OledCache缓存数组的特定位置中,然后只要在默认的设置下通过改变x,y参数就可以实现图像偏移了,充分利用了水平模式下可以连续写入数据的特性。唯一的问题是,它原本的代码中多加了一个0xB0的语句,导致图像向上偏移一行,也就导致了后来以上所有的讨论。其实只要注释掉0xb0,一切就完美了。
- void OLED_ShowBMP(int x, int y, int w, int h, uint8_t *bmp, uint8_t rf)
- {
- int i, j;
- uint8_t dat;
-
- for (j = 0; j < h; j++)
- {
- for (i = 0; i < w; i++)
- {
- dat = *bmp++; //读取图片数据
- if (fReverseMode) //如果反白显示则将数据取反
- dat = ~dat;
-
- OledCache[(y+j) * 128 + (x+i)] = dat;
- }
- }
-
- if (rf)
- OLED_Refresh(); //刷新显示
- }
复制代码
另外,我又看了下数据手册,还发现了一个有趣的命令,就是0xD3,这个似乎是ssd1306专门用于偏移图像所使用的命令。我也小试了一下,能够达到预期效果。
|