FPGA学习笔记:数据采集传输系统设计(五):ADC采集FIFO缓存UART发送系统

一、系统任务说明

前面已经完成了ADC采集模块的编写,也了解了FIFO存储器IP核的调用,数据采集传输系统还需要将采集到的数据用串口发送出去。
串口发送波特率为115200 bps时,每次发送10位数据,串口每秒可以发送11520字节数据,每个字节数据需要发送1s /11520 = 86.8us。而单次ADC转换速率大概3.7us,采集得到12位数字信号,需要用串口传输两次,串口传输速率远远小于AD采集速率。连续采样时,为了避免采样数据丢失,需要先将AD采集数据保存到FIFO存储器中,再由串口慢慢从FIFO存储器读取数据并发送。

二、代码设计

1.利用FIFO缓存ADC采集的数据adc_fifo.v

(1)模块端口说明

端口名称方向说明
Clkinput系统时钟
Rst_ninput系统复位
Startinput开始采集标志位
AD_Doneoutput采集完成标志位
ADC_OUTinputADC串行数字信号
ADC_CS_NoutputADC片选
ADC_DINoutput串行数据送给ADC芯片
ADC_SCLKoutputADC时钟
ADC_Doneoutput单次AD采集完成标志位,仿真时使用
fullinputFIFO满标志位
wrreqoutputFIFO写使能
FIFO_DATA [11:0]outputFIFO数据输入

在本模块中,将单次AD采集代码例化进来,多次调用该模块连续采集信号,并将AD转换结果存储至fifo存储器。ADC_Done信号为单次AD采集完成标志位,在仿真时使用,板级验证时不需要该端口。full、wrreq、FIFO_DATA三个信号为本模块和fifo模块之间传输的信号。本模块与fifo模块之间的关系如下:
ADC_FIFO

(2)adc_fifo.v代码设计

将ADC采集FIFO缓存划分为以下三个状态:
IDLE:空闲状态
WAIT_ADC_DONE:等待单次AD采集完成
WRITE_FIFO:延时一拍,数据写入FIFO

