用FPGA实现多路电压采集器:(1)在FPGA上实现异步串口通信
前几周微机课上布置了一个Project,要求用FPGA实现多路电压采集器,对多路模拟信号采集并显示其电压幅值。我们小组的设计思路是利用ADC芯片对模拟电压输入信号进行采集,转换为数字信号,然后交由FPGA进行处理,最后将处理结果通过串口传输给电脑并由串口助手显示读数。基本框图如下
经过好几天的努力,现在来做一个小小总结。
所谓多路电压采集器,指的是在同一时刻采集到的数据,虽然好多AD都能实现多路数据的采集,但大多是串行的,即采集完这一路,再采集下一路模拟信号,总是会有时间差,并非,Project的目的是用多片AD(每一片只用一路信号输入)同时采集,实现真正的多路信号同时采集,模拟量选用0~5v的电压
硬件:1.老师给发的DIGICUBE,XILINX SPARTAN3的FPGA,XC3S50,clk1=18.4320M,clk2=8M
2.AD芯片是ADC0809,淘宝直接买的模块,后期有时间自己焊电路
3.下载线,USB转串口线(用于串口通信)
软件:ISE13.3,串口助手
使用Verilog HDL
实现串口通信的代码是是学长们留下来的,稍作修改并增加注释
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: // Design Name: // Module Name: // Project Name: // Target Devices: // Tool versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// //同济大学11级自动化 module uart(reset, clk, rx_in, tx_out); //Port declaration //clk, reset, rx, tx input reset; input clk; input rx_in; output tx_out; //reg for tx reg [7:0] tx_reg;//发送数据寄存器,相当于buffer wire [7:0] tx_data; reg [15:0] tx_sample_cnt;//采样计数器 reg [3:0] tx_cnt; reg tx_done; reg tx_out; reg tx_enable; //reg for rx reg [7:0] rx_reg; wire [7:0] rx_data; reg [15:0] rx_sample_cnt; reg [3:0] rx_cnt;//接收到的数据位计数器 reg rx_frame_err;//接收帧错误标记,未使用 reg rx_done; reg rx_busy; reg rx_d; reg rx_enable; assign rx_data = (rx_done)?rx_reg:8'hzz;//zz为高阻状态 assign tx_data = rx_data; //uart rx logic always@(posedge clk or posedge reset) if(reset) begin rx_sample_cnt <= 0; rx_cnt <= 0; rx_frame_err <= 0; rx_done <= 0; rx_busy <= 0; rx_d <= 0; rx_enable <= 1; end else begin rx_d <= rx_in;//rx_in是从串口读入的数据,rx_d只有一位,数据帧开始的标志是0,结束是1(第9位) if(rx_done) begin rx_done <= 0;//接收完毕后,done标记位清零开始下一次接收 end if(rx_enable)//若接收使能 begin //Check if just received start of frame检查是否为一帧的开始 if(!rx_busy && !rx_d)//busy为0时表示不忙,d为0表示一帧的开始 begin//如果不忙...进入接收状态,使busy位置1,为了进行后面的处理 rx_busy <= 1;//busy置1后要对接收的数据进行处理 rx_sample_cnt <= 1; rx_cnt <= 0; rx_done <= 0; end //Start of frame detected, proceed with rest of data检测到一帧的开始 if(rx_busy)//busy置1后要对接收的数据进行处理 begin rx_sample_cnt <= rx_sample_cnt+1;//计数器每次+1 if(rx_sample_cnt == 1920)//clk=18.432MHz,clk/1920=9600 begin rx_sample_cnt <= 0; end //logic to sample at middle of data if(rx_sample_cnt == 960)//中间处采样 begin if((rx_d==1) && (rx_cnt==0)) begin rx_busy <= 0; rx_done <= 0; end else begin rx_cnt <= rx_cnt+1; rx_done <= 0; //start storing the rx data if(rx_cnt>0 && rx_cnt<9) begin rx_reg[rx_cnt-1] <= rx_d;//reg=rx_buffer,rx_d是接收到的每一位 end if(rx_cnt == 9) begin rx_busy <= 0;//接收完毕后把busy位置0 //check if end of frame received correctly if(rx_d == 0)//第9位为停止位,若停止位为0则报错 begin rx_frame_err <=1; end else//否则接收完成 begin rx_done <= 1; rx_frame_err <= 0; end end end end end end if(!rx_enable)//若接收使能为0,即不能接收则 begin rx_busy <= 0; rx_done <= 0; end end //uart tx logic,和rx类似 always@(posedge clk or posedge reset) if(reset) begin tx_reg <= 8'hff; tx_done <= 1; tx_out <= 1; tx_cnt <= 0; tx_sample_cnt <= 0; tx_enable <= 1; end else begin if(rx_done && tx_done && !tx_cnt) begin tx_reg <= tx_data; tx_done <= 0; tx_out <= 0; tx_cnt <= 1; tx_sample_cnt <= 0; end else begin tx_sample_cnt <= tx_sample_cnt+1; if(tx_sample_cnt == 1920) begin tx_sample_cnt <= 0; if(tx_enable && !tx_done) begin tx_cnt <= tx_cnt+1; if(tx_cnt >0 && tx_cnt<9) begin tx_out <= tx_reg[tx_cnt-1]; end if(tx_cnt == 9) begin tx_out <= 1; tx_cnt <= 0; tx_done <= 1; end end if(!tx_enable) begin tx_cnt <= 0; tx_sample_cnt <= 0; tx_reg <= 8'hff; tx_out <= 1; tx_done <= 1; end if(tx_done) begin tx_out <= 1; end end end end endmodule
实现的功能就是串口收发,把收到的数据再发回去
用usb转串口线脸上笔记本,打开选择相应端口号和9600的波特率即可测试