OLED SSD1306 驱动程序编写

OLED SSD1306 驱动程序编写

坑:

1、

if(da&0x80 == 0x80) 
if((da&0x80) == 0x80)

坑死爹了,“==” 优先级比 “&” 高。。。。。。。。。

2、(下面的东西写错了,留着提醒)

void IIC_Write_Byte(unsigned char IIC_Byte) {
	unsigned char i;
	for(i=0; i<8; i++) {
		OLED_SCL_Clear();     // µ±SCLÏßΪµÍʱ£¬SDAÏßµÄ״̬²Å¿ÉÒԸıä
		
		if((IIC_Byte&0x80) == 0x80) {
			OLED_SDA_Set();   // IIC_ByteµÄ¸ÃbitΪ1
		} else {
			OLED_SDA_Clear(); // IIC_ByteµÄ¸ÃbitΪ0
		}
		
		OLED_SCL_Set();       // µ±SCLÏßΪ¸ßʱ£¬SDAÏßµÄ״̬±»¶ÁÈ¡
		OLED_SCL_Clear();     // ½«SCLÏßÇåÁ㣬Íê³ÉÕâÒ»¸öbitµÄ´«Êä
		
		IIC_Byte = IIC_Byte << 1;
	}
}
void IIC_Write_Byte(unsigned char IIC_Byte) {
	unsigned char i, da;
	da = IIC_Byte;
	for(i=0; i<8; i++) {
		OLED_SCL_Clear();     // µ±SCLÏßΪµÍʱ£¬SDAÏßµÄ״̬²Å¿ÉÒԸıä
		
		if((da&0x80) == 0x80) {
			OLED_SDA_Set();   // IIC_ByteµÄ¸ÃbitΪ1
		} else {
			OLED_SDA_Clear(); // IIC_ByteµÄ¸ÃbitΪ0
		}
		
		OLED_SCL_Set();       // µ±SCLÏßΪ¸ßʱ£¬SDAÏßµÄ״̬±»¶ÁÈ¡
		OLED_SCL_Clear();     // ½«SCLÏßÇåÁ㣬Íê³ÉÕâÒ»¸öbitµÄ´«Êä
		
		da = da << 1;
	}
} 垃圾keil一堆乱码

第一段代码是错误的,第二段代码是正确的。
形参只能读取,不能修改????
错了错了,两段代码都对,问题出在 ”==“ 和 ”&“ 优先级上。

3、

..\HARDWARE\delay\delay.h(21): error C141: syntax error near 'void', expected ';'

报这种错误,肯定是报错的地方上面 忘加“ ;”,但是这一次这个错误没能立马解决,原因是:报错上方是引入头文件,那么就没想到是没加分号这个原因。
数组定义老是忘记加分号。。。。

unsigned char code Picture1[] = {
	0x00,0x00,0x00,0x00
}
unsigned char code Picture1[] = {
	0x00,0x00,0x00,0x00
};

上面是错的,下面是对的。

4、

头文件引用导致重复定义的问题, 以及再消除重复定义时导致的未定义问题。
重复定义发生在,在头文件中定义变量:如管脚定义,常量定义等。
比如:

main.c  中需要使用  unsigned char code Picture1[][128]

那么就直接在 main.c#include "oledbmp.h" #include "oledfont.h"
但是 main.c 中还#include "oled.h"
那么 oled.h 中就不能#include "oledbmp.h" #include "oledfont.h"而且 oled.c 中也不可以

原因是什么呢????不知道
(管脚定义冲突时应该更恶心)

5、

时序问题
本来打算的是采用 “管前不顾后” 的策略,即前包含后不包含,前面拉低SCL,写完数据后,后面就不拉低了,发现不行。。。。感觉没问题啊。。。。

