共用体除非必要,否则我们不推荐使用,枚举的用法比较简单,在本书 19 章的项目实践中有很好的示例,这节课我们先来练习一下结构体的使用。下边这个程序的功能是一个带日期的电子钟,相当于一个简易万年历了,并且加入了按键调时功能。学有余力的同学看到这里,不妨先不看我们提供的代码,自己写写试试。如果能够独立写一个按键可调的万年历程序,单片机可以说基本入门了。如果自己还不能够独立完成这个程序,那么还是老规矩,先抄并且理解,而后自己独立默写出来,并且要边默写边理解。

本例直接忽略了星期这项内容,通过上、下、左、右、回车、ESC 这 6 个按键可以调整时间。这也是一个具有综合练习性质的实例,虽然在功能实现上没有多少难度,但要进行的操作却比较多而且烦琐,同学们可以从中体会到把繁杂的功能实现分解为一步步函数操作的必要性以及方便灵活性。简单说一下这个程序的几个要点,方便大家阅读理解程序。

把 DS1302 的底层操作封装为一个 DS1302.c 文件,对上层应用提供基本的实时时间的操作接口,这个文件也是我们的又一个功能模块了,我们的积累也越来越多了。

定义一个结构体类型 sTime 用来封装日期时间的各个元素,又用该结构体定义了一个时间缓冲区变量 bufTime 来暂存从 DS1302 读出的时间和设置时间时的设定值。需要注意的是在其它文件中要使用这个结构体变量时,必须首先再声明一次 sTime 类型;

定义一个变量 setIndex 来控制当前是否处于设置时间的状态,以及设置时间的哪一位,该值为 0 就表示正常运行,1~12 分别代表可以修改日期时间的 12 个位;

由于这节课的程序功能要进行时间调整,用到了 1602 液晶的光标功能,添加了设置光标的函数,我们要改变哪一位的数字,就在 1602 对应位置上进行光标闪烁,所以 Lcd1602.c在之前文件的基础上添加了两个控制光标的函数;

时间的显示、增减、设置移位等上层功能函数都放在 main.c 中来实现,当按键需要这些函数时则在按键文件中做外部声明,这样做是为了避免一组功能函数分散在不同的文件内而使程序显得凌乱。

/***************************DS1302.c 文件程序源代码*****************************/

#include

sbit DS1302_CE = P1^7;

sbit DS1302_CK = P3^5;

sbit DS1302_IO = P3^4;

struct sTime { //日期时间结构体定义

unsigned int year; //年

unsigned char mon; //月

unsigned char day; //日

unsigned char hour; //时

unsigned char min; //分

unsigned char sec; //秒

unsigned char week; //星期

};

/* 发送一个字节到 DS1302 通信总线上 */

void DS1302ByteWrite(unsigned char dat){

unsigned char mask;

for (mask=0x01; mask!=0; mask<<=1){ //低位在前,逐位移出

if ((mask&dat) != 0){ //首先输出该位数据

DS1302_IO = 1;

}else{

DS1302_IO = 0;

}

DS1302_CK = 1; //然后拉高时钟

DS1302_CK = 0; //再拉低时钟,完成一个位的操作

}

DS1302_IO = 1; //最后确保释放 IO 引脚

}

/* 由 DS1302 通信总线上读取一个字节 */

unsigned char DS1302ByteRead(){

unsigned char mask;

unsigned char dat = 0;

for (mask=0x01; mask!=0; mask<<=1){ //低位在前,逐位读取

if (DS1302_IO != 0){ //首先读取此时的 IO 引脚,并设置 dat 中的对应位

dat |= mask;

}

DS1302_CK = 1; //然后拉高时钟

DS1302_CK = 0; //再拉低时钟,完成一个位的操作

}

return dat; //最后返回读到的字节数据

}

/* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */

void DS1302SingleWrite(unsigned char reg, unsigned char dat){

DS1302_CE = 1; //使能片选信号

DS1302ByteWrite((reg<<1)|0x80); //发送写寄存器指令

DS1302ByteWrite(dat); //写入字节数据

DS1302_CE = 0; //除能片选信号

}

/* 用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节 */

