摘要:
随着自动化和物联网行业的不断发展,数字信息时代已经到来。在这个以时间为主要提醒的代名词的时候,再加上在这个以数字为主流方向的时刻,数字时钟在生活和工作中显得尤为重要,需求也是不断的增强,对人们生活质量的提高也是尤为重要。
本设计系统就是以液晶进行显示数字时钟的一个微系统设计,本设计三大部分:分别包括信息处理部分、信息采集部分和信息显示部分。其中,信息处理部分是以单片机为核心,外加一些辅助电路组成最小系统;信息采集部分主要是以时钟芯片STM32F103单片机为核心的时间采集模块;信息显示部分是通过整个系统用STM32F103单片机作为中央控制器,由单片机执行采集内部定时器值,时钟信号通过单片机I/O口传给LCD12864,单片机模块控制驱动模块驱动显示模块,通过显示模块来实现信号的输出、LCD12864的显示及相关的控制功能。经过软件的编写进行单片机读取时间信息并处理和显示控制信息的发送与传输,使得本设计系统完成所需要求,本系统通过软硬件相结合,两者协同工作进而完成所需达到的功能。
单片机实现时钟的设计方法,具有电路简单、编程灵活、便于扩展、精确度高、稳定性好等优点。

关键词:电子时钟;STM32F103单片机;外部中断;液晶显示

Summary:
With the continuous development of automation and Internet of Things, the digital information age has come. In this time of time as the main reminder of the pronoun, coupled with this time of digital mainstream direction, digital clock in life and work is particularly important, the demand is constantly increasing, the quality of life of people is particularly important.
This design system is a microcontroller system which uses liquid crystal to display digital clock. This design consists of three parts: information processing part, information acquisition part and information display part. Among them, the information processing part takes the single chip computer as the core and some auxiliary circuits as the minimum system; the information acquisition part mainly takes the clock chip STM32F103 as the core of the time acquisition module; the information display part takes the STM32F103 single chip computer as the central controller of the whole system, and collects the internal timer value by the single chip computer, and the clock signal passes through the single chip I. / The O port is transmitted to LCD12864. The MCU module controls the driver module to drive the display module. Through the display module, the output of the signal, the display of LCD12864 and related control functions are realized. Through the compilation of software, MCU reads time information and processes and displays the transmission and transmission of control information, which makes the design system complete the required requirements. The system combines hardware and software, and the two work together to complete the required functions.
The design method of clock based on MCU has the advantages of simple circuit, flexible programming, easy expansion, high accuracy and good stability.
Key words: electronic clock; STM32F103 MCU; external interruption; LCD display

目 录

摘要: 1
1、本课程设计的背景、目的和要求 3
1.1本课程设计的背景 4
1.2本课程设计目的 4
1.3本课程设计的功能要求 4
2、本课题研究的主要内容 5
3、本课程设计的方案分析和选择 5
3.1单片机芯片选择方案 5
3.2定时器方案选择 6
4、系统硬件及电路设计 7
4.1 系统硬件 7
4.1.1 液晶显示器STM32开发板的驱动原理 7
4.1.2 MCU 8
4.1.3 按键介绍 9
4.2 单片机最小系统电路 10
4.2.1时钟电路 10
4.2.2震荡电路 11
4.2.3复位电路 11
5、系统软件设计 12
5.1系统主程序 12
5.2按键控制 12
5.3定时器中断控制计时 13
5.4 LCD相关函数分析 16
5.4.1 画圆函数 16
5.4.2 画线函数 17
5.4.3 显示数字函数 18
5.4.4 显示字符函数 18
5.4.5 画点函数 18
6、软件调试与测试结果 19
7、结论 21
致 谢 22
参考文献 23
附件 24

1、本课程设计的背景、目的和要求
1.1本课程设计的背景
随着中国物联网产业的持续快速发展,智能化程度越来越高,进而它带给人们很多方面的便利。近几年以来,随着现代通信技术和计算机事业的迅速发展,以及电子技术和微电子技术自动化,智能化程度的不断的提高,时间显示的智能化技术也在不断进步,在数字时钟的信息处理方面有了更进一步的发展和应用。正是由于无线通讯技术的进步和不断普及,时间处理技术已无处不在,而且应用成本已经降到很低,所以现在可以逐渐普及数字时钟显示的应用领域上。
当前的数字时钟显示系统的现状是刚刚起步,主要在于针对时间信息的采集方面,但随着对这种市场需求和认识的加深,市场一定会进入到一个发展的高速期。
以前大多时间显示都是电子表等等,不适用于大型建筑,显示的涉及空间范围的能见度也比较低,而且其使用成本,因而具有很大的局限性。所以更加智能的时间显示系统成为新的需要,其中就是以液晶数字时钟的显示技术为核心技术。
针对目前的现状,设计了一款基于单片机液晶显示的数字时钟显示系统,本设计是以STM32F103单片机为核心,并与时间信息采集技术为核心进行控制液晶显示模块进行大型的时间显示,显示时间准确,可见范围广。
1.2本课程设计目的
1)掌握STM32F103单片机GPIO的使用方法和扩展;掌握定时器和外中断的使用方法;掌握LCD显示屏的驱动原理;
2)掌握STM32F103单片机GPIO的使用方法和扩展;掌握LCD显示屏驱动原理。
3)掌握MDK5编程环境与STM32F103库函数开发技能,并能灵活运用于解决实际问题。
4)针对设计任务的要求,学会查阅手册和文献资料,培养学生独立分析和解决实际问题的能力。
1.3本课程设计的功能要求
1)采用STM32F103单片机和2.8寸液晶屏显示;
2)能显示当前的年、月、日、时、分、秒,24小时制;
3)月、日、时、分、秒均可以单独设置,设置时有提示;
4)外接3个按键,一个用于选择需要设置的项目,一个增加、一个减少;
2、本课题研究的主要内容
本系统设计包括:信息采集部分和信息处理部分以及信息显示部分。信息采集部分包含:单片机最小系统,时间信息采集电路;信息处理部分包含:单片机最小系统,信息采集部分包括:LED液晶显示电路。
为了实现达到人们所需要的要求,进行软件编程实现功能和硬件搭建LED液晶数字时钟系统。此液晶主子时钟系统主要有以下几个功能:信息采集部分进行采集时间信息;信息处理部分进行时间信息的获取,实现时间信息的运算和规划;信息显示部分进行将信息处理部分的时间信息进行通过控制驱动器进行LED液晶显示出来。
3、本课程设计的方案分析和选择
通过对本设计系统的功能需要确定实现方案,下面分别叙述各模块方案的选择原因和选择方案备选的优缺点。
3.1单片机芯片选择方案
在核心主控方案的选择方面,正确合理的选取适合本设计系统的方案是本设计的重中之重,它涉及到成本和研究所需的时间精力。正确的主控芯片在把成本上花费更少,在需要花费的时间精力上更是优化到最佳。下面就是针对本设计系统的需求和一些其他因素相比较进行方案的选择。
方案一:采用以嵌入式为核心的ARM系列的单片机,比较经典的一款芯片STM32单片机,该款单片机是ST公司生产制作的,在现实生活中有着广泛的应用。对于这款单片机,由于其功能强大,程序运行速度也是很快的,一般能达到人们正常所需,再加上相对应的嵌入式系统就可以实现人机交互方便,处理速度快速便捷的系统;另一方面在芯片价格上来讲,符合人们的需要,与平时多用的51单片机价格相差不大,只是价格高了那么一两成;本芯片虽然功能齐全,运转速度较快,但是在编程方面对比51单片机而言,就麻烦一些,对于引脚的设定和要求更高一些。对于本设计系统对于处理速度的要求和成本的考察,本方案也是在考虑范围之内的。
方案2 :采用STC89C52这款单片机作为本设计的主要控制系统。本款单片机是属于前面所述的51单片机的一类,其功能相对而言稍显不足,内部存储不高;但是在功耗和成本方面比较低,开发相比较嵌入式芯片的ARM系列单片机更加的简单方便。对于本设计系统,在要求程序运行速度方面是完全可以胜任的,再斟酌开放的难易程度方面,考虑研发周期和开发精力问题,该款单片机比较符合本设计系统的。
两种方案从单片机芯片主要性能角度出发,通过比较相关的便捷性、兼容性,误差性,还有节约成本等方面,本数字电子钟单片机芯片选择设计采用方案一,选择开发更简单,成本更低廉的51系列单片机STC89C52单片机芯片。
3.2定时器方案选择
方案1 RTC时钟:STM32自带RTC模块 ,实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。在RTC预分频器余数寄存器(RTC_DIVH / RTC_DIVL)赋值可改变时间。
方案2 定时器:定时器计时其实是通过计数来实现的。定时器内部有一个计数器,这个计数器根据一个时钟(这个时钟源来自于ARM的APB总线,然后经过时钟模块内部的分频器来分频得到)来工作。每隔一个时钟周期,计数器就计数一次,定时器的时间就是计数器计数值×时钟周期。定时器内部有1个寄存器TCNT,计时开始时我们会把一个总的计数值(譬如说300)放入TCNT寄存器中,然后每隔一个时钟周期(假设为1ms)TCNT中的值会自动减1(硬件自动完成,不需要CPU软件去干预),直到TCNT中减为0的时候,TCNT就会触发定时器中断。
定时器关注的是时间段,定时器计时从开启定时器的那一刻开始,到定的时间段结束为止产生中断; RTC中工作用的是时间点。RTC和定时器的区别,就相当于是钟表和闹钟的区别。综上所述,本数字电子钟数码管显示选择设计采用方案2定时器计时。
4、系统硬件及电路设计
整体框架已经建立,下面进行各个模块的说明及电路设计的介绍。下面介绍有:系统硬件、单片机最小系统电路。
4.1 系统硬件
4.1.1 液晶显示器STM32开发板的驱动原理
LCD液晶显示器是Liquid Crystal Display的简称,液晶是一种规则性排列的有机化合物,它是一种介于固体和液体之间的物质,这种材料可以在电信号的驱动下液晶分子进行旋转,旋转时会影响透光性,因此我们可以在整个液晶面板后面用白光照(称为背光),可以通过不同电信号让液晶分子进行选择性的透光,此时在液晶面板前面看到的就是各种各样不同的颜色,这就是LCD显示。
液晶的物理特性是:当通电时导通,排列变的有秩序,使光线容易通过;不通电时排列混乱,阻止光线通过。让液晶如闸门般地阻隔或让光线穿透。从技术上简单地说,液晶面板包含了两片相当精致的无钠玻璃素材,称为Substrates,中间夹著一层液晶。当光束通过这层液晶时,液晶本身会排排站立或扭转呈不规则状,因而阻隔或使光束顺利通过。
LCD技术是把液晶灌入两个列有细槽的平面之间。这两个平面上的槽互相垂直(相交成90度)。也就是说,若一个平面上的分子南北向排列,则另一平面上的分子东西向排列,而位于两个平面之间的分子被强迫进入一种90度扭转的状态。由于光线顺着分子的排列方向传播,所以光线经过液晶时也被扭转90度。但当液晶上加一个电压时,分子便会重新垂直排列,使光线能直射出去,而不发生任何扭转。
LCD是依赖极化滤光器(片)和光线本身。自然光线是朝四面八方随机发散的。极化滤光器实际是一系列越来越细的平行线。这些线形成一张网,阻断不与这些线平行的所有光线。极化滤光器的线正好与第一个垂直,所以能完全阻断那些已经极化的光线。只有两个滤光器的线完全平行,或者光线本身已扭转到与第二个极化滤光器相匹配,光线才得以穿透。
液晶模块显示原理图如图1所示:

