大家好,这次讲一下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没有字符库,所以需要用到取模软件手动取模,字符大小也要根据显示函数中字符大小来设置,取模时软件设置如图所示。
到此,“镜子里的屏幕”中所用到的最基本模块及程序就全部介绍完了,下一篇就是综合起来形成想要的效果。想知后事如何,请看下章分享(程序的整合与制作遇到的问题)。