项目背景
目的是提高同学们在暑假对于数字电路和FPGA的理解,培养学生掌握核心技术的创新意识,硬禾学堂开展了“暑假一起练”活动。通过免费获得板卡、提升技能的方式,为了让更多的同学通过一系列有趣的项目挑战提升自己的综合技能。而FPGA综合技能训练板基于STEP-MXO2核心板。STEP-MXO2第二代是小脚丫团队推出的最新一款FPGA开发板,选用了Lattice公司的MXO2系列的4000HC,逻辑资源较第一代产品提升了近4倍。同时,在板卡的背面集成了编程器,通过USB数据线即可完成FPGA的编程和下载,使用硬禾学堂的小脚丫训练板,可以实现多种不同方式的输入输出功能。除此以外,这个项目可以说是从零开始做,所以可以适合广大零基础的学生学习。
项目描述及需要实现的功能
-
旋转电位计可以产生0-3.3V的电压
-
利用板上的串行ADC对电压进行转换
-
将电压值在板上的OLED屏幕上显示出来
项目逻辑图
系统构成:
- ADC模块,将从可变电位计(0-3.3V)得到的模拟电压转化成数字电压,数字电压的精度则有编写的代码模块决定,最后在OLED上输出的精度在小数点后三位数。
- BCD模块,为了将得到的数字电压显示在显示屏上,且可以便于展示也需要一些文字说明,所以需要一个BCD模块来编码,之后编码之后的二进制数据才可以显示在OLED的屏幕之上,可以在其中编写二进制代码到简单数字和字母的一一对应。
- OLED模块,根据所需要的显示要求和从BCD模块处得到的数据将所有内容显示在OLED屏幕上,一共4位的电压,分别使用4个变量进行显示,其他的则是一些文字说明。
项目计划:
1、电位计手动旋转产生不同电压
2、由ADC模块将产生的电压进行数字化
3、通过BCD编码模块将电压转化为BCD码
4、将编码后的结果显示在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
BCD模块
负责接受来自ADC模块的数据,编码之后在OLED上显示,为电子森林已有代码。
module bin_to_bcd
(
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
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模块
该模块同样是使用电子森林已有代码,在其上进行一定程度的修改,因为代码量过大不将源码放出,只放出修改部分的代码。
input wire [19:0] bcd_code,
wire [7:0] volt1;
wire [7:0] volt2;
wire [7:0] volt3;
wire [7:0] volt4;
MAIN:begin
if(cnt_main >= 5'd4) 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 <= "----Xie Ning----";state <= SCAN; end
5'd2: begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= {" Volt: ",volt4,8'd46,volt3,volt2,volt1," V "};state <= SCAN; end
5'd3: begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= " programe is ok!";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
5'd5: begin y_p <= 8'hb0; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <=" "; state <= SCAN; end
5'd6: begin y_p <= 8'hb1; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= " "; state <= SCAN; end
5'd7: begin y_p <= 8'hb2; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= " "; state <= SCAN; end
5'd8: begin y_p <= 8'hb3; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= " "; state <= SCAN; end
default: state <= IDLE;
endcase
end
这里显示的电压共4位数字,其中有3位为小数点之后。
顶层模块
在顶层模块中,将上面所写的所有模块实例化并且串联起来。
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;
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 m1
(
.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采样数据
);
wire [15:0] bin_code = adc_data * 16'd129;
wire [19:0] bcd_code;
//BCD模块ADC数据进行BCD转码
bin_to_bcd m2
(
.rst_n (rst_n ), //系统复位,低有效
.bin_code (bin_code ), //需要进行BCD转码的二进制数据
.bcd_code (bcd_code ) //转码后的BCD码型数据输出
);
//OLED模块显示编码数据
OLED12832 m3
(
.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 )
);
endmodule
结果展示
项目目标就是将电位计旋转可以产生0-3.3V的电压这一过程展示到OLED的屏幕上,而这里也给出了结果显示,一张图为电位计产生0V电压的显示,一张为3.289V'的电压显示,从后面的结果也可以看出数字电压表的精度到小数点后三位为止。
项目过程中的困难
之前虽然有接触过一段时间的Verilog,但是已经是大二的事情了,代码逻辑已经有点搞不清楚了,又花了一些时间才把知识捡起来。同时还请教了一下学习过这个的同学,同样也再次锻炼了自己的学习能力,通过一些简单的代码示例快速的上了手,总体来说没有什么大困难。
项目总结与感悟
在还不熟悉的时候模块之间的数据传输有点困难,但逐渐上手之后,难点反而变成了BCD编码相关的部分,总体来说还是学到了很多,是一次很有意义的活动。
最后,非常感谢硬禾学堂带给我这次机会,让我对数字逻辑电路、FPGA有了更深入的认识,对Verilog语言的使用也有了不少的提升,同时也给我了一个更加充实的假期。