对IIC协议的OLed12864屏幕官方stm32库文件的优化及过程

IIC OLed”超频”? 刷新率直接干到37.5hz

先放结果吧:
优化后,在Stm32f103 72MHZ下,在128*64的图片/全屏填充中 单次速度从原来的80.70ms变为26.69ms
小号英文字符串168个字符全屏填充从原来的116.1ms变为26.81ms,并且官方库文字填充出错
大号英文字符串60个英文字符全屏填充从原来的101.61ms变为25.73ms,并且官方库文字填充出错

小号英文字符串336个字符过充填充为26.91ms 官方库直接黑屏 程序意外进入死循环
大号英文字符串120过充填充从原来的201.9ms变为25.60ms 并且官方库文字填充出错 没有做文字截断处理

综合性能测试中
重复执行100次下列操作↓

1
2
3
4
5
6
7
OLED_Fill(0xFF);//全屏点亮  
OLED_Fill(0x00);//全屏灭
OLED_ShowStr(0,3,(unsigned char *)"Hello,World!",1); //测试6*8字符
OLED_ShowStr(0,4,(unsigned char *)"Hello,World!",2); //测试8*16字符
OLED_CLS();//清屏
OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//测试BMP位图显示
OLED_CLS();//清屏

*的时间从42692ms降低到14710ms

“超频”的原理其实并不是真正的超频 只是对官方库文件通信部分进行了优化,对硬件寿命并没有任何影响


这件事情还得从一只蝙蝠说起…

原本假期列了一堆项目去做 但是自从疫情戒严之后 快递停运 我几乎全部项目都缺必要原材料而无法开工。
然后我就在家里搜摸一些还能用的模块 一个一个的放到stm32上进行适配 整理出库文件
然后摸到了一个ADS1115的ADC模块
一看是IIC协议 就打算自己手写一遍IIC协议的通信。
刚好一个月前整理过一个IIC协议的Oled 12864的库文件
就打算照着12864的程序自己写一遍ADS1115的库函数

于是先了解了下IIC通信协议

IIC通信协议

我认为讲的非常好的一篇帖子 不了解的可以先看一下
https://blog.csdn.net/xx_0305401/article/details/81914528

简单来说 IIC协议中 数据包格式为:

第一个字节是地址 后面的N个字节都是数据。

然后在Oled 12864中 第一个字节是地址 第二个字节是寄存器(存疑 没拿到官方手册 这个字节应该是用于决定数据类型的) 后面的字节是数据

但是官方库文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;

if(y1%8==0)
y = y1/8;
else
y = y1/8 + 1;
for(y=y0;y<y1;y++)
{
OLED_SetPos(x0,y);
for(x=x0;x<x1;x++)
{
WriteDat(BMP[j++]);
}
}
}

上传数据会循环调用下面的函数

1
2
3
4
void WriteDat(unsigned char I2C_Data)//写数据
{
I2C_WriteByte(0x40, I2C_Data);
}

最后循环调用这个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

I2C_GenerateSTART(I2C1, ENABLE);//开启I2C1
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));/*EV5,主模式*/

I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);//器件地址 -- 默认0x78
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

I2C_SendData(I2C1, addr);//寄存器地址
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

I2C_SendData(I2C1, data);//发送数据
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

I2C_GenerateSTOP(I2C1, ENABLE);//关闭I2C1总线
}

他直接把unsigned char数组拆成一个元素一个元素的单独发送
每发送一个元素 就重置一次IIC
也就是说 他每发送一个字节 都会浪费掉两个字节(一个是地址字节 一个是寄存器字节)

显然 通信效率相当的低。

从网上查到 并不是所有的模块都支持多字节连续读写

但是他库文件中 是1个地址字节+2个数据字节 很显然已经支持多字节连续读写。

然后动手改库文件。

Oled12864屏幕(横向)扫描方式为:

1
2
3
4
5
6
7
横向1个像素以及纵向8个像素为一组 被分为128*8组  
每一组图像由一个字节表示。字节的值可以由字模转换工具导出。

则X轴坐标取值范围为[0,127] Y轴为[0,7]

实际数据传输则为:
设定坐标 --> 输出若干字节

仔细一看 又发现好多其他的bug以及迷之操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;