图1 液晶模块显示原理图
4.1.2 MCU
本课程设计选择的是STM32F 103RET6作为MCU,STM32F系列属于中低端的32位ARM微控制器,该系列芯片是意法半导体(ST)公司出品,其内核是Cortex-M3。该系列芯片按片内Flash的大小可分为三大类:小容量(16K和32K)、中容量(64K和128K)、大容量(256K、384K和512K)。STM32F 103的型号众多,其价格低,作为一款低端开发板,选择STM32F 103RBT6是最佳的选择。MCU部分原理图如图2所示:

图2 MCU部分原理图
4.1.3 按键介绍
本数字电子时钟设计所需按键用于进行显示时间的调整与设置扩展的小键盘。单片机芯片4个I/O口可与按键直接相连,通过编程,单片机芯片即可控制按键接口电平的高低,即按键的开与关,以达到用按键进行显示时间的调整与设置扩展的小键盘的设计要求。KEY0和KEY1用作普通按键输入,分别连接在PC5和PA15上,KEY2是复位键,连接PA0;
按键原理图如图3所示:

图3 按键原理图
4.2 单片机最小系统电路
4.2.1时钟电路
我们知道,对于时序电路来说,除了电源外,还需要有稳定的时钟信号才能正常工作。作为数字系统,微控制器是一种复杂的时序逻辑电路,需要专门的时钟源为其提供脉冲信号。
STM32F103微控制器也不例外。对于STM32F103来说,尽管它内置了内部RC振荡器,可以为内部锁相环(Phase Locked Loop, PLL)提供时钟,这样STM32F103依靠内部振荡器就可以在72MHz的满速状态下运行。但是,内部RC振荡器相比外部晶振来说不够准确也不够稳定,因此在条件允许的情况下,尽量使用外部主时钟源。
外部主时钟源主要作为CortexM3内核和STM32外设的驱动时钟,一般称为高速外部时钟信号(HSE)。高速外部时钟信号可以由以下两种时钟源产生。
(1)外部晶体/陶瓷谐振器
在这种模式下,时钟电路由外部晶振和负载电容组成,如图7所示。负载电容的值需根据选定的晶振进行调节,而且位置要尽可能近地靠近晶振的引脚,以减小输出失真和启动稳定时间。外部晶振的频率可以是4~16MHz,例如,STM32F103通常选用8MHz的外部晶振。由于这种模式能产生非常精确而稳定的主时钟,因此它是STM32F103微控制器时钟电路的首选。其原理图如图4所示:

图4外部晶振和负载电容图
(2)用户提供的外部时钟信号
在这种模式下,必须提供一个外部时钟源。它的频率可高达25MHz。用户提供的外部时钟信号(可以是占空比为50%的方波、正弦波或三角波)必须连到STM32F103的OSC32_ IN引脚,同时保证STM32F103的OSC32_ OUT引脚悬空,其原理图如图5所示。

图5外部时钟信号图
4.2.2震荡电路
振荡电路也就是晶振电路是由固定频率的晶振电路的核心来源,相当于是一个机器的心脏跳动的,促进身体机理的运转。本电路就是这样的类似作用。一般这个频率是和单片机相匹配的,该频率本设计系统采用的是12MHZ。该晶振的引脚两端分别接两个固定容量的充电电容,一般是瓷片电容,用于振荡电路的晶振的充放电和滤波;瓷片电容的另一端相连接地,也就是与单片机的20号引脚相连。
4.2.3复位电路

复位电路是通过硬件电路进行控制单片机的RESET引脚的高低电平来控制单片机内部运行程序的。复位的方式有上电复位和手动复位两种。复位电路由一个10uF的电解电容的负极端与单片机的RESET引脚相连,正极端接电源的正极,也相当于和单片机的40号引脚相连,在其两端有一个自弹的独立按键;当需要复位的时候,按下复位按键,电解电容短路,单片机的复位引脚接到了高电平,单片机被复位。
5、系统软件设计
5.1系统主程序

在本设计系统中,从系统主程序开始,进入各模块的系统初始化,该初始化包括:串口初始化设置串口频率和波特率、定时器的初值设置、中断函数的进入条件、和端口的初始化点评状态;初始化完成后进入主函数的循环部分,读取时钟芯片的时间信息,并处理;将处理好的时间信息传递给显示模块进行显示;最后进行循环扫描显示时间信息。
系统程序结构属中断方式,在进行时间信息的读取中,就是采用的终端服务函数子函数的程序中完成,将读取的信息存到一个全局变量的数组中,当显示服务子函数需要时,进行直接调用显示,显示函数为定时器服务子函数的程序中完成。经过多次调试,模块化编程。一步步的实现本设计系统的功能,在克服了定时器中断和外部中断的冲突后,本设计系统趋于完成。
5.2按键控制
本次课程设计要求外接3个按键,按键2WK_UP是选择需要设置的项目,按键KEY1增加、按键KEY0减少,用按键来控制日期的增减。按键控制使用外部中断来实现,将PA0、PA15和PC5设置为三个按键的输入通道,响应优先级:KEY0>KEY1>WK_UP。按键初始化函数中定义WK_UP为下拉输入即按键按下为高电平,KEY1和KEY0为上拉输入即按键按下为低电平。
外部中断处理函数中设置变量flage来判断按键2WK_UP选择的是日期结构体中的成员变量,if判断语句实现对年、月、日、时、分、秒的单独设置。其流程图如图6所示:

图6按键执行流程图

5.3定时器中断控制计时
该实验用到了TIM3定时器,通过设置时钟预分频值psc,自动重装载值arr,arr设值为9999,psc设值为7199,时钟频率为72M。
初始化定时器,初始化时基单元(TIM_TimeBaseStructure)和定时器嵌套向量中断控制器(NVIC_InitStructure)。每溢出一次代表1s发生一次定时器中断,再在中断函数 TIM3_IRQHandler()中编写实现的中断。
在.h文件里预定义一个日期结构体来存放一些关于时间的成员变量结构体
void TIM3_Int_Init(u16 arr,u16 psc);
typedef struct
{
vu16 year;
vu8 month;
vu8 day;
vu8 hour;
vu8 minute;
vu8 second;
}_calendar_obj;
extern _calendar_obj calendar;
在该结构体定义了六个成员变量,分别为year、month、day、hour、minute、second
在定时器中断处理函数中判断定时器中断触发请求发生与否。发生请求,秒数增加,当秒钟计时到60,分钟+1,秒钟返回0。分钟同上,以此类推,时针计时到24,日期+1。
当显示的年份为闰年时:若当前月份为二月,日期计时到29,月份+1,日期更新为1。
当前年份为非闰年时:若当前月份为二月,日期计时到30,月份+1,而日期更新为1。4、6、9、11月这四个月的日期增加到31时月份+1,日期更新为1。其它月份日期增加到32时+1,日期变为1。当月份计时到13,年份+1。相关流程图如图所示:

NYN
YN
YNYNY   N
N
Y   Y
N   NY  Y   N

N

YY

N

5.4 LCD相关函数分析
5.4.1 画圆函数
使用用画圆函数LCD_Draw_Circle()画表盘,在圆上画刻度,将3、6、9、12对应的刻度加粗。画圆函数有三个参数,分别为(u16 x0,u16 y0,u8 r),对应圆心坐标(x0,y0)和半径r。Bresenham算法通过圆心判断下一个点位置坐标,定义画点颜色为灰棕蓝色,调用LCD_DrawPoint(uRow,uCol)画点函数找到八个点位置,从3、6、9、12的位置开始循环画点,每一次循环计算都会在原来八个点附近再出现八个点,所有点连接起来画出圆。
Bresenham算法的主要思想是:以坐标原点(0,0)为圆心的圆可以通过0度到45°的弧计算得到,即x从0增加到半径,然后利用对称性计算余下的七段圆弧。当x从0增加到时,y从R递减。
主函数中显示圆圈的代码:
LCD_Draw_Circle(120,120,98); //显示表盘最大的圈
LCD_Draw_Circle(120,120,95); //显示表盘第二大的圈
LCD_Draw_Circle(120,120,2) ; //显示表盘中间外面的圈
LCD_Draw_Circle(120,120,4); //显示表盘中间里面的圈

画圆部分相关的代码为:

