大家好,这次讲一下RTC时钟和OLED驱动函数,Let’s 学习吧!
一.RTC
1.1.RTC概述
实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。
简单来说就是一个STM32系统自带的时钟。RTCCLK 时钟源可以由 HSE 、 LSE 或 LSI 时钟提供。STM32F0 的 RTC 模块和 F1 的 RTC 模块最大区别在于 F0 模块中有“DATE”和“TIME”寄存器,也就是可以直接读取寄存器里面的值,而 F1 是秒计数寄存器的值,需要通过相关算法下才能得到时间的值。

1.2软件设计
//RTC初始化
void RTC_Initializes(void)
{
RTC_InitTypeDef RTC_InitStructure;
RTC_DateTimeTypeDef RTC_DateTimeStructure;
PWR_BackupAccessCmd(ENABLE); //允许操作RTC
if(RTC_ReadBackupRegister(RTC_BKP_DR0) != RTC_FLAG_BKP)
{
RTC_Configuration(); //配置RTC
RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv;
RTC_InitStructure.RTC_SynchPrediv = SynchPrediv;
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
if(RTC_Init(&RTC_InitStructure) == ERROR)
{
while(1); //初始化失败
}
RTC_DateTimeStructure.Hour = 10;
RTC_DateTimeStructure.Minute = 57;
RTC_DateTimeStructure.Second = 0;
RTC_SetDateTime(RTC_DateTimeStructure);
RTC_WriteBackupRegister(RTC_BKP_DR0, RTC_FLAG_BKP);
}
else
{
#ifdef RTC_CLOCK_SOURCE_LSI
RCC_LSICmd(ENABLE);
#endif
RTC_WaitForSynchro();
}
}
// RTC时间矫正
//RTC_OK ----------------- 成功
//RTC_ERR ---------------- 错误
RTC_RESULT RTC_TimeRegulate(RTC_DateTimeTypeDef RTC_DateTimeStructure)
{
RTC_TimeTypeDef RTC_TimeStructure;
RTC_TimeStructure.RTC_H12 = RTC_H12_AM;
RTC_TimeStructure.RTC_Hours = RTC_DateTimeStructure.Hour;
RTC_TimeStructure.RTC_Minutes = RTC_DateTimeStructure.Minute;
RTC_TimeStructure.RTC_Seconds = RTC_DateTimeStructure.Second;
if(RTC_SetTime(RTC_Format_BIN, &RTC_TimeStructure) == SUCCESS)
{
return RTC_OK;
}
else
{
return RTC_ERR;
}
}
//RTC时间设置
//RTC_OK ----------------- 成功
//RTC_ERR ---------------- 错误
RTC_RESULT RTC_SetDateTime(RTC_DateTimeTypeDef RTC_DateTimeStructure)
{
if(RTC_ERR == RTC_TimeRegulate(RTC_DateTimeStructure))
{
return RTC_ERR;
}
return RTC_OK;
}
//读取RTC时间
void RTC_GetDateTime(RTC_DateTimeTypeDef *RTC_DateTimeStructure)
{
RTC_TimeTypeDef RTC_TimeStructure;
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
RTC_DateTimeStructure->Hour = RTC_TimeStructure.RTC_Hours;
RTC_DateTimeStructure->Minute = RTC_TimeStructure.RTC_Minutes;
RTC_DateTimeStructure->Second = RTC_TimeStructure.RTC_Seconds;
}还有强调一下,RTC的时钟源可以有很多种,比如HSE 、 LSE 或 LSI等, LSE 时钟属于备份域的,但LSI 时钟不属于备份域时钟,因此:
若 LSE 被选为 RTC 时钟:只要维持 VBAT 正常供电,即使 VDD 掉电, RTC 仍会继续工作。
若 LSI 被 选为 RTC 时钟:当 VDD 掉电时, RTC 处于不定的状态。
在这个项目中需要选择时钟源由LSI 时钟提供,因为LSE需要外接32.768kHZ晶振和纽扣电池供电,恰巧没有…LSE的好处是可以做到掉电放丢失和时间精度高。经过测试,选用LSI时,15分钟时间误差为6秒左右,选用LSE时半小时0误差。所以有兴趣的可以搞一下这个。
如图所示。

通过这四个函数就可以设置RTC时钟时间,再通过OLED显示或串口打印出来,就能得到相应的时钟。
下面在简单说一下OLED的问题
二. OLED显示屏
2.1 OLED概述
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。
优势:主动发光的特性使 OLED 几乎没有视角限制,视角一般可达到 170 度,具
有较宽的视角,从侧面也不会失真。OLED 采用有机发光原理,所需材料很少,制作上比采用液体发光的液晶工序少,液晶显示屏少 3 道工序,成本大幅降低。
具有多种接口方式,该模块提供了总共 5 种接口包括: 6800、 8080 两种并行接口方式、 3线或4线的SPI接口,IIC接口方式。
本次DIY用到的是I2C通信的OLED显示屏,只需操作两个IO口,更加简单。
显示尺寸为0.96寸,分辨率为128*64。
OLED相当于64行128列点阵,每个像素点,0点亮,1熄灭 。
OLED将纵向64行分为8页,每页8行。
原理图如图所示

2.2 硬件设计
SCL —— PB8
SDA——PB9
VCC——VCC
GND——GND
2.3软件设计
//写命令
void WriteCmd(unsigned char IIC_Cmd)
{
IIC_Start();
Write_OneByte(0x78);
Write_OneByte(0x00);
Write_OneByte(IIC_Cmd);
IIC_Stop();
}
//写数据
void WriteDat(unsigned char IIC_Dat)
{
IIC_Start();
Write_OneByte(0x78);
Write_OneByte(0x40);
Write_OneByte(IIC_Dat);
IIC_Stop();
}
//设置坐标
void OLED_SetPos(unsigned char x,unsigned char y)
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}
//OLED全屏填充
void OLED_Fill(unsigned char fill_Data)
{
unsigned char m,n;
for(m=0;m<8;m++)
{
WriteCmd(0xb0+m);
WriteCmd(0x00);
WriteCmd(0x10);
for(n=0;n<128;n++)
{
WriteDat(fill_Data);
}
}
}
void OLED_CLS(void)
{
OLED_Fill(0x00);
}关于OLED的初始化函数如图所示

初始化里需要注意的主要就是这些指令,其中最关键的一些指令我通过一个表总结一下

最后还有显示字符的函数
void OLED_ShowStr(unsigned char x,unsigned char y,unsigned char ch[], unsigned char TextSize) //字符串显示函数
{
unsigned char c=0,i=0,j=0;
switch(TextSize)
{ case 1:
{
while(ch[j]!='\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat(F6x8[c][i]);
x += 6;
j++;
}
break;
}
case 2:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i+8]);
x += 8;
j++;
}
break;
}
}
}图中写的是两种大小的函数,分别是6*8和8*16两种大小,只需对应相应的ASCII字符,就能在显示屏上显示相应的英文,字符和数字。
OLED显示屏还可以显示中文字符和图片,具体函数有兴趣的同学可以研究研究,网上很容易能找到相应例程。
由于OLED没有字符库,所以需要用到取模软件手动取模,字符大小也要根据显示函数中字符大小来设置,取模时软件设置如图所示。

到此,“镜子里的屏幕”中所用到的最基本模块及程序就全部介绍完了,下一篇就是综合起来形成想要的效果。想知后事如何,请看下章分享(程序的整合与制作遇到的问题)。
我要赚赏金
