C51:IIC_OLED

文章详细介绍了IIC协议的原理,包括起始信号、终止信号和应答信号,以及简单的代码实现。同时,文章深入讲解了OLED显示的命令写入、寻址模式(页地址、水平地址、垂直地址)以及如何显示点、文字和图片。还提供了OLED初始化、清屏和显示文字的示例代码。

目录

​编辑

一 IIC协议

1.概述

 特点

 2.IIC协议

起始信号

终止信号

应答信号

 3.代码实现

 二 OLED

1.OLED写命令

2.OLED的寻址模式

显示一个点:

列地址:

清屏函数:

3.显示文字

显示一个字母A 

 显示“鸡你太美”

 4.显示图片


 

一 IIC协议

1.概述

IIC全称Inter-Integrated Circuit (集成电路总线)
是由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双工同步通信方式

 特点

简单性和有效性。

由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件

多主控(multimastering)

其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

构成

IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。

 

 2.IIC协议

IIC总线在传输数据的过程中一共有三种类型信号,分别为:开始信号、结束信号和应答信号。

//起始位,停止位,数据位,速度

这些信号中,起始信号是必需的,结束信号和应答信号

起始信号

终止信号

 

应答信号

发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。

应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;

应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

 

 数据发送的时序

 3.代码实现

void IIC_START()
{
	SCL = 1;
	SDA = 1;
	_nop_();
	SDA = 0;
	_nop_();
}

void IIC_END()
{
	SCL = 1;
	SDA = 0;
	_nop_();
	SDA = 1;
	_nop_();
}

char IIC_ACK()
{
	char flag;
	
	SDA = 1;
	_nop_();
	SCL = 1;
	_nop_();
	flag = SDA;
	_nop_();
	SCL = 0;
	_nop_();
	
	return flag;
}

void IIC_Send_Byte(char dataSend)
{
	int i;
	
	for(i = 0;i<8;i++)
	{
		SCL = 0;
		SDA = dataSend & 0x80;//1000 0000 获得dataSend最高位给SDA
		_nop_();//数据建立时间
		SCL = 1;//SCL拉高开始发送
		_nop_();//数据发送时间
		SCL = 0;//发送完拉低
		_nop_();
		
		dataSend <<= 1;
	}
}

 二 OLED

1.OLED写命令

1. start()
2. 写入从机地址 b0111 1000 0x78
3. ACK
4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
5. ACK
6. 写入指令/数据
7. ACK
8. STOP

void Oled_Write_Cmd(char OLED_cmd)
{
	// 1. start()
	IIC_START();
	// 2. 写入从机地址 b0111 1000 0x78
	IIC_Send_Byte(0x78);
	// 3. ACK
	IIC_ACK();
	// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	IIC_Send_Byte(0x00);
	// 5. ACK
	IIC_ACK();
	///6. 写入指令/数据
	IIC_Send_Byte(OLED_cmd);
	//7. ACK
	IIC_ACK();
	//8. STOP
	IIC_END();
}

void Oled_Write_Data(char OLED_data)
{
	// 1. start()
	IIC_START();
	// 2. 写入从机地址 b0111 1000 0x78
	IIC_Send_Byte(0x78);
	// 3. ACK
	IIC_ACK();
	// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	IIC_Send_Byte(0x00);
	// 5. ACK
	IIC_ACK();
	///6. 写入指令/数据
	IIC_Send_Byte(OLED_data);
	//7. ACK
	IIC_ACK();
	//8. STOP
	IIC_END();
}

2.OLED的寻址模式

如何显示一个点?
有三种,分别位页地址模式,水平地址模式和垂直地址模式,可以通过一下表格进行配置内存管理

 

 页地址模式

 

 水平地址模式

 

 垂直地址模式

 

列地址选择

 

 如果写入0x08(b00001000)会显示什么呢

一个字节负责一个Page的一列显示

初始化代码 

//OLED初始化代码,直接复制粘贴
void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128
	Oled_Write_Cmd(0xA1);//set segment remap
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	Oled_Write_Cmd(0xAF);//--turn on oled panel
}

显示一个点:

(如果雪花可以先往后看)