横线上方为状态跳转条件,横线下方为该状态对信号的操作。状态转换图如下:
在这里插入图片描述
空闲状态时,若ADC连续采集信号ADC_State为1,则使能单次AD采集标志ADC_Start。单次采集完成,ADC_Done信号为1,写请求使能,跳入下一状态。在下一状态,数据被写入FIFO。状态机代码如下:

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n) begin
		ADC_Cnt <= 11'd0;
		ADC_Start <= 1'b0;
		wrreq <= 1'b0;
		FIFO_DATA <= 12'd0;
		state <= IDLE;
	end
	else begin
		case(state)
			IDLE:
				if(ADC_State) begin
					ADC_Start <= 1'b1;//开启单次AD采集
					state <= WAIT_ADC_DONE;
				end
				else
					state <= IDLE;
			
			WAIT_ADC_DONE:begin
				ADC_Start <= 1'b0;
				if(ADC_Done == 1'b1) begin//等待AD采集完成
					FIFO_DATA <= ADC_DATA;
					wrreq = 1'b1;
					ADC_Cnt <= ADC_Cnt + 1'b1;
					state <= WRITE_FIFO;
				end
				else
					state <= WAIT_ADC_DONE;
			end
			
			WRITE_FIFO:begin
				wrreq = 1'b0;
				if(ADC_Cnt == ADC_Cnt_MAX)
					ADC_Cnt <= 11'd0;
				state <= IDLE;
			end
			
			default:state <= IDLE;
		endcase
	end

采集完成标志位和连续采集状态标志位可在fifo的满标志位full置1时赋值,此时连续采集的次数固定,利用计数器ADC_Cnt作为赋值条件可以自定义采集次数ADC_Cnt_MAX,需注意fifo的深度必须大于等于ADC_Cnt_MAX。

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		AD_Done <= 1'b0;
	else	if(ADC_Cnt == ADC_Cnt_MAX)
		AD_Done <= 1'b1;
	else 
		AD_Done <= 1'b0;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		ADC_State <= 1'b0;
	else if(Start)
		ADC_State <= 1'b1;
	else if(ADC_Cnt == ADC_Cnt_MAX)
		ADC_State <= 1'b0;	

adc_fifo.v源代码:

module adc_fifo(
	input Clk,						//系统时钟
	input Rst_n,					//系统复位
	input Start,					//开始采集标志位
	output reg AD_Done,				//采集完成标志位
			
	input wire ADC_OUT,				//ADC串行数字信号
	output wire ADC_CS_N,			//ADC片选
	output wire ADC_DIN,			//串行数据送给ADC芯片
	output wire ADC_SCLK,			//ADC时钟
	output wire ADC_Done,			//单次AD采集完成标志位,仿真时使用
			
	input full,						//FIFO满标志位
	output reg wrreq,				//FIFO写使能
	output reg [11:0] FIFO_DATA		//FIFO数据输入
);

	parameter ADC_Cnt_MAX = 11'd128;  //AD采集次数
	
/*****模块间信号连线*****/	
	reg ADC_Start;			//单次AD采集开始标志位
	wire [11:0] ADC_DATA;	//单次AD采集数据
	
/*****本模块内部寄存器、参数定义*****/	
	reg ADC_State;			//连续采集状态
	reg [10:0] ADC_Cnt;		//采集128次计数器
	reg [2:0] state;
	localparam 
		IDLE 			= 3'b001,	//空闲状态
		WAIT_ADC_DONE 	= 3'b010,	//等待单次AD采集完成
		WRITE_FIFO 		= 3'b100;	//延时一拍,数据写入FIFO
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n) begin
		ADC_Cnt <= 11'd0;
		ADC_Start <= 1'b0;
		wrreq <= 1'b0;
		FIFO_DATA <= 12'd0;
		state <= IDLE;
	end
	else begin
		case(state)
			IDLE:
				if(ADC_State) begin
					ADC_Start <= 1'b1;//开启单次AD采集
					state <= WAIT_ADC_DONE;
				end
				else
					state <= IDLE;
			
			WAIT_ADC_DONE:begin
				ADC_Start <= 1'b0;
				if(ADC_Done == 1'b1) begin//等待AD采集完成
					FIFO_DATA <= ADC_DATA;
					wrreq = 1'b1;
					ADC_Cnt <= ADC_Cnt + 1'b1;
					state <= WRITE_FIFO;
				end
				else
					state <= WAIT_ADC_DONE;
			end
			
			WRITE_FIFO:begin
				wrreq = 1'b0;
				if(ADC_Cnt == ADC_Cnt_MAX)
					ADC_Cnt <= 11'd0;
				state <= IDLE;
			end
			
			default:state <= IDLE;
		endcase
	end

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		AD_Done <= 1'b0;
	else	if(ADC_Cnt == ADC_Cnt_MAX)
		AD_Done <= 1'b1;
	else 
		AD_Done <= 1'b0;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		ADC_State <= 1'b0;
	else if(Start)
		ADC_State <= 1'b1;
	else if(ADC_Cnt == ADC_Cnt_MAX)
		ADC_State <= 1'b0;
		
	//ADC采集模块
	adc128s052 adc1(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.DATA(ADC_DATA),		//并行数字信号
		.Channel(3'd6),		//通道选择
		.Start(ADC_Start),   //开始标志位
		.Conv_done(ADC_Done),//完成标志位
		.ADC_CS_N(ADC_CS_N),	//片选
		.ADC_DIN(ADC_DIN), 	//串行数据送给ADC芯片
		.ADC_SCLK(ADC_SCLK),	//ADC时钟
		.ADC_OUT(ADC_OUT)		//串行数字信号
	);
	defparam adc1.DIV_PARAM = 8;//ADC时钟50/8 = 6.25Mhz
endmodule

2.UART从FIFO读取数据并发送fifo_uart_tx.v

(1)模块端口说明

端口名称方向说明
Clkinput系统时钟
Rst_ninput系统复位
Startinput开始发送数据标志位
emptyinputFIFO空标志位
FIFO_Q [11:0]input数据输入
rdreqoutputFIFO读使能
Uart_doneoutput所有数据发送完毕
uart_txoutput串口数据发送端

本模块中,将串口发送模块例化进来,多次调用该模块发送从FIFO读取的数据。empty、FIFO_Q、rdreq为本模块和fifo之间连接的信号。本模块和fifo模块之间的关系如下:
FIFO_UART

(2)fifo_uart_tx.v代码设计

将UART读取FIFO缓存并发送划分为以下五个状态:
IDLE:空闲状态
DELY:空一拍延时,等待FIFO_Q数据更新
SEND_HIGH:发送ADC高四位数据
SEND_LOW:发送ADC低八位数据
WAIT_SEND_DONE:等待发送结束

横线上方为状态跳转条件,横线下方为该状态对信号的操作。状态转换图如下:
在这里插入图片描述
若fifo为空,则empty为1,状态机处于空闲状态。fifo非空时,读请求rdreq置1,两个时钟周期后才能够发送数据,因此中间加入DELY状态,延时一个时钟周期,fifo中数据被放到FIFO_Q端口,这个数据在SEND_HIGH状态时被发送。串口先发送AD采集高四位,后发送AD采集低八位。状态机代码如下:

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n) begin
		rdreq <= 1'b0;
		send_en <= 1'b0;
		send_data <= 8'd0;
		UART_Cnt <= 11'd0;
		state <= IDLE;
	end
	else begin
		case(state)
			IDLE:
				if(empty == 1'b1) begin
					if(UART_Cnt == UART_Cnt_MAX)
						UART_Cnt <= 11'd0;
					state <= IDLE;
				end
				else begin
					rdreq <= 1'b1;
					state <= DELY;
				end
					
			DELY:begin//空一拍延时,此状态FIFO_Q数据更新
				rdreq <= 1'b0;
				state <= SEND_HIGH;
			end
			
			SEND_HIGH:begin
				send_en <= 1'b1;
				send_data <= {4'd0,FIFO_Q[11:8]};//发送ADC高四位
				state <= SEND_LOW;
			end
			
			SEND_LOW:begin
				if(tx_done)begin//等待发送完成
					send_en <= 1'b1;
					send_data <= FIFO_Q[7:0];//发送ADC低八位
					state <= WAIT_SEND_DONE;
				end
				else begin
					state <= SEND_LOW;
					send_en <= 1'b0;
				end
			end
		
			WAIT_SEND_DONE:begin
				if(tx_done) begin//等待发送完成
					UART_Cnt <= UART_Cnt + 1'b1;
					state <= IDLE;
				end
				else begin
					state <= WAIT_SEND_DONE;
					send_en <= 1'b0;
				end
			end
			
			default:state <= IDLE;	
		endcase
	end

添加串口发送计数器UART_Cnt,计数达到采样次数时,所有数据发送完毕,Uart_done置1。

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Uart_done <= 1'b0;
	else if(UART_Cnt == UART_Cnt_MAX)
		Uart_done <= 1'b1;
	else
		Uart_done <= 1'b0;

fifo_uart_tx.v源代码:

module fifo_uart_tx(
	input Clk,						//系统时钟
	input Rst_n,					//系统复位
	input Start,					//开始发送数据标志位
	
	input empty,					//FIFO空标志位
	input [11:0] FIFO_Q,			//FIFO数据输入
	output reg rdreq,				//FIFO读使能
	output reg Uart_done,		//所有数据发送完毕
	output wire uart_tx 			//串口数据发送端
);

	parameter UART_Cnt_MAX = 11'd128; //发送数据个数

/*****模块间信号连线*****/	
	reg send_en;				//单次发送使能
	reg [7:0] send_data;		//单次发送数据
	wire tx_done;				//单次发送结束标志

/*****本模块内部寄存器、参数定义*****/		
	reg [10:0] UART_Cnt;		//发送128次计数器
	reg [4:0] state;
	localparam
		IDLE 				= 5'b00001,		//空闲状态
		DELY 				= 5'b00010,		//空一拍延时,等待FIFO_Q数据更新
		SEND_HIGH 		= 5'b00100,		//发送ADC高四位数据
		SEND_LOW 		= 5'b01000,		//发送ADC低八位数据
		WAIT_SEND_DONE = 5'b10000;		//等待发送结束

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n) begin
		rdreq <= 1'b0;
		send_en <= 1'b0;
		send_data <= 8'd0;
		UART_Cnt <= 11'd0;
		state <= IDLE;
	end
	else begin
		case(state)
			IDLE:
				if(empty == 1'b1) begin
					if(UART_Cnt == UART_Cnt_MAX)
						UART_Cnt <= 11'd0;
					state <= IDLE;
				end
				else begin
					rdreq <= 1'b1;
					state <= DELY;
				end
					
			DELY:begin//空一拍延时,此状态FIFO_Q数据更新
				rdreq <= 1'b0;
				state <= SEND_HIGH;
			end
			
			SEND_HIGH:begin
				send_en <= 1'b1;
				send_data <= {4'd0,FIFO_Q[11:8]};//发送ADC高四位
				state <= SEND_LOW;
			end
			
			SEND_LOW:begin
				if(tx_done)begin//等待发送完成
					send_en <= 1'b1;
					send_data <= FIFO_Q[7:0];//发送ADC低八位
					state <= WAIT_SEND_DONE;
				end
				else begin
					state <= SEND_LOW;
					send_en <= 1'b0;
				end
			end
		
			WAIT_SEND_DONE:begin
				if(tx_done) begin//等待发送完成
					UART_Cnt <= UART_Cnt + 1'b1;
					state <= IDLE;
				end
				else begin
					state <= WAIT_SEND_DONE;
					send_en <= 1'b0;
				end
			end
			
			default:state <= IDLE;	
		endcase
	end
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Uart_done <= 1'b0;
	else if(UART_Cnt == UART_Cnt_MAX)
		Uart_done <= 1'b1;
	else
		Uart_done <= 1'b0;
	
//串口发送模块
	uart_data_tx data_tx(   
		.Clk(Clk),
		.Rst_n(Rst_n),
		.send_en(send_en),
		.data(send_data),
		.baud_set(3'd2),		//波特率9600
		.tx(uart_tx),			//数据发送端
		.tx_done(tx_done)
	);
endmodule

将这两部分模块编写完成后,通过顶层模块将这两个模块和fifo模块连接,并通过仿真模拟AD采集过程和发送过程,顶层模块和系统仿真在下一个笔记中给出。

  • 8
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值