if(y1%8==0)
y = y1/8;
else
y = y1/8 + 1;
for(y=y0;y<y1;y++)
{
OLED_SetPos(x0,y);
for(x=x0;x<x1;x++)
{
WriteDat(BMP[j++]);
}
}
}


这是干嘛的…

1
2
3
4
if(y1%8==0)
y = y1/8;
else
y = y1/8 + 1;

还有 输出文字居然没有长度检测? 如果文字超出屏幕范围直接bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
unsigned char c = 0,i = 0,j = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat(F6x8[c][i]);
x += 6;
j++;
}
}break;
case 2:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i+8]);
x += 8;
j++;
}
}break;
}
}

算了 毕竟是2014年的代码了 就不鞭尸了…

改进

改了一大堆之后 同样的工程 文件体积有少许增加
因为我新建了个128个成员的unsigned char数组去容纳所有的字节保证数据一次传输完成(keil居然连变长数组都不支持???)

官方库:

Program Size: Code=9666 RO-data=2534 RW-data=1232 ZI-data=1024

我的库:

Program Size: Code=10062 RO-data=2534 RW-data=1232 ZI-data=1024

好在stm32的flash很大。
我似乎猜到了官方这么做的原因:

  1. 方便移植到8051这种ROM巨小的MCU上

但是很显然 stm32并不适合这么做
不过初始化的void WriteCmd(unsigned char I2C_Command)就懒得改了 毕竟初始化就初始化一次就好 更改的意义不大。

成果

最终 同样的代码进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*优化后,在Stm32f103 72MHZ下,在128*64的图片/全屏填充中 单次速度从原来的80.70ms变为26.69ms
*小号英文字符串168个字符全屏填充从原来的116.1ms变为26.81ms,并且官方库文字填充出错
*大号英文字符串60个英文字符全屏填充从原来的101.61ms变为25.73ms,并且官方库文字填充出错

*小号英文字符串336个字符过充填充为26.91ms 官方库直接黑屏 程序意外进入死循环
*大号英文字符串120过充填充从原来的201.9ms变为25.60ms 并且官方库文字填充出错 没有做文字截断处理

*综合性能测试中
*重复执行100次
OLED_Fill(0xFF);//全屏点亮
OLED_Fill(0x00);//全屏灭
OLED_ShowStr(0,3,(unsigned char *)"Hello,World!",1);//测试6*8字符
OLED_ShowStr(0,4,(unsigned char *)"Hello,World!",2); //测试8*16字符
OLED_CLS();//清屏
OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//测试BMP位图显示
OLED_CLS();//清屏
*的时间从42692ms降低到14710ms

更改好的库文件’OLED_I2C.C’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
#include "OLED_I2C.h"
#include "delay.h"
#include "codetab.h"

#define timelimit 200
uint8_t errtime = 0;

void I2C_Configuration(void)
{
I2C_InitTypeDef I2C_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

/*STM32F103C8T6芯片的硬件I2C: PB6 -- SCL; PB7 -- SDA */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//I2C必须开漏输出
GPIO_Init(GPIOB, &GPIO_InitStructure);

I2C_DeInit(I2C1);//使用I2C1
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x30;//主机的I2C地址,随便写的
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 400000;//400K,峰值850K,对应刷新率73,800K能稳定,700K对应63.5

I2C_Cmd(I2C1, ENABLE);
I2C_Init(I2C1, &I2C_InitStructure);
}

void I2C_WriteByte(uint8_t addr,uint8_t data)
{
errtime = 0;

while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}

errtime = 0;
I2C_GenerateSTART(I2C1, ENABLE);//开启I2C1
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))//EV5,主模式
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}

errtime = 0;
I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);//器件地址 -- 默认0x78
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}

errtime = 0;
I2C_SendData(I2C1, addr);//寄存器地址
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}

I2C_SendData(I2C1, data);//发送数据
errtime = 0;
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}

I2C_GenerateSTOP(I2C1, ENABLE);//关闭I2C1总线
}


//--------------------------------------------------------------
// Prototype : void I2C_WriteData(uint8_t addr,uint8_t *data,unsigned char length);
// Calls :
// Parameters : addr,寄存器地址,表示是命令包还是数据包;*data,数据包数组;length,数据包长度
// Description : 连续写数据
//--------------------------------------------------------------