void IIC_Write_Byte(unsigned char IIC_Byte) {
	unsigned char i;
	for(i=0; i<8; i++) {
		OLED_SCL_Clear();     // 当SCL线为低时,SDA线的状态才可以改变
		
		if((IIC_Byte&0x80) == 0x80) {
			OLED_SDA_Set();   // IIC_Byte的该bit为1
		} else {
			OLED_SDA_Clear(); // IIC_Byte的该bit为0
		}
		
		OLED_SCL_Set();       // 当SCL线为高时,SDA线的状态被读取
//		OLED_SCL_Clear();     // 将SCL线清零,完成这一个bit的传输<<<<<<<<<<<<<<<<<<<<<<<<<<<
		
		IIC_Byte = IIC_Byte << 1;
	}
}
void IIC_Write_Byte(unsigned char IIC_Byte) {
	unsigned char i;
	for(i=0; i<8; i++) {
		OLED_SCL_Clear();     // 当SCL线为低时,SDA线的状态才可以改变
		
		if((IIC_Byte&0x80) == 0x80) {
			OLED_SDA_Set();   // IIC_Byte的该bit为1
		} else {
			OLED_SDA_Clear(); // IIC_Byte的该bit为0
		}
		
		OLED_SCL_Set();       // 当SCL线为高时,SDA线的状态被读取
		OLED_SCL_Clear();     // 将SCL线清零,完成这一个bit的传输<<<<<<<<<<<<<<<<<<<<<<<<<<<
		
		IIC_Byte = IIC_Byte << 1;
	}
}

第一段代码是错误的,第二段代码是正确的。

发现 “顾后不顾前”,貌似也能行:

void IIC_Write_Byte(unsigned char IIC_Byte) {
	unsigned char i;
	OLED_SCL_Clear();//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
	for(i=0; i<8; i++) {
//		OLED_SCL_Clear();     // 当SCL线为低时,SDA线的状态才可以改变<<<<<<<<<<<<<<<<<<<<<<<
		
		if((IIC_Byte&0x80) == 0x80) {
			OLED_SDA_Set();   // IIC_Byte的该bit为1
		} else {
			OLED_SDA_Clear(); // IIC_Byte的该bit为0
		}
		
		OLED_SCL_Set();       // 当SCL线为高时,SDA线的状态被读取
		OLED_SCL_Clear();     // 将SCL线清零,完成这一个bit的传输
		
		IIC_Byte = IIC_Byte << 1;
	}
}

上面的代码运行正确。

我明白为什么了!!!!
什么 ”顾前不顾后“ ”顾后不顾前“ 简直就是小孩子的说法,一点都不理论,一点都不贴近器件。SSD1306读取数据(也就是SDA线上的状态)是在SCL下降沿上读取的,所以每写 1bit 数据(也就是改变一次SDA线的状态),就要在SCL上产生一个下降沿,所以:::::

if((IIC_Byte&0x80) == 0x80) {
			OLED_SDA_Set();   // IIC_Byte的该bit为1
		} else {
			OLED_SDA_Clear(); // IIC_Byte的该bit为0
		}
		
		OLED_SCL_Set();       // 当SCL线为高时,SDA线的状态被读取<<<<<<<<<<<<<<<<<<<<<<<<<<<<
		OLED_SCL_Clear();     // 将SCL线清零,完成这一个bit的传输<<<<<<<<<<<<<<<<<<<<<<<<<<<<

这两行要在操作SDA后面!

基于以上的探索,在代码中删删减减,渐渐的找出了,哪些语句是必要的,哪些不是必要的(甚至是没用的),在这个过程,渐渐对 IIC总线 有了更准确的了解。

关于Acknowledge 后天反馈:以下错了

void IIC_Wait_Ack() {
	// 造个假的脉冲,告诉RECEIVER:TRANSMITTER已经接收到RECEIVER的acknowledge了
//	OLED_SCL_Clear();
//	OLED_SCL_Set();
//	OLED_SCL_Clear();
}

