项目任务需求
由于是第一次接触FPGA和Verilog,才起步学习了很多FPGA和Verilog的基本知识,随后又通过电子森林给出的一些实例和代码,自己分析逐渐掌握了diamond这个软件和Verilog。
在与群里同学交流之后我也弄懂了这个项目的设计思路。
下面就是我使用这块开发板所实现的功能:
根据改开发板项目要求,要完成任务需要用到OLED、电位计,SPI接口8位串行ADC 电路
实现的思路:
1、旋转电位计可以由人工完成
2、由ADC电路对于产生的模拟电压进行数字化
3、通过bin_to_bcd模块将电压值转化为BCD编码
4、将BCD编码传至OLED模块并将其显示出来
5、最后综合整理,完成顶层文件的编写
ADC模块实现
该模块直接使用了电子森林里面的源代码,该模块负责完成ADC数据的采样,并将其传输给后面的模块,便于后面进行BCD码转换。
module ADS7868
(
input clk, //系统时钟
input rst_n, //系统复位,低有效
output reg adc_cs, //SPI总线CS
output reg adc_clk, //SPI总线SCK
input adc_dat, //SPI总线SDA
output reg adc_done, //ADC采样完成标志
output reg [7:0] adc_data //ADC采样数据
);
localparam HIGH = 1'b1;
localparam LOW = 1'b0;
reg [7:0] cnt; //计数器
always @(posedge clk or negedge rst_n)
if(!rst_n) cnt <= 1'b0;
else if(cnt >= 8'd34) cnt <= 1'b0;
else cnt <= cnt + 1'b1;
reg [7:0] data;
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
adc_cs <= HIGH; adc_clk <= HIGH;
data <= 1'b0; adc_data <= 1'b0; adc_done <= LOW;
end else case(cnt)
8'd0 : begin adc_cs <= HIGH; adc_clk <= HIGH; end
8'd1 : begin adc_cs <= LOW; adc_clk <= HIGH; end
8'd2,8'd4,8'd6,8'd8,8'd10,8'd12,8'd14,8'd16,
8'd18,8'd20,8'd22,8'd24,8'd26,8'd28,8'd30,8'd32:
begin adc_cs <= LOW; adc_clk <= LOW; end
8'd3 : begin adc_cs <= LOW; adc_clk <= HIGH; end //0
8'd5 : begin adc_cs <= LOW; adc_clk <= HIGH; end //1
8'd7 : begin adc_cs <= LOW; adc_clk <= HIGH; end //2
8'd9 : begin adc_cs <= LOW; adc_clk <= HIGH; data[7] <= adc_dat; end //3
8'd11 : begin adc_cs <= LOW; adc_clk <= HIGH; data[6] <= adc_dat; end //4
8'd13 : begin adc_cs <= LOW; adc_clk <= HIGH; data[5] <= adc_dat; end //5
8'd15 : begin adc_cs <= LOW; adc_clk <= HIGH; data[4] <= adc_dat; end //6
8'd17 : begin adc_cs <= LOW; adc_clk <= HIGH; data[3] <= adc_dat; end //7
8'd19 : begin adc_cs <= LOW; adc_clk <= HIGH; data[2] <= adc_dat; end //8
8'd21 : begin adc_cs <= LOW; adc_clk <= HIGH; data[1] <= adc_dat; end //9
8'd23 : begin adc_cs <= LOW; adc_clk <= HIGH; data[0] <= adc_dat; end //10
8'd25 : begin adc_cs <= LOW; adc_clk <= HIGH; adc_data <= data; end //11
8'd27 : begin adc_cs <= LOW; adc_clk <= HIGH; adc_done <= HIGH; end //12
8'd29 : begin adc_cs <= LOW; adc_clk <= HIGH; adc_done <= LOW; end //13
8'd31 : begin adc_cs <= LOW; adc_clk <= HIGH; end //14
8'd33 : begin adc_cs <= LOW; adc_clk <= HIGH; end //15
8'd34 : begin adc_cs <= HIGH; adc_clk <= HIGH; end
default : begin adc_cs <= HIGH; adc_clk <= HIGH; end
endcase
endmodule
bin_to_bcd模块
该模块是将前面ADC采样的数据转化为BCD编码,便于在OLED屏幕上进行实现,该模块也是使用电子森林已有的代码。
module bin_to_bcd //此模块为了将ADC采样的数据转换为我们常用的十进制显示而存在
(
input rst_n, //系统复位,低有效
input [15:0] bin_code, //需要进行BCD转码的二进制数据
output reg [19:0] bcd_code //转码后的BCD码型数据输出
);
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
endmodule
OLED模块
该模块负责将前面的BCD编码进行显示,该模块电子森林也已给出,但是需要自己修改
wire [19:0] bcd_code;//将BCD数据与oled相连
wire [7:0] odin1;
wire [7:0] odin2;
wire [7:0] odin3;
wire [7:0] odin4;
assign odin3={4'b0,bcd_code[19:16]}; //实现数据相通
assign odin2={4'b0,bcd_code[15:12]};
assign odin1={4'b0,bcd_code[11:8]};
assign odin4={4'b0,bcd_code[7:4]};
在这里我们声明了odin1、2、3、4,用来存放前面的BCD码,并在后面将他们显示在屏幕上
MAIN:begin
if(cnt_main >= 5'd8) cnt_main <= 5'd2;//将电压显示在第二行,显示到小数点后三位
else cnt_main <= cnt_main + 1'b1;
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 <= "SJY's design ";state <= SCAN; end
5'd2: begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= {odin3,8'd46,odin2,odin1,odin4," V "};state <= SCAN; end
5'd3: begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " it is correct ";state <= SCAN; end
5'd4: begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "OLED TEST ";state <= SCAN; end
5'd5: begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <="OLED TEST "; state <= SCAN; end
5'd6: begin y_p <= 8'hb1; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= "OLED TEST "; state <= SCAN; end
5'd7: begin y_p <= 8'hb2; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= "OLED TEST "; state <= SCAN; end
5'd8: begin y_p <= 8'hb3; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= "OLED TEST "; state <= SCAN; end
default: state <= IDLE;
endcase
end
在MAIN中,我们将BCD码显示在了第二行,由于声明了4个odin,我们可以实现将电压显示在小数点后三位,比数码管上的电压精确许多。
顶层模块及例化
由于我是第一次接触FPGA,这个模块我学习了很久,花费了许多时间,并自己编写了OLED模块的例化,其他模块参考了电子森林的代码
module TOP
(
clk,
rst_n,
adc_dat,
adc_cs,
adc_clk,
seg1_sel,
seg1_led,
seg2_sel,
seg2_led,
oled_csn,
oled_rst,
oled_dcn,
oled_clk,
oled_dat
);
input clk;
input rst_n;
//ADC鎬荤嚎
input adc_dat;
output adc_cs;
output adc_clk;
output seg1_sel;
output [7:0] seg1_led;
output seg2_sel;
output [7:0] seg2_led;
output oled_csn;
output oled_rst;
output oled_dcn;
output oled_clk;
output oled_dat;
wire adc_done;
wire [7:0] adc_data;
//ADC功能,例化
ADS7868 u2
(
.clk (clk ), //系统时钟
.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采样数据
);
//将ADC采样数据按规则转换为电压数据(乘以0.0129),这里我们直接乘以129,得到的数据经过BCD转码后小数点左移4位即可
wire [15:0] bin_code = adc_data * 16'd129;
wire [19:0] bcd_code;
//将处理后的ADC数据进行BCD转码,例化
bin_to_bcd u3
(
.rst_n (rst_n ), //系统复位,低有效
.bin_code (bin_code ), //需要进行BCD转码的二进制数据
.bcd_code (bcd_code ) //转码后的BCD码型数据输出
);
OLED12832 u4
(
.clk (clk ),
.rst_n (rst_n ),
.oled_csn (oled_csn ),
.oled_rst (oled_rst ),
.oled_dcn (oled_dcn ),
.oled_clk (oled_clk ),
.oled_dat (oled_dat ),
.bcd_code (bcd_code )
);
//Segment led display module
Seg_led seg[1:0]
(
.seg_data (bcd_code[19:12] ), //seg_data input
.seg_dot ({1'b1,1'b0} ), //segment dot control
.seg_sel ({seg1_sel,seg2_sel}), //segment com port
.seg_led ({seg1_led,seg2_led}) //MSB~LSB = DP,G,F,E,D,C,B,A
);
endmodule
项目总结
这是我第一次接触FPGA和Verilog语言,因此项目开始前我做了一些准备,看网上的Verilog学习视频,向自己的同学请教,自己分析一些代码实例,为项目开始之后自己独立完成做了铺垫。同时这个过程还锻炼了我的自学能力,加强了我对于Verilog这一语言的 掌握能力,在分析顶层例化中,增强了我的思维能力。
完成项目过程中所遇到的问题:
整个项目最困扰我的是各个模块之间数据的交互,可能对于其他同学很简单,但对于刚上手FPGA的我,虽然有电子森林的源代码,但是我并不清楚这之间缺少什么,需要什么才能让他们运转起来实现整个功能。为此我也咨询了之前参加过FPGA的同学,通过与他们一同分析给出的代码,逐渐了解了基本原理,我也就自然而然的清楚了这些代码少的是什么,也顺其自然的将顶层例化编写出来了。
整体来说本次活动难度并不大,用心就能完成,丰富了我的知识,了解了FPGA,增强了我的信心。同时我也学会了掌握diamond软件的基本能力。
项目感悟:
这次能够参加电子森林的寒假一起练这个项目活动 我觉得很荣幸 也很感谢这个项目让我学到了很多,从什么都没接触过的小萌新,到一步步获取知识,完成项目,收获满满。很感谢这个平台所提供的多种资源,更便于我们去学习和成长。后面也会积极学习,向各位大佬们靠近。