unsigned char DS1302SingleRead(unsigned char reg){

unsigned char dat;

DS1302_CE = 1; //使能片选信号

DS1302ByteWrite((reg<<1)|0x81); //发送读寄存器指令

dat = DS1302ByteRead(); //读取字节数据

DS1302_CE = 0; //除能片选信号

return dat;

}

/* 用突发模式连续写入 8 个寄存器数据,dat-待写入数据指针 */

void DS1302BurstWrite(unsigned char *dat){

unsigned char i;

DS1302_CE = 1;

DS1302ByteWrite(0xBE); //发送突发写寄存器指令

for (i=0; i<8; i++){ //连续写入 8 字节数据

DS1302ByteWrite(dat[i]);

}

DS1302_CE = 0;

}

/* 用突发模式连续读取 8 个寄存器的数据,dat-读取数据的接收指针 */

void DS1302BurstRead(unsigned char *dat){

unsigned char i;

DS1302_CE = 1;

DS1302ByteWrite(0xBF); //发送突发读寄存器指令

for (i=0; i<8; i++){ //连续读取 8 个字节

dat[i] = DS1302ByteRead();

}

DS1302_CE = 0;

}

/* 获取实时时间,即读取 DS1302 当前时间并转换为时间结构体格式 */

void GetRealTime(struct sTime *time){

unsigned char buf[8];

DS1302BurstRead(buf);

time->year = buf[6] + 0x2000;

time->mon = buf[4];

time->day = buf[3];

time->hour = buf[2];

time->min = buf[1];

time->sec = buf[0];

time->week = buf[5];

}

/* 设定实时时间,时间结构体格式的设定时间转换为数组并写入 DS1302 */

void SetRealTime(struct sTime *time){

unsigned char buf[8];

buf[7] = 0;

buf[6] = time->year;

buf[5] = time->week;

buf[4] = time->mon;

buf[3] = time->day;

buf[2] = time->hour;

buf[1] = time->min;

buf[0] = time->sec;

DS1302BurstWrite(buf);

}

/* DS1302 初始化,如发生掉电则重新设置初始时间 */

void InitDS1302(){

unsigned char dat;

struct sTime code InitTime[] = { //2013 年 10 月 8 日 12:30:00 星期二

0x2013,0x10,0x08, 0x12,0x30,0x00, 0x02

};

DS1302_CE = 0; //初始化 DS1302 通信引脚

DS1302_CK = 0;

dat = DS1302SingleRead(0); //读取秒寄存器

if ((dat & 0x80) != 0){ //由秒寄存器最高位 CH 的值判断 DS1302 是否已停止

DS1302SingleWrite(7, 0x00); //撤销写保护以允许写入数据

SetRealTime(&InitTime); //设置 DS1302 为默认的初始时间

}

}

DS1302.c 最终向外提供出与具体时钟芯片寄存器位置无关的、由时间结构类型 sTime 作为接口的实时时间的读取和设置函数,如此处理体现了我们前面提到过的层次化编程的思想。应用层可以不关心底层实现细节,底层实现的改变也不会对应用层造成影响,比如说日后你可能需要换一款时钟芯片,而它与 DS1302 的操作和时间寄存器顺序是不同的,那么你需要做的也仅是针对这款新的时钟芯片设计出底层操作函数,最终提供出同样的以 sTime 为接口的操作函数即可,应用层无需做任何的改动。

/***************************Lcd1602.c 文件程序源代码*****************************/

#include

#define LCD1602_DB P0

sbit LCD1602_RS = P1^0;

sbit LCD1602_RW = P1^1;

sbit LCD1602_E = P1^5;

/* 等待液晶准备好 */

void LcdWaitReady(){

unsigned char sta;

LCD1602_DB = 0xFF;

LCD1602_RS = 0;

LCD1602_RW = 1;

do {

LCD1602_E = 1;

sta = LCD1602_DB; //读取状态字

LCD1602_E = 0;

} while (sta & 0x80); //bit7 等于 1 表示液晶正忙,重复检测直到其等于 0 为止

}

/* 向 LCD1602 液晶写入一字节命令,cmd-待写入命令值 */

void LcdWriteCmd(unsigned char cmd){

LcdWaitReady();

LCD1602_RS = 0;

LCD1602_RW = 0;

LCD1602_DB = cmd;

LCD1602_E = 1;

LCD1602_E = 0;

}

