内容介绍
关于精度问题,视频未重新录制,报告及代码已修改
项目描述
1.项目介绍
该项目是基于iCE40UP5K的FPGA学习平台制作一个数字电压表。多数FPGA上本没有ADC的功能,而一些应用往往需要ADC对模拟信号进行量化,如数字电压表所需要采集的直流电压。以Σ-ΔADC,将输入直流信号与通过一个一阶RC滤波器的PWM进行比较,通过FPGA输出的PWM占空比与捕获的比较结果,可在转换速率要求不高的情况下,以极低的成本实现ADC。并将电压值在板子上的OLED屏幕上显示出来,模拟电压由学习平台上的旋转电位器提供。
2.设计思路
系统框图
该系统由fpga以Sigma-Delta的方式采集模拟电压的量化值,再将其转化为0-3.3V的电压值(BCD码形式),再将其显示在OLED屏上。所以主要包含的模块有Sigma-Delta ADC,二进制转BCD码以及OLED的驱动。
Sigma-Delta ADC框图
Sigma-Delta ADC的实现由硬件与软件共同完成,依靠平台上的高速比较器与RC网络,在fpga逻辑上使RC网络的输入与比较器的输出相等,即可得到一个满足 占空比*比较器供电电压=模拟输入电压 的PWM信号,以数字低通滤波器做平滑滤波,即可得到较稳定的模拟电压信号量化值。
3.硬件介绍
该项目硬件所使用的是基于ICE40UP5K的FPGA学习平台。核心板为基于Lattice的ICE40UP5K FPGA,板载LPC11U35下载器,可以通过U盘形式拖拽rbt文件烧录代码,简单快捷。底板上包含12个WS2812B RGB三色灯,一个128*64 OLED显示屏,两个按键,4个单色LED,DAC电阻网络,一个电位器,以及一些传感器,可以做许多项目,是非常适合初学者入门FPGA的一个平台,也可取下核心板用于其他比赛。
4.实现的功能及图片展示
基于该平台,实现了数字电压表的功能,检测旋转电位器输出的0-3.30V的电压值,精确到小数点后两位,并将其显示在OLED屏上。同时将一些LED引脚约束,测得电压值的改变会引起LED的亮灭变化。
FPGA资源报告
Design Summary:
Number of slice registers: 310 out of 5280 (6%)
Number of I/O registers: 1 out of 117 (1%)
Number of LUT4s: 825 out of 5280 (16%)
Number of logic LUT4s: 653
Number of inserted feedthru LUT4s: 57
Number of replicated LUT4s: 23
Number of ripple logic: 46 (92 LUT4s)
Number of IO sites used: 18 out of 39 (46%)
Number of IO sites used for general PIO: 15
Number of IO sites used for I3C: 0 out of 2 (0%)
(note: If I3C is not used, its site can be used as general PIO)
Number of IO sites used for PIO+I3C: 15 out of 36 (42%)
Number of IO sites used for OD+RGB IO buffers: 3 out of 3 (100%)
(note: If RGB LED drivers are not used, sites can be used as OD outputs,
see TN1288 iCE40 LED Driver Usage Guide)
Number of IO sites used for PIO+I3C+OD+RGB: 18 out of 39 (46%)
Number of DSPs: 0 out of 8 (0%)
Number of I2Cs: 0 out of 2 (0%)
Number of High Speed OSCs: 0 out of 1 (0%)
Number of Low Speed OSCs: 0 out of 1 (0%)
Number of RGB PWM: 0 out of 1 (0%)
Number of RGB Drivers: 0 out of 1 (0%)
Number of SCL FILTERs: 0 out of 2 (0%)
Number of SRAMs: 0 out of 4 (0%)
Number of WARMBOOTs: 0 out of 1 (0%)
Number of SPIs: 0 out of 2 (0%)
Number of EBRs: 6 out of 30 (20%)
Number of PLLs: 0 out of 1 (0%)
Number of Clocks: 1
5.主要代码段及说明
FPGA逻辑上使RC网络的输入与比较器的输出相等,得到与模拟电压信号相对应的PWM信号。
always @ (posedge clk)
begin
delta <= analog_cmp; // capture comparitor output
end
assign analog_out = delta; // feedback to comparitor LPF
测量得到的PWM信号的占空比。
always @ (posedge clk or negedge rstn)
begin
if( ~rstn )
begin
sigma <= 0;
accum <= 0;
accum_rdy <= 0;
end else begin
if (rollover) begin
// latch top ADC_WIDTH bits of sigma accumulator (drop LSBs)
accum <= sigma[ACCUM_BITS-1:ACCUM_BITS-ADC_WIDTH]; //sigma的高八位(共十位)舍弃了两位精度
sigma <= delta; //重置累加器,sigma取当前的delta值
end else begin //在一个采样周期内,执行该部分
if (&sigma != 1'b1) // if not saturated 如果全为1
sigma <= sigma + delta; // accumulate 累加
end
accum_rdy <= rollover; // latch 'rdy' (to align with accum)
end
end
always @(posedge clk or negedge rstn)
begin
if( ~rstn ) begin
counter <= 0;
rollover <= 0;
end
else begin
counter <= counter + 1; //当counter未溢出时,加一
rollover <= &counter; //当counter全部为1时,'rollover'为1
end
end
经过数字滤波平滑处理,得到digital_out,数字滤波模块参考电子森林文章《通过高速比较器和FPGA逻辑实现Sigma Delta ADC 》
box_ave #(
.ADC_WIDTH(ADC_WIDTH),
.LPF_DEPTH_BITS(LPF_DEPTH_BITS))
box_ave (
.clk(clk),
.rstn(rstn),
.sample(accum_rdy),
.raw_data_in(accum),
.ave_data_out(digital_out),
.data_out_valid(sample_rdy)
);
digital_out为采集到的8位量化值,在3.3V下8位量化值的分辨率为0.012890625,将其扩大10000倍取129,得到digital_buff。
assign digital_buff = (digital_out << 7) + digital_out;
通过转BCD码模块,将其转化为BCD码,便于OLED显示。该模块参考野火
bcd_8421 u_bcd_8421(
.sys_clk(clk_in), //系统时钟,频率50MHz
.sys_rst_n(rstn), //复位信号,低电平有效
.data(digital_buff), //输入需要转换的数据
.unit(dis_unit), //个位BCD码
.ten(dis_ten), //十位BCD码
.hun(dis_hun), //百位BCD码
.tho(dis_tho), //千位BCD码
.t_tho(dis_t_tho), //万位BCD码
.h_hun() //十万位BCD码
);
将数值转化为字符串
ch1 = " ";
str_out = {ch1, str_out[`MAX_BIT:8]}; //16
str_out = {ch1, str_out[`MAX_BIT:8]}; //15
str_out = {ch1, str_out[`MAX_BIT:8]}; //14
str_out = {ch1, str_out[`MAX_BIT:8]}; //13
str_out = {ch1, str_out[`MAX_BIT:8]}; //12
str_out = {ch1, str_out[`MAX_BIT:8]}; //11
str_out = {ch1, str_out[`MAX_BIT:8]}; //10
ch1 = " ";
str_out = {ch1, str_out[`MAX_BIT:8]}; //9
ch1 = "V";
str_out = {ch1, str_out[`MAX_BIT:8]}; //8
ch1 = " ";
str_out = {ch1, str_out[`MAX_BIT:8]}; //7
ch1 = "0" + dis_hun;
str_out = {ch1, str_out[`MAX_BIT:8]}; //6
ch1 = "0" + dis_tho;
str_out = {ch1, str_out[`MAX_BIT:8]}; //5
ch1 = ".";
str_out = {ch1, str_out[`MAX_BIT:8]}; //4
ch1 = "0" + dis_t_tho;
str_out = {ch1, str_out[`MAX_BIT:8]}; //3
ch1 = " ";
str_out = {ch1, str_out[`MAX_BIT:8]}; //2
str_out = {ch1, str_out[`MAX_BIT:8]}; //1
并将其显示在OLED屏上,将OLED屏划分为四行,每行显示16个字符,第一行显示voltage,第二行显示电压值。
case(cnt_main) //MAIN状态
5'd0: begin state <= INIT; end
5'd1: begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " voltage ";state <= SCAN; end
5'd2: begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
5'd3: begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= str_out ;state <= SCAN; end
5'd4: begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " ";state <= SCAN; end
6.结尾感言
在项目的完成过程中遇到了许多问题,一开始的软件安装以及使用,由于Radiant推出时间并不太长,网上教程比较少,好在硬禾学堂提供了入门教程,得以点亮第一个LED。后续也在电子森林网站上找到了很多有用的文章,大大加快了项目进度。而遇到的主要难题并不是OLED驱动与sigmadelta,而是将八位二进制量化值转化为0-3.3V电压并显示,经学长提醒,得以在野火的FPGA教程中找到解决方法。
目前是大二,如果有机会的话希望在大三暑假再参加一次电赛,也希望如今所学能在电赛中有所应用。