差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
简易电压表设计 [2018/10/22 17:43] anran [实验原理] |
简易电压表设计 [2020/01/18 21:54] (当前版本) gongyu |
||
---|---|---|---|
行 1: | 行 1: | ||
- | =====简易电压表设计===== | + | ### 简易电压表设计 |
- | ----- | + | |
- | ====实验任务==== | + | --- |
+ | |||
+ | ####实验任务 | ||
* 任务:基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成简易电压表设计并观察调试结果 | * 任务:基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成简易电压表设计并观察调试结果 | ||
行 7: | 行 9: | ||
* 解析:通过FPGA编程驱动串行ADC芯片,得到数字量化的电压信息,将量化的数字信息转换成BCD码形式,同时驱动独立数码管将电压值显示出来。 | * 解析:通过FPGA编程驱动串行ADC芯片,得到数字量化的电压信息,将量化的数字信息转换成BCD码形式,同时驱动独立数码管将电压值显示出来。 | ||
- | ====实验目的==== | + | #### 实验目的 |
- | 在基础数字电路实验部分我们已经掌握了FPGA驱动独立数码管的原理及方法,本实验主要学习模数转换器ADC的相关知识,串行(SPI接口)ADC芯片ADC081S101的驱动设计,同时学习二进制数转换BCD码的设计方法。 | + | 在基础数字电路实验部分我们已经掌握了FPGA驱动独立数码管的原理及方法,本实验主要学习模数转换器ADC的相关知识,串行([[SPI]]接口)[[ADC]]芯片ADC081S101的驱动设计,同时学习二进制数转换BCD码的设计方法。 |
* 学习模数转换器ADC的相关知识 | * 学习模数转换器ADC的相关知识 | ||
- | * 串行(SPI接口)ADC芯片ADC081S101的驱动设计 | + | * 串行([[SPI]]接口)ADC芯片ADC081S101的驱动设计 |
* 学习二进制数转换BCD码的设计方法 | * 学习二进制数转换BCD码的设计方法 | ||
* 完成简易电压表设计实现 | * 完成简易电压表设计实现 | ||
- | ====设计框图==== | + | #### 设计框图 |
根据前面的实验解析我们可以得知,该设计可以拆分成三个功能模块实现, | 根据前面的实验解析我们可以得知,该设计可以拆分成三个功能模块实现, | ||
- | * ADC081S101_driver: 驱动SPI接口ADC芯片实现模拟电压信号采集。 | + | * ADC081S101_driver: 驱动[[SPI]]接口[[ADC]]芯片实现模拟电压信号采集。 |
* bin_to_bcd:将二进制数据转换成BCD码的方法。 | * bin_to_bcd:将二进制数据转换成BCD码的方法。 | ||
* Segment_led:通过驱动独立式数码管将电压数据显示出来。 | * Segment_led:通过驱动独立式数码管将电压数据显示出来。 | ||
{{:7-Top-Down层次设计.png?500|Top-Down层次设计}} {{:7-模块结构设计.png?500|模块结构设计}} | {{:7-Top-Down层次设计.png?500|Top-Down层次设计}} {{:7-模块结构设计.png?500|模块结构设计}} | ||
- | ====实验原理==== | ||
- | ===ADC介绍=== | + | #### 实验原理 |
+ | |||
+ | ##### ADC介绍 | ||
数字系统,是用数字信号完成对数字量进行算术运算和逻辑运算的电路称为数字电路,或数字系统。而我们生活的世界是模拟的,想要让数字系统帮我们处理我们模拟世界的问题,就需要一个桥梁来沟通数字系统和模拟系统。 | 数字系统,是用数字信号完成对数字量进行算术运算和逻辑运算的电路称为数字电路,或数字系统。而我们生活的世界是模拟的,想要让数字系统帮我们处理我们模拟世界的问题,就需要一个桥梁来沟通数字系统和模拟系统。 | ||
行 31: | 行 34: | ||
{{:7-模拟数字系统通信.png?600|模拟数字系统通信}} | {{:7-模拟数字系统通信.png?600|模拟数字系统通信}} | ||
- | 模数转换器即A/D转换器,或简称ADC,通常是指一个将模拟信号转变为数字信号的电子元件。通常的模数转换器是将一个输入电压信号转换为一个输出的数字信号。由于数字信号本身不具有实际意义,仅仅表示一个相对大小。故任何一个模数转换器都需要一个参考模拟量作为转换的标准,比较常见的参考标准为最大的可转换信号大小。而输出的数字量则表示输入信号相对于参考信号的大小。 | + | 模数转换器即A/D转换器,或简称[[ADC]],通常是指一个将模拟信号转变为数字信号的电子元件。通常的模数转换器是将一个输入电压信号转换为一个输出的数字信号。由于数字信号本身不具有实际意义,仅仅表示一个相对大小。故任何一个模数转换器都需要一个参考模拟量作为转换的标准,比较常见的参考标准为最大的可转换信号大小。而输出的数字量则表示输入信号相对于参考信号的大小。 |
- | 数模转换器,又称D/A转换器,简称DAC,它是把数字量转变成模拟的器件。D/A转换器基本上由4个部分组成,即权电阻网络、运算放大器、基准电源和模拟开关。模数转换器中一般都要用到数模转换器,模数转换器即A/D转换器,简称ADC,它是把连续的模拟信号转变为离散的数字信号的器件。 | + | 数模转换器,又称D/A转换器,简称[[DAC]],它是把数字量转变成模拟的器件。D/A转换器基本上由4个部分组成,即权电阻网络、运算放大器、基准电源和模拟开关。模数转换器中一般都要用到数模转换器,模数转换器即A/D转换器,简称ADC,它是把连续的模拟信号转变为离散的数字信号的器件。 |
作为模拟系统与数字系统转换的桥梁,ADC和DAC有很多参数指标来标识其性能: | 作为模拟系统与数字系统转换的桥梁,ADC和DAC有很多参数指标来标识其性能: | ||
行 47: | 行 50: | ||
* 串行ADC(以ADC081S101为例)与数字电路接口为三根线(cs,clk,din),兼容三线SPI总线,cs为芯片使能管脚,clk为芯片时钟管脚,din为芯片数据管脚,当ADC芯片使能时每个clk周期从din采集1bit的数据,但是根据ADC081S101的时序,需要16个clk完成一次采样,所以clk频率至少等于采样率的16倍。 | * 串行ADC(以ADC081S101为例)与数字电路接口为三根线(cs,clk,din),兼容三线SPI总线,cs为芯片使能管脚,clk为芯片时钟管脚,din为芯片数据管脚,当ADC芯片使能时每个clk周期从din采集1bit的数据,但是根据ADC081S101的时序,需要16个clk完成一次采样,所以clk频率至少等于采样率的16倍。 | ||
- | ===ADC模块电路连接=== | + | ##### ADC模块电路连接 |
这里我们以STEP BaseBoard V3.0底板上的ADC模块电路,其电路图如下: | 这里我们以STEP BaseBoard V3.0底板上的ADC模块电路,其电路图如下: | ||
行 53: | 行 56: | ||
{{:7-ADC模块电路.png?800|ADC模块电路}} | {{:7-ADC模块电路.png?800|ADC模块电路}} | ||
- | 如ADC模块电路所示,FPGA直接连接ADC081S101芯片的控制端,ADC有6个管脚,3脚Vin为VCC和Vref功能复用,即Vin = VCC = Vref。ADC前端是运放电路LMV721,运放模块为电压跟随电路,再往前端是一个跳冒排针,用来选择ADC采样信号的来源,当短路帽将1、2脚短路时,ADC采集电位计电压,当短路帽将2、3脚短路时,ADC采射频端子或P4排针信号。本设计我们是采样旋转编码器的电压,所以需要用短路帽将1、2脚短路。 | + | 如ADC模块电路所示,[[FPGA]]直接连接ADC081S101芯片的控制端,ADC有6个管脚,3脚Vin为VCC和Vref功能复用,即Vin = VCC = Vref。ADC前端是运放电路LMV721,运放模块为电压跟随电路,再往前端是一个跳冒排针,用来选择ADC采样信号的来源,当短路帽将1、2脚短路时,ADC采集电位计电压,当短路帽将2、3脚短路时,ADC采射频端子或P4排针信号。本设计我们是采样旋转编码器的电压,所以需要用短路帽将1、2脚短路。 |
- | ===ADC模块驱动设计=== | + | ##### ADC模块驱动设计 |
- | 前面我们了解ADC081S101芯片和FPGA之间连接有三根线(cs、clk、din),兼容SPI总线,SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线(cs、sck、mosi、miso),事实上3根也可以(单向传输时),占用管脚少节约了芯片的管脚,同时为PCB的布局上节省空间,正是出于这种简单易用的特性,如今越来越多的芯片集成这种通信协议。 | + | 前面我们了解ADC081S101芯片和FPGA之间连接有三根线(cs、clk、din),兼容[[SPI]]总线,SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线(cs、sck、mosi、miso),事实上3根也可以(单向传输时),占用管脚少节约了芯片的管脚,同时为PCB的布局上节省空间,正是出于这种简单易用的特性,如今越来越多的芯片集成这种通信协议。 |
SPI设备分为主设备和从设备,设备之间共用sck、mosi和miso,另外每个从设备有一根cs线(不共用),通信在主设备和从设备之间进行,从设备与从设备之间无法直接通信,主设备可以同时连接多个从设备,当主设备和某个从设备通信时,先控制该从设备cs信号拉低,然后通过sck、mosi和miso进行数据传输。 | SPI设备分为主设备和从设备,设备之间共用sck、mosi和miso,另外每个从设备有一根cs线(不共用),通信在主设备和从设备之间进行,从设备与从设备之间无法直接通信,主设备可以同时连接多个从设备,当主设备和某个从设备通信时,先控制该从设备cs信号拉低,然后通过sck、mosi和miso进行数据传输。 | ||
行 74: | 行 77: | ||
* 模式2:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是 SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。 | * 模式2:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是 SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。 | ||
* 模式3:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是 SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。 | * 模式3:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是 SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。 | ||
- | * 模式4:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是 SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿 | + | * 模式4:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是 SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。 |
- | * | + | |
ADC081S101管脚说明表: | ADC081S101管脚说明表: | ||
行 152: | 行 155: | ||
</code> | </code> | ||
- | ===系统总体实现=== | + | ##### 系统总体实现 |
因为需要更高的时钟供ADC模块使用,我们例化pll核得到24MHz时钟,例化PLL的方法我们在基础数字电路实验部分已经练习过,这里就简单描述一下过程 | 因为需要更高的时钟供ADC模块使用,我们例化pll核得到24MHz时钟,例化PLL的方法我们在基础数字电路实验部分已经练习过,这里就简单描述一下过程 | ||
行 160: | 行 163: | ||
在顶层模块Voltage_Meas中,同时例化pll模块和ADC081S101_driver模块,并将pll的c0输出与ADC081S101_driver模块的clk连线。 | 在顶层模块Voltage_Meas中,同时例化pll模块和ADC081S101_driver模块,并将pll的c0输出与ADC081S101_driver模块的clk连线。 | ||
+ | {{:7-PLL1.png?500|PLL}} {{:7-PLL2.png?500|PLL}} | ||
+ | Pll模块和ADC081S101_driver模块的连接程序实现如下: | ||
- | ====实验步骤==== | + | <code verilog> |
+ | wire clk_24mhz,locked; | ||
+ | pll u1 | ||
+ | ( | ||
+ | .areset (!rst_n ), //pll模块的复位为高有效 | ||
+ | .inclk0 (clk ), //12MHz系统时钟输入 | ||
+ | .c0 (clk_24mhz ), //24MHz时钟输出 | ||
+ | .locked (locked ) //pll lock信号输出 | ||
+ | ); | ||
+ | |||
+ | wire adc_done; | ||
+ | wire [7:0] adc_data; | ||
+ | //使用I2C总线驱动PCF8591的ADC功能,例化 | ||
+ | ADC081S101_driver u2 | ||
+ | ( | ||
+ | .clk (clk_24mhz ), //系统时钟 | ||
+ | .rst_n (rst_n ), //系统复位,低有效 | ||
+ | .adc_cs (adc_cs ), //SPI总线CS | ||
+ | .adc_clk (adc_clk ), //SPI总线SCK | ||
+ | .adc_dat (adc_dat ), //SPI总线SDA | ||
+ | .adc_done (adc_done ), //ADC采样完成标志 | ||
+ | .adc_data (adc_data ) //ADC采样数据 | ||
+ | ); | ||
+ | </code> | ||
+ | |||
+ | 现在可以得到ADC采样数据了,假设ADC模拟输入电压为3.3V,理论上我们得到的采样数据adc_data应该为8’hff,而电压表最终显示在数码管上的数据应该为3.3,我们如何将8’hff转换成可以显示的3.3数据呢?这就设计到ADC量化数据的逆向运算了, | ||
+ | |||
+ | 我们知道量化运算 N = 256 * Vin / Vref, | ||
+ | |||
+ | 那么逆向运算为Vin = N * Vref / 256,其中Vref = 3.3V,所以Vin = N * 0.0129 | ||
+ | |||
+ | 所以我们需要用FPGA计算adc_data * 0.0129的结果,然后为了使用十进制的显示,先将结果进行BCD转码,然后显示在数码管上。 | ||
+ | |||
+ | 将ADC采样数据按规则转换为电压数据(乘以0.0129),这里我们直接乘以129,得到的数据经过BCD转码后小数点左移4位即可,程序实现如下: | ||
+ | |||
+ | <code verilog> | ||
+ | wire [15:0] bin_code = adc_data * 16'd129; | ||
+ | </code> | ||
+ | |||
+ | 将二进制数转换成BCD码的形式,采用左移加三的算法(以8’hff为例): | ||
+ | 1、左移要转换的二进制码1位 | ||
+ | 2、左移之后,BCD码分别置于百位、十位、个位 | ||
+ | 3、如果移位后所在的BCD码列大于或等于5,则对该值加3 | ||
+ | 4、继续左移的过程直至全部移位完成 | ||
+ | |||
+ | {{:7-BIN2BCD.png?600|Bin to BCD}} | ||
+ | |||
+ | 二进制转BCD码程序实现如下: | ||
+ | <code verilog> | ||
+ | reg [35:0] shift_reg; | ||
+ | always@(bin_code or rst_n)begin | ||
+ | shift_reg = {20'h0,bin_code}; | ||
+ | if(!rst_n) bcd_code = 0; | ||
+ | else begin | ||
+ | repeat(16) begin //循环16次 | ||
+ | //BCD码各位数据作满5加3操作, | ||
+ | if (shift_reg[19:16] >= 5) shift_reg[19:16] = shift_reg[19:16] + 2'b11; | ||
+ | if (shift_reg[23:20] >= 5) shift_reg[23:20] = shift_reg[23:20] + 2'b11; | ||
+ | if (shift_reg[27:24] >= 5) shift_reg[27:24] = shift_reg[27:24] + 2'b11; | ||
+ | if (shift_reg[31:28] >= 5) shift_reg[31:28] = shift_reg[31:28] + 2'b11; | ||
+ | if (shift_reg[35:32] >= 5) shift_reg[35:32] = shift_reg[35:32] + 2'b11; | ||
+ | shift_reg = shift_reg << 1; | ||
+ | end | ||
+ | bcd_code = shift_reg[35:16]; | ||
+ | end | ||
+ | end | ||
+ | </code> | ||
+ | |||
+ | 最后得到20位的数据输出,每4位表示一个BCD码,所以有5位有效数据,这里我们还需要将小数点左移4位,计算出来的数应该是X.XXXX伏特,1个整数位和4个小数位,核心板上只有两个数码管,取最高的两个BCD码显示到数码管X.X伏特,个位小数点点亮,分位小数点熄灭,程序实现如下: | ||
+ | <code verilog> | ||
+ | //个位数码管模块例化 | ||
+ | Segment_led u4 | ||
+ | ( | ||
+ | .seg_dot (1'b1 ), //seg_dot input | ||
+ | .seg_data (bcd_code[19:16]), //seg_data input | ||
+ | .segment_led (seg_1 ) //MSB~LSB = SEG,DP,G,F,E,D,C,B,A | ||
+ | ); | ||
+ | |||
+ | //分位数码管模块例化 | ||
+ | Segment_led u5 | ||
+ | ( | ||
+ | .seg_dot (1'b0 ), //seg_dot input | ||
+ | .seg_data (bcd_code[15:12]), //seg_data input | ||
+ | .segment_led (seg_2 ) //MSB~LSB = SEG,DP,G,F,E,D,C,B,A | ||
+ | ); | ||
+ | </code> | ||
+ | |||
+ | 综合后的设计框图如下: | ||
+ | |||
+ | {{:7-RTL设计框图.png?800|RTL设计框图}} | ||
+ | |||
+ | #### 实验步骤 | ||
- 双击打开Quartus Prime工具软件; | - 双击打开Quartus Prime工具软件; | ||
- 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择); | - 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择); | ||
行 173: | 行 269: | ||
- | ====实验现象==== | + | #### 实验现象 |
+ | |||
+ | 将程序下载到FPGA中,P3接口用短路帽将1、2脚短路,旋转底板右上角的电位计,观察核心板数码管变化,如果有万用表可以测量P3短路处的电压,与数码管显示对比。 |