/* 向 LCD1602 液晶写入一字节数据,dat-待写入数据值 */

void LcdWriteDat(unsigned char dat){

LcdWaitReady();

LCD1602_RS = 1;

LCD1602_RW = 0;

LCD1602_DB = dat;

LCD1602_E = 1;

LCD1602_E = 0;

}

/* 设置显示 RAM 起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */

void LcdSetCursor(unsigned char x, unsigned char y){

unsigned char addr;

if (y == 0){ //由输入的屏幕坐标计算显示 RAM 的地址

addr = 0x00 + x; //第一行字符地址从 0x00 起始

}else{

addr = 0x40 + x; //第二行字符地址从 0x40 起始

}

LcdWriteCmd(addr | 0x80); //设置 RAM 地址

}

/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */

void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str){

LcdSetCursor(x, y); //设置起始地址

while (*str != '\0'){ //连续写入字符串数据,直到检测到结束符

LcdWriteDat(*str++);

}

}

/* 打开光标的闪烁效果 */

void LcdOpenCursor(){

LcdWriteCmd(0x0F);

}

/* 关闭光标显示 */

void LcdCloseCursor(){

LcdWriteCmd(0x0C);

}

/* 初始化 1602 液晶 */

void InitLcd1602(){

LcdWriteCmd(0x38); //16*2 显示,5*7 点阵,8 位数据接口

LcdWriteCmd(0x0C); //显示器开,光标关闭

LcdWriteCmd(0x06); //文字不动,地址自动+1

LcdWriteCmd(0x01); //清屏

}

为了本例的具体需求,在之前文件的基础上添加两个控制光标效果打开和关闭的函数,虽然函数都很简单,但为了保持程序整体上良好的模块化和层次化,还是应该在液晶驱动文件内以函数的形式提供,而不是由应用层代码直接来调用具体的液晶写命令操作。

/***************************keyboard.c 文件程序源代码****************************/

(此处省略,可参考之前章节的代码)

/*****************************main.c 文件程序源代码******************************/

#include

struct sTime { //日期时间结构体定义

unsigned int year;

unsigned char mon;

unsigned char day;

unsigned char hour;

unsigned char min;

unsigned char sec;

unsigned char week;

};

bit flag200ms = 1; //200ms 定时标志

struct sTime bufTime; //日期时间缓冲区

unsigned char setIndex = 0; //时间设置索引

unsigned char T0RH = 0; //T0 重载值的高字节

unsigned char T0RL = 0; //T0 重载值的低字节

void ConfigTimer0(unsigned int ms);

void RefreshTimeShow();

extern void InitDS1302();

extern void GetRealTime(struct sTime *time);

extern void SetRealTime(struct sTime *time);

extern void KeyScan();

extern void KeyDriver();

extern void InitLcd1602();

extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);

extern void LcdSetCursor(unsigned char x, unsigned char y);

extern void LcdOpenCursor();

extern void LcdCloseCursor();

void main(){

unsigned char psec=0xAA; //秒备份,初值 AA 确保首次读取时间后会刷新显示

EA = 1; //开总中断

ConfigTimer0(1); //T0 定时 1ms

InitDS1302(); //初始化实时时钟

InitLcd1602(); //初始化液晶

//初始化屏幕上固定不变的内容

LcdShowStr(3, 0, "20 - - ");

LcdShowStr(4, 1, " : : ");

while (1){

KeyDriver(); //调用按键驱动

if (flag200ms && (setIndex == 0)){ //每隔 200ms 且未处于设置状态时,

flag200ms = 0;

GetRealTime(&bufTime); //获取当前时间

if (psec != bufTime.sec){ //检测到时间有变化时刷新显示

RefreshTimeShow();

psec = bufTime.sec; //用当前值更新上次秒数

}

}

}

}

/* 将一个 BCD 码字节显示到屏幕上,(x,y)-屏幕起始坐标,bcd-待显示 BCD 码 */

void ShowBcdByte(unsigned char x, unsigned char y, unsigned char bcd){

unsigned char str[4];

str[0] = (bcd >> 4) + '0';

str[1] = (bcd&0x0F) + '0';

str[2] = '\0';

LcdShowStr(x, y, str);

}

