本章导读

电子系统中常用的显示设备有数码管、LCD液晶以及VGA显示器等。其中数码管又可分为段式显示(7段、米字型等)以及点阵显示(8*8、16*16等),LCD液晶的应用可以分为字符式液晶(1602、12864等)以及真彩液晶屏,VGA显示器一般是现在的电脑显示器。芯航线开发板对以上三种设备均提供了硬件接口。

本章将实现FPGA驱动数码管动态显示并提取出实现的电路结构,从电路结构入手编写代码,仿真对设计进行验证。最终板级调试时使用In system sources and probes editor(ISSP,系统的源和探测器工具),输入需要显示的数据,数码管则显示对应数值。本节课核心不再是代码,而是电路结构,电路结构确定后编写代码只是照图施工的过程。这也是越来越接近FPGA设计本质的硬件思维。

数码管驱动原理

其中8段数码管的结构图如图15.1所示,

图15.1 8段数码管结构图

由上图可以看出数码管有两种结构:共阴极与共阳极。这两者的区别在于,公共端是连接到地还是高电平,对于共阴数码管需要给对应段以高电平才会使其点亮,而对于共阳极数码管则需要给低电平才会点亮。AC620上板载的是共阳数码管。同时为了显示数字或字符,必须对数字或字符进行编码译码。这里先不考虑小数点也就是简化为7段数码管,其编码译码格式如表15.1所示:

表15.1 数码管编码译码表

段式数码管工作方式有两种:静态显示方式和动态显示方式。静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法由于每一个数码管均需要独立的数据线因此硬件电路比较复杂,成本较高,很少使用。

为了节约IO以及成本一般采用如图15.2所示的电路结构,这样3个数码管接在一起就比静态的少了7*2个I/O。

图15.2三位数码管等效电路图

这样就实现了另一种显示模式,动态显示。动态显示的特点是将所有位数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。

