简介
oled屏选择ssd1306,使用RTC实现简易实时时钟
OLED部分
1、MCU接口选择
SSD1306单片机接口由8个数据引脚和5个控制引脚组成。通过BS[2:0]引脚上的硬件选择可以设置不同的MCU模式
通过控制BS[2:0]引脚可以设置MCU与OLED屏的通信方式。因为我使用的是正点原子的开发板,所以我用了适配的接口8080。即BS0=0;BS1=1;BS2=2;
2、单片机并行8080接口
并行接口由8个双向数据引脚(D[7:0])、RD、WR、D/C和CS组成。D/ C中的LOW表示COMMAND读/写,HIGH表示DATA读/写。RS输入的上升沿作为数据读锁存信号,而cs#保持低电平。在 RD 的上升沿, 使数据锁存到数据线(D[7:0])上;在 WR 的上升沿,使数据写入到 SSD1306 里面。
CS:OLED 片选信号。
WR:向 OLED 写入数据。
RD:从 OLED 读取数据。
D[7:0]:8 位双向数据线。
RST(RES):硬复位 OLED。
DC:命令/数据标志(0,读写命令;1,读写数据)
8080接口读时序过程:
首先将CS拉低,表示设备被选中;WR设置为高,RD设置为低,表示读取数据;然后在RD的上升沿将数据从总线上读取出来。
8080接口写时序过程:
首先将CS拉低,表示设备被选中;WR设置为低,RD设置为高,表示写数据;然后在WR的上升沿将数据写入总线。
SSD1306 的 8080 接口方式下,控制脚的信号状态所对应的功能如表
3、命令编译器
该模块决定输入的数据是被解释为数据还是命令,如果D/C为高则写入的是数据,如果D/C为低则写入的是命令/
4、复位电路
当RET输入为LOW时,芯片初始化为以下状态。
1.显示关闭
2. 128 × 64显示模式
3.正常段和显示数据列地址和行地址的映射(SEG0映射到地址00h, COM0映射到地址00h)
4. 在串行接口中清除移位寄存器数据
5. 显示起始行设置在显示RAM地址0处
6. 列地址计数器设置为0
7. COM输出的正常扫描方向
8. 对比度控制寄存器设置为7Fh
9. 正常显示模式(相当于A4h命令)
5、图形显示数据(GDDRAM)即显存
GDDRAM是位映射静态RAM,保存要显示的位模式。RAM的大小为128 × 64位,RAM分为8个页面,从PAGE0到PAGE7,用于单色128x64点阵显示,如图
因为我们每次是通过控制128x64点阵里的每一个点的状态(0/1)来控制OLED屏显示图形;这就要求我们在想要显示图形的时候就应该把每一个点的状态都提前想好,否则我们写入的数据会覆盖之前的数据。所以我们采用的办法是在 STM32 的内部建立一个 OLED 的 GRAM(共 128*8 个字节),在每次修改的时候,只是修改 STM32 上的 GRAM(实际上就是 SRAM),在修改完了之后,一次性把 STM32 上的 GRAM 写入到 OLED 的 GRAM。
6、SSD1306命令
这里列举一些SSD1306的一些常用的命令(参考原子哥的开发指南)
第一个命令为 0X81,用于设置对比度的,这个命令包含了两个字节,第一个 0X81 为命令,
随后发送的一个字节为要设置的对比度的值。这个值设置得越大屏幕就越亮。
第二个命令为 0XAE/0XAF。0XAE 为关闭显示命令;0XAF 为开启显示命令。
第三个命令我也不知干啥的,反正开就行了
第四个命令为 0XB0~B7,该命令用于设置页地址,其低三位的值对应着 GRAM 的页地址。(三位表示0-7足够了)
第五个指令为 0X00~0X0F,该指令用于设置显示时的起始列地址低四位。
第六个指令为 0X10~0X1F,该指令用于设置显示时的起始列地址高四位。(五六命令要一起使用)
7、内存寻址模式
在SSD1306中有3种不同的内存寻址模式:页寻址模式、水平寻址模式和垂直寻址模式。该命令将内存寻址方式设置为上述三种模式之一
1、页寻址模式(A[1:0]=10xb)在页寻址模式下,读取/写入显示RAM后,列地址指针自动增加1。如果列地址指针到达列结束地址,则将列地址指针重置为列开始地址,不修改页面地址指针。用户必须设置新的页面和列地址,以便访问下一页RAM内容。
定义RAM的起始访问指针位置:
- 通过命令B0h设置目标显示位置的页面起始地址为B7h。
- 通过命令00h~0Fh设置指针的下起始列地址。
- 通过命令10h~1Fh设置指针上起始列地址
2、水平寻址模式
水平寻址模式(A[1:0]=00b)在水平寻址模式下,读/写显示RAM后,列地址指针自动增加1。当列地址指针到达列结束地址时,列地址指针重置为列开始地址,页面地址指针加1
3、垂直寻址方式
A[1:0]=01b
在垂直寻址模式下,读/写显示RAM后,页地址指针自动增加1。当页地址指针到达页结束地址时,将页地址指针重置为页起始地址,列地址指针加1。
需要以下步骤来定义RAM访问指针的位置:
通过命令21h设置目标显示位置的列起始地址和列结束地址。
通过命令22h设置目标显示位置的页面起始和结束地址。
注释:在程序中我使用的是页寻址模式,只有在发送0X20命令(设置内存地址模式)之后再写入0x10命令才能成功设置成页寻址模式。
8、代码实战
1、设置STM32与OLED模块相连接的IO
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG, ENABLE); //使能PC,D,G端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_6; //PD3,PD6推挽输出 PD3=DC
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOD, &GPIO_InitStructure); //初始化GPIOD3,6
GPIO_SetBits(GPIOD,GPIO_Pin_3|GPIO_Pin_6); //PD3,PD6 输出高
#if OLED_MODE==1 //8080模式
GPIO_InitStructure.GPIO_Pin =0xFF; //PC0~7 OUT推挽输出
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC,0xFF); //PC0~7输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //PG13,14,15 OUT推挽输出
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_SetBits(GPIOG,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PG13,14,15 OUT 输出高
对应的引脚都设置成推挽输出(这样既可以输出高电平也可输出低电平)
PD3=DC ; PG13=RD ; PC0=D0 ; PC2=D2 ; PC4=D4; PC6=D6; PB4=悬空
PD6=CS ; PG14=RW ; PG15=RST PC1=D1; PC3=D3 ;PC5=D5; PC7=D7;
2、初始化OLED屏幕
OLED_CS=1;
OLED_RS=1; //DC位 DC=1表示写数据
OLED_RST=0; //RST=0复位,在每次初始化OLED屏幕之前都应该复位
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(0x10,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10; 必须紧贴着0x20
OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127;
OLED_WR_Byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
OLED_WR_Byte(0xDA,OLED_CMD); //设置COM硬件引脚配置
OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置
OLED_WR_Byte(0x81,OLED_CMD); //对比度设置
OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮
OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期
OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2;
OLED_WR_Byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率
OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示
OLED_WR_Byte(0xAF,OLED_CMD); //开启显示
OLED_Clear();
这里不需要知道太多,参考驱动IC的初始化代码就行了(这里我也没深入了解)
3、将数据显示在OLED屏幕上
此代码主要参考正点原子的历程,我在此部分将介绍一些我对这部分代码的理解
这里如果是采用页寻址的话,应该是128行8列的数组,应该是原子哥描述有问题。
//画点
//x:0~127
//y:0~63
//t:1 填充 0,清空
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
u8 pos,bx,temp=0;
if(x>127||y>63)return;//超出范围了.
pos=7-y/8; // 当y=0-7 pos=7 当y=8-15 pos=6 y=1 pos=7
bx=y%8; //当y=0-7 bx=0-7 当y=8-15 bx=0-7 bx=1
temp=1<<(7-bx); // temp=1<<(7-1)=1<<6
if(t)
{
OLED_GRAM[x][pos]|=temp; //OLED_GRAM[X][1]=OLED_GRAM[X][1]| temp; 非0
}
else
{
OLED_GRAM[x][pos]&=~temp; //OLED_GRAM[X][1]=OLED_GRAM[X][1]| temp; 等于0
}
}
画点函数,就是点亮OLED128 × 64位其中一个点,后续的显示字符,显示数字函数都是在此函数的基础上改进的。其中x表示点的列数,y表示点的行数;其中POS用来表示显示的页数(0-7页),因为每一页有8行,也就是8位,所以使用bx来控制位的偏移,这样就可以精确到每一位。
//向SSD1306写入一个字节。
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 cmd)
{
DATAOUT(dat); //GPIO_Write()输出 此处为什么要在片选拉低之前就写值
OLED_RS=cmd; //DC位 DC=0表示命令 DC=1表示数据
OLED_CS=0;
OLED_WR=0; //WR位
OLED_WR=1; //在WR的上升沿将数据写入到SSD1306中
OLED_CS=1;
OLED_RS=1; DC位 DC=0表示命令 DC=1表示数据
}
写数据或者命令函数,其中DATAOUT(dat)写的是一个字节也就是8位,也就是D0-D7。
文章来源:https://www.toymoban.com/news/detail-797020.html
结语
因为时间有限,本人是在闲暇之余整理的稳定,目前实验已经做完,时间有限只能先写到这了,后续明天会补充,时间不早了,大家早点休息。实验效果已经上传置B站,大家可以去我的页面看是点灯实验室呀的个人空间-是点灯实验室呀个人主页-哔哩哔哩视频文章来源地址https://www.toymoban.com/news/detail-797020.html
到了这里,关于stm32-OLED屏+RTC实现简易实时时钟(上篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!