哈哈哈,全删了问题也不大。补充:全删了有问题。。。真不知道那一次没有问题的情况是怎么发生的
又瞎几把搞了,还是得从原理出发!!!
为什么会有Acknowledge这个信号,就是为了告诉transmitter receiver是否收到了byte,注意Acknowledge这个信号是在SDA上的,为什么我们的程序操作的是SCL呢???因为这个IIC总线的SDA,SCL都是单片机产生的,现在单片机不关心从机是否收到数据,默认传输是可靠,所以产生一个不伴随任何收发任何判断程序的时钟脉冲,如果不产生这个脉冲,那么SSD1306不知道你没产生,它照常通过SDA向主机发送Acknowledge信号,但是同时主机也向从机发送byte,这样就发生了冲突,通信失败!
万事要从理论思考,这样知识才能上升一个层次。(但是同时也不否定瞎几把尝试的作用,为理解原理提供了原料,所以要认真对待瞎几把试的结果,能透彻的分析结果,发现矛盾,接近真理!)

6、

我有发现一个神奇的事情,大小写不判断问题,头文件的函数声明和其他文件的对其引用,是要求大小写严格对应的,但是头文件的函数声明和其对应C文件的函数定义确实大小写通用的!!!!!!!
oled.c:

void oled_display_full_picture(unsigned char picture[][128]) {   <<<<<<<<<<<<<<<<<<<<<<<<<<
	unsigned char i, n;
	for(i=0; i<8; i++) {
		IIC_Write_Command(0xb0+i);
		IIC_Write_Command(0x00);
		IIC_Write_Command(0x10);
		
		for(n=0; n<128; n++) {
			IIC_Write_Data(picture[i][n]);
		}
	}
}

oled.h:

void OLED_DISPLAY_FULL_PICTURE(unsigned char picture[][128]);  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

main.c:

	OLED_DISPLAY_FULL_PICTURE(Picture2);                      <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

7、数组也是个大坑,谁让自己没学好啊!!!

// 汉字库16x10
unsigned char code Character[][2][10] = {	
		{0x00,0xE0,0x70,0xE0,0x20,0x20,0x30,0x30,0x20,0x20},
		{0x31,0x1F,0x2D,0x3F,0x20,0x1F,0x01,0x01,0x3F,0x38}/*"航",0*/	
};
// 汉字库16x10
unsigned char code Character[][2][10] = {
	{
		{0x00,0xE0,0x70,0xE0,0x20,0x20,0x30,0x30,0x20,0x20},
		{0x31,0x1F,0x2D,0x3F,0x20,0x1F,0x01,0x01,0x3F,0x38}/*"航",0*/
	},
};
main.c中
	OLED_Display_Part_Picture(6, 48+2, 2, 10, Character[0]);

第一块代码不行,只能打印出 “航” 的上半部分;第二块代码可以。
受二维数组影响,第一维可以不加括号,比如:

a[][3] = {1, 2, 3, 4, 5, 6};
a[][3] = {{1, 2, 3}, {4, 5, 6}}

效果是一样的,为何到三维就不行了呢!

我好像知道了!!!!!!!!!!!
只有最低维可以不加括号,或者是没加括号时,优先填最低维,填满再升一维再填。
显然第一段代码错误的原因是,最低维加括号了,想省第二维的括号,但是C语言不知道啊,它发现括号层级不够,那么就先省第一维,所以成了这样:

unsigned char code Character[][2][10] = {	
		{0x00,0xE0,0x70,0xE0,0x20,0x20,0x30,0x30,0x20,0x20},
		{0x31,0x1F,0x2D,0x3F,0x20,0x1F,0x01,0x01,0x3F,0x38}/*"航",0*/	
};
===========>>>>>>>>>>>>> 变成下面这样
unsigned char code Character[][2][10] = {	
		{
			{0x00,0xE0,0x70,0xE0,0x20,0x20,0x30,0x30,0x20,0x20}, 
			{}
		},
		{
			{0x31,0x1F,0x2D,0x3F,0x20,0x1F,0x01,0x01,0x3F,0x38},
			{}
		}
		/*"航",0*/	
};

所以只显示了汉字的上半部分,哈哈哈哈原因找到了

8、又是数组,传参为多维数组

