文章目录
前言
STM32嵌入式学习已经好久了,一些基础知识总是忘记,正好下半年需要找工作,有时间就把之前学习的东西拾起来看看。
实验环境:STM32F103系列开发板,SSD1306型号OLED、KEIL
提示:以下是本篇文章正文内容,下面案例可供参考
一、OLED
OLED有多种接口方式:6800、8080、IIC、3线SPI和4线SPI,在此先讨论8080并口
OLED关键引脚:
CS:OLED片选引脚;
WR:向SSD1306写入数据;
RD:从SSD1306读取数据;
RST:硬复位OLED;
D[7:0]:8位双向数据线;
DC:命令/数据标志(0:命令;1:数据)。
SSD1306的8080并口写时序
图1 8080并口的写时序
8080并口写过程:首先根据要写入的数据类型,设置DC为高(数据)/低(命令),然后拉低片选信号CS,选中模块,然后再WR出现上升沿时将数据写入到SSD1306中。
SSD1306的8080并口写时序
图2 8080并口的读时序
8080并口读过程:首先根据要写入的数据类型,设置DC为高(数据)/低(命令),然后拉低片选信号CS,选中模块,然后再RD出现上升沿时将数据锁存到D[7:0]。
根据8080的读写过程,控制脚的信号状态所对应的功能如表1所示。
功能 | RD | WR | CS | DC |
写命令 | H | 上升沿 | L | L |
读状态 | 上升沿 | H | L | L |
写数据 | H | 上升沿 | L | H |
读数据 | 上升沿 | H | L | H |
二、软件设计
SSD1306常用的指令:
0X81:设置对比度,后面加上一个字节的对比度值;
0XAE/0XAF:0XAE为关闭显示命令;0XAF为开启显示命令;
0X8D:电荷泵设置,后面一字节的BIT2为0则关闭,为1则开启;
0XB0~0XB7:设置页地址,低三位对于GRAM的页地址;
0X00~0X0F:设置列地址低四位;
0X10~0X1F:设置列地址高四位。
OLED与STM32F103系列开发板IO口对应关系:
CS:PD6 RST:PG15 DC:PD3 WR:PG14 RD:PG13 D[7:0]:PC[7:0]
1.初始化SSD1306
void Oled_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG,
ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_6; //PD3:DC PD6:CS
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_SetBits(GPIOD,GPIO_Pin_3|GPIO_Pin_6);
GPIO_InitStructure.GPIO_Pin =0xFF;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC,0xFF);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_SetBits(GPIOG,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
//PG13:RD PG14:WR PG15:RST
OLED_CS=1;
OLED_RS=1;
OLED_RST=0;
delay_ms(100);
OLED_RST=1;
OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示
OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,振荡频率
OLED_WR_Byte(80,OLED_CMD); //[3:0],分频因子;[7:4],震荡因子
OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数
OLED_WR_Byte(0X3F,OLED_CMD); //默认0X3F(1/64)
OLED_WR_Byte(0xD3,OLED_CMD); //设置偏移显示
OLED_WR_Byte(0X00,OLED_CMD); //默认为0
OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数
OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置
OLED_WR_Byte(0x14,OLED_CMD); //bit2 开启/关闭
OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式
OLED_WR_Byte(0x02,OLED_CMD);
OLED_WR_Byte(0xA1,OLED_CMD);
OLED_WR_Byte(0xC0,OLED_CMD);
OLED_WR_Byte(0xDA,OLED_CMD);
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0x81,OLED_CMD);
OLED_WR_Byte(0xEF,OLED_CMD);
OLED_WR_Byte(0xD9,OLED_CMD);
OLED_WR_Byte(0xf1,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);
OLED_WR_Byte(0x30,OLED_CMD);
OLED_WR_Byte(0xA4,OLED_CMD);
OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式
OLED_WR_Byte(0xAF,OLED_CMD); //开启显示
OLED_Clear();
}
2.SSD1306基础功能
//开启OLED
void OLED_Display_On(void)
{
OLED_WR_Byte(0X8D,OLED_CMD);
OLED_WR_Byte(0X14,OLED_CMD); //开启电荷泵
OLED_WR_Byte(0XAF,OLED_CMD); //开启显示
}
//关闭OLED显示
void OLED_Display_Off(void)
{
OLED_WR_Byte(0X8D,OLED_CMD);
OLED_WR_Byte(0X10,OLED_CMD); //关闭电荷泵
OLED_WR_Byte(0XAE,OLED_CMD); //关闭显示
}
//更新显存到OLED
void OLED_Refresh_Gram(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址
OLED_WR_Byte (0x00,OLED_CMD); //设置列地址低四位
OLED_WR_Byte (0x10,OLED_CMD); //设置列地址高四位
for(n=0;n<128;n++)OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
}
}
//清屏
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;
OLED_Refresh_Gram();//更新显存
//向SSD1306写字节
//dat:要写入的字节 cmd:数据/命令标志;
void Oled_WR_Byte(u8 dat,u8 cmd)
{
DATAOUT(dat); //向PC[7:0]写数据/命令
OLED_DC=cmd; // 数据/命令标志
OLED_CS=0; //选中SSD1306
OLED_WR=0; //写入拉低
OLED_WR=1; //写入拉高
OLED_CS=1; //片选拉高
OLED_DC=1; // 数据标志
}
上述的几个函数都是根据SSD1306的命令以及80880并口的读写时序图编写的。
3. OLED功能函数
//画点函数
//x:0~27,y:0~63, t:0(清空)/1(填充)
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
u8 pos,bx,temp=0;
if(x>127||y>63)return;//超出OLED分辨率 128*64
pos=7-y/8;
bx=y%8;
temp=1<<(7-bx);
if(t)OLED_GRAM[x][pos]|=temp;
else OLED_GRAM[x][pos]&=~temp;
}
OLED_GRAM[x][y]中的x代表列数(x:0~127),y代表页数(y:0~8),一共有8页,每页又有8行,一共64行,从高到低对应行数的由小到大。
//OLED填充对角区域
//x1<=x2,y1<=y2, 0<=x<=127,0<=y<=63
//dot:0(清空)/1(填充)
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot)
{
u8 x,y;
for(x=x1;x<=x2;x++)
{
for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot);
}
OLED_Refresh_Gram();//更新显示
}
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode)
{
u8 temp,t,t1;
u8 y0=y;
u8 csize=(size/8+((size%8)?1:0))*(size/2);
chr=chr-' ';
for(t=0;t<csize;t++)
{
if(size==12)temp=asc2_1206[chr][t];
else if(size==16)temp=asc2_1608[chr][t];
else if(size==24)temp=asc2_2412[chr][t];
else return;
for(t1=0;t1<8;t1++)
{
if(temp&0x80)OLED_DrawPoint(x,y,mode);
else OLED_DrawPoint(x,y,!mode);
temp<<=1;
y++;
if((y-y0)==size)
{
y=y0;
x++;
break;
}
}
}
}
//显示字符串
//x,y:起始点坐标
//*p:字符串起始地址
//size:字符串长度
void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size)
{
while((*p<='~')&&(*p>=' '))//判断是否为非法字符
{
if(x>(128-(size/2))){x=0;y+=size;}
if(y>(64-size)){y=x=0;OLED_Clear();}
OLED_ShowChar(x,y,*p,size,1);
x+=size/2;
p++;
}
}
总结
OLED的8080并口使用需要能够看懂8080并口的读写时序以及学会如何写入字符,然后学会如何写入字符串。