🎀 文章作者:二土电子
🌸 关注文末公众号获取其他资料和工程文件!
🐸 期待大家一起学习交流!
OLED是开发时常用的显示器件,这里介绍的是0.96寸的四针脚OLED,IIC控制。
供电电压在3V~5V,用3.3V即可。
I2C通信接口由从地址位DC、I2C总线数据信号SDA(输出SDAOUT/D2输出,SDAIN/D1输入)和I2C总线时钟信号SCL(D0)组成。数据信号和时钟信号都必须连接到上拉电阻器上。
从属地址位(SA0)必须在通过I2C总线传输或接收任何信息之前,先识别该从属地址。设备将响应从地址,后面是从地址位(“SA0”位)和读/写选择位(“R/W#”,具有以下字节格式的位)
“SA0”位为从属地址提供了一个扩展位。可以选择“0111100”或“0111101”作为OLED的从属地址。D/C# pin作为SA0进行从属地址的选择”。“收发#”位用于确定I2c总线接口的操作模式。R/W#=1,它处于读取模式。R/W#=0,它处于写模式。
从属地址 | DC | 说明 |
---|---|---|
0111 100(0x78) | DC = 0 | 默认值 |
0111 1010(0x7A) | DC = 1 | 用户需要切换电阻位置来修改IIC地址 |
数据手册中会给出一些底层程序,可以通过这写程序快速的了解到如何使用。后面的程序设计中会详细介绍这部分,这里就不再做介绍了。
IIC(Inter-Integrated Circuit)其实是IICBus简称,中文是集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。IIC支持一主多从,主机通过寻址的方式呼叫从机,然后进行数据传输。
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有从设备的SDA和SLK都接到总线上。
IIC协议中有两种帧,一种是地址帧,用来寻找从设备。一种是数据帧,用来作主从机之间的数据交互。IIC协议还规定了起始信号,终止信号和应答信号。
起始信号和终止信号都是由主机发送的,起始信号产生之后,总线处于被占用的状态,在终止信号产生之后,总线就处于空闲状态。
当从机接收完一帧时,会发送一个应答信号。应答信号为低电平时,规定为有效应答(ACK,简称应答位),表示接收器已经成功地接受了该字节应答位为高电平时,规定为非应答信号(NACK),一般表示接收器接收该字节没有成功。
IIC通信协议有自己的数据格式,每一个字节必须保证是8bit长度。数据传送时,先传送最高位,每一个被传送的字节后面都必须跟随1bit的应答位(即每一帧数据一共有9bit)。
主设备往从设备写入数据需要有下面的过程
OLED初始化包括两部分,一部分是初始化STM32的GPIO,另一部分是根据数据手册提供的程序初始化OLED。STM32F103ZET6提供了两个IIC接口
SCL | SDA | |
---|---|---|
IIC1 | PB6 | PB7 |
IIC2 | PB10 | PB11 |
实际使用软件模拟IIC时,使用任意的GPIO即可,不必要使用上面规定的IIC引脚。这里只是使用了IIC1的引脚而已。
首先是数据手册提供的IIC相关函数
/**==============================================================================*函数名称:IIC_delay*函数功能:IIC延时*输入参数:无*返回值:无*备 注:数据手册提供*==============================================================================*/
void IIC_delay (void)
{u8 t = 1;while (t--);
}
/**==============================================================================*函数名称:I2C_Start*函数功能:IIC起始信号*输入参数:无*返回值:无*备 注:数据手册提供*==============================================================================*/
void I2C_Start (void)
{OLED_SDA_Set();OLED_SCL_Set();IIC_delay();OLED_SDA_Clr();IIC_delay();OLED_SCL_Clr();IIC_delay();
}
/**==============================================================================*函数名称:I2C_Stop*函数功能:IIC终止信号*输入参数:无*返回值:无*备 注:数据手册提供*==============================================================================*/
void I2C_Stop (void)
{OLED_SDA_Clr();OLED_SCL_Set();IIC_delay();OLED_SDA_Set();
}
/**==============================================================================*函数名称:I2C_WaitAck*函数功能:IIC等待应答*输入参数:无*返回值:无*备 注:数据手册提供*==============================================================================*/
void I2C_WaitAck (void)
{OLED_SDA_Set();IIC_delay();OLED_SCL_Set();IIC_delay();OLED_SCL_Clr();IIC_delay();
}
/**==============================================================================*函数名称:Send_Byte*函数功能:写入一个字节*输入参数:dat:需要写入的数据*返回值:无*备 注:数据手册提供*==============================================================================*/
void Send_Byte (u8 dat)
{u8 i;for (i = 0;i < 8;i ++){// 发送数据时,从高位依次写入if (dat & 0x80){OLED_SDA_Set();}else{OLED_SDA_Clr();}IIC_delay();OLED_SCL_Set();IIC_delay();OLED_SCL_Clr();// dat左移1位dat <<= 1;}
}
/**==============================================================================*函数名称:OLED_WR_Byte*函数功能:IIC发送一个字节数据*输入参数:dat:要发送的数据;mode:0是指令,1是数据*返回值:无*备 注:无*==============================================================================*/
void OLED_WR_Byte (u8 dat,u8 mode)
{I2C_Start();Send_Byte(0x78); // 寻址I2C_WaitAck();// 发送数据if (mode){Send_Byte(0x40);}// 发送指令else{Send_Byte(0x00);}I2C_WaitAck();Send_Byte(dat);I2C_WaitAck();I2C_Stop();
}
/**==============================================================================*函数名称:Drv_Oled_Init*函数功能:初始化OLED*输入参数:无*返回值:无*备 注:无*==============================================================================*/
void Drv_Oled_Init (void)
{// 结构体定义GPIO_InitTypeDef GPIO_InitStructure;// 开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);// 初始化GPIO结构体GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽式输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);// 全部拉高,IIC处于空闲状态GPIO_SetBits(GPIOB,GPIO_Pin_6 | GPIO_Pin_7); // 根据数据手册提供的例程,初始化OLEDdelay_ms(200); // 延时200ms OLED_WR_Byte(0xAE,OLED_CMD);//--display offOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address OLED_WR_Byte(0xB0,OLED_CMD);//--set page addressOLED_WR_Byte(0x81,OLED_CMD); // contract controlOLED_WR_Byte(0xFF,OLED_CMD);//--128 OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverseOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 dutyOLED_WR_Byte(0xC8,OLED_CMD);//Com scan directionOLED_WR_Byte(0xD3,OLED_CMD);//-set display offsetOLED_WR_Byte(0x00,OLED_CMD);// OLED_WR_Byte(0xD5,OLED_CMD);//set osc divisionOLED_WR_Byte(0x80,OLED_CMD);// OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode offOLED_WR_Byte(0x05,OLED_CMD);// OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge PeriodOLED_WR_Byte(0xF1,OLED_CMD);// OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartionOLED_WR_Byte(0x12,OLED_CMD);// OLED_WR_Byte(0xDB,OLED_CMD);//set VcomhOLED_WR_Byte(0x30,OLED_CMD);// OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enableOLED_WR_Byte(0x14,OLED_CMD);// OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}
下面是相关宏定义
// SCL
#define OLED_SCL_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_6)
#define OLED_SCL_Set() GPIO_SetBits(GPIOB,GPIO_Pin_6)
// SDA
#define OLED_SDA_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_7)
#define OLED_SDA_Set() GPIO_SetBits(GPIOB,GPIO_Pin_7)#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
数据手册中提供了一些指令,在编写OLED控制函数时可以参考。
根据数据手册描述,发送指令“1010 111x0”,其中x0为0时,显示关闭,x0为1时,显示开启。因此,显示开关程序如下
/**==============================================================================*函数名称:Med_Oled_Display_On*函数功能:开启OLED显示*输入参数:无*返回值:无*备 注:无*==============================================================================*/
void Med_Oled_Display_On(void)
{OLED_WR_Byte(0X8D,OLED_CMD); // 设置充电泵启用/禁用OLED_WR_Byte(0X14,OLED_CMD); // 设置(0x10)禁用OLED_WR_Byte(0XAF,OLED_CMD); // DISPLAY ON
}
/**==============================================================================*函数名称:Med_Oled_Display_Off*函数功能:关闭OLED显示*输入参数:无*返回值:无*备 注:无*==============================================================================*/
void OLED_Display_Off(void)
{OLED_WR_Byte(0X8D,OLED_CMD); // 设置充电泵启用/禁用OLED_WR_Byte(0X10,OLED_CMD); // 设置高列地址OLED_WR_Byte(0XAE,OLED_CMD); // DISPLAY OFF
}
想要显示汉字,需要先创建字库。汉字取模使用PCtoLCD2002完美版,取模时的配置已经标注
// 汉字字模数据结构定义
struct Cn16CharTypeDef // 汉字字模数据结构
{unsigned char Index[2]; // 汉字内码索引,一个汉字占两个字节 unsigned char Msk[32]; // 点阵码数据(16*16/8)
};// 汉字取模要求
// 阴码,逆向,列行式,16X16
struct Cn16CharTypeDef const CnChar16x16[]=
{
"太",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,/*"太",0*/"陈",0x00,0xFE,0x22,0x5A,0x86,0x08,0x88,0x68,0x18,0x0F,0xE8,0x08,0x08,0x08,0x08,0x00,
0x00,0xFF,0x04,0x08,0x07,0x20,0x11,0x0D,0x41,0x81,0x7F,0x01,0x05,0x09,0x30,0x00,/*"陈",1*/"抱",0x10,0x10,0x10,0xFF,0x90,0x20,0x10,0xEC,0x27,0x24,0x24,0xE4,0x04,0xFC,0x00,0x00,
0x02,0x42,0x81,0x7F,0x00,0x00,0x00,0x3F,0x42,0x42,0x4A,0x53,0x48,0x47,0x70,0x00,/*"抱",2*/"不",0x00,0x02,0x02,0x02,0x02,0x82,0x42,0xF2,0x0E,0x42,0x82,0x02,0x02,0x02,0x00,0x00,
0x10,0x08,0x04,0x02,0x01,0x00,0x00,0xFF,0x00,0x00,0x00,0x01,0x02,0x0C,0x00,0x00,/*"不",3*/"动",0x40,0x44,0xC4,0x44,0x44,0x44,0x40,0x10,0x10,0xFF,0x10,0x10,0x10,0xF0,0x00,0x00,
0x10,0x3C,0x13,0x10,0x14,0xB8,0x40,0x30,0x0E,0x01,0x40,0x80,0x40,0x3F,0x00,0x00,/*"动",4*/};
汉字显示函数如下
/**==============================================================================*函数名称:Med_Oled_ShowCHinese16x16*函数功能:显示一组16*16的汉字*输入参数:x:横坐标;y:纵坐标(0~3);cn:要显示的汉字*返回值:无*备 注:无*==============================================================================*/
void Med_Oled_ShowCHinese16x16 (u8 x,u8 y,u8 *cn)
{ u8 i,num;while (*cn != '