之前传参是这样的:

void oled_display_full_picture(unsigned char picture[][128]) { 

可见最低维是写死的,但是有时候不能用128,但是函数的其他部分都是需要的,那么该怎么办??128是不能换成变量的,这个应该和C语言的编译原理相关的,定义时就要把所需空间声明,而不是调用时再说。

所以我搞出来个这玩意: 使用指针,就不用写死最低维了,维度以形参变量传入

void OLED_Display_Part_Picture(unsigned char Start_Page, unsigned char Start_Column, 
							   unsigned char Delta_Page, unsigned char Delta_Column, 
							   unsigned char Dimension, unsigned char * Picture) {
}

在单片机环境下 “开疆辟土” 太懵逼了,调试法宝printf都没有,所以我选择先去DEV-C++验证一下

#include "stdio.h"
int main() {
	unsigned char Character[][2][2] = {
		{
			{1, 2},
			{3, 4}
		}
	};
	void function(unsigned char a, unsigned char b, unsigned char dimension, unsigned char * Picture) {
		char i, n;
		for (i = 0; i< a; i++) {
			for (n = 0; n < b; n++) {
				printf("(%d, %d) = %d\n", i, n, Picture[n]);
			}
			Picture += dimension;
		}
	}
	unsigned char ** a = Character;
	function(2, 2, 2, Character[0]);
	return 0;
}

OLED SSD1306 驱动程序编写_第1张图片
问题不大
再去Keil,发现警告,不管,继续,成功显示。警告如下:

main.c(16): warning C182: pointer to different objects
main.c(17): warning C182: pointer to different objects
main.c(18): warning C182: pointer to different objects
main.c(19): warning C182: pointer to different objects
main.c(20): warning C182: pointer to different objects
main.c(21): warning C182: pointer to different objects

10、

我好像遇到了一个大问题,C51函数传参最多传3个。。。。。。
http://www.51hei.com/bbs/dpj-31890-1.html
OLED SSD1306 驱动程序编写_第2张图片

11、

又进坑了,C语言功底不够深厚啊。这个坑是关于ASCII码的
前32个ASCII码(0~31)是ASCII控制字符,是有字符解释的。单片机做屏幕显示字库时,不许考虑只管从32开始的打印字符就好。
所以字库中是从ASCII码为32的字符开始存的

// ASCII库
const unsigned char code ASCII_8x6[][6] =		
{
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// sp 32
    0x00, 0x00, 0x00, 0x2f, 0x00, 0x00,// !  33
    0x00, 0x00, 0x07, 0x00, 0x07, 0x00,// "  34
    0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14,// #
    0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12,// $

所以我们使用char字符型变量索引ASCII字库时,要给一个偏移量 ‘ ’(空格space)。

void OLED_Display_Char_8x6(char Char, unsigned char ASCII[][6]) {
	unsigned char n;
	for (n = 0; n < 6; n++) {
		IIC_Write_Data(ASCII[Char - ' '][n]); <<<<<<<<<<<<<<<<<<<<<<<
	}
}

12、

啊啊啊啊啊啊,我又把自己坑死了,太菜了。。。。。
针对屏幕打印停不下来,呈滚动效果。这里需要严肃检讨!!!!!!!!!!!!!!!
出现这个问题,我最先不是仔细的思考问题的原因,而是自动把问题原因归结到自己写的,取数字每一位并显示的代码上。其实这个问题仔细一想,就是循环的问题,人总是把问题归结到自己最不熟的东西上,而不是认真根据现象分析原因,但是循环上的问题我也找了一大会。。。。。。。
这次是unsigned char坑了我。。。。

	unsigned char i = 0; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

else {
		for (i = num_bit; i>=0; i--) {      <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
			OLED_Relative_Display_Char_8x6(num_bit_arr[i] + 48, ASCII);
		}
	}

i = 0 —> i- - —> i = 255 —> i >= 0
用完unsigned char,也就是全是正数了,这个就成死循环了。

你可能感兴趣的:(51单片机)