目录
一、任务书确定
1、蓝牙控制OLED12864显示2级菜单界面 |
2、按键控制MCU进入掉电模式,外部中断唤醒 |
3、按键切换主界面字体大小 |
4、通过蓝牙模块设置时间、1个闹钟 |
二、进度表
起止时间 | 任务简述 | |
Start | 耗时(天) | |
2021.10.20 | 2 | 1.1、确定选题+任务书 |
2021.10.23 | 7 | 1.2、绘制原理图、PCB |
1.3、华秋、淘宝采购元器件 | ||
2021.11.3 | 5 | 1.4、嘉立创打板、检查样板(结果:封装错误) |
2021.11.9 | 2 | 1.5、修改原理图、PCB |
2021.11.10 | 5 | 1.6、捷配打样板 |
2021.11.16 | 2 | 1.7、检查+焊接样板 |
2021.11.18 | 2 | 1.8、调试+检查PCB(结果:没问题) |
2021.11.30 | 3 | 1.9、在原有基础上改善PCB(减少飞线) |
2021.11.20 | 1 | 2.0、LCD1602显示模块测试 |
2021.12.2 | 2 | 2.1、OLED12864模块显示测试 |
2021.12.4 | 3 | 2.2、OLED12864初始化界面+显示基本时钟功能 |
2021.12.8 | 1 | 2.3、蓝牙模块测试 |
2021.12.9 ~ 2021.12.17 | 9 | 2.4.1、DS18B20温度测量并显示 |
2.4.2、蓝牙控制OLED12864显示2级菜单界面 | ||
2.4.3、按键关机、开机 | ||
2.4.4、按键切换主界面字体大小 | ||
2.4.5、蓝牙控制显示2级菜单并设置时间、1个闹钟 |
三、原理框图、原理图、PCB
四、简要程序流程图
五、 部分程序
5.1、主函数
#include "main.h"
#include "timing.h"
#include "Bluetooth.h"
#include "Menu.h"
#include "Keyscan.h"
bit MODE = 0; // 全局变量,控制显示字体大小(0:32 or 1:48)
/**
* @brief : 主函数
* @param :
* @retval :
*/
void main(void)
{
OPEN_Init();
while(1)
{
Menu_Proc(Uart2_RecByte());
T1_Proc();
Keyscan();
}
}
5.2、菜单部分
#ifndef __MAIN_H
#define __MAIN_H
#include <STC15F2K60S2.H>
typedef unsigned int uint32;
typedef unsigned short int uint16;
typedef unsigned char uint8;
extern bit MODE;
// 晶振频率
#define SYSCLK 11059200UL
//-------------OLED12864 字符位置信息------------
#define SIZE(MODE) (MODE) ? 48 : 32 // 字体大小(MODE: 0:32 1:48)
// 日期 显示大小、位置
#define DA_SZ(MODE) (MODE) ? 8 : 16
#define DA_POS(MODE) (MODE) ? 34 : 24
// 时间 显示 位置
#define T_WIDE(MODE) (MODE) ? 24 : 16
#define T_POSX(MODE) (MODE) ? 4 : 24
#define T_POSY(MODE) (MODE) ? 1 : 2
#define C_POSX(MODE) (MODE) ? 52 : 56 // T_POSX(MODE) + (字宽度*2)
#define MIN_POSX (MODE) ? 76 : 72 // CHAR_POSX(MODE) + 字符宽度 (':'的位置)
// 菜单字符显示信息
#define MENU_POSY(MODE) (MODE) ? 7 : 6
//-------------LED、按键引脚定义------------
sbit LED_RED = P3^5; // 低电平点亮
sbit LED_GREEN = P4^5;
sbit SPEAKER = P1^1;
#endif
#ifndef __MENU_H
#define __MENU_H
#include "main.h"
extern uint8 Clocking[2]; // 0:时 1:分
extern bit CLOCK_FLAG;
void OPEN_Init(void);
void Show_Info(void);
void Menu_Proc(uint8 dat);
bit Set_Time(void);
bit Set_Clock(void);
bit SetDate(uint8 set_mode);
void Clock_Check(uint8 Min_Dat);
#endif
#include "Menu.h"
#include "oled.h"
#include "bmp.h"
#include "Bluetooth.h"
#include "ds1302.h"
#include "ds18b20.h"
#include "Keyscan.h"
#include "timing.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
static uint8 Clocking[2]; // 0:时 1:分
bit CLOCK_FLAG = 0;
/**
* @brief : 多功能电子时钟初始化
* @param :
* @retval :
*/
void OPEN_Init(void)
{
LED_RED = 1;
LED_GREEN = 1;
//RTC_Set(RTC_Dat); // 设置默认时钟
OLED_Init(); // 初始化 OLED12864
OLED_ColorTurn(0); // 0正常显示,1 反色显示
OLED_DisplayTurn(0); // 0正常显示 1 屏幕翻转显示
OLED_DrawBMP(0, 0, 128, 64, BMP1); // 显示图片
delay_ms(300);
Show_Info();
INTx_Init(); // 外部中断初始化
T1_Init(); // 初始化定时器,应放在OLED初始化之后
Bluetooth_Init(); // 初始化蓝牙串口
}
/**
* @brief : 显示信息
* @param :
* @retval :
*/
void Show_Info(void)
{
uint8 Buf[11];
OLED_Clear(); // 清屏
RTC_Read(RTC_Dat); // 读时间
RTC_Dat[0] = Read_Ds1302_Byte(RTC_Addr_R_Year); // 读 年
sprintf(Buf, "20%02x.%02x.%02x", \
(uint32)RTC_Dat[0], \
(uint32)RTC_Dat[2], \
(uint32)RTC_Dat[3]);
OLED_ShowString(DA_POS(MODE), 0, Buf, DA_SZ(MODE));
// 显示初始时间
sprintf(Buf, "%02x:%02x", \
(uint32)RTC_Dat[4], \
(uint32)RTC_Dat[5]);
OLED_ShowStr32or48(T_POSX(MODE), T_POSY(MODE), Buf, SIZE(MODE));
OLED_ShowString(0, MENU_POSY(MODE), "Menu", DA_SZ(MODE));
sprintf(Buf, "%5.1f", (float)rd_temperature());
OLED_ShowString(80, MENU_POSY(MODE), Buf, DA_SZ(MODE)); // 显示温度
}
/**
* @brief : 功能菜单页面处理
* @param :
* @retval :
*/
void Menu_Proc(uint8 dat)
{
if(dat == '0')
{
ET1 = 0; // 关 T1 中断
OLED_ShowMenu();
Uart2_SendStr("\r\n菜单界面\r\n");
while(1)
{
if((dat=Uart2_RecByte()) == '0')
{
Uart2_SendStr("Return\r\n");
Show_Info();
ET1 = 1; // 开 T1 中断
return;
}
else
{
switch(dat)
{
case '1':
if(Set_Time() == 1)
{
Show_Info();
Uart2_SendStr("\r\n时间设置成功\r\n");
ET1 = 1;
return;
}
else
{
Show_Info();
Uart2_SendStr("\r\n时间设置失败\r\n");
ET1 = 1;
return;
}
break;
case '2':
if(Set_Clock() == 1)
{
Show_Info();
Uart2_SendStr("\r\n闹钟设置成功\r\n");
ET1 = 1;
return;
}
else
{
Show_Info();
Uart2_SendStr("\r\n闹钟设置失败\r\n");
ET1 = 1;
return;
}
break;
}
}
}
}
}
/**
* @brief : 设置时间
* @param :
* @retval :
*/
bit Set_Time(void)
{
uint8 i = 0, ch;
OLED_Clear();
for(i=0; i<4; i++) // 显示:设置时间
OLED_ShowChinese((32+i*16), 0, (2+i), 16);
Uart2_SendStr("\r\n请设置时间\r\n");
if(SetDate(0) == 1) // 设置年月日成功
{
Uart2_SendStr("\r\n是(1)否(0)继续设置时间?\r\n");
while(1)
{
ch=Uart2_RecByte();
if(ch == '0')
break;
else if(ch == '1')
{
if(SetDate(1)==0) // 设置时分
return 0;
else
return 1;
}
}
return 1;
}
else
return 0;
}
/**
* @brief : 设置闹钟
* @param :
* @retval :
*/
bit Set_Clock(void)
{
uint8 i;
OLED_Clear();
for(i=0; i<2; i++) // 设置
OLED_ShowChinese((32+i*16), 0, (2+i), 16);
for(i=0; i<2; i++) // 闹钟
OLED_ShowChinese((64+i*16), 0, (6+i), 16);
if(SetDate(2) == 1) // 设置闹钟成功
{
CLOCK_FLAG = 1; // 有闹钟
return 1;
}
else
{
CLOCK_FLAG = 0; // 无闹钟
return 0;
}
}
/**
* @brief : 设置年月日时间
* @param :
* @retval :
*/
bit SetDate(uint8 set_mode)
{
uint32 temp;
uint8 *str_addr, *p;
const uint8 *str = ".", *str1 = ":";
if(set_mode == 0)
Uart2_SendStr("\r\n请设置年、月、日(格式:20xx.xx.xx+回车)(退出:x+回车)\r\n");
else if(set_mode == 1)
Uart2_SendStr("\r\n请设置时间(格式:xx:xx:+回车)\r\n");
else if(set_mode == 2)
Uart2_SendStr("\r\n请设置闹钟(格式:xx:xx:+回车)\r\n");
while(1)
{
str_addr = Uart2_RecStr();
if((strlen(str_addr)>0) && (strcmp(str_addr, "x")!=0))
{
Uart2_SendStr(str_addr); // 蓝牙返回字符串,提示接收到
if(set_mode == 0)
p = strtok(str_addr, str); // 切割字符串
else if((set_mode==1) || (set_mode==2))
p = strtok(str_addr, str1); // 切割字符串
temp = atoi(p); // 数字字符串转整型数据
if(set_mode == 0)
{
if((temp>=2000) && (temp<=2099)) // 判断年输入格式是否正确
{
RTC_Dat[0] = ((temp/10%10)<<4) | (temp%10);
RTC_SetOne(RTC_Dat[0], RTC_Addr_W_Year); // 设置年
OLED_ShowString(0, 2, "Year:", 16);
OLED_ShowString(40, 2, p, 16);
p = strtok(NULL, str);
temp = atoi(p);
RTC_Dat[2] = ((temp/10%10)<<4) | (temp%10);
RTC_SetOne(RTC_Dat[2], RTC_Addr_W_Month); // 设置月
OLED_ShowString(0, 4, "Month:", 16);
OLED_ShowString(48, 4, p, 16);
p = strtok(NULL, str);
temp = atoi(p);
RTC_Dat[3] = ((temp/10%10)<<4) | (temp%10);
RTC_SetOne(RTC_Dat[3], RTC_Addr_W_Day); // 设置日
OLED_ShowString(0, 6, "Day:", 16);
OLED_ShowString(32, 6, p, 16);
memset(str_addr, 0, sizeof(str_addr)); // 清空字符串
Uart2_SendStr("\r\n设置年月日成功\r\n");
return 1;
}
else
{
temp = 0;
memset(str_addr, 0, sizeof(str_addr)); // 清空字符串
Uart2_SendStr("输入错误\r\n");
}
}
else if((set_mode==1) || (set_mode==2))
{
if((temp>=0) && (temp<=23)) // 判断 时间 输入格式是否正确
{
if(set_mode==1)
{
RTC_Dat[4] = ((temp/10%10)<<4) | (temp%10);
RTC_SetOne(RTC_Dat[4], RTC_Addr_W_Hour); // 设置时
}
else if(set_mode==2)
{
Clocking[0] = ((temp/10%10)<<4) | (temp%10); // 设置闹钟时
}
OLED_ShowString(64, 4, "Hour:", 16);
OLED_ShowString(96, 4, p, 16);
p = strtok(NULL, str1);
temp = atoi(p);
if(set_mode==1)
{
RTC_Dat[5] = ((temp/10%10)<<4) | (temp%10);
RTC_SetOne(RTC_Dat[5], RTC_Addr_W_Min); // 设置分
}
else if(set_mode==2)
{
Clocking[1] = ((temp/10%10)<<4) | (temp%10); // 设置闹钟分
}
OLED_ShowString(64, 6, "Min:", 16);
OLED_ShowString(96, 6, p, 16);
memset(str_addr, 0, sizeof(str_addr)); // 清空字符串
Uart2_SendStr("\r\n设置时分成功\r\n");
delay_ms(500);
return 1;
}
else
{
temp = 0;
memset(str_addr, 0, sizeof(str_addr)); // 清空字符串
Uart2_SendStr("输入错误\r\n");
}
}
}
else if(strcmp(str_addr, "x") == 0)
{
Uart2_SendStr("\r\n退出设置\r\n");
memset(str_addr, 0, sizeof(str_addr)); // 清空字符串
temp = 0;
return 0;
}
}
}
/**
* @brief : 闹钟检查函数
* @param : Min_Dat:分钟
* @retval :
*/
void Clock_Check(uint8 Min_Dat)
{
if(Min_Dat != Clocking[1]) // 若闹钟分钟未到
return;
if(Read_Ds1302_Byte(RTC_Addr_R_Hour) == Clocking[0]) // 若闹钟时分皆符合
{
ET1 = 0; // 关T1中断
OLED_Clear();
OLED_DrawBMP(32, 0, 64, 64, BMP2); // 显示闹钟提示
while(1)
{
if(Uart2_RecByte() == '0')
{
Show_Info(); // 显示时钟界面
Clocking[0] = Clocking[1] = 0; // 清除闹钟
CLOCK_FLAG = 0;
break;
}
else
{
SPEAKER = ~SPEAKER;
delay_ms(1);
}
}
ET1 = 1;
}
}
六、实物、功能展示
七、源码+其它资料
链接:https://pan.baidu.com/s/1OkA2VG4j6fXIKFJ15vdRHA
提取码:stej