void I2C_WriteData(uint8_t addr,uint8_t *data,unsigned char length)
{
unsigned char t = 0;
errtime = 0;

while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}

errtime = 0;
I2C_GenerateSTART(I2C1, ENABLE);//开启I2C1
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))//EV5,主模式
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}

errtime = 0;
I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);//器件地址 -- 默认0x78
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}

errtime = 0;
I2C_SendData(I2C1, addr);//寄存器地址
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}


while(t < length)
{
I2C_SendData(I2C1, data[t]);//发送数据
errtime = 0;
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))//等待应答位
{
errtime ++;
if(errtime > timelimit)
{
errfunction();
return;
}
}
t++;
}
I2C_GenerateSTOP(I2C1, ENABLE);//关闭I2C1总线
}



void WriteCmd(unsigned char I2C_Command)//写命令
{
I2C_WriteByte(0x00, I2C_Command); //0x00对应二进制为0000000 即写命令时 第9位为0
}

void OLED_Init(void)
{
DelayMs(100); //这里的延时很重要
WriteCmd(0xAE); //display off
WriteCmd(0x20); //Set Memory Addressing Mode
WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
WriteCmd(0xc8); //Set COM Output Scan Direction
WriteCmd(0x00); //---set low column address
WriteCmd(0x10); //---set high column address
WriteCmd(0x40); //--set start line address
WriteCmd(0x81); //--set contrast control register
WriteCmd(0xff); //亮度调节 0x00~0xff
WriteCmd(0xa1); //--set segment re-map 0 to 127
WriteCmd(0xa6); //--set normal display
WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
WriteCmd(0x3F); //
WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
WriteCmd(0xd3); //-set display offset
WriteCmd(0x00); //-not offset
WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
WriteCmd(0xf0); //--set divide ratio
WriteCmd(0xd9); //--set pre-charge period
WriteCmd(0x22); //
WriteCmd(0xda); //--set com pins hardware configuration
WriteCmd(0x12);
WriteCmd(0xdb); //--set vcomh
WriteCmd(0x20); //0x20,0.77xVcc
WriteCmd(0x8d); //--set DC-DC enable
WriteCmd(0x14); //
WriteCmd(0xaf); //--turn on oled panel
}

void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}

void OLED_Fill(unsigned char fill_Data)//全屏填充
{
unsigned char n,fill[128];
for(n = 0;n < 128;n ++)
fill[n] = fill_Data;
for(n=0;n<8;n++)
{
WriteCmd(0xb0+n); //page0-page1
WriteCmd(0x00); //low column start address
WriteCmd(0x10); //high column start address
OLED_SetPos(0,n);
I2C_WriteData(0x40,fill,128);
}
}

void OLED_CLS(void)//清屏
{
OLED_Fill(0x00);
}

//--------------------------------------------------------------
// Prototype : void OLED_ON(void)
// Calls :
// Parameters : none
// Description : 将OLED从休眠中唤醒
//--------------------------------------------------------------
void OLED_ON(void)
{
WriteCmd(0X8D); //设置电荷泵
WriteCmd(0X14); //开启电荷泵
WriteCmd(0XAF); //OLED唤醒
}

//--------------------------------------------------------------
// Prototype : void OLED_OFF(void)
// Calls :
// Parameters : none
// Description : 让OLED休眠 -- 休眠模式下,OLED功耗不到10uA
//--------------------------------------------------------------
void OLED_OFF(void)
{
WriteCmd(0X8D); //设置电荷泵
WriteCmd(0X10); //关闭电荷泵
WriteCmd(0XAE); //OLED休眠
}