/* 刷新日期时间的显示 */

void RefreshTimeShow(){

ShowBcdByte(5, 0, bufTime.year);

ShowBcdByte(8, 0, bufTime.mon);

ShowBcdByte(11, 0, bufTime.day);

ShowBcdByte(4, 1, bufTime.hour);

ShowBcdByte(7, 1, bufTime.min);

ShowBcdByte(10, 1, bufTime.sec);

}

/* 刷新当前设置位的光标指示 */

void RefreshSetShow(){

switch (setIndex){

case 1: LcdSetCursor(5, 0); break;

case 2: LcdSetCursor(6, 0); break;

case 3: LcdSetCursor(8, 0); break;

case 4: LcdSetCursor(9, 0); break;

case 5: LcdSetCursor(11, 0); break;

case 6: LcdSetCursor(12, 0); break;

case 7: LcdSetCursor(4, 1); break;

case 8: LcdSetCursor(5, 1); break;

case 9: LcdSetCursor(7, 1); break;

case 10: LcdSetCursor(8, 1); break;

case 11: LcdSetCursor(10, 1); break;

case 12: LcdSetCursor(11, 1); break;

default: break;

}

}

/* 递增一个 BCD 码的高位 */

unsigned char IncBcdHigh(unsigned char bcd){

if ((bcd&0xF0) < 0x90){

bcd += 0x10;

}else{

bcd &= 0x0F;

}

return bcd;

}

/* 递增一个 BCD 码的低位 */

unsigned char IncBcdLow(unsigned char bcd){

if ((bcd&0x0F) < 0x09){

bcd += 0x01;

}else{

bcd &= 0xF0;

}

return bcd;

}

/* 递减一个 BCD 码的高位 */

unsigned char DecBcdHigh(unsigned char bcd){

if ((bcd&0xF0) > 0x00){

bcd -= 0x10;

}else{

bcd |= 0x90;

}

return bcd;

}

/* 递减一个 BCD 码的低位 */

unsigned char DecBcdLow(unsigned char bcd){

if ((bcd&0x0F) > 0x00){

bcd -= 0x01;

}else{

bcd |= 0x09;

}

return bcd;

}

/* 递增时间当前设置位的值 */

void IncSetTime(){

switch (setIndex){

case 1: bufTime.year = IncBcdHigh(bufTime.year); break;

case 2: bufTime.year = IncBcdLow(bufTime.year); break;

case 3: bufTime.mon = IncBcdHigh(bufTime.mon); break;

case 4: bufTime.mon = IncBcdLow(bufTime.mon); break;

case 5: bufTime.day = IncBcdHigh(bufTime.day); break;

case 6: bufTime.day = IncBcdLow(bufTime.day); break;

case 7: bufTime.hour = IncBcdHigh(bufTime.hour); break;

case 8: bufTime.hour = IncBcdLow(bufTime.hour); break;

case 9: bufTime.min = IncBcdHigh(bufTime.min); break;

case 10: bufTime.min = IncBcdLow(bufTime.min); break;

case 11: bufTime.sec = IncBcdHigh(bufTime.sec); break;

case 12: bufTime.sec = IncBcdLow(bufTime.sec); break;

default: break;

}

RefreshTimeShow();

RefreshSetShow();

}

/* 递减时间当前设置位的值 */

void DecSetTime(){

switch (setIndex){

case 1: bufTime.year = DecBcdHigh(bufTime.year); break;

case 2: bufTime.year = DecBcdLow(bufTime.year); break;

case 3: bufTime.mon = DecBcdHigh(bufTime.mon); break;

case 4: bufTime.mon = DecBcdLow(bufTime.mon); break;

case 5: bufTime.day = DecBcdHigh(bufTime.day); break;

case 6: bufTime.day = DecBcdLow(bufTime.day); break;

case 7: bufTime.hour = DecBcdHigh(bufTime.hour); break;

case 8: bufTime.hour = DecBcdLow(bufTime.hour); break;

case 9: bufTime.min = DecBcdHigh(bufTime.min); break;

case 10: bufTime.min = DecBcdLow(bufTime.min); break;

case 11: bufTime.sec = DecBcdHigh(bufTime.sec); break;

case 12: bufTime.sec = DecBcdLow(bufTime.sec);

default: break;

}

RefreshTimeShow();

RefreshSetShow();

}

