声明:本专题主要针对训练营的同学开设的,因为了解到大家对于一些常用模块还是不会使用,因此开设次专栏去给大家讲解,如何在项目中来快速入门一些模块,转载请注明出处。
作者:渣渣鑫
专题:OLED的使用说明
OLED介绍
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。
OLED显示技术具有自发光的特性,采用非常薄的有机材料涂层和玻璃基板,当有电流通过时,这些有机材料就会发光,而且OLED显示屏幕可视角度大,并且能够节省电能,从2003年开始这种显示设备在MP3播放器上得到了应用。
LCD都需要背光,而OLED不需要,因为它是自发光的。这样同样的显示,OLED效果要来得好一些。以目前的技术,OLED的尺寸还难以大型化,但是分辨率确可以做到很高。
硬件配置
主要使用的IIC总线上的SDA数据线和SCL时钟线,这里接上拉电阻的目的是当iic总线处于空闲状态的时候,能够处于高电平状态。从图中我们可以看出来iic总线是支持多设备的。
软件配置(IIC协议层)
在配置IIC总线之前,我们需要先把相应的GPIO端口控制线打开
/*********************************** 重写IIC时序 *****************************************************/
//初始化GPIO
/*SDA输出模式 PA3*/
void OLED_SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_OLED_SDA, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = OLED_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
GPIO_Init(OLED_SDA_PORT, &GPIO_InitStructure);
}
/*SDA输入模式 PA3*/
void OLED_SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_OLED_SDA, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = OLED_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; /* // 浮空输入 */
GPIO_Init(OLED_SDA_PORT, &GPIO_InitStructure);
}
先要简单了解SDA与SCL的作用:当SCL处于高电平的时候,SDA上面的数据必须保持稳定。只有当SCL为低电平的时候,才允许SDA线的数据发生变化。
时序图必须要能看懂!
1、启动信号配置:SCL高电平期间,SDA电平由高到低跳变,我们代码就是去模拟这个过程。
/*i2c 起始信号*/
void OLED_Start(void)
{
OLED_SDA_OUT(); //sda线输出
OLED_SCL_HIGH;
OLED_SDA_HIGH;
i2c_delay();
/*当CLK为高时,SDA由高变为低*/
OLED_SDA_L0W;
i2c_delay();
/*将SCL电位拉低,钳住SCL线,准备发送地址数据*/
OLED_SCL_L0W;
}
2、停止信号:当SCL为高电平期间,SDA由低电平变为高电平
/*i2c 停止信号*/
void OLED_Stop(void)
{
OLED_SDA_OUT(); //sda线输出
/*scl为高电平时,SDA由低电平变为高电平*/
OLED_SDA_L0W;
OLED_SCL_L0W;
i2c_delay();
OLED_SCL_HIGH;
OLED_SDA_HIGH;
i2c_delay();
}
3、空闲状态:当IIC总线的数据线SDA和时钟线SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
4、应答信号:发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
检测应答:
-
SCL为高电平的时候可以读取SDA的状态,因此可以将SDA模式切换为输入模式,读取SDA引脚状态,0位应答,1位非应答
-
SCL为低电平允许数据发生变化
-
SDA:为高电平的时候可以占用总线,此时将SDA拉低,开始通信。当为低电平的时候,SDA已经被占用。
SCL: SCL为高电平的时候要求数据稳定 SCL为低电平的时候允许数据改变
等待应答的作用就是来判断SDA传给的是低电平还是高电平。
/*等待应答信号到来 (有效应答:从机第9个 SCL=0 时 SDA 被从机拉低,
并且 SCL = 1时 SDA依然为低)
返回值: 1,接收应答失败
0,接收应答成功*/
uint8_t OLED_WaitAck(void)
{
uint8_t ucErrTime=0;
OLED_SDA_IN(); //SDA设置为输入模式 (从机给一个低电平做为应答)
OLED_SDA_HIGH;i2c_delay();
OLED_SCL_HIGH;i2c_delay();
//读取SDA的电平状态
while(OLED_SDA_Read)
{
ucErrTime++;
if(ucErrTime>250)
{
OLED_Stop();
return 1;//接收应答失败
}
}
OLED_SCL_L0W; //时钟输出0
return 0; //接收应答成功
}
有效应答和无效应答:
void OLED_Ack(void)
{
OLED_SCL_L0W;
OLED_SDA_OUT();
OLED_SDA_L0W;
i2c_delay();
OLED_SCL_HIGH;
i2c_delay();
OLED_SCL_L0W;
}
void OLED_NAck(void)
{
OLED_SCL_L0W;
OLED_SDA_OUT();
OLED_SDA_HIGH;
i2c_delay();
OLED_SCL_HIGH;
i2c_delay();
OLED_SCL_L0W;
}
5、字节格式:
SDA数据线上的每个字节必须是8位,每次传输的字节数量没有限制。每个字节后必须跟一个响应位(ACK)。首先传输的数据是最高位(MSB),SDA上的数据必须在SCL高电平周期时保持稳定,数据的高低电平翻转变化发生在SCL低电平时期。
-
每一个字节后面跟着一个ACK,有ACK就可以继续写或读。NACK,就停止
-
ACK:主机释放总线,传输完字节最后1位后的SCL的高电处,从机拉低电平。
-
NACK:主机释放总线,传输完字节最后1位后的SCL的高电处,从机无响应,总线为高电平。
发送数据:
/******************************************************************************
*函 数:void IIC_SendByte(uint8_t txd)
*功 能:IIC发送一个字节
*参 数:data 要写的数据
*返回值:无
*备 注:主机往从机发
*******************************************************************************/
void OLED_SendByte(uint8_t data)
{
uint8_t t;
OLED_SDA_OUT();
OLED_SCL_L0W; //拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((data&0x80)>>7)//0x80=1000 0000
OLED_SDA_HIGH;
else
OLED_SDA_L0W;
data<<=1;
i2c_delay();
OLED_SCL_HIGH;
i2c_delay();
OLED_SCL_L0W;
i2c_delay();
}
}
读取数据:
uint8_t OLED_ReadByte(uint8_t ack)
{
uint8_t i,receive=0;
OLED_SDA_IN(); //SDA设置为输入模式 等待接收从机返回数据
for(i=0;i<8;i++ )
{
OLED_SCL_L;
i2c_delay();
OLED_SCL_H;
receive<<=1;
if(OLED_SDA_Read)receive++; //从机发送的电平
i2c_delay();
}
if(ack)
OLED_Ack(); //发送ACK
else
OLED_NAck(); //发送nACK
return receive;
}
总结:最难的时序部分代码完成后,就可以使用模拟的iic代码来完成oled屏幕代码的初始化了
oled配置代码(这个大家直接移植就行)
//初始化SSD1306
// 端口用PA5,PA6
void OLED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能A端口时钟
GPIO_InitStructure.GPIO_Pin = OLED_SCL_PIN|OLED_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化
GPIO_SetBits(GPIOA,OLED_SCL_PIN|OLED_SDA_PIN);
delay_ms(800);
OLED_WR_Byte(0xAE,OLED_CMD);//--display off
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address
OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
OLED_WR_Byte(0x81,OLED_CMD); // contract control
OLED_WR_Byte(0xFF,OLED_CMD);//--128
OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap
OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
OLED_WR_Byte(0x00,OLED_CMD);//
OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
OLED_WR_Byte(0x80,OLED_CMD);//
OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
OLED_WR_Byte(0x05,OLED_CMD);//
OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
OLED_WR_Byte(0xF1,OLED_CMD);//
OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
OLED_WR_Byte(0x12,OLED_CMD);//
OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
OLED_WR_Byte(0x30,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
OLED_WR_Byte(0x14,OLED_CMD);//
OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}
/********************************************
// fill_Picture
********************************************/
void fill_picture(unsigned char fill_Data)
{
unsigned char m,n;
for(m=0;m<8;m++)
{
OLED_WR_Byte(0xb0+m,0); //page0-page1
OLED_WR_Byte(0x00,0); //low column start address
OLED_WR_Byte(0x10,0); //high column start address
for(n=0;n<128;n++)
{
OLED_WR_Byte(fill_Data,1);
}
}
}
/***********************Delay****************************************/
void Delay_50ms(unsigned int Del_50ms)
{
unsigned int m;
for(;Del_50ms>0;Del_50ms--)
for(m=6245;m>0;m--);
}
void Delay_1ms(unsigned int Del_1ms)
{
unsigned char j;
while(Del_1ms--)
{
for(j=0;j<123;j++);
}
}
//坐标设置
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
OLED_WR_Byte(0xb0+y,OLED_CMD);
OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
OLED_WR_Byte((x&0x0f),OLED_CMD);
}
//开启OLED显示
void OLED_Display_On(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON
OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址
OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
} //更新显示
}
void OLED_On(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址
OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA);
} //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{
unsigned char c=0,i=0;
c=chr-' ';//得到偏移后的值
if(x>Max_Column-1){x=0;y=y+2;}
if(Char_Size ==16)
{
OLED_Set_Pos(x,y);
for(i=0;i<8;i++)
OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
OLED_Set_Pos(x,y+1);
for(i=0;i<8;i++)
OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
}
else {
OLED_Set_Pos(x,y);
for(i=0;i<6;i++)
OLED_WR_Byte(F6x8[c][i],OLED_DATA);
}
}
//m^n函数
u32 oled_pow(u8 m,u8 n)
{
u32 result=1;
while(n--)result*=m;
return result;
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{
u8 t,temp;
u8 enshow=0;
for(t=0;t<len;t++)
{
temp=(num/oled_pow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
continue;
}else enshow=1;
}
OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2);
}
}
void OLED_Float(unsigned char Y,unsigned char X,double real,unsigned char N)
{
unsigned char i_Count=1;
unsigned char n[12]={0};
long j=1;
int real_int=0;
double decimal=0;
unsigned int real_decimal=0;
if(real<0)
{
real_int=(int)(-real);
}
else
{
real_int=(int)real;
}
decimal=real-real_int;
real_decimal=decimal*1e4;
while(real_int/10/j!=0)
{
j=j*10;i_Count++;
}
n[0]=(real_int/10000)%10;
n[1]=(real_int/1000)%10;
n[2]=(real_int/100)%10;
n[3]=(real_int/10)%10;
n[4]=(real_int/1)%10;
n[5]='.';
n[6]=(real_decimal/1000)%10;
n[7]=(real_decimal/100)%10;
n[8]=(real_decimal/10)%10;
n[9]=real_decimal%10;
n[6+N]='\0';
for(j=0;j<10;j++) n[j]=n[j]+16+32;
if(real<0)
{
i_Count+=1;
n[5-i_Count]='-';
}
n[5]='.';
n[6+N]='\0';
OLED_ShowString(X,Y,&n[5-i_Count],12);
}
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
{
unsigned char j=0;
while (chr[j]!='\0')
{ OLED_ShowChar(x,y,chr[j],Char_Size);
x+=8;
if(x>120){x=0;y+=2;}
j++;
}
}
//显示汉字
void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{
u8 t,adder=0;
OLED_Set_Pos(x,y);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
adder+=1;
}
OLED_Set_Pos(x,y+1);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
adder+=1;
}
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
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_Set_Pos(x0,y);
for(x=x0;x<x1;x++)
{
OLED_WR_Byte(BMP[j++],OLED_DATA);
}
}
}
这里会用到一个子模库:
简单讲一下字模软件的使用吧(没有的找我要)
配置要正确
最后我把完整的工程代码给大家!如果前面都看不懂的,就按我这个配置来!
#include "./OLED/oled.h"
#include "oledfont.h"
#include "./systick/bsp_systick.h"
#include "stdlib.h"
/*因为oled与6050的I2C引脚相同,所以oled要单独模拟I2C来用
SCL----PA4
SDA----PA5*/
/*宏定义*/
/*********************************************************************************/
#define RCC_OLED_SCL RCC_APB2Periph_GPIOA //端口时钟
#define OLED_SCL_PORT GPIOA
#define OLED_SCL_PIN GPIO_Pin_4
#define RCC_OLED_SDA RCC_APB2Periph_GPIOA //端口时钟
#define OLED_SDA_PORT GPIOA
#define OLED_SDA_PIN GPIO_Pin_5
/*********************************************************************************/
/*操作IO口*/
/*********************************************************************************/
#define OLED_SCL_L0W (OLED_SCL_PORT->BRR = OLED_SCL_PIN)
#define OLED_SCL_HIGH (OLED_SCL_PORT->BSRR = OLED_SCL_PIN)
#define OLED_SDA_L0W (OLED_SDA_PORT->BRR = OLED_SDA_PIN)
#define OLED_SDA_HIGH (OLED_SDA_PORT->BSRR = OLED_SDA_PIN)
/*读取电平状态,低电平返回0,高电平返回非0*/
#define OLED_SDA_Read ((OLED_SDA_PORT->IDR &OLED_SDA_PIN) != 0)
/*********************************************************************************/
/*********************************************************************************/
/*简单的延时函数*/
static void i2c_delay(void)
{
delay_us(4);
}
/*********************************************************************************/
/*********************************************************************************/
/*先将SDA,SCL配置成开漏输出模式,再配置SDA的输入输出模式*/
/*i2c端口模式初始化
SDA---->PA3
SCL---->PA2
*/
//void i2c_MoniConfig(void)
//{
// GPIO_InitTypeDef GPIO_InitStructure;
// RCC_APB2PeriphClockCmd(RCC_OLED_SCL, ENABLE); /* 打开GPIO时钟 */
// GPIO_InitStructure.GPIO_Pin = OLED_SCL_PIN ;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
// GPIO_Init(OLED_SCL_PORT, &GPIO_InitStructure);
// OLED_SCL_HIGH;
//
// RCC_APB2PeriphClockCmd(RCC_OLED_SDA, ENABLE); /* 打开GPIO时钟 */
// GPIO_InitStructure.GPIO_Pin = OLED_SDA_PIN;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
// GPIO_Init(OLED_SDA_PORT, &GPIO_InitStructure);
// OLED_SDA_HIGH;
//}
/*SDA输出模式 PA3*/
void OLED_SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_OLED_SDA, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = OLED_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
GPIO_Init(OLED_SDA_PORT, &GPIO_InitStructure);
}
/*SDA输入模式 PA3*/
void OLED_SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_OLED_SDA, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = OLED_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; /* // 浮空输入 */
GPIO_Init(OLED_SDA_PORT, &GPIO_InitStructure);
}
/*********************************************************************************/
/*i2c 起始信号*/
void OLED_Start(void)
{
OLED_SDA_OUT(); //sda线输出
OLED_SCL_HIGH;
OLED_SDA_HIGH;
i2c_delay();
/*当CLK为高时,SDA由高变为低*/
OLED_SDA_L0W;
i2c_delay();
/*将SCL电位拉低,钳住SCL线,准备发送地址数据*/
OLED_SCL_L0W;
}
/*i2c 停止信号*/
void OLED_Stop(void)
{
OLED_SDA_OUT(); //sda线输出
/*scl为高电平时,SDA由低电平变为高电平*/
OLED_SDA_L0W;
OLED_SCL_L0W;
i2c_delay();
OLED_SCL_HIGH;
OLED_SDA_HIGH;
i2c_delay();
}
/*等待应答信号到来 (有效应答:从机第9个 SCL=0 时 SDA 被从机拉低,
并且 SCL = 1时 SDA依然为低)
返回值: 1,接收应答失败
0,接收应答成功*/
uint8_t OLED_WaitAck(void)
{
uint8_t ucErrTime=0;
OLED_SDA_IN(); //SDA设置为输入 (从机给一个低电平做为应答)
OLED_SDA_HIGH;i2c_delay();
OLED_SCL_HIGH;i2c_delay();
while(OLED_SDA_Read)
{
ucErrTime++;
if(ucErrTime>250)
{
OLED_Stop();
return 1;
}
}
OLED_SCL_L0W; //时钟输出0
return 0;
}
void OLED_Ack(void)
{
OLED_SCL_L0W;
OLED_SDA_OUT();
OLED_SDA_L0W;
i2c_delay();
OLED_SCL_HIGH;
i2c_delay();
OLED_SCL_L0W;
}
void OLED_NAck(void)
{
OLED_SCL_L0W;
OLED_SDA_OUT();
OLED_SDA_HIGH;
i2c_delay();
OLED_SCL_HIGH;
i2c_delay();
OLED_SCL_L0W;
}
/******************************************************************************
*函 数:void IIC_SendByte(uint8_t txd)
*功 能:IIC发送一个字节
*参 数:data 要写的数据
*返回值:无
*备 注:主机往从机发
*******************************************************************************/
void OLED_SendByte(uint8_t data)
{
uint8_t t;
OLED_SDA_OUT();
OLED_SCL_L0W; //拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((data&0x80)>>7)
OLED_SDA_HIGH;
else
OLED_SDA_L0W;
data<<=1;
i2c_delay();
OLED_SCL_HIGH;
i2c_delay();
OLED_SCL_L0W;
i2c_delay();
}
}
#define OLED_SCL_L OLED_SCL_L0W
#define OLED_SCL_H OLED_SCL_HIGH
/******************************************************************************
*函 数:uint8_t IIC_ReadByte(uint8_t ack)
*功 能:IIC读取一个字节
*参 数:ack=1 时,主机数据还没接收完 ack=0 时主机数据已全部接收完成
*返回值:无
*备 注:从机往主机发
*******************************************************************************/
uint8_t OLED_ReadByte(uint8_t ack)
{
uint8_t i,receive=0;
OLED_SDA_IN(); //SDA设置为输入模式 等待接收从机返回数据
for(i=0;i<8;i++ )
{
OLED_SCL_L;
i2c_delay();
OLED_SCL_H;
receive<<=1;
if(OLED_SDA_Read)receive++; //从机发送的电平
i2c_delay();
}
if(ack)
OLED_Ack(); //发送ACK
else
OLED_NAck(); //发送nACK
return receive;
}
void Write_IIC_Command(unsigned char IIC_Command)
{
OLED_Start();
OLED_SendByte(0x78); //Slave address,SA0=0
OLED_WaitAck();
OLED_SendByte(0x00); //write command
OLED_WaitAck();
OLED_SendByte(IIC_Command);
OLED_WaitAck();
OLED_Stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{
OLED_Start();
OLED_SendByte(0x78); //D/C#=0; R/W#=0
OLED_WaitAck();
OLED_SendByte(0x40); //write data
OLED_WaitAck();
OLED_SendByte(IIC_Data);
OLED_WaitAck();
OLED_Stop();
}
/*******************************************************************************************************************/
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
if(cmd)
{
Write_IIC_Data(dat);
}
else {
Write_IIC_Command(dat);
}
}
//初始化SSD1306
// 端口用PA5,PA6
void OLED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能A端口时钟
GPIO_InitStructure.GPIO_Pin = OLED_SCL_PIN|OLED_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化
GPIO_SetBits(GPIOA,OLED_SCL_PIN|OLED_SDA_PIN);
delay_ms(800);
OLED_WR_Byte(0xAE,OLED_CMD);//--display off
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address
OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
OLED_WR_Byte(0x81,OLED_CMD); // contract control
OLED_WR_Byte(0xFF,OLED_CMD);//--128
OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap
OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
OLED_WR_Byte(0x00,OLED_CMD);//
OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
OLED_WR_Byte(0x80,OLED_CMD);//
OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
OLED_WR_Byte(0x05,OLED_CMD);//
OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
OLED_WR_Byte(0xF1,OLED_CMD);//
OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
OLED_WR_Byte(0x12,OLED_CMD);//
OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
OLED_WR_Byte(0x30,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
OLED_WR_Byte(0x14,OLED_CMD);//
OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}
/********************************************
// fill_Picture
********************************************/
void fill_picture(unsigned char fill_Data)
{
unsigned char m,n;
for(m=0;m<8;m++)
{
OLED_WR_Byte(0xb0+m,0); //page0-page1
OLED_WR_Byte(0x00,0); //low column start address
OLED_WR_Byte(0x10,0); //high column start address
for(n=0;n<128;n++)
{
OLED_WR_Byte(fill_Data,1);
}
}
}
/***********************Delay****************************************/
void Delay_50ms(unsigned int Del_50ms)
{
unsigned int m;
for(;Del_50ms>0;Del_50ms--)
for(m=6245;m>0;m--);
}
void Delay_1ms(unsigned int Del_1ms)
{
unsigned char j;
while(Del_1ms--)
{
for(j=0;j<123;j++);
}
}
//坐标设置
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
OLED_WR_Byte(0xb0+y,OLED_CMD);
OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
OLED_WR_Byte((x&0x0f),OLED_CMD);
}
//开启OLED显示
void OLED_Display_On(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON
OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址
OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
} //更新显示
}
void OLED_On(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址
OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA);
} //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{
unsigned char c=0,i=0;
c=chr-' ';//得到偏移后的值
if(x>Max_Column-1){x=0;y=y+2;}
if(Char_Size ==16)
{
OLED_Set_Pos(x,y);
for(i=0;i<8;i++)
OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
OLED_Set_Pos(x,y+1);
for(i=0;i<8;i++)
OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
}
else {
OLED_Set_Pos(x,y);
for(i=0;i<6;i++)
OLED_WR_Byte(F6x8[c][i],OLED_DATA);
}
}
//m^n函数
u32 oled_pow(u8 m,u8 n)
{
u32 result=1;
while(n--)result*=m;
return result;
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{
u8 t,temp;
u8 enshow=0;
for(t=0;t<len;t++)
{
temp=(num/oled_pow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
continue;
}else enshow=1;
}
OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2);
}
}
void OLED_Float(unsigned char Y,unsigned char X,double real,unsigned char N)
{
unsigned char i_Count=1;
unsigned char n[12]={0};
long j=1;
int real_int=0;
double decimal=0;
unsigned int real_decimal=0;
if(real<0)
{
real_int=(int)(-real);
}
else
{
real_int=(int)real;
}
decimal=real-real_int;
real_decimal=decimal*1e4;
while(real_int/10/j!=0)
{
j=j*10;i_Count++;
}
n[0]=(real_int/10000)%10;
n[1]=(real_int/1000)%10;
n[2]=(real_int/100)%10;
n[3]=(real_int/10)%10;
n[4]=(real_int/1)%10;
n[5]='.';
n[6]=(real_decimal/1000)%10;
n[7]=(real_decimal/100)%10;
n[8]=(real_decimal/10)%10;
n[9]=real_decimal%10;
n[6+N]='\0';
for(j=0;j<10;j++) n[j]=n[j]+16+32;
if(real<0)
{
i_Count+=1;
n[5-i_Count]='-';
}
n[5]='.';
n[6+N]='\0';
OLED_ShowString(X,Y,&n[5-i_Count],12);
}
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
{
unsigned char j=0;
while (chr[j]!='\0')
{ OLED_ShowChar(x,y,chr[j],Char_Size);
x+=8;
if(x>120){x=0;y+=2;}
j++;
}
}
//显示汉字
void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{
u8 t,adder=0;
OLED_Set_Pos(x,y);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
adder+=1;
}
OLED_Set_Pos(x,y+1);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
adder+=1;
}
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
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_Set_Pos(x0,y);
for(x=x0;x<x1;x++)
{
OLED_WR_Byte(BMP[j++],OLED_DATA);
}
}
}
总结
以上就是oled屏幕和iic时序的全部讲解啦!
我想对训练营的同学说一下,态度决定成败!我为大家付出这么多,希望各位不要辜负我的努力。希望大家都能够将我安排的任务认真完成。
点赞+再看是对我最大的鼓励!!!
想学习更多知识可以加入我的公众号哈!
有想加入训练营的小伙伴可以私聊我哦!