现在举例假设将扫描时间定为1S,这三个数码管分成3s,第1秒时sel数据线上为`b100,这时数码管0被选中,这时a=0,数码管0的LED0就可以点亮;第2秒时sel数据线上为`b010,这时数码管1被选中,这时b=0,数码管1的LED1就可以点亮;第3秒时sel数据线上为`b001,这时数码管2被选中,这时c=0,数码管2的LED2就可以点亮。这时的效果就会是数码管0的LED0亮一秒后数码管1的LED1亮一秒最后是数码管2的LED2亮一秒,这样再次循环。

这样如果使用1ms刷新时间的话由于数码管的余辉效应以及人的视觉暂留这样就会出现数码管0的LED0、数码管1的LED1以及数码管2的LED2 "同时"亮,并不会有闪烁感。

图15.3 7段8位的数码管原理图

三线制数码管电路设计

AC620开发板上配备的是8段8位的数码管,如果按照图15.3电路进行设计,可以看出仍需要16个IO进行驱动。下面提出另外一种三线制数码电路设计方法,其电路图如图15.4。这样的电路设计仍旧属于动态显示,但是这里通过外接了由两片8位74HC595移位寄存器级联后构成16位移位寄存器并将级联后的输出连接到位选及段选口,可以直接通过三个IO即可控制8位8段数码管。

图15.4 三线制数码管电路图

74HC595是8位串行移位寄存器,带有存储寄存器和三态寄存器,其中移位寄存器和存储寄存器分别采用不同的时钟。其内部结构图如图15.5所示,其可以把串行的信号转为并行的信号,因此常用做各种数码管以及点阵屏的驱动芯片。芯片的IO功能描述如表15.2所示。

图15.5 74HC595内部结构图

表15.2 74HC595 IO功能描述

数码管动态扫描驱动设计

模块接口设计及内部功能划分

由上面的分析可以得出图15.4的框图,其接口列表如表15.2所示:

图15.4 数码管模块框图

表15.2 模块接口列表

根据以上的分析可知,首先要有一个周期为1ms的驱动时钟,因此需要一个分频电路;在进行数码管的位选时,需要一个循环移位;在选择位后,需要选择器来选通数据输入位;要实现表15.1的功能需要一个由译码器。

数码管驱动模块逻辑电路图可以简化成如图15.5所示的,其中每一部分的作用如表15.3所示。

图15.5 数码管驱动模块逻辑电路图

表15.3 子功能块功能描述

名称

功能描述

divder

分频产生1KHz的扫描时钟

Shift8

8位循环移位寄存器

MUX8

数据输入选择

MUX2

使能选择

LUT

数据译码器

扫描时钟模块设计

从系统时钟50M分频得到1KHz的扫描时钟,计数器值即为25000d,这样计数器的位宽定义为15位即可。

     reg [:]divider_cnt;
reg clk_1K; always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
divider_cnt <= 'd0;
else if(!En)
divider_cnt <= 'd0;
else if(divider_cnt == 'd24999)
divider_cnt <= 'd0;
else
divider_cnt <= divider_cnt + 'b1; always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
clk_1K <= 'b0;
else if(divider_cnt == 'd24999)
clk_1K <= ~clk_1K;
else
clk_1K <= clk_1K;

数码管位选模块设计

接下来编写8位循环移位寄存器,这里利用循环移位寄存器实现0000_0001b→1000_0000b的变化,进而实现数码管的位选,即实现每个扫描时钟周期选择一个数码管。移位寄存器输出值与数码管选通的对应关系如表15.4所示,其中sel7为高位。

表15.4 移位寄存器与数码管对应关系

sel0

sel1

sel2

sel3

sel4

sel5

sel6

sel7

被选通数码管

1

0

0

0

0

0

0

0

数码管0

0

1

0

0

0

0

0

0

数码管1

0

0

1

0

0

0

0

0

数码管2

……

               

0

0

0

0

0

0

0

1

数码管7

     reg [:]sel_r;    

     always@(posedge clk_1K or negedge Rst_n)
if(!Rst_n)
sel_r <= 'b0000_0001;
else if(sel_r == 'b1000_0000)
sel_r <= 'b0000_0001;
else
sel_r <= sel_r << ;

数码管数据显示设计

利用8选1多路器,选择端为当前扫描到的数码管也就是循环移位寄存器的输出端,利用多路器将待显示数据输送到对应到数码管上。

     reg [:]data_tmp;
always@(*)
case(sel_r)
'b0000_0001:data_tmp = disp_data[3:0];
'b0000_0010:data_tmp = disp_data[7:4];
'b0000_0100:data_tmp = disp_data[11:8];
'b0000_1000:data_tmp = disp_data[15:12];
'b0001_0000:data_tmp = disp_data[19:16];
'b0010_0000:data_tmp = disp_data[23:20];
'b0100_0000:data_tmp = disp_data[27:24];
'b1000_0000:data_tmp = disp_data[31:28];
default:data_tmp = 'b0000;
endcase

显示数据译码设计

前面所说如果要使数码管显示数字或字符,须对数字或字符进行编码译码。这里利用一个4输入查找表,来实现7位的输出显示译码。

 always@(*)
case(data_tmp)
'h0:seg = 7'b1000000;
'h1:seg = 7'b1111001;
'h2:seg = 7'b0100100;
'h3:seg = 7'b0110000;
'h4:seg = 7'b0011001;
'h5:seg = 7'b0010010;
'h6:seg = 7'b0000010;
'h7:seg = 7'b1111000;
'h8:seg = 7'b0000000;
'h9:seg = 7'b0010000;
'ha:seg = 7'b0001000;
'hb:seg = 7'b0000011;
'hc:seg = 7'b1000110;
'hd:seg = 7'b0100001;
'he:seg = 7'b0000110;
'hf:seg = 7'b0001110;
endcase

模块使能设计

模块化的设计理念是,使得每个模块独立化,其端口设计要便于以后被调用与控制。基于这种理念,这里需要加入使能信号。关于使能子模块,直接利用一个二选一多路器即可实现。

assign sel = (En)?sel_r:8'b0000_0000;

数码管显示模块仿真测试

以下生成了复位信号以及使能信号、待显示数据的初始化以及切换,分别在数码管上显示"87654321"以及"89abcdef"。

   initial begin
Rst_n = 'b0;
En = ;
disp_data = 'h12345678;
#(`clk_period*);
Rst_n = ;
#(`clk_period*);
#;
disp_data = 'h87654321;
#;
disp_data = 'h89abcdef;
#;
$stop;
end