/* 右移时间设置位 */

void RightShiftTimeSet(){

if (setIndex != 0){

if (setIndex < 12){

setIndex++;

}else{

setIndex = 1;

}

RefreshSetShow();

}

}

/* 左移时间设置位 */

void LeftShiftTimeSet(){

if (setIndex != 0){

if (setIndex > 1){

setIndex--;

}else{

setIndex = 12;

}

RefreshSetShow();

}

}

/* 进入时间设置状态 */

void EnterTimeSet(){

setIndex = 2; //把设置索引设置为 2,即可进入设置状态

LeftShiftTimeSet(); //再利用现成的左移操作移到位置 1 并完成显示刷新

LcdOpenCursor(); //打开光标闪烁效果

}

/* 退出时间设置状态,save-是否保存当前设置的时间值 */

void ExitTimeSet(bit save){

setIndex = 0; //把设置索引设置为 0,即可退出设置状态

if (save){ //需保存时即把当前设置时间写入 DS1302

SetRealTime(&bufTime);

}

LcdCloseCursor(); //关闭光标显示

}

/* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */

void KeyAction(unsigned char keycode){

if ((keycode>='0') && (keycode<='9')){ //本例中不响应字符键

}else if (keycode == 0x26){ //向上键,递增当前设置位的值

IncSetTime();

}else if (keycode == 0x28){ //向下键,递减当前设置位的值

DecSetTime();

}else if (keycode == 0x25){ //向左键,向左切换设置位

LeftShiftTimeSet();

}else if (keycode == 0x27){ //向右键,向右切换设置位

RightShiftTimeSet();

}else if (keycode == 0x0D){ //回车键,进入设置模式/启用当前设置值

if (setIndex == 0){ //不处于设置状态时,进入设置状态

EnterTimeSet();

}else{ //已处于设置状态时,保存时间并退出设置状态

ExitTimeSet(1);

}

}else if (keycode == 0x1B){ //Esc 键,取消当前设置

ExitTimeSet(0);

}

}

/* 配置并启动 T0,ms-T0 定时时间 */

void ConfigTimer0(unsigned int ms){

unsigned long tmp; //临时变量

tmp = 11059200 / 12; //定时器计数频率

tmp = (tmp * ms) / 1000; //计算所需的计数值

tmp = 65536 - tmp; //计算定时器重载值

tmp = tmp + 28; //补偿中断响应延时造成的误差

T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节

T0RL = (unsigned char)tmp;

TMOD &= 0xF0; //清零 T0 的控制位

TMOD |= 0x01; //配置 T0 为模式 1

TH0 = T0RH; //加载 T0 重载值

TL0 = T0RL;

ET0 = 1; //使能 T0 中断

TR0 = 1; //启动 T0

}

/* T0 中断服务函数,执行按键扫描和 200ms 定时 */

void InterruptTimer0() interrupt 1{

static unsigned char tmr200ms = 0;

TH0 = T0RH; //重新加载重载值

TL0 = T0RL;

KeyScan(); //按键扫描

tmr200ms++;

if (tmr200ms >= 200){ //定时 200ms

tmr200ms = 0;

flag200ms = 1;

}

}

main.c 主文件,负责所有应用层的功能实现,文件比较长,还是那句话“不难但比较烦琐”,希望对具体问题分析细化能力还不太强的同学们把这个文件多练习几遍,学习一下其中把具体问题逐步细化并一步步实现出来的编程思想,多进行此类练习,锻炼程序思维能力,将来遇到具体项目设计需求的时候,你很快就可以找到方法并实现它们了。