void main()
{
	//Oled初始化
	Oled_Init();
	
	//确认页寻址模式
	Oled_Write_Cmd(0x20);
	Oled_Write_Cmd(0x02);
	
	//选择PAGE0
	Oled_Write_Cmd(0xB0);
	
	//显示一个点
	Oled_Write_Data(0x08);
	
	while(1);
}

列地址:

如果显示雪花,可能需要清屏一下

清屏并显示一条线(注意这里用char很容易雪花,建议用int),清屏函数往后再看。

void Oled_Clear()
{
	unsigned char i,j; //-128 --- 127
	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xB0 + i);//page0--page7
		//每个page从0列
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//0到127列,依次写入0,每写入数据,列地址自动偏移
		for(j = 0;j<128;j++){
			Oled_Write_Data(0);
		}
	}
}

void main()
{
	char i;
	
	//Oled初始化
	Oled_Init();
	
	//确认页寻址模式
	Oled_Write_Cmd(0x20);
	Oled_Write_Cmd(0x02);
	
	//清屏
	Oled_Clear();
	
	//选择PAGE0
	Oled_Write_Cmd(0xB0);
	
	//显示一条线
	for(i = 0;i<50;i++){
		Oled_Write_Data(0x08);
	}
	
	//选择PAGE5
	Oled_Write_Cmd(0xB5);
	
	//显示一条线
	for(i = 0;i<50;i++){
		Oled_Write_Data(0x08);
	}
	
	while(1);
}

显示了两条线,但是发现第二条线起始位不是屏幕边缘,而是跟着上一条线的屁股的横坐标继续显示,列在递增。

所以配置列,使得其不递增。

在最后一列显示一个点。

	//选择PAGE6
	Oled_Write_Cmd(0xB6);
	//高位7,低位f,即01111111.对应127,最后一列
	Oled_Write_Cmd(0x0f);
	Oled_Write_Cmd(0x17);
	//显示一个点
	Oled_Write_Data(0x08);

所以可以使得两条线并行: 

	//选择PAGE0
	Oled_Write_Cmd(0xB0);
	
	//显示一条线
	for(i = 0;i<30;i++){
		Oled_Write_Data(0x08);
	}
	
	//选择PAGE5
	Oled_Write_Cmd(0xB5);
	
	//重新从第一列开始
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	
	//显示一条线
	for(i = 0;i<30;i++){
		Oled_Write_Data(0x08);
	}

清屏函数:

unsigned char i,j;         char型本身范围小//-128 --- 127

void Oled_Clear()
{
	unsigned char i,j; //-128 --- 127
	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xB0 + i);//page0--page7
		//每个page从0列
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		//0到127列,依次写入0,每写入数据,列地址自动偏移
		for(j = 0;j<128;j++){
			Oled_Write_Data(0);
		}
	}
}

3.显示文字

屏幕本身没字库,可以借助字模软件。

宋体字号12 、其它选项:

 

 输入A,ctrl+enter    取模,选择C51模式,可得:

/*--  文字:  A  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,

 一个page高度为8,所以把A拆分成上下部分。

/*--  文字:  A  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
char A1[] = {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00};
char A1[] = {0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20};

显示一个字母A 

//显示一个字母A	
	//选择PAGE0
	Oled_Write_Cmd(0xB0);
		//从0列开始
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
		//显示上半部分
	for(i = 0;i<8;i++)	Oled_Write_Data(A1[i]);
	//选择PAGE1
	Oled_Write_Cmd(0xB1);
		//从0列开始
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
		//显示下半部分
	for(i = 0;i<8;i++)	Oled_Write_Data(A2[i]);

 显示“鸡你太美”

/*--  文字:  鸡  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x08,0x48,0x88,0x08,0xC8,0x38,0x00,0x00,0xFC,0x06,0x15,0x44,0x84,0x7C,0x00,0x00,
0x20,0x10,0x0C,0x03,0x04,0x18,0x00,0x10,0x13,0x12,0x12,0x52,0x92,0x42,0x3E,0x00,

/*--  文字:  你  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x80,0x60,0xF8,0x07,0x40,0x20,0x18,0x0F,0x08,0xC8,0x08,0x08,0x28,0x18,0x00,
0x01,0x00,0x00,0xFF,0x00,0x10,0x0C,0x03,0x40,0x80,0x7F,0x00,0x01,0x06,0x18,0x00,

/*--  文字:  太  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xFF,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,
0x80,0x80,0x40,0x20,0x10,0x0C,0x13,0x60,0x03,0x0C,0x10,0x20,0x40,0x80,0x80,0x00,

/*--  文字:  美  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x04,0x24,0x24,0x25,0x26,0x24,0xFC,0x24,0x26,0x25,0x24,0x24,0x04,0x00,0x00,
0x81,0x89,0x89,0x49,0x49,0x29,0x19,0x0F,0x19,0x29,0x49,0x49,0x89,0x89,0x81,0x00,

 容量太大,加上code前缀

/*--  文字:  鸡  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char j1[] = {0x08,0x48,0x88,0x08,0xC8,0x38,0x00,0x00,0xFC,0x06,0x15,0x44,0x84,0x7C,0x00,0x00};
char j2[] = {0x20,0x10,0x0C,0x03,0x04,0x18,0x00,0x10,0x13,0x12,0x12,0x52,0x92,0x42,0x3E,0x00};

/*--  文字:  你  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char n1[] = {0x00,0x80,0x60,0xF8,0x07,0x40,0x20,0x18,0x0F,0x08,0xC8,0x08,0x08,0x28,0x18,0x00};
char n2[] = {0x01,0x00,0x00,0xFF,0x00,0x10,0x0C,0x03,0x40,0x80,0x7F,0x00,0x01,0x06,0x18,0x00};

/*--  文字:  太  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char t1[] = {0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xFF,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00};
code char t2[] = {0x80,0x80,0x40,0x20,0x10,0x0C,0x13,0x60,0x03,0x0C,0x10,0x20,0x40,0x80,0x80,0x00};

/*--  文字:  美  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
code char m1[] = {0x00,0x04,0x24,0x24,0x25,0x26,0x24,0xFC,0x24,0x26,0x25,0x24,0x24,0x04,0x00,0x00};
code char m2[] = {0x81,0x89,0x89,0x49,0x49,0x29,0x19,0x0F,0x19,0x29,0x49,0x49,0x89,0x89,0x81,0x00};

先显示一个鸡字:

/*--  文字:  鸡  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char j1[] = {0x08,0x48,0x88,0x08,0xC8,0x38,0x00,0x00,0xFC,0x06,0x15,0x44,0x84,0x7C,0x00,0x00};
char j2[] = {0x20,0x10,0x0C,0x03,0x04,0x18,0x00,0x10,0x13,0x12,0x12,0x52,0x92,0x42,0x3E,0x00};

void main()
{
	int i;
	
	//Oled初始化
	Oled_Init();
	
	//确认页寻址模式
	Oled_Write_Cmd(0x20);
	Oled_Write_Cmd(0x02);
	
	//清屏
	Oled_Clear();
	
//显示	鸡
	//选择PAGE0
	Oled_Write_Cmd(0xB0);
		//从0列开始
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
		//显示上半部分
	for(i = 0;i<16;i++)	Oled_Write_Data(j1[i]);
	//选择PAGE1
	Oled_Write_Cmd(0xB1);
		//从0列开始
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
		//显示下半部分
	for(i = 0;i<16;i++)	Oled_Write_Data(j2[i]);
	
	while(1);
}

 一个个打太麻烦,封装一个函数,分别用两个指针数组表示字符串上下部分,函数中用指针数组地址偏移的方式:

*(*(str1+i)+j)

代码:

//创2个指针数组
char* str1[4] = {j1,n1,t1,m1};
char* str2[4] = {j2,n2,t2,m2};
//
void Print_On_Oled(char* str1[],char* str2[]){
	
	unsigned char i,j;
	
	//选择PAGE0
	Oled_Write_Cmd(0xB0);
	//显示上半
	for(i = 0;i<4;i++){
		for(j = 0;j<16;j++){
			Oled_Write_Data(*(*(str1+i)+j));
		}
	}
	
	//选择PAGE1
	Oled_Write_Cmd(0xB1);
	//列数归0
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	//显示下半
	for(i = 0;i<4;i++){
		for(j = 0;j<16;j++){
			Oled_Write_Data(*(*(str2+i)+j));
		}
	}
}

void main()
{
	//Oled初始化
	Oled_Init();
	
	//确认页寻址模式
	Oled_Write_Cmd(0x20);
	Oled_Write_Cmd(0x02);
	
	//清屏
	Oled_Clear();
	
	//显示字符串
	Print_On_Oled(str1,str2);
	
	while(1);
}

效果: 

 4.显示图片

 支持BMP格式图片。

 代码逻辑类似清屏函数:

/*--  调入了一幅图像:C:\Users\26982\Desktop\2.bmp  --*/
/*--  宽度x高度=128x64  --*/

