STM32+OLED显示屏制作指针式电子钟
【摘要】 该电子钟使用的是STM32内部RTC实时时钟,电子钟的界面仪表盘画面更新是在RTC秒中断里调用,实现时间指针的更新,编写OLED驱动代码,编写常用的OLED接口函数,比如:字符串显示,画点,划线等。
一、硬件环境介绍
单片机: STM32F103C8T6
RTC时钟来源: 使用STM32内部RTC定时器得到时间。
显示屏: 中景园0.96寸 SPI接口的OLED显示屏
编程软件: keil5
二、实现效果图与代码技术部分介绍
2.1 实现的效果图
2.2 代码技术部分介绍
核心代码内容里分为以下几个部分:
(1) RTC时钟代码部分:该电子钟使用的是STM32内部RTC实时时钟,需要编写RTC初始化代码。
(2) 电子钟界面逻辑代码部分:电子钟的界面仪表盘画面更新是在RTC秒中断里调用,实现时间指针的更新。
(3) OLED驱动代码部分:编写OLED驱动代码,编写常用的OLED接口函数,比如:字符串显示,画点,划线等。
OLED在程序的驱动方式采用显存的方式驱动,定义一个显存数组,程序里的所有逻辑代码先绘制在显存数组里,然后再刷新到OLED显示屏上。
三、核心代码
3.1 OLED显示屏驱动代码
(1). oled.h
#ifndef _OLED_H
#define _OLED_H
#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include <string.h>
extern const u8 ChineseFont_16_16[][32];
extern const u8 ChineseFont_24_24[][24*24/8];
extern const u8 ASCII_8_16[][16];
extern const u8 bmp_foot_18_29_1[];
extern const u8 bmp_foot_17_32_2[];
extern const u8 ASCII_12_24[][12*3];
/*
初始化OLED显示屏硬件
硬件连接:
D0--PB14--时钟线
D1--PB13--数据线
RES-PB12-复位脚
DC--PB1--命令数据选择脚
CS--PA7--片选
*/
#define OLED_SCK PBout(14)
#define OLED_MOSI PBout(13)
#define OLED_RES PBout(12)
#define OLED_DC PBout(1)
#define OLED_CS PAout(7)
//定义命令
#define OLED_WRITE_CMD 0
#define OLED_WRITE_DAT 1
//函数声明
void OLED_SPI_WriteOneByte(u8 data,u8 flag);
void OLED_Init(void);
void OLED_Clear(u8 data);
void OLED_SetPos(u8 x,u8 y);
void OLED_DisplayPoint(u8 x,u8 y,u8 c);
void OLED_DisplayData(u8 x,u8 y,u8 w,u8 h,u8 *p);
void OLED_WriteGRAM(void);
void OLED_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2); //两点画线
void OLED_DrawAngleLine(u32 x,u32 y,float du,u32 len,u32 w,u8 c);//角度画线
void OLED_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2); //矩形
void OLED_Circle(u16 x0,u16 y0,u8 r); //圆
void OLED_DrawAngleLine2(u32 x,u32 y,int du,u32 len,u8 c);
void OLED_ShowString(u8 x,u8 y,u8 h,char *str);
void OLED_ShowChineseFont(u8 x,u8 y,u8 size,u8 number);
extern u8 OLED_GRAM[8][128]; //8行128列---->8页128列
#endif
3.2 rtc驱动代码
(1) rtc.c
#include "rtc.h"
//定义RTC标准结构体
struct RTC_CLOCK rtc_clock;
/*
函数功能: RTC初始化函数
*/
void RTC_Init(void)
{
if(BKP->DR1!=0xAB) //表示RTC第一次初始化
{
//1. 备份寄存器时钟
RCC->APB1ENR|=1<<27; //备份时钟接口
RCC->APB1ENR|=1<<28; //电源时钟接口
PWR->CR|=1<<8; //允许写入RTC和后备寄存器
//2. 配置RTC时钟源
RCC->BDCR|=1<<0; //开启外部32.768K时钟
while(!(RCC->BDCR&1<<1)){} //等待时钟就绪
RCC->BDCR&=~(0x3<<8); //清空时钟配置
RCC->BDCR|=0x1<<8; //选择外部32.768K时钟
//3. 配置RTC核心寄存器
RCC->BDCR|=1<<15; //开启RTC时钟
while(!(RTC->CRL&1<<5)){} //判断上一次寄存器是否写完成
RTC->CRL|=1<<4; //进入配置模式
RTC->PRLH=0; //预分频高位
RTC->PRLL=0x7FFF; //32767 预分频低位
RTC->CNTH=0; //计数器高位
RTC->CNTL=0; //计数器低位
RTC->ALRH=0; //闹钟寄存器高位
RTC->ALRL=60; //闹钟寄存器低位
RTC->CRL&=~(1<<4);//退出配置模式
while(!(RTC->CRL&1<<5)){} //判断上一次寄存器是否写完成
BKP->DR1=0xAB; //表示配置成功了
}
RTC->CRH|=1<<0; //秒中断
RTC->CRH|=1<<1; //闹钟中断
STM32_SetPriority(RTC_IRQn,2,2); //优先级
}
extern void Update_FrameShow(void);
/*
函数功能: RTC闹钟中断服务函数
*/
void RTC_IRQHandler(void)
{
u32 SecCnt;
if(RTC->CRL&1<<0)
{
SecCnt=RTC->CNTH<<16;//获取高位
SecCnt|=RTC->CNTL; //获取低位
RTC_GetTime(SecCnt); //转换标准时间
RTC_GetWeek(SecCnt);
// printf("%d-%d-%d %d:%d:%d week:%d\n",rtc_clock.year,rtc_clock.mon,rtc_clock.day,rtc_clock.hour,rtc_clock.min,rtc_clock.sec,rtc_clock.week);
Update_FrameShow(); //更新显示
RTC->CRL&=~(1<<0); //清除秒中断标志位
}
if(RTC->CRL&1<<1)
{
// printf("闹钟时间到达!....\n");
// BEEP=1;
// DelayMs(500);
// BEEP=0;
RTC->CRL&=~(1<<1); //清除闹钟中断标志位
}
}
//闰年的月份
static int mon_r[12]={31,29,31,30,31,30,31,31,30,31,30,31};
//平年的月份
static int mon_p[12]={31,28,31,30,31,30,31,31,30,31,30,31};
/*
函数功能: 设置RTC时间
函数形参:
u32 year; 2018
u32 mon; 8
u32 day;
u32 hour;
u32 min;
u32 sec;
*/
void RTC_SetTime(u32 year,u32 mon,u32 day,u32 hour,u32 min,u32 sec)
{
u32 i;
u32 SecCnt=0; //总秒数
/*1. 累加已经过去的年份*/
for(i=2017;i<year;i++) //基准年份:20170101000000
{
if(RTC_GetYearState(i))
{
SecCnt+=366*24*60*60; //闰年一年的秒数
}
else
{
SecCnt+=365*24*60*60; //平年一年的秒数
}
}
/*2. 累加过去的月份*/
for(i=0;i<mon-1;i++)
{
if(RTC_GetYearState(year))
{
SecCnt+=mon_r[i]*24*60*60; //闰年一月的秒数
}
else
{
SecCnt+=mon_p[i]*24*60*60; //平年一月的秒数
}
}
/*3. 累加过去的天数*/
SecCnt+=(day-1)*24*60*60;
/*4. 累加过去小时*/
SecCnt+=hour*60*60;
/*5. 累加过去的分钟*/
SecCnt+=min*60;
/*6. 累加过去的秒*/
SecCnt+=sec;
/*7. 设置RTC时间*/
RCC->APB1ENR|=1<<27; //备份时钟接口
RCC->APB1ENR|=1<<28; //电源时钟接口
PWR->CR|=1<<8; //允许写入RTC和后备寄存器
while(!(RTC->CRL&1<<5)){} //判断上一次寄存器是否写完成
RTC->CRL|=1<<4; //进入配置模式
RTC->CNTH=SecCnt>>16; //计数器高位
RTC->CNTL=SecCnt&0xFFFF; //计数器低位
RTC->CRL&=~(1<<4);//退出配置模式
while(!(RTC->CRL&1<<5)){} //判断上一次寄存器是否写完成
}
/*
函数功能: 获取RTC时间
函数参数: u32 sec 秒单位时间
*/
void RTC_GetTime(u32 sec)
{
u32 i;
rtc_clock.year=2017; //基准年份
/*1. 计算当前的年份*/
while(1)
{
if(RTC_GetYearState(rtc_clock.year))
{
if(sec>=366*24*60*60) //够一年
{
sec-=366*24*60*60;
rtc_clock.year++;
}
else break;
}
else
{
if(sec>=365*24*60*60) //够一年
{
sec-=365*24*60*60;
rtc_clock.year++;
}
else break;
}
}
/*2. 计算当前的月份*/
rtc_clock.mon=1;
for(i=0;i<12;i++)
{
if(RTC_GetYearState(rtc_clock.year))
{
if(sec>=mon_r[i]*24*60*60)
{
sec-=mon_r[i]*24*60*60;
rtc_clock.mon++;
}
else break;
}
else
{
if(sec>=mon_p[i]*24*60*60)
{
sec-=mon_p[i]*24*60*60;
rtc_clock.mon++;
}
else break;
}
}
/*3. 计算当前的天数*/
rtc_clock.day=1;
while(1)
{
if(sec>=24*60*60)
{
sec-=24*60*60;
rtc_clock.day++;
}
else break;
}
/*4. 计算当前的小时*/
rtc_clock.hour=0;
while(1)
{
if(sec>=60*60)
{
sec-=60*60;
rtc_clock.hour++;
}
else break;
}
/*5. 计算当前的分钟*/
rtc_clock.min=0;
while(1)
{
if(sec>=60)
{
sec-=60;
rtc_clock.min++;
}
else break;
}
/*6. 计算当前的秒*/
rtc_clock.sec=sec;
}
/*
函数功能: 判断年份是否是平年、闰年
返回值 : 0表示平年 1表示闰年
*/
u8 RTC_GetYearState(u32 year)
{
if((year%4==0&&year%100!=0)||year%400==0)
{
return 1;
}
return 0;
}
/*
函数功能: 获取星期
*/
void RTC_GetWeek(u32 sec)
{
u32 day1=sec/(60*60*24); //将秒单位时间转为天数
switch(day1%7)
{
case 0:
rtc_clock.week=0;
break;
case 1:
rtc_clock.week=1;
break;
case 2:
rtc_clock.week=2;
break;
case 3:
rtc_clock.week=3;
break;
case 4:
rtc_clock.week=4;
break;
case 5:
rtc_clock.week=5;
break;
case 6:
rtc_clock.week=6;
break;
}
}
(2) rtc.h
#ifndef RTC_H
#define RTC_H
#include "stm32f10x.h"
#include "sys.h"
#include "usart.h"
#include "delay.h"
//定时RTC时钟结构体
struct RTC_CLOCK
{
u32 year;
u32 mon;
u32 day;
u32 hour;
u32 min;
u32 sec;
u32 week;
};
extern struct RTC_CLOCK rtc_clock;
//函数声明
void RTC_Init(void);
u8 RTC_GetYearState(u32 year);
void RTC_GetTime(u32 sec);
void RTC_GetWeek(u32 sec);
void RTC_SetTime(u32 year,u32 mon,u32 day,u32 hour,u32 min,u32 sec);
#endif
3.3 界面绘制部分代码-主函数
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include <string.h>
#include "timer.h"
#include "oled.h"
#include "rtc.h"
/*
函数功能: 绘制时钟表盘框架
*/
void DrawTimeFrame(void)
{
u8 i;
OLED_Circle(32,32,31);//画外圆
OLED_Circle(32,32,1); //画中心圆
//画刻度
for(i=0;i<60;i++)
{
if(i%5==0)OLED_DrawAngleLine(32,32,6*i,31,3,1);
}
OLED_WriteGRAM(); //刷新数据到OLED屏幕
}
/*
函数功能: 更新时间框架显示,在RTC中断里调用
*/
char TimeBuff[20];
void Update_FrameShow(void)
{
/*1. 绘制秒针、分针、时针*/
OLED_DrawAngleLine2(32,32,rtc_clock.sec*6-6-90,27,0);//清除之前的秒针
OLED_DrawAngleLine2(32,32,rtc_clock.sec*6-90,27,1); //画秒针
OLED_DrawAngleLine2(32,32,rtc_clock.min*6-6-90,24,0);
OLED_DrawAngleLine2(32,32,rtc_clock.min*6-90,24,1);
OLED_DrawAngleLine2(32,32,rtc_clock.hour*30-6-90,21,0);
OLED_DrawAngleLine2(32,32,rtc_clock.hour*30-90,21,1);
//绘制电子钟时间
sprintf(TimeBuff,"%d",rtc_clock.year);
OLED_ShowString(65,16*0,16,TimeBuff); //年份字符串
OLED_ShowChineseFont(66+32,16*0,16,4); //显示年
sprintf(TimeBuff,"%d/%d",rtc_clock.mon,rtc_clock.day);
OLED_ShowString(75,16*1,16,TimeBuff); //月
if(rtc_clock.sec==0)OLED_ShowString(65,16*2,16," "); //清除多余的数据
sprintf(TimeBuff,"%d:%d:%d",rtc_clock.hour,rtc_clock.min,rtc_clock.sec);
OLED_ShowString(65,16*2,16,TimeBuff); //秒
//显示星期
OLED_ShowChineseFont(70,16*3,16,5); //星
OLED_ShowChineseFont(70+16,16*3,16,6); //期
OLED_ShowChineseFont(70+32,16*3,16,rtc_clock.week+7); //具体的值
}
int main()
{
LED_Init();
BEEP_Init();
KEY_Init();
USART1_Init(115200);
TIMER1_Init(72,20000); //超时时间20ms
USART2_Init(9600);//串口-蓝牙
TIMER2_Init(72,20000); //超时时间20ms
USART3_Init(115200);//串口-WIFI
TIMER3_Init(72,20000); //超时时间20ms
USART1_Printf("正在初始化WIFI请稍等.\n");
RTC_Init(); //RTC初始化
OLED_Init();
OLED_Clear(0x00); //清屏
RTC_SetTime(2020,7,15,10,52,10);
DrawTimeFrame();
while(1)
{
}
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)