设置好仿真脚本后进行功能仿真,可以看到如图15.6所示的局部波形文件,可以看出在复位信号置高之前数码管均显示0,在复位结束后数码管才开始正常显示,且当待显示数据为89ABCDEFh(MSB)后,数码管从1到8依次被选通且分别显示为FEDCBA98h(LSB)。即仿真通过。

图15.6 数码管功能仿真波形图

74HC595驱动设计

在数据手册中可以看出,不同工作温度和工作电压下的芯片工作频率值不相同,分别如表15.5与15.6所示。由于在学习板中芯片采用3.3V供电,这样在设计其工作频率时,直接使用50M晶振四分频后的时钟作为其工作时钟。

表15.5 芯片工作频率与温度对照

SYMBOL

PARAMETER

Vcc(V)

MIN

TYP

MAX

UNIT

 

maximum clock frequency SH_CP and ST_CP

2.0

9

30

-

MHz

4.5

30

91

-

MHz

6.0

35

108

-

MHz

 

表15.6芯片工作频率与温度对照

SYMBOL

PARAMETER

Vcc(V)

MIN

TYP

MAX

UNIT

 

maximum clock frequency SH_CP and ST_CP

2.0

4.8

-

-

MHz

4.5

24

-

-

MHz

6.0

28

-

-

MHz

 

74HC595模块接口设计

从上面的分析可得出如图15.10所示的框图,其接口列表15.7如所示

图15.10 74HC595驱动模块框图

表15.7 74HC595驱动模块接口功能描述

信号名称

I/O

功能描述

Clk

I

50M时钟

Rst_n

I

复位信号

En

I

数码管使能信号1使能,0关闭

Data[15:0]

I

8个数码管待显示数据,每四位组成一个BCD码

DS

O

串行数据输出

SH_CP

O

移位寄存器的时钟输出

ST_CP

O

存储寄存器的时钟输出

74HC595驱动模块设计

首先工作时钟产生,对50M时钟进行4分频。

     parameter CNT_MAX = ; //分频数
reg [:] divider_cnt;//分频计数器 always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
divider_cnt <= 'd0;
else if(divider_cnt == CNT_MAX)
divider_cnt <= 'd0;
else
divider_cnt <= divider_cnt + 'b1; wire sck_pluse;
assign sck_pluse = (divider_cnt == CNT_MAX);

对sck_pluse进行计数,用于查找表实现数据的串行输入以及移位时钟SH_CP与存储时钟ST_CP的产生。

     reg [:]SHCP_EDGE_CNT;//SH_CP EDGE counter

    always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
