IIC协议
一、IIC协议
1.1 IIC协议概述
IIC全称Inter-Integrated Circuit (集成电路总线)
是由PHILIPS(飞利浦)公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双工同步通信方式
特点
- 简单性和有效性。
由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件
- 一根线(SDA),同一时间只能发送或者是接收。、
- 蓝牙串口是两个线
- IIC有两条总线,一条双向的串行数据线SDA,一条串行时钟线SCL。
- SDA(Serial data):串行数据线,用来传送数据;
- SCL(Serial clock line):时钟线,用来控制数据发送的时序。
- 多主控(multimastering)
其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
构成
IIC串行总线一般有两根信号线,
- 一根是双向的数据线SDA,
- 另一根是时钟线SCL,
其时钟信号是由主控器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。
1.2 IIC协议
IIC总线在传输数据的过程中一共有三种类型信号,分别为:开始信号、结束信号和应答信号。
//起始位,停止位,数据位,速度
1. 起终信号
这些信号中,起始信号是必需的,结束信号和应答信号
- 起始信号
- SCL =1
- SDA = 1;
- 延时
- while(!sda)
- 延时
- 终止信号
2. 应答信号
发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节; 应答信号为高电平时,规定为非应答(ACK),一般表示接收器接收该字节没有成功。
sda(应答信号)为高电平,延迟5微妙,(时钟线)SCL为高电平。延迟5微妙之后,去读sda(应答信号),为高\低电平,高电平(非应答)\低电平(应答),延迟5微妙,(时钟线)SCL为低电平,延迟5微妙
char IIC_ACK() // 应答
{char flag;sda = 1;//就在时钟脉冲9期间释放数据线_nop_();scl = 1;_nop_();flag = sda;_nop_();scl = 0;_nop_();return flag;
}
3. 数据发送的字节
注意当scl属于高电平期间,就是在传输数据,SDA不能出现数据翻转,防止系统误以为是起终止信号,当SCL属于低电平期间时,数据可以进行翻转
- 代码实现
void IIC_Send_Byte(char dataSend)
{int i;for(i = 0;i<8;i++){scl = 0;//scl拉低,让sda做好数据准备sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda_nop_();//发送数据建立时间scl = 1;//scl拉高开始发送_nop_();//数据发送时间scl = 0;//发送完毕拉低_nop_();//dataSend = dataSend << 1;}
}
- OLED写命令
- 写命令/数据的代码
void Oled_Write_Cmd(char dataCmd)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 写入指令/数据IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 写入指令/数据IIC_Send_Byte(dataData);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}
4. OLED的寻址模式
- 如何显示一个点?
有三种,分别位页地址模式,水平地址模式和垂直地址模式,可以通过一下表格进行配置
内存管理
- 显示一行横线
如果写入0x08(b00001000)会显示什么呢
一个字节负责一个Page的一列显示
- 代码实现
#include "reg52.h"
#include "intrins.h"sbit scl = P2^5;
sbit sda = P2^6;void IIC_Start()
{sda = 1;scl = 1;_nop_();sda = 0;_nop_();
}void IIC_Stop()
{sda = 0;scl = 1;_nop_();sda = 1;_nop_();
}char IIC_ACK()
{char flag;sda = 1;//就在时钟脉冲9期间释放数据线_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;//scl拉低,让sda做好数据准备sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda_nop_();//发送数据建立时间scl = 1;//scl拉高开始发送_nop_();//数据发送时间scl = 0;//发送完毕拉低_nop_();//dataSend = dataSend << 1;}
}void Oled_Write_Cmd(char dataCmd)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(0)000000 写入命令 IIC_Send_Byte(0x00);// 5. ACKIIC_ACK();//6. 写入指令/数据IIC_Send_Byte(dataCmd);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Write_Data(char dataData)
{// 1. start()IIC_Start();// // 2. 写入从机地址 b0111 1000 0x78IIC_Send_Byte(0x78);// 3. ACKIIC_ACK();// 4. cotrol byte: (0)(1)000000写入数据IIC_Send_Byte(0x40);// 5. ACKIIC_ACK();///6. 写入指令/数据IIC_Send_Byte(dataData);//7. ACKIIC_ACK();//8. STOPIIC_Stop();
}void Oled_Init(void){ // 初始化Oled_Write_Cmd(0xAE);//--display offOled_Write_Cmd(0x00);//---set low column addressOled_Write_Cmd(0x10);//---set high column addressOled_Write_Cmd(0x40);//--set start line address Oled_Write_Cmd(0xB0);//--set page addressOled_Write_Cmd(0x81); // contract controlOled_Write_Cmd(0xFF);//--128 Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverseOled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)Oled_Write_Cmd(0x3F);//--1/32 dutyOled_Write_Cmd(0xC8);//Com scan directionOled_Write_Cmd(0xD3);//-set display offsetOled_Write_Cmd(0x00);//Oled_Write_Cmd(0xD5);//set osc divisionOled_Write_Cmd(0x80);//Oled_Write_Cmd(0xD8);//set area color mode offOled_Write_Cmd(0x05);//Oled_Write_Cmd(0xD9);//Set Pre-Charge PeriodOled_Write_Cmd(0xF1);//Oled_Write_Cmd(0xDA);//set com pin configuartionOled_Write_Cmd(0x12);//Oled_Write_Cmd(0xDB);//set VcomhOled_Write_Cmd(0x30);//Oled_Write_Cmd(0x8D);//set charge pump enableOled_Write_Cmd(0x14);//Oled_Write_Cmd(0xAF);//--turn on oled panel
}void main()
{int a = 10;int i;//1. OLED初始化Oled_Init();//2. 选择一个位置//2.1 确认页寻址模式Oled_Write_Cmd(0x20);Oled_Write_Cmd(0x02);//2.2 选择PAGE 0 1011 0000// 0xB0(8421法)Oled_Write_Cmd(0xB0);
// 发送指令0xB0,
// 这个指令选择第0页(PAGE 0)。
// OLED的页地址从0xB0到0xB7,每一个指令分别对应一个页。//3. 显示一个行 列地址会自动向后偏移Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);Oled_Write_Data(0x08);while(1);
}
- 在最后一行显示一个点
- Oled_Write_Cmd(0x0f); Oled_Write_Cmd(0x17);是如何显示为最后一列的
-
- 列数为128列
- 2的7方 =128
- 低四位:0000 1111 0x0F (取其中的低四位F)
- 高四位:1000 01111 0x17(取其中的低四位7)
- 结果为:0x7F 换成十进制为 7^16+15^1=127
为127列 +0列,是最后一行
Oled_Write_Cmd(0x0f);Oled_Write_Cmd(0x17);Oled_Write_Data(0x08);
- 清屏
void Oled_Clear()
{unsigned char i,j; // 定义两个无符号字符变量 i 和 j,用于循环控制for(i=0;i<8;i++){ // 外层循环,循环8次,分别清除8页(每页8行,总共64行)Oled_Write_Cmd(0xB0 + i);// 设置当前页地址,从 page0 到 page7// 每个 page 从第 0 列开始Oled_Write_Cmd(0x00); // 设置列地址的低四位为 0Oled_Write_Cmd(0x10); // 设置列地址的高四位为 1,结合低四位,列地址为 0// 从第 0 列到第 127 列,依次写入 0,每写入一个数据,列地址会自动增加for(j = 0;j<128;j++){ // 内层循环,循环128次,清除当前页的所有列(每页128列)Oled_Write_Data(0); // 向当前列写入数据 0,即清除该列}}
}
- 详细解释:
-
- 变量声明
unsigned char i,j;
-
- 定义两个无符号字符变量
i
和j
,用于循环控制。
- 定义两个无符号字符变量
-
- 外层循环
for(i=0;i<8;i++){
-
- 外层循环循环8次,对应 OLED 显示屏的8页(每页8行,总共64行)。
-
- 设置当前页地址
Oled_Write_Cmd(0xB0 + i);
-
- 设置当前页地址。从
0xB0
到0xB7
分别对应page0
到page7
。
- 设置当前页地址。从
-
- 设置列地址
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
-
- 将列地址设置为 0。首先发送
0x00
设置低四位为 0,然后发送0x10
设置高四位为 0,这样列地址被设置为0x00
。
- 将列地址设置为 0。首先发送
-
- 内层循环
for(j = 0;j<128;j++){
-
- 内层循环循环128次,对应每页的128列。
-
- 清除当前列
Oled_Write_Data(0);
-
- 向当前列写入数据
0
,即清除该列。写入数据后,列地址会自动增加,直到当前页的所有列都被清除。
- 向当前列写入数据
- 另一种写法
void Oled_Screen_Clear(void){char i,n;Oled_Write_Cmd (0x20); // 设置内存寻址模式Oled_Write_Cmd (0x02); // 选择页寻址模式for(i=0;i<8;i++){Oled_Write_Cmd(0xb0+i); // 设置页地址(0~7)Oled_Write_Cmd(0x00); // 设置显示位置列低地址Oled_Write_Cmd(0x10); // 设置显示位置列高地址 for(n=0;n<128;n++) // 循环128次,对应每页的128列Oled_Write_Data(0x00); // 写入数据0,清除该列}
}
- 显示一个字符“A”
/*-- 文字: A --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
char A1[8] = {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00};
char A2[8] = {0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20};void main()
{int i;//1. OLED初始化Oled_Init();//2. 选择一个位置//2.1 确认页寻址模式Oled_Write_Cmd(0x20);Oled_Write_Cmd(0x02);Oled_Clear();//2.2 选择PAGE0 1011 0000// 0xB0// 打印字符‘A’Oled_Write_Cmd(0xB0); // 打印上半身Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);for(i=0;i<8;i++){Oled_Write_Data(A1[i]);}Oled_Write_Cmd(0xB1);// 打印下半身Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);for(i=0;i<8;i++){Oled_Write_Data(A2[i]);}while(1);
}
5. OLED 显示字符 'A' 的实现
先使用点阵液晶取模软件生成字符
-
- 初始化 OLED
Oled_Init();
-
- 选择页寻址模式
Oled_Write_Cmd(0x20);
Oled_Write_Cmd(0x02);
-
- 清屏
Oled_Clear();
-
- 显示字符 'A'
-
- 显示上半部分
Oled_Write_Cmd(0xB0); // 设置页地址为 PAGE0
Oled_Write_Cmd(0x00); // 设置列地址低位
Oled_Write_Cmd(0x10); // 设置列地址高位
for (i = 0; i < 8; i++) {Oled_Write_Data(A1[i]);
}
-
- 显示下半部分
Oled_Write_Cmd(0xB1); // 设置页地址为 PAGE1
Oled_Write_Cmd(0x00); // 设置列地址低位
Oled_Write_Cmd(0x10); // 设置列地址高位
for (i = 0; i < 8; i++) {Oled_Write_Data(A2[i]);
}
6. 显示一个字符串“LXL”
/*-- 文字: L --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
code char L1[8] = {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00}; // "L" 字符的上半部分点阵数据
code char L2[8] = {0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00}; // "L" 字符的下半部分点阵数据/*-- 文字: X --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
code char L3[8] = {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08}; // "X" 字符的上半部分点阵数据
code char L4[8] = {0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20}; // "X" 字符的下半部分点阵数据void main()
{int i,j; // 定义循环变量 i 和 j// 1. OLED初始化Oled_Init(); // 调用 OLED 初始化函数// 2. 选择一个位置// 2.1 确认页寻址模式Oled_Write_Cmd(0x20); // 设置内存寻址模式Oled_Write_Cmd(0x02); // 设置为页寻址模式Oled_Clear(); // 清屏// 2.2 选择PAGE0 (1011 0000)// 设置起始位置为第0页 (Page 0)Oled_Write_Cmd(0xB0); // 设置页地址为 Page 0Oled_Write_Cmd(0x00); // 设置列地址低4位为 0x00Oled_Write_Cmd(0x10); // 设置列地址高4位为 0x10,即列地址为 0x00// 显示第一个字符 "L" 的上半部分for(i=0;i<8;i++){Oled_Write_Data(L1[i]); // 依次写入 "L" 字符上半部分的点阵数据}// 显示字符 "X" 的上半部分for(i=0;i<8;i++){Oled_Write_Data(L3[i]); // 依次写入 "X" 字符上半部分的点阵数据}// 再次显示字符 "L" 的上半部分for(i=0;i<8;i++){Oled_Write_Data(L1[i]); // 依次写入 "L" 字符上半部分的点阵数据}// 选择PAGE1 (1011 0001)// 设置起始位置为第1页 (Page 1)Oled_Write_Cmd(0xB1); // 设置页地址为 Page 1Oled_Write_Cmd(0x00); // 设置列地址低4位为 0x00Oled_Write_Cmd(0x10); // 设置列地址高4位为 0x10,即列地址为 0x00// 显示字符 "L" 的下半部分for(j=0;j<8;j++){Oled_Write_Data(L2[j]); // 依次写入 "L" 字符下半部分的点阵数据}// 显示字符 "X" 的下半部分for(j=0;j<8;j++){Oled_Write_Data(L4[j]); // 依次写入 "X" 字符下半部分的点阵数据}// 再次显示字符 "L" 的下半部分for(j=0;j<8;j++){Oled_Write_Data(L2[j]); // 依次写入 "L" 字符下半部分的点阵数据}while(1); // 无限循环,保持显示
}
- L1、L2、L3、L4 数组: 每个字符的点阵数据分为上半部分(L1、L3)和下半部分(L2、L4)。在 OLED 显示屏上,数据需要分开写入不同的页面(page)才能显示完整的字符。
Oled_Write_Cmd
函数: 用于发送命令到 OLED,比如设置页面地址、列地址等。Oled_Write_Data
函数: 用于发送数据到 OLED,比如每个字符的点阵数据。Oled_Init
函数: 初始化 OLED 显示屏,使其进入可操作状态。Oled_Clear
函数: 清空 OLED 显示屏的内容。- 两个
for
循环: 第一个 for 循环显示字符的上半部分(Page 0),第二个 for 循环显示字符的下半部分(Page 1)。这样分开写入可以确保字符显示正确。 - 原始方法的优势
- 字符点阵的排列方式:在原始方法中,每个字符的数据分别通过单独的
for
循环写入 OLED。这意味着第一个字符的所有点阵数据(上半部分)会首先写入,然后是第二个字符的所有点阵数据,依次类推。这样可以确保每个字符的数据连续写入 OLED 显示屏的相应列,不会混淆。 - 显示顺序:每个
for
循环对应一个字符的数据块,这样可以保持字符在 OLED 显示屏上的正确显示顺序。例如,第一个字符“上”的上半部分数据在s1
数组中,依次写入 OLED 显示屏的第一页;第二个字符“官”的上半部分数据在g1
数组中,依次写入同一页面的后续列。
- 修改后的方法的问题
- 数据混淆:如果你使用修改后的方法,将所有字符的数据在一个
for
循环中交替写入,字符的点阵数据会交错。例如,第一个字符“上”的第一列数据(s1[0])写入后,接着是第二个字符“官”的第一列数据(g1[0]),然后是第三个字符的数据(k1[0]),依次类推。这会导致字符的显示数据混在一起,无法正确显示。 - 位置不对:OLED 显示屏的列地址会在写入数据后自动递增,因此在单个
for
循环中交替写入不同字符的数据会导致它们无法正确排列在显示屏上。每个字符的数据会被打乱,显示效果会非常混乱。
- 结论
原始方法通过多个 for
循环确保每个字符的数据独立、连续地写入 OLED 显示屏,保持字符的正确显示顺序和位置。修改后的方法则会导致字符数据交错,显示效果混乱。因此,必须保持原始方法的写法,以确保 OLED 显示屏上字符的正确显示。
- 补充说明
为了更好地理解这一点,可以试想一个示例:
假设有两个字符“A”和“B”,它们的点阵数据如下:
- A 的上半部分数据:
{A1[0], A1[1], A1[2], ..., A1[7]}
- B 的上半部分数据:
{B1[0], B1[1], B1[2], ..., B1[7]}
如果使用原始方法:
- A 的上半部分数据完整写入 OLED,接着是 B 的上半部分数据完整写入。
如果使用修改后的方法:
- A 和 B 的数据交替写入,导致显示效果完全混乱。
因此,保持原始方法的 for
循环写法是正确的,确保字符正确显示。
7. ode 关键字
code 指定字符存储位置,以防存储空间过大
在 C51 单片机编程中,可以使用 code
关键字来将常量数据存储在代码存储区(Flash)中,而不是在数据存储区(RAM)中。这对于存储大量的字符点阵数据非常有用,因为 RAM 的空间通常非常有限,而 Flash 的空间相对较大。
8. 显示文字”越加越强“
/*-- 文字: 越 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char y1[16] = {0x40,0x48,0x48,0xFF,0x48,0x48,0x00,0xF8,0x08,0x08,0xFF,0x08,0x89,0x6A,0x00,0x00};
code char y2[16] = {0x80,0x7E,0x10,0x3F,0x44,0x44,0x40,0x4F,0x44,0x50,0x49,0x46,0x49,0x5C,0x40,0x00};/*-- 文字: 加 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char j1[16] = {0x10,0x10,0x10,0xFF,0x10,0x10,0xF0,0x00,0x00,0xF8,0x08,0x08,0x08,0xF8,0x00,0x00};
code char j2[16] = {0x80,0x40,0x30,0x0F,0x40,0x80,0x7F,0x00,0x00,0x7F,0x20,0x20,0x20,0x7F,0x00,0x00};/*-- 文字: 越 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char y3[16] = {0x40,0x48,0x48,0xFF,0x48,0x48,0x00,0xF8,0x08,0x08,0xFF,0x08,0x89,0x6A,0x00,0x00};
code char y4[16] = {0x80,0x7E,0x10,0x3F,0x44,0x44,0x40,0x4F,0x44,0x50,0x49,0x46,0x49,0x5C,0x40,0x00};/*-- 文字: 强 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char q1[16] = {0x02,0xE2,0x22,0x22,0x3E,0x00,0x80,0x9E,0x92,0x92,0xF2,0x92,0x92,0x9E,0x80,0x00};
code char q2[16] = {0x00,0x43,0x82,0x42,0x3E,0x40,0x47,0x44,0x44,0x44,0x7F,0x44,0x44,0x54,0xE7,0x00};void main()
{int i;//1. OLED初始化Oled_Init();//2. 选择一个位置//2.1 确认页寻址模式Oled_Write_Cmd(0x20);Oled_Write_Cmd(0x02);Oled_Clear(); // 清屏//2.2 选择PAGE0 1011 0000// 0xB0// 从第一行第0列开始打印上半部分Oled_Write_Cmd(0xB0);Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);for(i=0;i<16;i++){Oled_Write_Data(y1[i]);}for(i=0;i<16;i++){Oled_Write_Data(j1[i]);}for(i=0;i<16;i++){Oled_Write_Data(y3[i]);}for(i=0;i<16;i++){Oled_Write_Data(q1[i]);}// 从第二行第0列开始打印下半部分Oled_Write_Cmd(0xB1);Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);for(i=0;i<16;i++){Oled_Write_Data(y2[i]);}for(i=0;i<16;i++){Oled_Write_Data(j2[i]);}for(i=0;i<16;i++){Oled_Write_Data(y4[i]);}for(i=0;i<16;i++){Oled_Write_Data(q2[i]);}while(1);
}
- 输出结果
9. 显示一张图片和一段字符串”lxl“
code unsigned char bmpImager[] = {/*-- 调入了一幅图像:C:\Users\86199\Pictures\无标题.bmp --*/
/*-- 宽度x高度=128x64 --128X8X8*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0xF0,0xD0,0xD6,0xD6,0xDE,0xDE,0xDE,0xDE,0x5E,0x5E,0x56,0x56,0x50,0x50,0x50,0x50,
0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0xD8,0x88,0x88,0x88,
0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x10,0x1F,0x08,0x08,0x08,0x0E,0x1A,0x1A,0x1A,
0x0E,0x02,0x02,0x02,0x0E,0x0A,0x0A,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};void Oled_Show_Image(unsigned char *image)
{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(image[j]);}}
}
/*-- 文字: L --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
code char L1[8] = {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00}; // "L" 字符的上半部分点阵数据
code char L2[8] = {0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00}; // "L" 字符的下半部分点阵数据/*-- 文字: X --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
code char L3[8] = {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08}; // "X" 字符的上半部分点阵数据
code char L4[8] = {0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20}; // "X" 字符的下半部分点阵数据void main()
{int i,j;//1. OLED初始化Oled_Init();//2. 选择一个位置//2.1 确认页寻址模式Oled_Write_Cmd(0x20);Oled_Write_Cmd(0x02);Oled_Clear();Oled_Show_Image(bmpImager);Oled_Write_Cmd(0xB0); // 设置页地址为 Page 0Oled_Write_Cmd(0x00); // 设置列地址低4位为 0x00Oled_Write_Cmd(0x10); // 设置列地址高4位为 0x10,即列地址为 0x00// 显示第一个字符 "L" 的上半部分for(i=0;i<8;i++){Oled_Write_Data(L1[i]); // 依次写入 "L" 字符上半部分的点阵数据}// 显示字符 "X" 的上半部分for(i=0;i<8;i++){Oled_Write_Data(L3[i]); // 依次写入 "X" 字符上半部分的点阵数据}// 再次显示字符 "L" 的上半部分for(i=0;i<8;i++){Oled_Write_Data(L1[i]); // 依次写入 "L" 字符上半部分的点阵数据}// 选择PAGE1 (1011 0001)// 设置起始位置为第1页 (Page 1)Oled_Write_Cmd(0xB1); // 设置页地址为 Page 1Oled_Write_Cmd(0x00); // 设置列地址低4位为 0x00Oled_Write_Cmd(0x10); // 设置列地址高4位为 0x10,即列地址为 0x00// 显示字符 "L" 的下半部分for(j=0;j<8;j++){Oled_Write_Data(L2[j]); // 依次写入 "L" 字符下半部分的点阵数据}// 显示字符 "X" 的下半部分for(j=0;j<8;j++){Oled_Write_Data(L4[j]); // 依次写入 "X" 字符下半部分的点阵数据}// 再次显示字符 "L" 的下半部分for(j=0;j<8;j++){Oled_Write_Data(L2[j]); // 依次写入 "L" 字符下半部分的点阵数据}while(1); // 无限循环,保持显示
}
先将照片设置成128x64像素,并且将照片保存为BMP类型的格式,然后使用字模识别软件,打开该图片。生成c51格式代码,封装成函数调用即可
void Oled_Show_Image(unsigned char *image)
{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(image[j]);}}
}
- 函数头部
c
复制代码
void Oled_Show_Image(unsigned char *image)
-
- 这个函数名为
Oled_Show_Image
,它接受一个指向无符号字符数组(即图像数据)的指针image
作为参数。
- 这个函数名为
- 变量声明
c
复制代码
unsigned char i;
unsigned int j;
-
- 声明了两个变量:
i
用于循环遍历OLED屏幕的页(共8页),j
用于遍历每一页中的像素数据。
- 声明了两个变量:
- 遍历每个页
c
复制代码
for(i=0; i<8; i++) {Oled_Write_Cmd(0xB0 + i); // 选择当前页(page0--page7)
-
- 这个
for
循环遍历了OLED屏幕的8个页,每个页包含8行像素(总共64行)。Oled_Write_Cmd(0xB0 + i);
选择了当前操作的页,i
的范围是从0到7,对应页地址从0xB0到0xB7。
- 这个
- 设置列地址
c
复制代码Oled_Write_Cmd(0x00); // 设置当前列的低位地址为0Oled_Write_Cmd(0x10); // 设置当前列的高位地址为0
-
- 这两条命令将当前页的列地址设置为0列(也就是从左边的第一列开始)。OLED显示屏的列地址有两部分:低位地址和高位地址,分别用0x00和0x10命令设置。
- 写入图像数据
c
复制代码for(j = 128 * i; j < (128 * (i + 1)); j++) {Oled_Write_Data(image[j]); // 写入当前页的图像数据}
}
-
- 这个
for
循环从图像数据中取出对应当前页的128个字节(对应128列)的数据并写入OLED屏幕。 j = 128 * i
计算当前页的起始位置,128 * (i + 1)
计算该页数据的结束位置。Oled_Write_Data(image[j]);
这一行将图像数组中的数据发送到OLED屏幕,显示在当前页的对应列上。
- 这个
- 总结
-
Oled_Show_Image
函数的主要功能是将一幅128x64像素的图像(分8页,每页128列)显示到OLED屏幕上。每个页的数据依次写入OLED显示屏的对应位置。
- 代码实现
相关文章:

IIC协议
一、IIC协议 1.1 IIC协议概述 IIC全称Inter-Integrated Circuit (集成电路总线) 是由PHILIPS(飞利浦)公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双工同步通信方式 特点 简单性和有效性。 由于接口直接在组件之上,…...

如何在linux系统上部署nginx
1)首先去 nginx.org/download 官网下载你所需要的版本 我这里是下载的 nginx-1-23-3.tar.gz 2)然后执行 yum -y install lrzsz 安装文件上传软件 执行 rz 选择你下载nginx的位置进行上传 yum -y install lrzsz 3)执行 tar -zxvf nginx-1.23…...

香港网站服务器抵御恶意攻击的一些措施
香港网站服务器因为在互联网中扮演着重要的角色,因此也在面临着网络中各种恶意攻击的威胁,为了确保香港网站服务器的安全和稳定运行,可以通过安全措施来进行防御,本文就来分享一些香港网站服务器来抵御恶意攻击的关键措施。 一、网…...

实战:docker部署filesite.io完美解决家庭相册需求-2024.8.10(测试成功)
https://wiki.onedayxyy.cn/docs/filesite.io-photot-install-full...

美团到店面经
redis中大key引起的问题 1、阻塞请求 Big Key对应的value较大,我们对其进行读写的时候,需要耗费较长的时间,这样就可能阻塞后续的请求处理。Redis的核心线程是单线程,单线程中请求任务的处理是串行的,前面的任务完不成…...

【CSS入门】第五课 - font字体
这一节,我们说一说font这个字体。做网页开发,网页中几乎不可能没有文字的,为了使网页更漂亮,用户体验更好。人们可算是绞尽脑汁,其中一部分就是在字体上下的大功夫。 接下来,我们学习一下,font…...

STM32-门电路-储存器-寄存器-STM32f1-MCU-GPIO-总线-keil5-点led-寄存器编程
1、门电路 门电路组成简单加法器: 二进制对电路的影响: 0和1代表无和有; 以下图例,演示与门:左1右1输出1; 电平标准:使用不同的电压表示数字0和1; 高电平:1࿱…...

【动态规划算法题记录】343. 整数拆分 | 96.不同的二叉搜索树
整数拆分 题目🔗 题目描述 给定一个正整数 n ,将其拆分为 k个正整数的和(k > 2),并使这些整数的乘积最大化。 返回你可以获得的最大乘积 。 思路分析 dp数组含义:dp[i]表示整数i拆分后的最大乘积。…...

网页上预览Excel文件
如何运行: 需要发布在服务器 如Tomcat 实例图片: 需要展示的文件: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>excel预览</title><link rel"stylesheet" href"…...

Unity射击游戏开发教程:(31)制造一定追踪行为的敌人
在本文中,我们将介绍如何在两种敌人行为之间切换。本文是前两篇文章的延续,分别介绍了敌人躲避玩家射击以及敌人不断旋转并向玩家射击的情况。我只是介绍如何在这两种行为之间进行转换。 这种新的敌人行为的目标: 当不开火时,敌人可以躲避玩家的射击。射击时,敌人无法躲避…...

springboot mybatis plus 固定查询条件及可选查询条件的组合查询,使用QueryWrapper.and()来解决。
1、我们在写查询SQL的时候,经常会碰到,比如,同一个类别下的某一个编号的物料信息,或者是同一批次的物料库存问题等等。 所属类别fid物料编号bm物料批次pc110.01.0220240807110.01.0320240807 210.02.0120240805 2、那么我…...

使用ollama取代openai的api进行graphRAG失败记录
pip install ollama pip install langchain_ollama graph_documents llm_transformer.convert_to_graph_documents(split_documents) print(graph_documents) 偶尔会成功,但是大部分是失败的: 报错记录如下,暂时没想到好的办法ÿ…...

MyBatis 配置与测试方式
目录 一,什么是MyBatis 二,准备工作 创建项目 配置数据库连接 持久层代码 单元测试 一,什么是MyBatis 简单来说,MyBatis 是一款优秀的持久层框架,用于简化JDBC的开发,能更简单完成程序与数据库之间…...

C#实现代理服务器
在C#中实现一个简单的代理服务器,可以使用System.Net.Sockets命名空间下的TcpListener类来监听客户端的连接请求,并使用TcpClient来处理与客户端的通信。以下是一个简单的代理服务器示例: using System; using System.IO; using System.Net;…...

react的路由实战使用
环境配置:vitetsreact18 1、安装包 npm i react-router-dom 2、 根路由配置以及路由挂载 a、在src下面创建router文件夹配置简单的路由信息: router/index.tsx import { createBrowserRouter } from "react-router-dom"; import UserLogin…...

python 字典转成类 构建类
目录 python 字典转成类 复杂嵌套示例: 动态实例化类 太好用了! python 字典转成类 class DictToClass:def __init__(self, dictionary):for key, value in dictionary.items():if isinstance(value, dict):# 如果值是字典,递归转换为类的实例setattr(self, key, DictToC…...

springboot 过滤器
1、过滤器的实现 springboot中过滤器通过实现接口Filter并重写init、doFilter、destroy三个方法。在三个方法中加入自己的业务逻辑处理。 【注意】Filter接口的完整包名在不同的jdk版中中的变化。这里示例中使用的版本为 open-jdk17。完整名称 jakarta.servlet.Filter。如果使…...

【C语言篇】深入理解指针1
文章目录 内存和地址内存编址 指针变量和地址取地址操作符指针变量和解引用操作符指针变量指针变量类型解引用操作符指针变量的大小 指针变量类型的意义指针的解引用指针-整数void*指针 const修饰指针指针运算指针-整数指针-指针指针的关系运算 野指针野指针成因如何规避野指针…...

IAP程序升级 与 电脑BIOS 的关系
IAP (In-Application Programming) 程序升级 IAP程序升级是一种技术,允许设备在运行过程中更新其自身的固件或软件,而不需要外部工具或设备的介入。这种技术特别适用于嵌入式系统和物联网(IoT)设备。其主要由三部分构成࿰…...

Java使用MQTT协议
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议)是一种轻量级的、基于发布/订阅模式的物联网通信协议。它构建于TCP/IP协议之上,由IBM在1999年发布。MQTT的主要特点包括: 轻量级与高效:M…...

等级+时间的优先级算法
简介 本算法为等级与时间结合计算对应优先级逻辑 等级越高者优先级越高 同等级下,时间越小者优先级越高 实现 主方法 calculatePriority import com.zk.blog.enums.TypeEnum; import org.apache.commons.lang3.StringUtils;/*** program: * description:* autho…...

物流仓库安全视频智能管理方案:构建全方位、高效能的防护体系
一、背景分析 随着物流行业的快速发展和仓储需求的日益增长,仓库安全成为企业运营中不可忽视的重要环节。传统的人工监控方式不仅效率低下,且难以做到全天候、无死角覆盖,给仓库资产和人员安全带来潜在风险。因此,引入仓库安全视…...

jackson反序列化漏洞
jackson反序列化漏洞 反序列化漏洞触发根因jackson介绍jackson反序列化漏洞关键点enableDefaultTypingactivateDefaultTypingJsonTypeInfo 漏洞触发场景漏洞复现环境引入依赖pocactivateDefaultTypingenableDefaultTypingJsonTypeInfo 参考 很久没写blog,最近慢慢开…...

Java | Leetcode Java题解之第328题奇偶链表
题目: 题解: class Solution {public ListNode oddEvenList(ListNode head) {if (head null) {return head;}ListNode evenHead head.next;ListNode odd head, even evenHead;while (even ! null && even.next ! null) {odd.next even.nex…...

100 Exercises To Learn Rust 挑战!准备篇
公司内部的学习会非常活跃!我也参与了Rust学习会,并且一直在研究rustlings。最近,我发现了一个类似于rustlings的新教程网站:Welcome - 100 Exercises To Learn Rust。 rustlings是基于Rust的权威官方文档《The Rust Programming…...

瑞_RabbitMQ_初识MQ
文章目录 1 初识MQ1.1 同步调用1.1.1 同步调用的优势1.1.2 同步调用的缺点 1.2 异步调用1.2.1 异步调用的角色1.2.2 异步调用的优势1.2.3 异步调用的缺点1.2.4 异步调用的场景 1.3 MQ技术选型 2 RabbitMQ2.1 安装2.1.1 资源准备2.1.2 安装步骤 2.2 RabbitMQ架构2.3 RabbitMQ管理…...

系统内存管理:虚拟内存、内存分段与分页、页表缓存TLB以及Linux内存管理
虚拟内存 虚拟内存是一种操作系统提供的机制,用于将每个进程分配的独立的虚拟地址空间映射到实际的物理内存地址空间上。通过使用虚拟内存,操作系统可以有效地解决多个应用程序直接操作物理内存可能引发的冲突问题。 在使用虚拟内存的情况下࿰…...

Java每日一练_模拟面试题5(堆和栈的区别)
在Java中,堆(Heap)和栈(Stack)是两个不同的内存区域,它们在存储内容、管理方式、空间大小、分配方式等多个方面存在显著的区别。以下是Java中堆和栈的主要区别: 1. 存储内容不同 堆࿱…...

传感器校正和测试
是 一。舵机在使用过程中为了防止手动扭动损坏其中的齿轮,一般会使用代码测试并校正到0位。 #include <Servo.h> Servo myservo; // 创建一个Servo对象 // 连接到舵机信号线的Arduino引脚 int servoPin 9; void setup() { myservo.attach(servoPin…...

Eclipse 悬浮提示:提高编程效率的利器
Eclipse 悬浮提示:提高编程效率的利器 引言 在当今的软件开发领域,Eclipse 是一款广受欢迎的集成开发环境(IDE)。它以其强大的功能和灵活性而著称,被全球的开发者用于各种编程语言和项目。Eclipse 的一个显著特点是其…...