单片机设计时钟程序c语言,单片机电子时钟程序设计相关推荐

  1. 单片机旋转led程序c语言,[ 单片机 ] 旋转LED制做过程

    8.我采用的是USB烧写器,烧写的程序 9.我用的电源是一个LM317可调稳压电源 10.通电试验 需要注意的是要安排好电机轴两边的电路重量尽量相近. 2.程序 程序很简单,我这里给出一个C51 的示 ...

  2. 单片机按键检测程序c语言,单片机检测按键短击,连击c程序

    /****************************************************************************/ //文 件 名:key.c //功 能:短 ...

  3. c语言单片机定时器的应用程序,51单片机定时器中断程序(C语言)

    51c程序 51单片机定时器中断程序(C语言) 用C语言实现的,先要定义好定时器的初值 不管你使用多大的晶振,使用51单片机,一般都是12分频出来,也就可以得出一个机器周期 机器周期=12/n(n指晶 ...

  4. 数字时钟程序c语言,C语言实现电子时钟程序

    本文实例为大家分享了C语言实现电子时钟程序的具体代码,供大家参考,具体内容如下 Qt 里面运行 #include #include #include #include #include typedef ...

  5. 单片机设计时钟程序c语言,基于.C51单片机的数字时钟课程设计(C语言,带闹钟).pdf...

    - - .. -- 单片机技术课程设计 数字电子钟 学院 : word 可编辑 . - - .. -- 班级 : 姓名 : 学号 : 教师 : 摘 要 电子钟在生活中应用非常广泛 ,而一种简单方便的数 ...

  6. 单片机课设:基于MSP430F249的电子时钟设计

    基于MSP430F249的电子时钟(完整代码+protues仿真图+系统流程图) 设计要求 整体功能实现方案 按键功能分析 系统流程 主程序(main())流程图 TIMER_A中断流程图 外部中断程 ...

  7. 单片机实现水位控制C语言,单片机课程设计 水位自动控制.doc

    单片机课程设计 水位自动控制 河南机电高等专科学校电气工程系 电子课程设计报告 设计题目:水位自动控制 专业: 电机与电器 班级: 101 班 学号: 姓名: 指导教师: 设计时间: 2012-6-2 ...

  8. 【单片机毕业设计】【mcuclub-306】万年历电子时钟 | 数字时钟 | 多功能时钟 | 智能闹钟

    设计简介: 项目名:基于单片机的万年历电子时钟的设计.基于单片机的多功能时钟的设计.基于单片机的数字时钟的设计 单片机:STC89C52 功能简介: 1.通过DS1302实时获取时间,并掉电保存时间 ...

  9. C语言制作电子时钟程序(内附代码)

    今天来用C语言制作一款电子时钟程序,相信你们一定能用的上. 效果展示 1.窗口创建   2.基本绘图 3.时间获取 4.代码展示 绘制表盘部分代码: #include <stdio.h> ...

最新文章

  1. HarmonyOS 实现跑马灯效果
  2. 1137 - Sin your life sin公式 + 枚举
  3. WebForm中关于DataGrid的20篇经典文章
  4. Java 怎么 get char_Java KeyCharacterMap.getDeadChar方法代码示例
  5. 如何启用ruler?
  6. julia(6)-循环与猜数字
  7. 深度学习模型保存_Web服务部署深度学习模型
  8. C# Winform 窗体美化(四、镂空窗体)
  9. 变频器说明书_图解变频器接线方法
  10. 手机MODEM开发(27)---VOLTE SIP 信令流程图
  11. 网络 一篇博文搞懂五种常见的IO模型
  12. Mathematica颜色系,你喜欢哪一个?
  13. Filter -- 过滤敏感词汇
  14. python基础课件
  15. 图片放大后变模糊了怎么办?
  16. 【08月21日】指数估值排名
  17. android 计步器acc,利用腾讯云云函数执行部署修改小米运动步数代码_每天自动修改步数...
  18. 如何在支付宝开直播,这篇入驻教程不要错过,看完你就会了
  19. 降本增效,合作伙伴营销助力业绩增长
  20. 采集HTTP与HTTPS

热门文章

  1. 列表流和feed流_通过流而不是列表
  2. jolokia_Hawtio和Jolokia的Hibernate统计
  3. lambda java_Java Lambdas简介
  4. Spring Data JPA教程
  5. 无需再忙了:Lambda-S3缩略图,由SLAppForge Sigma钉牢!
  6. Java Bean验证基础
  7. jvm类加载机制和类加载器_在JVM之下–类加载器
  8. adf时间作用域_ADF:在任务流终结器中支持bean作用域
  9. Tomcat的默认连接器
  10. JDK 8 SummaryStatistics类