code unsigned char image[] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,

... ...
... ...
... ...

0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};

void Oled_Image(unsigned char *bmp)
{
	unsigned char i;
	unsigned int j;
	
	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xB0 + i);//page0--page7
		
		//每个page从0列
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);
		
		//0到127列,依次写入0,每写入数据,列地址自动偏移
		
		for(j = 128 * i;j<(128 * (i+1));j++){
			Oled_Write_Data(bmp[j]);
		}
	}
}

<think>我们正在处理一个编译警告:warningC206:'IIC_Start':missingfunction-prototype这个警告出现在oled.c文件中,提示IIC_Start函数缺少函数原型声明。根据C语言规范,函数在使用前必须声明。如果函数定义在使用之后,或者函数定义在另一个文件中而没有在调用文件中声明,就会导致这个警告。引用[1]中提到了类似的警告:warningC206:'While':missingfunction-prototype。这里是因为'While'拼写错误(应该是'while'),并且缺少函数原型声明。但我们的情况是函数名'IIC_Start',所以不是关键字拼写错误,而是函数声明问题。解决方案:1.在调用IIC_Start函数之前,确保有它的函数原型声明。可以在oled.c文件的开头(包含头文件之后)添加一个函数声明,或者在一个头文件中声明并在oled.c中包含该头文件。2.检查函数定义:确保函数IIC_Start的定义存在,并且函数名拼写正确(注意大小写)。3.如果IIC_Start函数定义在另一个源文件中(比如iic.c),那么我们需要在oled.c中包含声明它的头文件(比如iic.h)。步骤详解:步骤1:创建或修改头文件如果还没有头文件,创建一个iic.h头文件,并在其中声明IIC_Start函数:```c//iic.h#ifndefIIC_H#defineIIC_HvoidIIC_Start(void);//根据实际函数参数和返回值类型声明#endif```步骤2:在oled.c中包含头文件```c//oled.c#include"iic.h"//包含IIC_Start的声明```步骤3:检查函数定义在定义IIC_Start的源文件(比如iic.c)中,确保函数定义与声明一致:```c//iic.c#include"iic.h"voidIIC_Start(void)//实际函数定义{//函数体}```步骤4:如果IIC_Start函数定义在oled.c中,那么确保在调用之前有声明或定义。方法一:将函数定义放在调用之前。方法二:在调用之前添加函数声明:```c//oled.c中调用IIC_Start之前voidIIC_Start(void);//函数声明```注意:根据引用[2]中提到,Keil工程中通常将.c文件加入工程,而.h文件放在同一目录下(或包含路径中),编译器会自动寻找。因此,确保头文件在包含路径中。如果问题仍然存在,检查:-头文件是否被正确包含(检查文件路径)-函数声明和定义是否一致(包括返回类型、参数列表)示例:假设在oled.c中有如下调用:```cvoidWriteCommandToOLED12864(unsignedcharCommand){IIC_Start();//这里调用了IIC_Start//...其他代码}```但IIC_Start的定义在oled.c的后面:```cvoidIIC_Start(){//启动IIC总线的代码}```这时,编译器在调用IIC_Start时还未看到它的声明或定义,就会产生警告。解决方法:在oled.c的开头(在包含头文件之后,任何函数之前)添加函数声明:```c#include"oled.h"//假设有oled.h//添加IIC_Start的声明voidIIC_Start(void);```或者将IIC_Start函数的定义移到WriteCommandToOLED12864函数之前。总结:warningC206表示函数在使用前没有声明。解决方法是确保在调用函数之前有函数原型声明。相关问题:1.如何正确使用头文件声明函数?2.在Keil中如何设置头文件包含路径?3.函数声明和函数定义有什么区别?4.为什么有时候函数不声明也能编译通过?5.如何避免函数声明中的常见错误(如参数不匹配)?</think>### 解决编译警告 `warning C206: 'IIC_Start': missing function-prototype` 这个警告表示编译器在调用 `IIC_Start()` 函数前没有找到它的原型声明。以下是完整的解决方案: #### 问题原因分析 1. **函数声明缺失** 编译器在调用 `IIC_Start()` 时未看到其声明,违反C语言的"先声明后使用"规则[^1] 2. **头文件未包含** 函数声明可能存在于头文件中,但调用文件未包含该头文件 3. **作用域问题** 函数声明在调用点之后(如放在源文件末尾) 4. **大小写敏感问题** `IIC_Start` 和实际声明可能存在大小写不一致(如 `iic_start`) #### 解决方案步骤 ##### 1. 在头文件中声明函数(推荐) ```c // iic.h #ifndef IIC_H // 头文件保护 #define IIC_H // 函数声明 void IIC_Start(void); // 注意分号结尾 #endif ``` ##### 2. 在调用文件中包含头文件 ```c // oled.c #include "iic.h" // 包含声明 void WriteCommandToOLED12864(uchar Command) { IIC_Start(); // 此时编译器已知函数原型 // ...其他操作 } ``` ##### 3. 检查函数定义一致性 确保声明与定义完全匹配: ```c // iic.c // 定义必须与声明一致 void IIC_Start(void) // void表示无参数 { // 具体实现代码... SDA = 1; SCL = 1; // ... } ``` ##### 4. 临时解决方案(不推荐) 若无法修改头文件,可在调用前添加临时声明: ```c // oled.c void IIC_Start(void); // 临时声明 void WriteCommandToOLED12864(uchar Command) { IIC_Start(); // 调用 // ... } ``` #### 关键注意事项 1. **声明与定义匹配** - 返回类型必须一致(如 `void`) - 参数列表必须一致(`void` 表示无参数) - 大小写完全一致 2. **头文件管理最佳实践** - 所有函数声明放在 `.h` 头文件中[^2] - 源文件包含对应的头文件 - 使用头文件保护防止重复包含 3. **Keil工程设置** 确保头文件路径已添加到工程设置: ``` Project → Options for Target → C51 → Include Paths ``` #### 错误示例分析 ```c // 错误1:声明后缺少分号 void IIC_Start(void) // 缺少分号! // 错误2:大小写不一致 void iic_start(void); // 声明 IIC_Start(); // 调用 → 大小写不匹配 // 错误3:参数列表不一致 void IIC_Start(int); // 声明 IIC_Start(); // 调用 → 参数不匹配 ``` #### 完整解决方案示例 ```c /*---------- iic.h ----------*/ #ifndef IIC_H #define IIC_H void IIC_Start(void); void IIC_Stop(void); void IIC_SendByte(uchar dat); #endif /*---------- oled.c ----------*/ #include "reg52.h" #include "iic.h" // 关键包含 void WriteCommandToOLED12864(uchar Command) { IIC_Start(); // 正确调用 IIC_SendByte(0x78); // OLED地址 // ...其他操作 } ``` > **调试提示**:如果仍有警告,检查函数名拼写(如 `IIC_Start` vs `IIC_start`),80%的类似错误源于大小写不一致或缺少头文件包含[^1][^2]。 ### 相关问题 1. 如何在Keil C51中正确配置头文件搜索路径? 2. C语言函数声明和定义的区别是什么? 3. 为什么有些编译器允许隐式函数声明而C51不行? 4. 如何避免头文件循环包含问题? 5. C51编程中函数参数传递有哪些特殊限制?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值