void LCD_Draw_Circle(u16 x0,u16 y0,u8 r)
{
int a,b;
int di;
a=0;b=r;
di=3-(r<<1); //判断下个点的位置
while(a<=b)
{
POINT_COLOR=LBBLUE;//表盘圆颜色为灰棕蓝色
LCD_DrawPoint(x0-b,y0-a); //从刻度为9的位置开始
LCD_DrawPoint(x0+b,y0-a); //从刻度为3的位置开始
LCD_DrawPoint(x0-a,y0+b);//从刻度为12的位置开始

 LCD_DrawPoint(x0-a,y0-b);//从刻度为6的位置开始LCD_DrawPoint(x0+b,y0+a);  //3LCD_DrawPoint(x0+a,y0-b);  //6LCD_DrawPoint(x0+a,y0+b);  //12 LCD_DrawPoint(x0-b,y0+a);  //9a++;// 利用Bresenham算法来画圆if(di<0)di +=4*a+6;   else{  di+=10+4*(a-b);   b--;} }

}

5.4.2 画线函数
调用void DrawLine()画线函数来画时、分、秒指针,此函数有五个参数,分别是(u16 x1, u16 y1, u16 x2, u16 y2,u16 COLOR),对应的是起始坐标(x1,y1),终止坐标(x2,y2)。首先计算坐标增量并且设置单步方向,通过设定起点坐标(x1,y1)和终点坐标(x2,y2)计算斜率,根据斜率在该条线上的每个点坐标,使用LCD_Fast_DrawPoint()函数把每个点都画出来,从而画出指针。
画指针部分的相关代码为:

void DrawLine(u16 x1, u16 y1, u16 x2, u16 y2,u16 COLOR)
{
u16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if(delta_x0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if(delta_y0)incy=0;//水平线
else{incy=-1;delta_y=-delta_y;}
if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0;t<=distance+1;t++ )//画线输出
{
LCD_Fast_DrawPoint(uRow,uCol,COLOR);
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
通过调用画线函数在圆上画刻度,将3、6、9、12对应的刻度加粗,函数中有POINT_COLOR,在主函数中定义为灰蓝色。
画刻度部分的相关代码为

LCD_DrawLine(120,25,120,35);
LCD_DrawLine(119,25,119,35);
LCD_DrawLine(121,25,121,35);//12

LCD_DrawLine(25,120,35,120);
LCD_DrawLine(25,119,35,119);
LCD_DrawLine(25,121,35,121);//9LCD_DrawLine(120,205,120,215);
LCD_DrawLine(119,205,119,215);
LCD_DrawLine(121,205,121,215);//6LCD_DrawLine(205,120,215,120);
LCD_DrawLine(205,119,215,119);
LCD_DrawLine(205,121,215,121);//3LCD_DrawLine(168,38,164,45);//1
LCD_DrawLine(202,72,195,76);//2
LCD_DrawLine(202,168,195,164);//4
LCD_DrawLine(168,202,164,195);//5
LCD_DrawLine(72,202,76,195);//7
LCD_DrawLine(38,168,45,164);//8
LCD_DrawLine(38,72,45,76);//10
LCD_DrawLine(72,38,76,45);//

5.4.3 显示数字函数
此函数有五个参数,分别是(u16 x,u16 y,u32 num,u8 len,u8 size),x、y对应的是起始坐标,显示数值num,数字的位数len,字体大小size,颜色可通过POINT_COLOR设定。
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size)
{
u8 t,temp;
for(t=0;t<len;t++)
{
POINT_COLOR=GRAYBLUE;
temp=(num/LCD_Pow(10,len-t-1))%10; //求余来提取每一位数字
LCD_ShowChar(x+(size/2)*t,y,temp+‘0’,size,0); //显示这一位的数字
}
}
5.4.4 显示字符函数
在 LCD_ShowChar 函数里面采用快速画点函数 LCD_Fast_DrawPoint 来画点显示字符,该函数同 LCD_DrawPoint 一样,只是带了颜色参数,且减少了函数调用的时间。x、y是起始坐标,width、 height是字符显示区域的宽和高,*str是指向字符串的指针,size是字体大小, mode是模式(0为非叠加方式,1为叠加方式)。显示数字和字符,判断位置和是否换行(实现自动换行)。

void Show_Str(u16 x,u16 y,u16 width,u16 height,u8*str,u8 size,u8 mode)
{
u16 x0=x;
u16 y0=y;
u8 bHz=0; // 字符或中文
5.4.5 画点函数
//x,y:坐标
void LCD_DrawPoint(u16 x,u16 y)
{
LCD_SetCursor(x,y); //设置光标位置
LCD_WriteRAM_Prepare(); //开始写入GRAM
LCD_WR_DATA(POINT_COLOR); //POINT_COLOR:此点的颜色
}
此函数实现比较简单,先设置坐标,再往坐标写颜色。其中 POINT_COLOR 定义的一个全局变量,存放画笔颜色,BACK_COLOR代表 LCD 的背景色。
6、软件调试与测试结果
6.1编程工具

本设计的软件都是在Keil μVision 4上进行编写、编译、调试以及运行操作并生成Hex文件。
软件编程步骤:
(1)打开Keil μ Vision 4软件;
(2)新建工程,并把工程存放在自己方便的地方;
(3)新建C文件,并C 文件添加到新建的工程中;
(4)设置选项,勾选生成HEX文件选项;
(5)编写代码,实现功能,语法正确,并编译通过,生成HEX文件。
6.2 PROTEUS的应用
Proteus软件的使用步骤如下:
(1)打开Proteus软件。
(2)选择菜单栏的元器件选项,点击P标识,找到所需的元器件,根据自己的需要,选择菜单栏下方的旋转按钮,将元器件旋转到方便的角度,然乎在一定区域内部上对其进行放置,放置完成后,进行设置元器件的值和标号。全部器件摆放设置完成后,鼠标移动到元器件的引脚端,进行点击开始引线到需要连接的地方,然后双击确定,以此类推,将全部器件之间进行连接,并保存。
保存后点击PLAY 按钮,是左下方的三角符号,确认无误后解除仿真,即左下角的正方块图形。
(4)下载hex文件到单片机中:双击51单片机,在对话框中找到保存好的程序生成文件hex文件打开,再单击确定,再次保存。
(5)点击左下角的三角符合按钮,进行电路运行仿真与调试,观察仿真结果进行相应的改动,直到出现正确的结果。
6.3 软件调试过程
经过系统的了解程序编写软件和实物仿真软件这两部分,两者相互结合进行本课题系统功能的仿真与调试,确定其方案的是否可行?了解系统方案的工程量和主要原理应用。
在通过编写程序,解决语法错误之后,生成一个hex文件,放在仿真文件夹中,以方便仿真文件的调用;进行软件系统搭建硬件平台,采用PROTUSE软件把,系统方案中选择的各部分,找到对应的元器件,进行硬件电路的模拟搭建,检查电气连接的准确性,确保完成后,双击进入主控芯片的配置中,找到软件程序生成的HEX文件,选择,进行硬件型软件模拟调试,出现的问题,及时找到问题根源,并改正后再次进行软件仿真,直到正确的结果和功能的完全实现。
6.4测试结果
初始默认时间是2019年6月13日21时07分10秒
显示结果:表盘显示清晰,指针位置与时间相吻合,能观察到秒针在转动。

按键后的显示结果:选择按键按下,相应被选择项会闪烁,按下加减键能正确改变时间,表盘随时间的设置正确变化。

7、结论
此次课程设计完成了STM32F103单片机和2.8寸液晶屏显示,显示当前的年、月、日、时、分、秒。其中遇到了许多知识面的困难,如画圆函数、画线函数,将数学知识转化为计算机语言,最后得以解决。这次综合设计使我受益匪浅,所涉及的东西都是我们专业相关的。起初学习单片机和嵌入式全部是理论知识,没有过多的实践。导致在课程设计开始的时候无从下手。为了完成设计,我们小组成员去图书馆、网上查找相关资料,学习了嵌入式和C语言,虽然以前学过嵌入式,但是感觉自己都不太懂这方面的知识,纯粹是为了应付考试,所以导致这回编写代码实现过程比较懵懂,需要花费的时间也比较长,但最后也完成了课程设计。

致 谢
通过此次课程设计我们成功的完成了STM32F103单片机和2.8寸液晶屏显示,显示当前的年、月、日、时、分、秒。其中遇到了许多困难,但是通过团队的不懈努力,通过网络查询资料最终我们克服了困难解决了问题。
  另外,感谢有课程设计这样一个机会,我能够了解一个项目的开发过程,并在这个过程当中,学到了许多,使我们在这学期快要结束的时候,能够将学到的知识应用到实践中,增强了我们实践操作和动手应用能力,提高了独立思考的能力。同时,我也深深意识到自身专业知识的匮乏以及动手实践能力的低下。在老师和同学的帮助下,自己受益匪浅。今后我会不忘初心,更加努力汲取专业知识。
感谢张老师和小组同学在这次课设给自己的指导和帮助,正是由于他们,我才能在各方面取得显着的进步,在此向他们表示我由衷的谢意。
在这次课程设计的撰写中,我得到了许多人的帮助。首先我要感谢我的老师在课程设计上给予我的指导、提供给我的支持和帮助,这是我能顺利完成这次设计的主要原因,更重要的是老师帮我解决了许多技术上的难题,让我能把系统做得更加完善。在此期间,我不仅学到了许多新的知识,而且也开阔了视野,提高了自己的设计能力。其次,我要感谢帮助过我的同学,他们也为我解决了不少我不太明白的设计上的难题。
最后再一次感谢所有在设计中帮助过我的良师益友和同学。

参考文献
[1] 康华光.电子技术基础模拟部分第四版[M].北京:高等教育出版社,1999.6.
[2] 阎石.数字电子技术基础第四版[M].北京:高等教育出版社,1999.6.
[3]王福瑞等.单片微机测控系统设计大全[M].北京航空航天大学出版社,1998(331-337).
[4] 韩敬海,王蕊.Cortext-M3开发技术与实现[M].西安:西安电子科技大学出版社,2013.
[5] 李华 .MCS-51单片机实用接口技术[M].北京航空航天出版社,1997
[6] 徐仁贵.微型计算机接口技术及应用[M].机械工业出版社,1998
[7] 诸昌钤.LED显示屏系统原理及工程技术[M].电子科技大学出版社,2000
[8] 梅开乡 .数字逻辑电路(第2版)[M].电子工业出版社,2005

代码附件

main函数
#include “led.h”
#include “delay.h”
#include “sys.h”
#include “usart.h”
#include “lcd.h”
#include “math.h”
#include “exti.h”
#include “key.h”
#include “timer.h”
#include “usmart.h”
#include “malloc.h”
#include “exfuns.h”
#include “MMC_SD.h”
#include “text.h”
#include “fontupd.h”
#include “ff.h”
#include “text.h”
#include “malloc.h”
#define PI 3.1415926

int main(void){
static u8 key_up=1;//按键按松开标志
u8 sxi=120;
u8 syi=120;
u8 hxi,hyi,mxi,myi;
u8 t;
u8 key;
//u8 n;

delay_init();//延时函数初始化
uart_init(9600);//串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init();

EXTIX_Init();//外部中断初始化
TIM3_Int_Init(9999,7199);//10Khz的计数频率,计数到10000为1s

 usmart_dev.init(72);    //usmart初始化 mem_init();             //初始化内存池

exfuns_init(); //为fatfs相关变量申请内存
f_mount(fs[0],“0:”,1); //挂载SD卡
f_mount(fs[1],“1:”,1); //挂载FLASH.

 while(font_init())  //检查字库
{  LCD_Clear(WHITE);//清屏POINT_COLOR=GRAYBLUE;          //设置字体为灰蓝色            LCD_ShowString(60,50,200,16,16,"mini STM32");while(SD_Initialize())//检测SD卡{LCD_ShowString(60,70,200,16,16,"SD Card Failed!");delay_ms(200);LCD_Fill(60,70,200+60,70+16,WHITE);delay_ms(200);            }                                   LCD_ShowString(60,70,200,16,16,"SD Card OK");
LCD_ShowString(60,90,200,16,16,"Font Updating...");
key=update_font(20,110,16);//更新字库while(key)//更新失败      {                     LCD_ShowString(60,110,200,16,16,"Font Update Failed!");delay_ms(200);       LCD_Fill(20,110,200+20,110+16,WHITE);delay_ms(200);              }          LCD_ShowString(60,110,200,16,16,"Font Update Success!");delay_ms(1500); LCD_Clear(WHITE);//清屏
}  LCD_Draw_Circle(120,120,98);//显示表盘最大的圈LCD_Draw_Circle(120,120,95);//显示表盘第二个圈LCD_Draw_Circle(120,120,2);//显示表盘中间的里面的圈LCD_Draw_Circle(120,120,4);//显示表盘中间外面的圈/*显示数字*/
LCD_ShowNum(113,35,12,2,16);//显示表盘上数字12的位置
LCD_ShowNum(195,112,3,1,16);//显示表盘上数字3的位置
LCD_ShowNum(116,190,6,1,16);//显示表盘上数字6的位置
LCD_ShowNum(37,112,9,1,16);//显示表盘上数字9的位置
LCD_ShowNum(158,45,1,1,16);
LCD_ShowNum(184,70,2,1,16);
LCD_ShowNum(186,152,4,1,16);
LCD_ShowNum(158,180,5,1,16);
LCD_ShowNum(78,184,7,1,16);
LCD_ShowNum(46,155,8,1,16);
LCD_ShowNum(45,70,10,2,16);
LCD_ShowNum(78,45,11,2,16);//画指针 3,6,9,12 加粗LCD_DrawLine(120,25,120,35);LCD_DrawLine(119,25,119,35);LCD_DrawLine(121,25,121,35);//12LCD_DrawLine(25,120,35,120);LCD_DrawLine(25,119,35,119);LCD_DrawLine(25,121,35,121);//9LCD_DrawLine(120,205,120,215);LCD_DrawLine(119,205,119,215);
LCD_DrawLine(121,205,121,215);//6LCD_DrawLine(205,120,215,120);LCD_DrawLine(205,119,215,119);
LCD_DrawLine(205,121,215,121);//3LCD_DrawLine(168,38,164,45);//1LCD_DrawLine(202,72,195,76);//2
LCD_DrawLine(202,168,195,164);//4
LCD_DrawLine(168,202,164,195);//5LCD_DrawLine(72,202,76,195);//7LCD_DrawLine(38,168,45,164);//8LCD_DrawLine(38,72,45,76);//10LCD_DrawLine(72,38,76,45);//11     while(1)
{if(t!=calendar.second){//显示时间t=calendar.second;

LCD_ShowNum(40,240,calendar.year,4,24); Show_Str(90,245,16,16,“年”,16,0);
LCD_ShowNum(110,240,calendar.month,2,24);
Show_Str(135,245,16,16,“月”,16,0);
LCD_ShowNum(150,240,calendar.day,2,24);
Show_Str(175,245,16,16,“日”,16,0);
LCD_ShowNum(40,270,calendar.hour,2,24); Show_Str(65,275,16,16,“时”,16,0);
LCD_ShowNum(90,270,calendar.minute,2,24); Show_Str(115,275,16,16,“分”,16,0);
LCD_ShowNum(140,270,calendar.second,2,24);
Show_Str(165,275,16,16,“秒”,16,0);
LED0=!LED0;
}
delay_ms(100);
//读数据

 DrawLine(120,120,sxi,syi,0xFFFF);   //两个if语句添加按键控制
if(calendar.second==00||(key_up&&(KEY0==0||KEY1==0||WK_UP==1))){

DrawLine(120,120,hxi,hyi,0xFFFF); //清除
hxi=120+cos(PI0.5-PI(calendar.hour)/6-PI*(calendar.minute)/360)30; hyi=120-sin(PI0.5-PI*(calendar.hour)/6-PI*(calendar.minute)/360)30;
DrawLine(120,120,hxi,hyi,0x0000);//黑色 时针
}
else
{
hxi=120+cos(PI
0.5-PI*(calendar.hour)/6-PI*(calendar.minute)/360)30;
hyi=120-sin(PI
0.5-PI*(calendar.hour)/6-PI*(calendar.minute)/360)30;
DrawLine(120,120,hxi,hyi,0x0000);
}
if(calendar.second00||(key_up&&(KEY00||KEY10||WK_UP1)))
{
DrawLine(120,120,mxi,myi,0xFFFF mxi=120+cos(PI
0.5-PI*(calendar.minute)/30)50; myi=120-sin(PI0.5-PI*(calendar.minute)/30)50; DrawLine(120,120,mxi,myi,0XFC07);//棕色 分
}
else
{
mxi=120+cos(PI
0.5-PI*(calendar.minute)/30)50; myi=120-sin(PI0.5-PI*(calendar.minute)/30)50 DrawLine(120,120,mxi,myi,0XFC07);
}
sxi=120+cos(PI
0.5-PI*(calendar.second)/30)60;
syi=120-sin(PI
0.5-PI*(calendar.second)/30)*60;
DrawLine(120,120,sxi,syi,0x001F);//蓝色 秒
LCD_Draw_Circle(120,120,2);
LCD_Draw_Circle(120,120,4);
}
}

外部中断函数
#include “exti.h”
#include “led.h”
#include “key.h”
#include “delay.h”
#include “usart.h”
#include “stm32f10x_exti.h”
#include “timer.h”
#include “lcd.h”

int flage;
int t=0;
//外部中断初始化函数
void EXTIX_Init(void)
{

  EXTI_InitTypeDef EXTI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);KEY_Init();
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
EXTI_InitStructure.EXTI_Line=EXTI_Line5;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);

EXTI_InitStructure.EXTI_Line=EXTI_Line15;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void)
{
int i=0;
delay_ms(10); //消抖
if(WK_UP == 1)
{
//LED0=!LED0;
//LED1=!LED1;
t++;
if(t7)
t=0;
/* switch(t)
{
case 1:flage=1;break;//年
case 2:flage=2;break;//月
case 3:flage=3;break;//日
case 4:flage=4;break;//时
case 5:flage=5;break;//分
case 6:flage=6;break;//秒
}*/
if(t1)
{
flage=1;
for(i=0;i<10;i++)
LCD_ShowString(40,240,200,24,24," “);
}
if(t2)
{
flage=2;
for(i=0;i<10;i++)
LCD_ShowString(110,240,200,24,24," ");
}
if(t3)
{
flage=3;
for(i=0;i<10;i++)
LCD_ShowString(150,240,200,24,24,” “);
}
if(t4)
{
flage=4;
for(i=0;i<10;i++)
LCD_ShowString(40,270,200,24,24," ");
}
if(t5)
{
flage=5;
for(i=0;i<10;i++)
LCD_ShowString(90,270,200,24,24,” “);
}
if(t==6)
{
flage=6;
for(i=0;i<10;i++)
LCD_ShowString(140,270,200,24,24,” ");
}
}
EXTI_ClearITPendingBit(EXTI_Line0);
//清除EXTI0线路挂起位
}

void EXTI9_5_IRQHandler(void)
{ int n=0;
delay_ms(10);//消抖
if(KEY00)
{
if(flage1)//年
{
for(n=0;n<10;n++){LCD_ShowString(40,240,200,24,24," “);}
calendar.year=calendar.year-1;
}
if(flage2)//月
{
for(n=0;n<10;n++){LCD_ShowString(110,240,200,24,24," ");}
calendar.month=calendar.month-1;
if(calendar.month<1)
{
calendar.month=12;
}
}
if(flage3)//日
{
for(n=0;n<10;n++){LCD_ShowString(150,240,200,24,24,” ");}
calendar.day=calendar.day-1; if(calendar.month1|calendar.month3|calendar.month5|calendar.month7|calendar.month8|calendar.month10|calendar.month12)
{
if(calendar.day<1)
calendar.day=31;
} if(calendar.month4|calendar.month6|calendar.month9|calendar.month11)
{
if(calendar.day<1)
calendar.day=30;
}
if(calendar.month2)
{
if(calendar.year%40)
{
if(calendar.year%1000)//是闰年
{
if(calendar.day<1)
{calendar.day=29;}
}
}
else{
if(calendar.day<1)
{calendar.day=28;}
}
}
}
if(flage4)//时
{
for(n=0;n<10;n++){LCD_ShowString(40,270,200,24,24," ");}
calendar.hour=calendar.hour-1;
if(calendar.hour0)
{calendar.hour=23;}
}
if(flage5)//分
{
for(n=0;n<10;n++){LCD_ShowString(90,270,200,24,24," ");}
calendar.minute–;
if(calendar.minute0)
{calendar.minute=59;}
}
if(flage6)//秒
{
for(n=0;n<10;n++){LCD_ShowString(140,270,200,24,24," ");}
calendar.second–;
if(calendar.second0)
{calendar.second=59;}
}
}
EXTI_ClearITPendingBit(EXTI_Line5); //清除LINE5上的中断标志位
}

void EXTI15_10_IRQHandler(void)
{
int m=0;
delay_ms(10); //消抖
if(KEY10)
{
if(flage1)//年
{
for(m=0;m<10;m++){LCD_ShowString(40,240,200,24,24," “);}
calendar.year=calendar.year+1;
}
if(flage2)//月
{
for(m=0;m<10;m++){LCD_ShowString(110,240,200,24,24," ");}
calendar.month=calendar.month+1;
if(calendar.month>12)
{
calendar.month=1;
}
}
if(flage3)//日
{
for(m=0;m<10;m++){LCD_ShowString(150,240,200,24,24,” ");}
calendar.day=calendar.day+1;
if(calendar.month1|calendar.month3|calendar.month5|calendar.month7|calendar.month8|calendar.month10|calendar.month12)
{
if(calendar.day>31)
calendar.day=1;
}
if(calendar.month4|calendar.month6|calendar.month9|calendar.month11)
{
if(calendar.day>30)
calendar.day=1;
}
if(calendar.month2)
{
if(calendar.year%40)
{
if(calendar.year%1000)//是闰年
{
if(calendar.day>29)
{calendar.day=1;}
}
}
else{
if(calendar.day>28)
{calendar.day=1;}
}
}
}

    if(flage==4)//时{  for(m=0;m<10;m++){LCD_ShowString(40,270,200,24,24,"  ");} calendar.hour=calendar.hour+1;if(calendar.hour>24){calendar.hour=1;}}if(flage==5)//分{for(m=0;m<10;m++){LCD_ShowString(90,270,200,24,24,"  ");} calendar.minute++;if(calendar.minute>59){calendar.minute=0;}} if(flage==6)//秒{for(m=0;m<10;m++){LCD_ShowString(140,270,200,24,24,"  ");} calendar.second++;if(calendar.second>59){calendar.second=0;}} EXTI_ClearITPendingBit(EXTI_Line15);  //清除LINE15线路挂起位

}}

按键函数
#include “key.h”
#include “delay.h”
//按键初始化函数
//PA0.15和PC5 设置成输入
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟

GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭jtag,使能SWD,可以用SWD模式调试GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_15;//PA15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA15GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_5;//PC5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC5GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;//PA0
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPD; //PA0设置成输入,默认下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0

}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//返回值:
//0,没有任何按键按下
//KEY0_PRES,KEY0按下
//KEY1_PRES,KEY1按下
//WKUP_PRES,WK_UP按下
//注意此函数有响应优先级,KEY0>KEY1>WK_UP!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY00||KEY10||WK_UP1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY00)return KEY0_PRES;
else if(KEY10)return KEY1_PRES;
else if(WK_UP1)return WKUP_PRES;
}else if(KEY01&&KEY11&&WK_UP==0)key_up=1;
return 0;// 无按键按下
}

定义结构体变量
#include “timer.h”
#include “led.h”
#include “stm32f10x_tim.h”
//定义结构体变量, 并给结构体里面成员赋值
_calendar_obj calendar={2019,6,13,21,7,10};//时钟结构体,年,月,天,小时,分钟,秒,

//extern int second,minute,hour,day,month
//extern int minu ,hou;
//extern int mMark,hMark,mMark1;//分针标志 与时标志

//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

NVIC_InitTypeDef      NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

TIM_TimeBaseStructure.TIM_ClockDivision=0;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period=arr;
TIM_TimeBaseStructure.TIM_Prescaler=psc;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);

NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM3,ENABLE);

}

void TIM3_IRQHandler(){
if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET){
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
calendar.second++;
if (calendar.second60)
{
calendar.minute++;
calendar.second=0;
if(calendar.minute60)
{
calendar.hour++;
calendar.minute=0;
if(calendar.hour==24)
{
calendar.day++;
calendar.hour =0;
if(calendar.month == 2 || calendar.month == 4 ||calendar.month == 6||calendar.month == 9|| calendar.month == 11)
{
if(calendar.month == 2)
{
if(calendar.day == 30)
{
calendar.day = 1;
calendar.month++;
}
}
else
{
if(calendar.day == 31)
{
calendar.day = 1;
calendar.month++;
}
}
}
else
{
if(calendar.day == 32)
{
calendar.day = 1;
calendar.month++;
if(calendar.month == 13)
{calendar.month = 1;}
}
}
}
}
}
}
}

#include “lcd.h”
#include “stdlib.h”
#include “font.h”
#include “usart.h”
#include “delay.h”

//LCD的画笔颜色和背景色
u16 POINT_COLOR=0x0000; //画笔颜色
u16 BACK_COLOR=0XFFFF ; //背景色

//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;

//写寄存器函数
//data:寄存器值
void LCD_WR_REG(u16 data)
{
LCD_RS_CLR;//写地址
LCD_CS_CLR;
DATAOUT(data);
LCD_WR_CLR;
LCD_WR_SET;
LCD_CS_SET;
}
//写数据函数
//可以替代LCD_WR_DATAX宏,拿时间换空间.
//data:寄存器值
void LCD_WR_DATAX(u16 data)
{
LCD_RS_SET;
LCD_CS_CLR;
DATAOUT(data);
LCD_WR_CLR;
LCD_WR_SET;
LCD_CS_SET;
}
//读LCD数据
//返回值:读到的值
u16 LCD_RD_DATA(void)
{
u16 t;
GPIOB->CRL=0X88888888; //PB0-7 上拉输入
GPIOB->CRH=0X88888888; //PB8-15 上拉输入
GPIOB->ODR=0X0000; //全部输出0

LCD_RS_SET;
LCD_CS_CLR;
//读取数据(读寄存器时,并不需要读2次)
LCD_RD_CLR;
if(lcddev.id==0X8989)delay_us(2);//FOR 8989,延时2us
t=DATAIN;
LCD_RD_SET;
LCD_CS_SET; GPIOB->CRL=0X33333333; //PB0-7  上拉输出
GPIOB->CRH=0X33333333; //PB8-15 上拉输出
GPIOB->ODR=0XFFFF;    //全部输出高
return t;

}
//写寄存器
//LCD_Reg:寄存器编号
//LCD_RegValue:要写入的值
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue)
{
LCD_WR_REG(LCD_Reg);
LCD_WR_DATA(LCD_RegValue);
}
//读寄存器
//LCD_Reg:寄存器编号
//返回值:读到的值
u16 LCD_ReadReg(u16 LCD_Reg)
{
LCD_WR_REG(LCD_Reg); //写入要读的寄存器号
return LCD_RD_DATA();
}
//开始写GRAM
void LCD_WriteRAM_Prepare(void)
{
LCD_WR_REG(lcddev.wramcmd);
}
//LCD写GRAM
//RGB_Code:颜色值
void LCD_WriteRAM(u16 RGB_Code)
{
LCD_WR_DATA(RGB_Code);//写十六位GRAM
}
//从ILI93xx读出的数据为GBR格式,而我们写入的时候为RGB格式。
//通过该函数转换
//c:GBR格式的颜色值
//返回值:RGB格式的颜色值
u16 LCD_BGR2RGB(u16 c)
{
u16 r,g,b,rgb;
b=(c>>0)&0x1f;
g=(c>>5)&0x3f;
r=(c>>11)&0x1f;
rgb=(b<<11)+(g<<5)+(r<<0);
return(rgb);
}
//读取个某点的颜色值
//x,y:坐标
//返回值:此点的颜色
u16 LCD_ReadPoint(u16 x,u16 y)
{
u16 r,g,b;
if(x>=lcddev.width||y>=lcddev.height)return 0; //超过了范围,直接返回
LCD_SetCursor(x,y);
if(lcddev.id0X9341||lcddev.id0X6804||lcddev.id0X5310)LCD_WR_REG(0X2E); //9341/6804/5310发送读GRAM指令
else if(lcddev.id0X5510)LCD_WR_REG(0X2E00); //5510 发送读GRAM指令
else LCD_WR_REG(R34); //其他IC发送读GRAM指令
GPIOB->CRL=0X88888888; //PB0-7 上拉输入
GPIOB->CRH=0X88888888; //PB8-15 上拉输入
GPIOB->ODR=0XFFFF; //全部输出高

LCD_RS_SET;
LCD_CS_CLR;
//读取数据(读GRAM时,第一次为假读)
LCD_RD_CLR;
delay_us(1);//延时1us
LCD_RD_SET;
//dummy READ
LCD_RD_CLR;
delay_us(1);//延时1us
r=DATAIN;      //实际坐标颜色
LCD_RD_SET;
if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510)   //9341/NT35310/NT35510要分2次读出
{    LCD_RD_CLR;                       b=DATAIN;//读取蓝色值      LCD_RD_SET;g=r&0XFF;//对于9341,第一次读取的是RG的值,R在前,G在后,各占8位g<<=8;
}else if(lcddev.id==0X6804)
{LCD_RD_CLR;                       LCD_RD_SET;r=DATAIN;//6804第二次读取的才是真实值
}
LCD_CS_SET;
GPIOB->CRL=0X33333333;      //PB0-7  上拉输出
GPIOB->CRH=0X33333333;      //PB8-15 上拉输出
GPIOB->ODR=0XFFFF;          //全部输出高
if(lcddev.id==0X9325||lcddev.id==0X4535||lcddev.id==0X4531||lcddev.id==0X8989||lcddev.id==0XB505)return r;    //这几种IC直接返回颜色值
else if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510)return (((r>>11)<<11)|((g>>10)<<5)|(b>>11));//ILI9341/NT35310/NT35510需要公式转换一下
else return LCD_BGR2RGB(r); //其他IC

}
//LCD开启显示
void LCD_DisplayOn(void)
{
if(lcddev.id0X9341||lcddev.id0X6804||lcddev.id0X5310)LCD_WR_REG(0X29); //开启显示
else if(lcddev.id0X5510)LCD_WR_REG(0X2900); //开启显示
else LCD_WriteReg(R7,0x0173); //开启显示
}
//LCD关闭显示
void LCD_DisplayOff(void)
{
if(lcddev.id0X9341||lcddev.id0X6804||lcddev.id0X5310)LCD_WR_REG(0X28); //关闭显示
else if(lcddev.id0X5510)LCD_WR_REG(0X2800); //关闭显示
else LCD_WriteReg(R7, 0x0);//关闭显示
}
//画点
//x,y:坐标
//POINT_COLOR:此点的颜色
void LCD_DrawPoint(u16 x,u16 y)
{
LCD_SetCursor(x,y); //设置光标位置
LCD_WriteRAM_Prepare(); //开始写入GRAM
LCD_WR_DATA(POINT_COLOR);
}
//快速画点
//x,y:坐标
//color:颜色
void LCD_Fast_DrawPoint(u16 x,u16 y,u16 color)
{
if(lcddev.id0X9341||lcddev.id0X5310)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(x>>8);
LCD_WR_DATA(x&0XFF);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(y>>8);
LCD_WR_DATA(y&0XFF);
}else if(lcddev.id0X5510)
{
LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(x>>8);
LCD_WR_REG(lcddev.setxcmd+1);LCD_WR_DATA(x&0XFF);
LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(y>>8);
LCD_WR_REG(lcddev.setycmd+1);LCD_WR_DATA(y&0XFF);
}else if(lcddev.id0X6804)
{
if(lcddev.dir1)x=lcddev.width-1-x;//横屏时处理
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(x>>8);
LCD_WR_DATA(x&0XFF);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(y>>8);
LCD_WR_DATA(y&0XFF);
}else
{
if(lcddev.dir1)x=lcddev.width-1-x;//横屏其实就是调转x,y坐标
LCD_WriteReg(lcddev.setxcmd,x);
LCD_WriteReg(lcddev.setycmd,y);
}
LCD_RS_CLR;
LCD_CS_CLR;
DATAOUT(lcddev.wramcmd);//写指令
LCD_WR_CLR;
LCD_WR_SET;
LCD_CS_SET;
LCD_WR_DATA(color);//写数据
}
//设置LCD显示方向
//dir:0,竖屏;1,横屏
void LCD_Display_Dir(u8 dir)
{
if(dir0) //竖屏
{
lcddev.dir=0; //竖屏
lcddev.width=240;
lcddev.height=320;
if(lcddev.id0X9341||lcddev.id0X6804||lcddev.id0X5310)
{
lcddev.wramcmd=0X2C;
lcddev.setxcmd=0X2A;
lcddev.setycmd=0X2B;
if(lcddev.id0X6804||lcddev.id0X5310)
{
lcddev.width=320;
lcddev.height=480;
}
}else if(lcddev.id0X8989)
{
lcddev.wramcmd=R34;
lcddev.setxcmd=0X4E;
lcddev.setycmd=0X4F;
}else if(lcddev.id0x5510)
{
lcddev.wramcmd=0X2C00;
lcddev.setxcmd=0X2A00;
lcddev.setycmd=0X2B00;
lcddev.width=480;
lcddev.height=800;
}else
{
lcddev.wramcmd=R34;
lcddev.setxcmd=R32;
lcddev.setycmd=R33;
}
}else //横屏
{
lcddev.dir=1; //横屏
lcddev.width=320;
lcddev.height=240;
if(lcddev.id0X9341||lcddev.id0X5310)
{
lcddev.wramcmd=0X2C;
lcddev.setxcmd=0X2A;
lcddev.setycmd=0X2B;
}else if(lcddev.id0X6804)
{
lcddev.wramcmd=0X2C;
lcddev.setxcmd=0X2B;
lcddev.setycmd=0X2A;
}else if(lcddev.id0X8989)
{
lcddev.wramcmd=R34;
lcddev.setxcmd=0X4F;
lcddev.setycmd=0X4E;
}else if(lcddev.id0x5510)
{
lcddev.wramcmd=0X2C00;
lcddev.setxcmd=0X2A00;
lcddev.setycmd=0X2B00;
lcddev.width=800;
lcddev.height=480;
}else
{
lcddev.wramcmd=R34;
lcddev.setxcmd=R33;
lcddev.setycmd=R32;
}
if(lcddev.id0X6804||lcddev.id==0X5310)
{
lcddev.width=480;
lcddev.height=320;
}
}
LCD_Scan_Dir(DFT_SCAN_DIR); //默认扫描方向
}

//清屏函数
//color:要清屏的填充色
void LCD_Clear(u16 color)
{
u32 index=0;
u32 totalpoint=lcddev.width;
totalpoint*=lcddev.height; //得到总点数
if((lcddev.id0X6804)&&(lcddev.dir1))//6804横屏的时候特殊处理
{
lcddev.dir=0;
lcddev.setxcmd=0X2A;
lcddev.setycmd=0X2B;
LCD_SetCursor(0x00,0x0000); //设置光标位置
lcddev.dir=1;
lcddev.setxcmd=0X2B;
lcddev.setycmd=0X2A;
}else LCD_SetCursor(0x00,0x0000); //设置光标位置
LCD_WriteRAM_Prepare(); //开始写入GRAM
for(index=0;index<totalpoint;index++)LCD_WR_DATA(color);
}
//在指定区域内填充指定颜色
//区域大小:(xend-xsta+1)(yend-ysta+1)
//xsta
//color:要填充的颜色
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
{
u16 i,j;
u16 xlen=0;
u16 temp;
if((lcddev.id0X6804)&&(lcddev.dir1)) //6804横屏的时候特殊处理
{
temp=sx;
sx=sy;
sy=lcddev.width-ex-1;
ex=ey;
ey=lcddev.width-temp-1;
lcddev.dir=0;
lcddev.setxcmd=0X2A;
lcddev.setycmd=0X2B;
LCD_Fill(sx,sy,ex,ey,color);
lcddev.dir=1;
lcddev.setxcmd=0X2B;
lcddev.setycmd=0X2A;
}else
{
xlen=ex-sx+1;
for(i=sy;i<=ey;i++)
{
LCD_SetCursor(sx,i); //设置光标位置
LCD_WriteRAM_Prepare(); //开始写入GRAM
for(j=0;j<xlen;j++)LCD_WR_DATA(color); //设置光标位置
}
}
}
//在指定区域内填充指定颜色块
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)
(ey-sy+1)
//color:要填充的颜色
void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
{
u16 height,width;
u16 i,j;
width=ex-sx+1; //得到填充的宽度
height=ey-sy+1; //高度
for(i=0;i<height;i++)
{
LCD_SetCursor(sx,sy+i);
//设置光标位置
LCD_WriteRAM_Prepare();
//开始写入GRAM
for(j=0;j<width;j++)LCD_WR_DATA(color[i
width+j]);//写入数据
}
}
//画线
//x1,y1:起点坐标
//x2,y2:终点坐标
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2)
{
u16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1;
delta_y=y2-y1;
uRow=x1;
uCol=y1;

if(delta_x>0)incx=1;
else if(delta_x==0)incx=0;
else {incx=-1;delta_x=-delta_x;}if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;
else{incy=-1;delta_y=-delta_y;} if( delta_x>delta_y)distance=delta_x;
else distance=delta_y; for(t=0;t<=distance+1;t++ )
{  LCD_DrawPoint(uRow,uCol);xerr+=delta_x ; yerr+=delta_y ; if(xerr>distance) { xerr-=distance; uRow+=incx; } if(yerr>distance) { yerr-=distance; uCol+=incy; }
}

}
//画矩形
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)
{
LCD_DrawLine(x1,y1,x2,y1);
LCD_DrawLine(x1,y1,x1,y2);
LCD_DrawLine(x1,y2,x2,y2);
LCD_DrawLine(x2,y1,x2,y2);
}
//在指定位置画一个指定大小的圆
//(x,y):中心点
//r :半径
void LCD_Draw_Circle(u16 x0,u16 y0,u8 r)
{
int a,b;
int di;
a=0;b=r;
di=3-(r<<1); //判断下个点位置的标志
while(a<=b)
{
POINT_COLOR=LBBLUE;//表盘的颜色灰棕蓝
LCD_DrawPoint(x0-b,y0-a); //3
LCD_DrawPoint(x0+b,y0-a); //0
LCD_DrawPoint(x0-a,y0+b); //1
LCD_DrawPoint(x0-b,y0-a); //7
LCD_DrawPoint(x0-a,y0-b); //2
LCD_DrawPoint(x0+b,y0+a); //4
LCD_DrawPoint(x0+a,y0-b); //5
LCD_DrawPoint(x0+a,y0+b); //6
LCD_DrawPoint(x0-b,y0+a);
a++;
//使用Bresenham算法画圆
if(di<0)di +=4a+6;
else
{
di+=10+4
(a-b);
b–;
}
// LCD_DrawPoint(x0+a,y0+b);
}
}
//在指定位置显示一个字符
//x,y:起始坐标
//num:要显示的字符:" “—>”~"
//size:字体大小 12/16/24
//mode:叠加方式(1)还是非叠加方式(0)
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode)
{
u8 temp,t1,t;
u16 y0=y;
u8 csize=(size/8+((size%8)?1:0))(size/2); num=num-’ ';
for(t=0;t<csize;t++)
{
if(size12)temp=asc2_1206[num][t];
else if(size16)temp=asc2_1608[num][t];
else if(size24)temp=asc2_2412[num][t];
else return;
for(t1=0;t1<8;t1++)
{
if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);
else if(mode0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);
temp<<=1;
y++;
if(x>=lcddev.width)return;
if((y-y0)==size)
{
y=y0;
x++;
if(x>=lcddev.width)return;
break;
}
}
}
}
//m^n函数
//返回值:m^n次方.
u32 LCD_Pow(u8 m,u8 n)
{
u32 result=1;
while(n–)result
=m;
return result;
}
//显示数字,高位为0,则不显示
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//color:颜色
//num:数值(0~4294967295);
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size)
{
u8 t,temp;
//u8 enshow=0;
for(t=0;t<len;t++)
{
POINT_COLOR=GRAYBLUE;//数字为灰蓝色
temp=(num/LCD_Pow(10,len-t-1))%10;
LCD_ShowChar(x+(size/2)*t,y,temp+‘0’,size,0);
}
}

