这是第一次写博客,请多多关照
后续功能会添加,也请广大网友给本设计出出主意,若有错误或更好的方法,请多多指正,虚心受教,谢谢
用库函数配置
//"RTC"中断向量配置
void RTC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure; //初始化中断源
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //优先级分组
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //中断源
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_Init(&NVIC_InitStructure);
}
RTC设置(可添加按键修改参数)
//RTC时钟初始化设置
u8 RTC_Init(void)
{
u8 temp = 0; //检查是否第一次配置时钟
if(BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //若第一次配置
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE); //使能PWR和BKP时钟
PWR->CR|=1<<8; //取消备份区写保护
RCC->BDCR|=1<<16;//备份区域软复位
RCC->BDCR&=~(1<<16);//备份区域软复位结束
PWR_BackupAccessCmd(ENABLE); //使能后被寄存器访问
RCC_LSEConfig(RCC_LSE_ON); //设置LSE
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250) //检查制定的RCC标志位设置与否,等待LSE就绪
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1; //初始化时钟失败,晶振有问题
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟,选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC,ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_EnterConfigMode(); //允许配置
RTC_SetPrescaler(32767); //设置RTC预分频的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_Set(2018,11,5,22,31,30); //设置时间
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1,0x5050); //向指定的后被寄存器中写入用户程序数据
}
else //系统继续计时
{
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
}
RTC_NVIC_Config(); //RTC中断分组设置
RTC_Get(); //更新时间
GPIO_ResetBits(GPIOC, GPIO_Pin_13);//点亮c13
return 0; //ok
}
//RTC时钟中断,秒触发
void RTC_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_SEC) != RESET) //秒钟中断
{
RTC_Get(); //更新时间
}
// if(RTC_GetITStatus(RTC_IT_ALR) != RESET) //闹钟中断
// {
// RTC_ClearITPendingBit(RTC_IT_ALR); //清闹钟中断
// }
RTC_ClearITPendingBit(RTC_IT_SEC); //清闹钟中断溢出
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
}
时钟显示为年月日时分秒:
//设置时钟
u8 RTC_Set(u16 year,u8 mon,u8 day,u8 hour,u8 min,u8 sec)
{
u16 t;
u32 SecCount = 0;
if(year<1970||year>2099) return 1; //计数时间为1970~2099年,一般最大130年
for(t=1970;tAPB1ENR|=1<<28;//使能电源时钟
RCC->APB1ENR|=1<<27;//使能备份时钟
PWR->CR|=1<<8; //取消备份区写保护
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
RTC_SetCounter(SecCount); //设置RTC计数器的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_Get(); //更新时间
return 0;
}
2.年月日时分秒处理;
//计算时间temp1:月份 temp:天数
u8 RTC_Get(void)
{
static u16 DayCount=0;
u32 SecCount=0;
u32 temp=0;
u16 temp1=0;
SecCount = RTC_GetCounter();
temp=SecCount/86400; //得到天数
if(DayCount != temp) //超过一天
{
DayCount=temp;
temp1=1970; //从1970年开始
while (temp>=365)
{
if(Is_LeapYear(temp1)) //是闰年
{
if(temp>=366)
temp -= 366; //减掉闰年的天数
else
{
//temp1++;
break;
}
}
else
temp -= 365; //平年
temp1++;
}
RealTime.year = temp1; //得到年份
temp1 = 0;
while (temp>=28) //超过一个月
{
if(Is_LeapYear(RealTime.year)&&temp1==1) //当年使闰年且轮循到2月
{
if(temp>=29)
temp -=29;
else
break;
}
else
{
if(temp>=Mon_Tab[temp1]) //平年
temp -= Mon_Tab[temp1];
else
break;
}
temp1++;
}
RealTime.mon = temp1+1; //得到月份,temp1=0表示1月,所以这要‘1‘
RealTime.date = temp+1; //得到日期,因为这一天还没过完,但是显示的时候要显示正常日期
}
temp=SecCount%86400; //得到秒数
RealTime.hour=temp/3600; //小时
RealTime.min=(temp%3600)/60; //分钟
RealTime.sec=(temp%3600)%60; //秒
RealTime.week=Get_Week(RealTime.year,RealTime.mon,RealTime.date);//获取星期
return 0;
}
//设置星期(蔡勒公式)
u8 Get_Week(u16 Year,u8 Month,u8 Date)
{
int W,Y,C,M,D;
u8 day,i;
C = Year/100;
Y = Year%100;
M = Month;
if(M<3){M+=12;Y--;}
D = Date;
W = Y + Y/4 + C/4 - 2*C + 26*(M+1)/10 + D - 1;
while(W<0) W += 7;
day = W%7;
day = W%7; //得到的星期为0,1,2,3,4,5,6;0为周日
if(day==0)
day = 5;
for(i=1;i<7;i++)
{
if(day == i)
day = i+7;
}
return day;
}
#ifndef __RTC_H
#define __RTC_H
#include "stm32f10x.h"
//时钟结构体
typedef struct{
u16 year;
u8 day;
u8 mon;
u8 date;
u8 hour;
u8 min;
u8 sec;
u8 week;
}TimeStruct;
extern TimeStruct RealTime; //时钟
//函数声明
void RTC_NVIC_Config(void);
u8 RTC_Init(void);
void RTC_IRQHandler(void);
void Read_RTCTime(TimeStruct *Time);
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
u8 RTC_Get(void);
u8 Get_Week(u16 Year,u8 Month,u8 Date);
//u8 RTC_GetWeek(u16 year,u8 month,u8 day);
#endif
学习的是正点原子的oled显示方法
以下是驱动函数
#include "stm32f10x.h"
#include "oled.h"
#include "delay.h"
#include "oledfont.h"
#include "cnfont.h"
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127
u8 OLED_GRAM[128][8];
void OLED_Init(void) //初始化
{
// OLED_CS_OH;
// OLED_DC_OH;
// OLED_RES_OL;
// delay_ms(100);
OLED_RES_OH;
OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示
OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率
OLED_WR_Byte(0x80,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); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
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_CLC();
}
/*SPI写命令写数据*/
void OLED_WR_Byte(u8 data,u8 Mode)
{
int i = 0;
if(Mode)
{
OLED_DC_OH; //DC引脚输入高,写数据
}
else
{
OLED_DC_OL; //写命令
}
OLED_CS_OL; //片选使能
for(i = 0;i < 8;i++)
{
OLED_SCL_OL;
if(data & 0x80) //判断传输的数据最高位为1还是0
{
OLED_SDA_OH;
}
else
{
OLED_SDA_OL;
}
OLED_SCL_OL;
data <<= 1 ;//将数据左移一位
OLED_SCL_OH;
}
OLED_SCL_OL;
OLED_CS_OH; //片选失能
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_CLC(void)
{
u8 i,n;
for(i=0;i<8;i++)
for(n=0;n<128;n++)
OLED_GRAM[n][i]=0X00;
OLED_Refresh_Gram();//更新显示
}
//开启OLED显示
void OLED_Display_On(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON
OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
//更新显存到LCD
void OLED_Refresh_Gram(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte(0xb0+i,OLED_CMD); //设置页地址(0~7)
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);
}
}
//画点
//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; //pos
bx=y%8;
temp=1<<(7-bx);
if(t)OLED_GRAM[x][pos]|=temp;
else OLED_GRAM[x][pos]&=~temp;
}
//x1,y1,x2,y2 填充区域的对角坐标
//确保x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=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();//更新显示
}
//m^n函数
u32 mypow(u8 m,u8 n)
{
u32 result=1;
while(n--)result*=m;
return result;
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 12/16/24
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=' '))//判断是不是非法字符!
{
if(x>(128-(size/2))){x=0;y+=size;}
if(y>(64-size))
{
y=x=0;
OLED_CLC();
}
OLED_ShowChar(x,y,*p,size,1);
x+=size/2;
p++;
}
}
//显示汉字,设置坐标x,y
void OLED_ShowChinese(u16 x,u16 y,u8 fnum)
{
u8 temp,t,t1;
u16 y0 = y;
u8 *dzk;
u8 csize=32; //一个24*24的汉字72字节
dzk=(u8*)OLED_HZK_TBL[fnum]; //得到汉字编号对应的点阵库
for(t=0;t
修改一下与stm32连接的引脚就可以使用啦
#ifndef __OLED_H
#define __OLED_H
#include "sys.h"
#include "stdlib.h"
//---------------------------OLED端口定义--------------------------
#define OLED_CS PBout(1)
#define OLED_RES PBout(10)
#define OLED_DC PBout(2)
#define OLED_SCL PBout(12)
#define OLED_SDA PBout(11)
#define OLED_GPIOB GPIOB
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
#define OLED_SCL_OL GPIO_ResetBits(GPIOB, GPIO_Pin_12) //D0 IO口输出低电平
#define OLED_SCL_OH GPIO_SetBits(GPIOB, GPIO_Pin_12) //D0 IO口输出高电平
#define OLED_SDA_OL GPIO_ResetBits(GPIOB, GPIO_Pin_11) //D0 IO口输出低电平
#define OLED_SDA_OH GPIO_SetBits(GPIOB, GPIO_Pin_11) //D0 IO口输出高电平
#define OLED_CS_OL GPIO_ResetBits(GPIOB, GPIO_Pin_1) //D0 IO口输出低电平
#define OLED_CS_OH GPIO_SetBits(GPIOB, GPIO_Pin_1) //D0 IO口输出高电平
#define OLED_DC_OL GPIO_ResetBits(GPIOB, GPIO_Pin_2) //D0 IO口输出低电平
#define OLED_DC_OH GPIO_SetBits(GPIOB, GPIO_Pin_2) //D0 IO口输出高电平
#define OLED_RES_OL GPIO_ResetBits(GPIOB, GPIO_Pin_10) //D0 IO口输出低电平
#define OLED_RES_OH GPIO_SetBits(GPIOB, GPIO_Pin_10) //D0 IO口输出高电平
void OLED_Init(void);
void OLED_WR_Byte(u8 data,u8 Mode);
//void write_d(unsigned char dat);
void delay(unsigned int i);
//void OLED_ADD(unsigned char x ,unsigned char y);
//void OLED_Display_On(void);
//void OLED_Display_Off(void);
void OLED_DrawPoint(u8 x,u8 y,u8 t);
void OLED_Refresh_Gram(void);
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);
void OLED_CLC(void);
void OLED_Refresh_Gram(void);
void OLED_ShowString(u8 x,u8 y,char *p,u8 size);
void OLED_ShowChinese(u16 x,u16 y,u8 fnum);
#endif
这里要用到字库文件,请大家注意了
因为我参照的是正点原子的oled显示,所以字库比较了解,这只是显示屏的函数,对时钟没有关联,大家可以自行参考
我这里简单的拿一点出来做示范
#ifndef __OLEDFONT_H
#define __OLEDFONT_H
//常用ASCII表
//偏移量32
//ASCII字符集: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
//PC2LCD2002取模方式设置:阴码+逐列式+顺向+C51格式
//总共:3个字符集(12*12、16*16和24*24),用户可以自行新增其他分辨率的字符集。
//每个字符所占用的字节数为:(size/8+((size%8)?1:0))*(size/2),其中size:是字库生成时的点阵大小(12/16/24...)
//12*12 ASCII字符集点阵
const unsigned char asc2_1206[95][12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/}
//24*24 ASICII字符集点阵
const unsigned char asc2_2412[95][36]={{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/}
#endif
因为我中途加了DHT11温湿度传感器,报错大家删掉无关的东西就可以了
#include "stm32f10x.h"
#include "sys.h"
#include "led.h"
#include "oled.h"
#include "delay.h"
#include "usart.h"
#include "rtc.h"
#include "stdio.h"
#include "key.h"
#include "dht11.h"
//TimeStruct RealTime; //时钟
int main(void)
{
u8 t;
u8 temperature;
u8 humidity;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,115200); //串口初始化为115200
LED_Init(); //初始化GPIO
OLED_Init(); //初始化OLED
RTC_Init();
RTC_NVIC_Config();
DHT11_Init();
while(1)
{
OLED_ShowString(8+0,0,"20",16);
OLED_ShowNum(8+16,0,RealTime.year,2,16);
OLED_ShowChinese(8+16+16,0,3); //年
OLED_ShowNum(8+16+16+16,0,RealTime.mon,2,16);
OLED_ShowChinese(8+16+16+16+16,0,4); //月
OLED_ShowNum(8+16+16+16+16+16,0,RealTime.date,2,16);
OLED_ShowChinese(8+16+16+16+16+16+16,0,5);//日
OLED_ShowChinese(0,18,6); //星
OLED_ShowChinese(16,18,7); //期
OLED_ShowChinese(16+16,18,RealTime.week);
if(t%10==0) //每100ms读取一次
{
DHT11_Read_Data(&temperature,&humidity); //读取温湿度值
OLED_ShowNum(70,18,temperature,2,16); //显示温度
OLED_ShowString(86,18,"C",16);
OLED_ShowNum(104,18,humidity,2,16); //显示湿度
OLED_ShowString(120,18,"%",16);
}
delay_ms(10);
t++;
OLED_ShowString(0,40," : : ",24); //左移,下移(注意上下间距),字体,大小
OLED_ShowNum(24+24+24+12,40,RealTime.sec,2,24); //显示ASCII字符的码值
OLED_ShowNum(24+24,40,RealTime.min,2,24); //显示ASCII字符的码值
OLED_ShowNum(16,40,RealTime.hour,2,24); //显示ASCII字符的码
OLED_Refresh_Gram(); //更新显示到OLED
}
}