//--------------------------------------------------------------
// Prototype : void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch[],unsigned char TextSize)
// Calls :
// Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
//--------------------------------------------------------------
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
unsigned char c = 0,i = 0,j0 = 0,j1 = 0,x0 = x,x1 = x,y0,t0 = 0,str0[128];//一行最多放下128个像素,因为Keil不支持变长数组,所以很恶心....
switch(TextSize)
{
case 1: //6*8,6个字节
{
while(1)
{
if(x0 > 122) //检测是否换行
{
OLED_SetPos(x,y);
I2C_WriteData(0x40,str0,t0);
t0 = 0;
x = 0;
x0 = 0;
y++;
if (y > 7)
return; //超过屏幕限制 退出函数
}
if(ch[j0] == '\0') //检测是否输出完毕
{
if (t0 != 0)
{
OLED_SetPos(x,y);
I2C_WriteData(0x40,str0,t0);
}
return; //输出完毕 退出函数
}
c = ch[j0] - 32;
for(i=0;i<6;i++)
{
str0[t0++] = F6x8[c][i];
}
x0 += 6;
j0++;
}
}
case 2: //16*8,对于16*8的,如果想一次性输出并且不想再多创建一个数组 只能牺牲代码简洁性了。写这一个case的代码的时候我心态也是崩溃的...
{
/*先检测是否需要换行*/
if(x > 118)
{
y += 2;
x = 0;
x0 = 0;
x1 = 0;
if (y > 6)
return; //超过屏幕范围 结束函数
}

y0 = y+1;
while(1)
{
/*先处理上半部分*/
c = ch[j0] - 32;
for(i=0;i<8;i++)
str0[t0++] = F8x16[c][i]; //将字体上半部分打包
j0++;
x0 += 8;

if(x0 > 118) //检测是否需要换行
{
OLED_SetPos(x,y);
I2C_WriteData(0x40,str0,t0);
t0 = 0;
x0 = 0;
y += 2;
if (y > 6)
goto a;
}

if(ch[j0] == '\0') //检测上半部分是否输出完毕
{
a:
if (t0 != 0)
{
OLED_SetPos(x,y);
I2C_WriteData(0x40,str0,t0);
t0 = 0;
}

/*开始输出下半部分*/
while(1)
{
c = ch[j1] - 32;
for(i=0;i<8;i++)
str0[t0++] = F8x16[c][i+8]; //将字体下半部分打包
j1++;
x1 += 8;

if(x1 > 118) //检测是否需要换行
{
OLED_SetPos(x,y0);
I2C_WriteData(0x40,str0,t0);
t0 = 0;
x1 = 0;
y0 += 2;
if(y0 > 7)
return;
}

if(ch[j1] == '\0')
{
if (t0 != 0)
{
OLED_SetPos(x,y0);
I2C_WriteData(0x40,str0,t0);
}
return;
}
}
}
}
}
}
}

//--------------------------------------------------------------
// Prototype : void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
// Calls :
// Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引
// Description : 显示codetab.h中的汉字,16*16点阵
//--------------------------------------------------------------
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
unsigned char wm=0,t = 0,str0[16];
unsigned int adder=32*N;

for(wm = 0;wm < 16;wm++)
{
str0[t++]=F16x16[adder];
adder += 1;
}
OLED_SetPos(x , y);
I2C_WriteData(0x40,str0,t);

t = 0;
for(wm = 0;wm < 16;wm++)
{
str0[t++]=F16x16[adder];
adder += 1;
}
OLED_SetPos(x,y + 1);
I2C_WriteData(0x40,str0,t);
}

//--------------------------------------------------------------
// Prototype : void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);
// Calls :
// Parameters : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
// Description : 显示BMP位图
//--------------------------------------------------------------
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
unsigned char x,y;
x = x1 - x0;
for(y=y0;y<y1;y++)
{
OLED_SetPos(x0,y);
I2C_WriteData(0x40,BMP,x);
BMP += x;
}
}

//--------------------------------------------------------------
// Prototype : void errfunction();
// Calls :
// Parameters :
// Description : 检测到显示屏错误后自动重启显示屏(不重启单片机)。即使屏幕掉线再插上也可以正常使用
//--------------------------------------------------------------
void errfunction()
{
DelayMs(500);
I2C_Configuration();
OLED_Init();
OLED_CLS();
}

库文件所有指令通用 主程序不需要更改 只需要把’OLED_I2C.C’覆盖即可

Download

Stm32 IIC Oled12864

Video

真正超频

另外呢 有心的人应该注意到了(或者说写过IIC协议的人都知道)
stm32是可以设置IIC的速度的

1
I2C_InitStructure.I2C_ClockSpeed = 400000;//400K

这一句就是对总线速度进行设置
目前频率可以拉到850K 刷新率是72.5hz