//显示字符串
//x,y:起点坐标
//width,height:区域大小
//size:字体大小
//*p:字符串起始地址
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)
{
u8 x0=x;
width+=x;
height+=y;
while((*p<=’~’)&&(*p>=’ '))//判断是不是非法字符!
{
if(x>=width){x=x0;y+=size;}
if(y>=height)break;//退出
LCD_ShowChar(x,y,*p,size,0);
x+=size/2;
p++;
}
}

void DrawLine(u16 x1, u16 y1, u16 x2, u16 y2,u16 COLOR)
{
u16 t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;

delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if(delta_x==0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;//水平线
else{incy=-1;delta_y=-delta_y;}
if( delta_x>delta_y)distance=delta_x;

//选取基本增量坐标轴
else distance=delta_y;
for(t=0;t<=distance+1;t++ )//画线输出
{
LCD_Fast_DrawPoint(uRow,uCol,COLOR);
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}

基于STM32F103的液晶显示电子钟相关推荐

  1. 基于STM32f103的电子秤系统设计

    许久前整理的一项课程设计,具备以下资源: 1.论文:25页,近9000字 2.程序:Keil5源程序 3.实物:实测可用 软件程序上传在百度网盘,请自行下载: 链接:https://pan.baidu ...

  2. 基于STM32单片机的电子钟(Proteus仿真+程序)

    编号:12 基于STM32单片机的电子钟 功能描述: 本设计由STM32单片机+液晶1602+按键+RTC时钟组成. 1.采用STM32F103最小系统板. 2.利用STM32内部自带RTC时钟提供时 ...

  3. 《STM32库开发实战指南:基于STM32F103(第2版)》——2.1节仿真器简介

    本节书摘来自华章社区<STM32库开发实战指南:基于STM32F103(第2版)>一书中的第2章,第2.1节仿真器简介,作者刘火良 杨森,更多章节内容可以访问云栖社区"华章社区& ...

  4. 机智云代码移植_IoT开发者 | 基于STM32F103的机智云宠物屋外加4路继电器开源教程...

    [ 写在前面 ] 自智云社区开辟IoT开源项目专区以来,一直有IoT开发者在贡献案例.玛莉甄选了一些具有代表性的案例分享给IoT爱好者们,本文亦如此. 若你有好的案例,想和IoT爱好者们分享,欢迎投稿 ...

  5. 简易电子钟c语言程序,(最新整理)基于51单片机的电子钟C语言程序

    <(最新整理)基于51单片机的电子钟C语言程序>由会员分享,可在线阅读,更多相关<(最新整理)基于51单片机的电子钟C语言程序(9页珍藏版)>请在人人文库网上搜索. 1.完整) ...

  6. 基于STM32F103的步进电机S型曲线加减速算法与实现

    步进电机是将电脉冲信号转变为角位移或线位移的开环控制电机,是现代数字程序控制系统中的主要执行元件,应用极为广泛.在非超载的情况下,电机的转速.停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的 ...

  7. 基于Stm32f103硬件iic驱动LM75A温度传感器

    这是LM75A温度传感器的概述,本文主要介绍基于Stm32f103的硬件iic驱动LM75A温度传感器. 这是我所使用的硬件电路,很简单. 对于该传感器的使用,主要是读取温度值,查看数据手册我们知道需 ...

  8. 基于STM32F103芯片实现LED灯闪烁

    基于STM32F103芯片实现LED灯闪烁 前言 一,寄存器配置 1,时钟控制 2,GPIO端口设置: 二.实际操作 1.具体代码 2.keil5项目运行 3.硬件的连接 4.链接到 mcuisp 串 ...

  9. 基于STM32F103平台的ADS79xx系列ADC(TI公司)应用方案

    目录 第一章 ADS79xx系列芯片特性简介 第二章 芯片内部结构简介 第三章 封装介绍 第四章 硬件布线设计 第五章 基于STM32F103平台的Keil编程 参考文献 第一章 ADS79xx系列芯 ...

最新文章

  1. Linux 下实现虚拟光驱功能,查看iso文件内容
  2. maven配置默认jdk版本
  3. CentOS7中多台服务器配置时钟同步
  4. C语言中用函数实现录入信息模块,C语言程序设计用函数实现模块化程序设计(73页)-原创力文档...
  5. 如何使用卡巴斯基急救盘清理感染的PC
  6. sizeof不是java关键字是_下列哪项不是Java语言的关键字。
  7. 阿里巴巴Android面试都问些什么?系列篇
  8. leetcode 338. 比特位计数
  9. 1.3、解析并创建ApplicationListener(ok)
  10. codable swift_使用Codable进行Swift JSON解析
  11. 如何在Linux中安装/挂载CD-ROM
  12. 基于ConfuserEx的混淆加壳
  13. 5号,7号电池充电时间计算方法
  14. 梦幻西游手游经验任务链计算机,梦幻西游手游经验和道具任务链攻略
  15. hpux oracle19c dbca DBT-05509 Failed To Connect To The Specified Database
  16. java设置post超时时间_HttpClient 如何设置超时时间
  17. 解决显卡驱动错误43
  18. 【基于Simulink+UG NX MCD 一级倒立摆控制系统仿真】建模和分析(一)
  19. AI文件资源导出方法
  20. C语言第6次上课随堂讲义

热门文章

  1. VB.net开发微信、支付宝扫码支付源码
  2. 爬虫-漫画喵的100行逆袭
  3. c++ vector 一部分_要去|原创TheShy要去SKT、小C会给阿水打辅助!各大战队面临重新洗牌...
  4. c++语言程序中,main()函数必须放在程序开始的部分,C++多选题(附答案)
  5. 阿里云盘 Mac客户端(附mac/win版)v2.1.6中文内测版
  6. java一个整数加100是完全平方_Java计算一个数加上100是完全平方数,加上168还是完全平方数...
  7. QQ查询信息php,查询QQ信息
  8. VDI桌面虚拟化和IDV桌面虚拟化你更倾向谁
  9. 【Java开发 | 工具使用】JAVA使用exe4j打包项目为exe文件
  10. 华为linux连蓝牙鼠标,huawei蓝牙鼠标怎么样? 华为蓝牙鼠标连接使用教程