SHCP_EDGE_CNT <= 'd0;
else if(sck_pluse)begin
if(SHCP_EDGE_CNT == 'd31)
SHCP_EDGE_CNT <= 'd0;
else
SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 'd1;
end
else
SHCP_EDGE_CNT <= SHCP_EDGE_CNT;

查找表实现状态输出。

 always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
SH_CP <= 'b0;
ST_CP <= 'b0;
DS <= 'b0;
end
else begin
case(SHCP_EDGE_CNT)
'd0:begin SH_CP <= 1'b0; ST_CP <= 'b1; DS <= r_data[15]; end
'd1:begin SH_CP <= 1'b1; ST_CP <= 'b0;end
'd2:begin SH_CP <= 1'b0; DS <= r_data[];end
'd3:begin SH_CP <= 1'b1; end
'd4:begin SH_CP <= 1'b0; DS <= r_data[];end
'd5:begin SH_CP <= 1'b1; end
'd6:begin SH_CP <= 1'b0; DS <= r_data[];end
'd7:begin SH_CP <= 1'b1; end
'd8:begin SH_CP <= 1'b0; DS <= r_data[];end
'd9:begin SH_CP <= 1'b1; end
'd10:begin SH_CP <= 1'b0; DS <= r_data[];end
'd11:begin SH_CP <= 1'b1; end
'd12:begin SH_CP <= 1'b0; DS <= r_data[];end
'd13:begin SH_CP <= 1'b1; end
'd14:begin SH_CP <= 1'b0; DS <= r_data[];end
'd15:begin SH_CP <= 1'b1; end
'd16:begin SH_CP <= 1'b0; DS <= r_data[];end
'd17:begin SH_CP <= 1'b1; end
'd18:begin SH_CP <= 1'b0; DS <= r_data[];end
'd19:begin SH_CP <= 1'b1; end
'd20:begin SH_CP <= 1'b0; DS <= r_data[];end
'd21:begin SH_CP <= 1'b1; end
'd22:begin SH_CP <= 1'b0; DS <= r_data[];end
'd23:begin SH_CP <= 1'b1; end
'd24:begin SH_CP <= 1'b0; DS <= r_data[];end
'd25:begin SH_CP <= 1'b1; end
'd26:begin SH_CP <= 1'b0; DS <= r_data[];end
'd27:begin SH_CP <= 1'b1; end
'd28:begin SH_CP <= 1'b0; DS <= r_data[];end
'd29:begin SH_CP <= 1'b1; end
'd30:begin SH_CP <= 1'b0; DS <= r_data[];end
'd31:begin SH_CP <= 1'b1; end
endcase
end

74HC595驱动模块仿真测试

此处仿真与前面类似,不再重复。部分波形如图15.11所示,可以看出在模块复位结束后每在移位时钟SH_CP上升沿时串行数据依次输出且高位在前。移位结束后ST_CP产生一个高电平更新显示数据,因此各信号工作正常。

图15.11 74HC595驱动仿真

ISSP生成及使用

为了更便捷地进行板级调试,这里介绍Qusrtus Prime自带的的In system sources and probes editor(ISSP)调试工具,测试数码管可以只用其提供的源,而至于其探针功能的使用会在后面的RAM章节再做详细介绍。这样测试整体模块框图就可以简化为如图15.12所示。

图15.12 整体测试框图

这里ISSP是以IP核的形式提供的,因此第一步单击Tools来启动IP Catalog来新建一个定制IP核。这里需要指出的是这里与Quartus II不同,在Quartus Prime中与qsys相关的IP核均集中到Qsys中。在Quartus II的使用方法在本节末也有相应讲解,可以对比参考。

在弹出的图15.13 IP目录中,找到Simulation;Debug and Verification下的Altera In-System Source & Probes,并将输出目录确定为工程文件夹下的ip文件夹,并以hex_data保存,单击Next。

图15.13 新建ISSP

图15.14设置ISSP名称及文件位置

在弹出的图15.15配置界面中将源位宽定义为32,探针位宽定义为0,然后单击Next并将语言类型选择为Verilog HDL。

图15.15 ISSP配置界面

图15.16 语言选择成Verilog HDL并启动转换

在图15.16点击Generate后软件会将ISSP先保存为Qsys系统。在提示系统保存成功后点击Close会自动启动转换。弹出如图15.18界面,标志完成。

图15.17 Qsys系统保存完成

图15.18 转换完成

单击Close,然后在主界面点击Finish。此时会弹出如图15.19所示的汇总信息,确认无误后单击Close即可。

图15.19 汇总信息

这时会弹出图15.20提示需要手动将Qsys系统添加到工程中。这样直接在Files右键选择Add/Remove Files in Project,找到对应的文件即可。

图15.20 添加qsys到工程

图15.21 手动添加qsys到工程

ISSP依旧属于IP核,因此在使用前需要将其在工程文件中进行例化。ISSP在生成的文件中含有例化模板可以直接打开进行复制修改。可以打开生成的IP目录下的hex_data_inst.v文件。

图15.22 例化文件所在文件夹位置

图15.23 例化模板

板级调试与验证

新建顶层文件HEX_top.v,并将编写好的数码管驱动模块以及ISSP例化到顶层文件中。

 module HXE_top(Clk,Rst_n,SH_CP,ST_CP,DS);

     input Clk;  //50M
input Rst_n; output SH_CP; //shift clock
output ST_CP; //latch data clock
output DS; //shift serial data wire[:]disp_data;
wire [:] sel;//数码管位选(选择当前要显示的数码管)
wire [:] seg;//数码管段选(当前要显示的内容) hex_data hex_data(
.probe(),
.source(disp_data)
); HXE8 HXE8(
.Clk(Clk),
.Rst_n(Rst_n),
.En('b1),
.disp_data(disp_data),
.sel(sel),
.seg(seg)
); HC595_Driver HC595_Driver(
.Clk(Clk),
.Rst_n(Rst_n),
.Data({'b1,seg,sel}),
.S_EN('b1),
.SH_CP(SH_CP),
.ST_CP(ST_CP),
.DS(DS)
); endmodule

分配引脚并全编译无误后下载到开发板中。这时可以看到数码管全显示0,与设计一致。在Quartus Prime中点击Tools→In-System Source and Probes Editor启动ISSP,启动后的界面如图15.24所示。这里需手动选择下载器,并将数据格式改为设计中的hex格式。

图15.24 启动ISSP

图15.25 ISSP操作界面

图15.26 切换数据格式

这样ISSP界面的Data中输入相应的数据即可在数码管上看到与之对应的显示,如图15.27所示。至此完成了数码管的动态显示。

图15.27 数码管显示数据

本章中介绍了数码管的两种驱动方式,静态扫描与动态扫描。最终实现了三线制数码管的驱动显示。得益于模块化设计的优势,如果需要实现FPGA直接驱动数码管,只需直接调用HEX8文件即可。在板级验证中使用了In system sources and probes editor(ISSP)调试工具。

在本章的基础上,可以结合第14章的内容,设计一款具有清零及暂停功能的秒表。

Quartus II中ISSP使用方法

在Quartus II中使用方式如下所示:

这里ISSP是以IP核的形式提供的,因此第一步单击Tools→Mega Wizard Plug-In Manager来启动Mega Wizard插件管理器,并新建一个定制IP核;

图15.28 启动Mega Wizard插件管理器

在弹出的图15.29 Mega Wizard插件管理器的参数设置界面中,找到JTAG-accessible Extensions下选择In-System Source and Probes,并将输出目录确定为工程文件夹下的ip文件夹,并以hex_data保存,单击Next。

在弹出的图15.30配置界面中将源位宽定义为32,探针位宽定义为0,然后单击Next即可。

图15.29 Mega Wizard插件管理器参数设置

图15.30.1 ISSP配置界面

图15.30.2 ISSP配置界面

图15. 30.3 ISSP配置界面

在调用时打开生成后的hex_data.v复制端口并进行相应修改即可。剩余使用方式就与Quartus Prime一致了。

 
     hex_data hex_data(

         .probe(),

         .source(disp_data)

     );

AC620教程 第十五节 8位7段数码管驱动设计与验证的更多相关文章

  1. 10-8位7段数码管驱动实验——小梅哥FPGA设计思想与验证方法视频教程配套文档

    芯航线--普利斯队长精心奉献   实验目的: 1.实现FPGA驱动数码管动态显示: 2.使用In system sources and probes editor工具,输入需要显示在数码管上的的数据, ...

  2. 第四百一十五节,python常用排序算法学习

    第四百一十五节,python常用排序算法学习 常用排序 名称 复杂度 说明 备注 冒泡排序Bubble Sort O(N*N) 将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮 ...

  3. 第三百三十五节,web爬虫讲解2—Scrapy框架爬虫—豆瓣登录与利用打码接口实现自动识别验证码

    第三百三十五节,web爬虫讲解2—Scrapy框架爬虫—豆瓣登录与利用打码接口实现自动识别验证码 打码接口文件 # -*- coding: cp936 -*- import sys import os ...

  4. centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件、目录属性 shell数组简单用法 $( ) 和${ } 和$(( )) 与 sh -n sh -x sh -v 第三十五节课

    centos   shell脚本编程1 正则  shell脚本结构  read命令  date命令的用法  shell中的逻辑判断  if 判断文件.目录属性  shell数组简单用法 $( ) 和$ ...

  5. centos Linux系统日常管理2 tcpdump,tshark,selinux,strings命令, iptables ,crontab,TCP,UDP,ICMP,FTP网络知识 第十五节课

    centos  Linux系统日常管理2  tcpdump,tshark,selinux,strings命令, iptables ,crontab,TCP,UDP,ICMP,FTP网络知识 第十五节课 ...

  6. 【OpenCV新手教程之十五】水漫金山:OpenCV漫水填充算法(Floodfill)

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.    文章链接: http://blog.csdn.net/poem_qianmo/article/details/28261997 作者:毛星云( ...

  7. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  8. Senparc.Weixin.MP SDK 微信公众平台开发教程(十五):消息加密

    前不久,微信的企业号使用了强制的消息加密方式,随后公众号也加入了可选的消息加密选项.目前企业号和公众号的加密方式是一致的(格式会有少许差别). 加密设置 进入公众号后台的“开发者中心”,我们可以看到U ...

  9. 大白话5分钟带你走进人工智能-第十五节L1和L2正则几何解释和Ridge,Lasso,Elastic Net回归

    第十五节L1和L2正则几何解释和Ridge,Lasso,Elastic Net回归 上一节中我们讲解了L1和L2正则的概念,知道了L1和L2都会使不重要的维度权重下降得多,重要的维度权重下降得少,引入 ...

随机推荐

  1. [安全分享]斗鱼&360补天沙龙分享-跨域资源那些事

    [安全分享]斗鱼&360补天沙龙分享-跨域资源那些事 主要内容: 文件: http://scan.javasec.cn/补天&斗鱼-跨域资源那些事.pdf

  2. SSD固态盘应用于Ceph集群的四种典型使用场景

    在虚拟化及云计算技术大规模应用于企业数据中心的科技潮流中,存储性能无疑是企业核心应用是否虚拟化.云化的关键指标之一.传统的做法是升级存储设备,但这没解决根本问题,性能和容量不能兼顾,并且解决不好设备利 ...

  3. Nginx not running with no error message

    Nginx not running with no error message #!/bin/shecho "start"rm /etc/nginx/sites-enabled/d ...

  4. Java-从堆栈常量池解析equals()与==

    一.基本概念 ①JAVA中的基本数据类型(简单类型,内置类型): 字节型(byte),短整型(short),整型(int),长整型(long),字符型(char),浮点型(float),双精度型(do ...

  5. tornado 之 异步非阻塞

    异步非阻塞 1.基本使用 装饰器 + Future 从而实现Tornado的异步非阻塞 import tornado.web import tornado.ioloop from tornado im ...

  6. aop中通知详情

  7. 结对作业——四则运算 Part3. 对于结对编程的总结与思考

    结对作业——四则运算 Part3. 对于结对编程的总结与思考 PB15061303 刘梓轩PB16061489 艾寅中 GITHUB 地址 戳这里 目录 Part 1. Core代码编写部分 Part ...

  8. HttpServletResponse返回页面弹窗

    下载方法: @RequestMapping(value = "/download.htm") public void downLoadFile(String id,HttpServ ...

  9. Android开发实战之简单音乐播放器

    最近开始学习音频相关.所以,很想自己做一个音乐播放器,于是,花了一天学习,将播放器的基本功能实现了出来.我觉得学习知识点还是蛮多的,所以写篇博客总结一下关于一个音乐播放器实现的逻辑.希望这篇博文对你的 ...

  10. java-tip-各种Map的区别及如何选择

    这里主要讨论这三种:HashMap.LinkedHashMap.TreeMap 1. HashMap是常规的哈希表,查询以及插入的性能最好,如果没有特殊要求,应该使用这个 2. LinkedHashM ...