上篇文章详细介绍了ADS1256的芯片手册,本文来讲一下代码。注:一定要了解完芯片的手册后再来敲代码,否则会变得不幸。FPGA控制ADS1256的ADC采集(一)
硬件条件
也没啥硬件条件好说的了,一块FPGA板子加一个淘宝上的ADS1256模块(本人不甚擅长画板),再用杜邦线连个电源ok了。
软件编写
本次设计使用SPI通信,整体流程为:上电->复位->初始化->校准->检验->数据读取。所以可以设定状态如下所示:
localparam power_reset_state = 4'd1; //上电之后复位
localparam wait_reset_state = 4'd2; //等待复位结束
localparam ads1256_init_state = 4'd3; //寄存器初始化
localparam wait_calibration_state = 4'd4; //进行校准操作
localparam read_config_state = 4'd5; //读取配置信息
localparam wait_result_state = 4'd6; //等待DRDY变低
localparam read_result_mux_state = 4'd7; //通道选择状态
localparam write_config_wait_state = 4'd8; //等初始化结束
localparam read_result_sync_state = 4'd9; //第一同步状态
localparam read_result_wakeup_state = 4'd10; //第二唤醒状态
localparam read_result_rdata_state = 4'd11; //最终读取数据
看过上一篇文章的朋友一定知道,本设计为了节省资源,将绝大部分的延时时间以及间隔设定为5us,开了一个5us的时间计数器,再对5us进行计数,达到控制状态跳转的目的。
计数代码如下所示(注:重复代码可以合并,为了介绍方便,分开来写):
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
time_5us_cnt <= 8'd0;
else begin
case(curr_state)
power_reset_state:begin
if(time_5us_cnt == time_5us_cnt_max)
time_5us_cnt <= 8'd0;
else
time_5us_cnt <= time_5us_cnt + 1'd1;
end
ads1256_init_state:begin
if(time_5us_cnt == time_5us_cnt_max)
time_5us_cnt <= 8'd0;
else
time_5us_cnt <= time_5us_cnt + 1'd1;
end
read_config_state:begin
if(time_5us_cnt == time_5us_cnt_max)
time_5us_cnt <= 8'd0;
else
time_5us_cnt <= time_5us_cnt + 1'd1;
end
wait_calibration_state:begin
if(time_5us_cnt == time_5us_cnt_max)
time_5us_cnt <= 8'd0;
else
time_5us_cnt <= time_5us_cnt + 1'd1;
end
read_result_mux_state:begin
if(time_5us_cnt == time_5us_cnt_max)
time_5us_cnt <= 8'd0;
else
time_5us_cnt <= time_5us_cnt + 1'd1;
end
read_result_sync_state:begin
if(time_5us_cnt == time_5us_cnt_max)
time_5us_cnt <= 8'd0;
else
time_5us_cnt <= time_5us_cnt + 1'd1;
end
read_result_wakeup_state:begin
if(time_5us_cnt == time_5us_cnt_max)
time_5us_cnt <= 8'd0;
else
time_5us_cnt <= time_5us_cnt + 1'd1;
end
read_result_rdata_state:begin
if(time_5us_cnt == time_5us_cnt_max)
time_5us_cnt <= 8'd0;
else
time_5us_cnt <= time_5us_cnt + 1'd1;
end
default:time_5us_cnt <= 8'd0;
endcase
end
end
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
state_cnt <= 8'd0;
else begin
case(curr_state)
ads1256_init_state:begin
if(time_5us_cnt == time_5us_cnt_max)begin
if(state_cnt == 8'd97) //1 + 16 * 6 + 1 = 98次 - 1次 = 97次
state_cnt <= 8'd0;
else
state_cnt <= state_cnt + 1'd1;
end
else
state_cnt <= state_cnt;
end
read_config_state:begin
if(time_5us_cnt == time_5us_cnt_max)begin
if(state_cnt == 8'd101) //1 + 16 * 6 + 1 + 4= 102次 - 1次 = 101次
state_cnt <= 8'd0;
else
state_cnt <= state_cnt + 1'd1;
end
else
state_cnt <= state_cnt;
end
wait_calibration_state:begin
if(time_5us_cnt == time_5us_cnt_max)begin
if(state_cnt == 8'd17) //1 + 16 * 1 + 1= 18次 - 1次 = 17次
state_cnt <= 8'd0;
else
state_cnt <= state_cnt + 1'd1;
end
else
state_cnt <= state_cnt;
end
read_result_mux_state:begin
if(time_5us_cnt == time_5us_cnt_max)begin
if(state_cnt == 8'd49) //1 + 16 * 3 + 1= 50次 - 1次 = 49次
state_cnt <= 8'd0;
else
state_cnt <= state_cnt + 1'd1;
end
else
state_cnt <= state_cnt;
end
read_result_sync_state:begin
if(time_5us_cnt == time_5us_cnt_max)begin
if(state_cnt == 8'd17) //1 + 16 * 1 + 1= 18次 - 1次 = 17次
state_cnt <= 8'd0;
else
state_cnt <= state_cnt + 1'd1;
end
else
state_cnt <= state_cnt;
end
read_result_wakeup_state:begin
if(time_5us_cnt == time_5us_cnt_max)begin
if(state_cnt == 8'd17) //1 + 16 * 1 + 1= 18次 - 1次 = 17次
state_cnt <= 8'd0;
else
state_cnt <= state_cnt + 1'd1;
end
else
state_cnt <= state_cnt;
end
read_result_rdata_state:begin
if(time_5us_cnt == time_5us_cnt_max)begin
if(state_cnt == 8'd69) //1 + 16 * 4 + 1 + 4= 70次 - 1次 = 69次
state_cnt <= 8'd0;
else
state_cnt <= state_cnt + 1'd1;
end
else
state_cnt <= state_cnt;
end
default:state_cnt <= 8'd0;
endcase
end
end
我们只需要根据state_cnt的值就可以知道当前状态进行到哪一步了,而每一个状态表示除开等待DRDY变低的状态外,其他状态都表示一个指令周期,所以所需要等待的state_cnt的计数值为:1+16×指令和数据个数+1即可,如若该状态下需要读取数据还要+4表示延迟。前面的1表示CS拉低后1个5us周期我们才开始让SCK工作,后一个表示1最后一个SCK结束后等待5us我们才让CS拉高。所以我们得到如下状态跳转的代码:
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
curr_state <= power_reset_state;
else begin
case(curr_state)
power_reset_state:begin
if(time_5us_cnt == time_5us_cnt_max)
curr_state <= wait_reset_state;
else
curr_state <= power_reset_state;
end
wait_reset_state:begin
if(ads1256_drdy == 1'b0)
curr_state <= ads1256_init_state;
else
curr_state <= wait_reset_state;
end
ads1256_init_state:begin
if((state_cnt == 8'd97) && (time_5us_cnt == time_5us_cnt_max)) //1 + 16 * 6 + 1 = 98次 - 1次 = 97次
curr_state <= write_config_wait_state;
else
curr_state <= ads1256_init_state;
end
write_config_wait_state:begin
if(ads1256_drdy == 1'b0)
curr_state <= read_config_state;
else
curr_state <= write_config_wait_state;
end
read_config_state:begin
if((state_cnt == 8'd101) && (time_5us_cnt == time_5us_cnt_max)) //1 + 16 * 6 + 1 + 4= 102次 - 1次 = 101次
curr_state <= wait_calibration_state; //上面的4表示20us的命令——数据间隔
else
curr_state <= read_config_state;
end
wait_calibration_state:begin
if((state_cnt == 8'd17) && (time_5us_cnt == time_5us_cnt_max)) //1 + 16 * 1 + 1= 18次 - 1次 = 17次
curr_state <= wait_result_state;
else
curr_state <= wait_calibration_state;
end
wait_result_state:begin
if(ads1256_drdy == 1'b0)
curr_state <= read_result_mux_state;
else
curr_state <= wait_result_state;
end
read_result_mux_state:begin
if((state_cnt == 8'd49) && (time_5us_cnt == time_5us_cnt_max)) //1 + 16 * 3 + 1= 50次 - 1次 = 49次
curr_state <= read_result_sync_state;
else
curr_state <= read_result_mux_state;
end
read_result_sync_state:begin
if((state_cnt == 8'd17) && (time_5us_cnt == time_5us_cnt_max)) //1 + 16 * 1 + 1= 18次 - 1次 = 17次
curr_state <= read_result_wakeup_state;
else
curr_state <= read_result_sync_state;
end
read_result_wakeup_state:begin
if((state_cnt == 8'd17) && (time_5us_cnt == time_5us_cnt_max)) //1 + 16 * 1 + 1= 18次 - 1次 = 17次
curr_state <= read_result_rdata_state;
else
curr_state <= read_result_wakeup_state;
end
read_result_rdata_state:begin
if((state_cnt == 8'd69) && (time_5us_cnt == time_5us_cnt_max)) //1 + 16 * 4 + 1 + 4= 70次 - 1次 = 69次
curr_state <= wait_result_state;
else
curr_state <= read_result_rdata_state;
end
default:curr_state <= power_reset_state;
endcase
end
end
得到状态跳转的以及各个状态内部分执行后,就是输出的端口代码了,根据手册说明以及状态表述,下面的代码变顺理成章了:
- 复位信号
// 复位
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
ads1256_reset <= 1'b1;
else if(curr_state == power_reset_state)
ads1256_reset <= 1'b0;
else
ads1256_reset <= 1'b1;
end
- CS选通
//CS选通信号
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
spi_cs <= 1'b1;
else begin
case(curr_state)
ads1256_init_state:begin
if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd0))
spi_cs <= 1'b0;
else if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd97))
spi_cs <= 1'b1;
else
spi_cs <= spi_cs;
end
read_config_state:begin
if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd0))
spi_cs <= 1'b0;
else if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd101))
spi_cs <= 1'b1;
else
spi_cs <= spi_cs;
end
wait_calibration_state:begin
if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd0))
spi_cs <= 1'b0;
else if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd17))
spi_cs <= 1'b1;
else
spi_cs <= spi_cs;
end
read_result_mux_state:begin
if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd0))
spi_cs <= 1'b0;
else if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd49))
spi_cs <= 1'b1;
else
spi_cs <= spi_cs;
end
read_result_sync_state:begin
if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd0))
spi_cs <= 1'b0;
else if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd17))
spi_cs <= 1'b1;
else
spi_cs <= spi_cs;
end
read_result_wakeup_state:begin
if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd0))
spi_cs <= 1'b0;
else if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd17))
spi_cs <= 1'b1;
else
spi_cs <= spi_cs;
end
read_result_rdata_state:begin
if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd0))
spi_cs <= 1'b0;
else if((time_5us_cnt == time_5us_cnt_max) && (state_cnt == 8'd69))
spi_cs <= 1'b1;
else
spi_cs <= spi_cs;
end
default:spi_cs <= 1'b1;
endcase
end
end
- SCK时钟信号
//SCK
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
spi_sck <= 1'b0;
else begin
case(curr_state)
ads1256_init_state:begin
if((state_cnt > 8'd0) && (state_cnt < 8'd97))begin //0表示CS下降的计时5us,97表示CS上升的计时5us
if(time_5us_cnt == time_5us_cnt_max)
spi_sck <= ~spi_sck;
else
spi_sck <= spi_sck;
end
else
spi_sck <= 1'b0;
end
read_config_state:begin
if( ((state_cnt > 8'd0) && (state_cnt < 8'd33)) ||
((state_cnt > 8'd36) && (state_cnt < 8'd101)) )begin //33 34 35 36四次计数周期表示命令——数据间隔
if(time_5us_cnt == time_5us_cnt_max)
spi_sck <= ~spi_sck;
else
spi_sck <= spi_sck;
end
else
spi_sck <= 1'b0;
end
wait_calibration_state:begin
if((state_cnt > 8'd0) && (state_cnt < 8'd17))begin //0表示CS下降的计时5us,17表示CS上升的计时5us
if(time_5us_cnt == time_5us_cnt_max)
spi_sck <= ~spi_sck;
else
spi_sck <= spi_sck;
end
else
spi_sck <= 1'b0;
end
read_result_mux_state:begin
if((state_cnt > 8'd0) && (state_cnt < 8'd49))begin //0表示CS下降的计时5us,49表示CS上升的计时5us
if(time_5us_cnt == time_5us_cnt_max)
spi_sck <= ~spi_sck;
else
spi_sck <= spi_sck;
end
else
spi_sck <= 1'b0;
// if( ((state_cnt > 8'd0) && (state_cnt < 8'd97)) ||
// ((state_cnt > 8'd100) && (state_cnt < 8'd149)) )begin //97 98 99 100四次计数周期表示命令--数据间隔
// if(time_5us_cnt == time_5us_cnt_max)
// spi_sck <= ~spi_sck;
// else
// spi_sck <= spi_sck;
// end
// else
// spi_sck <= 1'b0;
end
read_result_sync_state:begin
if((state_cnt > 8'd0) && (state_cnt < 8'd17))begin //0表示CS下降的计时5us,17表示CS上升的计时5us
if(time_5us_cnt == time_5us_cnt_max)
spi_sck <= ~spi_sck;
else
spi_sck <= spi_sck;
end
else
spi_sck <= 1'b0;
end
read_result_wakeup_state:begin
if((state_cnt > 8'd0) && (state_cnt < 8'd17))begin //0表示CS下降的计时5us,17表示CS上升的计时5us
if(time_5us_cnt == time_5us_cnt_max)
spi_sck <= ~spi_sck;
else
spi_sck <= spi_sck;
end
else
spi_sck <= 1'b0;
end
read_result_rdata_state:begin
if( ((state_cnt > 8'd0) && (state_cnt < 8'd17)) ||
((state_cnt > 8'd20) && (state_cnt < 8'd69)) )begin //17 18 19 20四次计数周期表示命令--数据间隔
if(time_5us_cnt == time_5us_cnt_max)
spi_sck <= ~spi_sck;
else
spi_sck <= spi_sck;
end
else
spi_sck <= 1'b0;
end
default:spi_sck <= 1'b0;
endcase
end
end
- MOSI数据输出
下述代码中有一个curr_data表示数据锁存,根据锁存的data进行移位操作,移出MOSI。
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
curr_data <= 8'd0;
else begin
case(curr_state)
ads1256_init_state:begin
case(state_cnt)
8'd0: curr_data <= command_wreg | 8'h00;
8'd16: curr_data <= 8'h03;
8'd32: curr_data <= register_status_value;
8'd48: curr_data <= register_Multiplexer_value;
8'd64: curr_data <= register_pga_value;
8'd80: curr_data <= register_Data_Rate_value;
default:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
curr_data <= {curr_data[6:0], 1'b0};
else
curr_data <= curr_data;
end
endcase
end
read_config_state:begin
case(state_cnt)
8'd0: curr_data <= command_rreg | 8'h00;
8'd16: curr_data <= 8'h03;
default:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
curr_data <= {curr_data[6:0], 1'b0};
else
curr_data <= curr_data;
end
endcase
end
wait_calibration_state:begin
case(state_cnt)
8'd0: curr_data <= command_selfcal;
default:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
curr_data <= {curr_data[6:0], 1'b0};
else
curr_data <= curr_data;
end
endcase
end
read_result_mux_state:begin
case(state_cnt)
8'd0: curr_data <= command_wreg | 8'h01;
8'd16: curr_data <= 8'h00;
8'd32: curr_data <= register_Multiplexer_value;
default:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
curr_data <= {curr_data[6:0], 1'b0};
else
curr_data <= curr_data;
end
endcase
// case(state_cnt)
// 8'd0: curr_data <= command_wreg | 8'h01;
// 8'd16: curr_data <= 8'h00;
// 8'd32: curr_data <= register_Multiplexer_value;
// 8'd48: curr_data <= command_sync;
// 8'd64: curr_data <= command_wakeup;
// 8'd80: curr_data <= command_rdata;
// default:begin
// if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
// curr_data <= {curr_data[6:0], 1'b0};
// else
// curr_data <= curr_data;
// end
// endcase
end
read_result_sync_state:begin
case(state_cnt)
8'd0: curr_data <= command_sync;
default:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
curr_data <= {curr_data[6:0], 1'b0};
else
curr_data <= curr_data;
end
endcase
end
read_result_wakeup_state:begin
case(state_cnt)
8'd0: curr_data <= command_wakeup;
default:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
curr_data <= {curr_data[6:0], 1'b0};
else
curr_data <= curr_data;
end
endcase
end
read_result_rdata_state:begin
case(state_cnt)
8'd0: curr_data <= command_rdata;
default:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
curr_data <= {curr_data[6:0], 1'b0};
else
curr_data <= curr_data;
end
endcase
end
default:curr_data <= 8'd0;
endcase
end
end
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)
spi_mosi <= 1'b0;
else begin
case(curr_state)
ads1256_init_state:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
spi_mosi <= curr_data[7];
else
spi_mosi <= spi_mosi;
end
read_config_state:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
spi_mosi <= curr_data[7];
else
spi_mosi <= spi_mosi;
end
wait_calibration_state:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
spi_mosi <= curr_data[7];
else
spi_mosi <= spi_mosi;
end
read_result_mux_state:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
spi_mosi <= curr_data[7];
else
spi_mosi <= spi_mosi;
end
read_result_sync_state:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
spi_mosi <= curr_data[7];
else
spi_mosi <= spi_mosi;
end
read_result_wakeup_state:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
spi_mosi <= curr_data[7];
else
spi_mosi <= spi_mosi;
end
read_result_rdata_state:begin
if((spi_sck == 1'b0) && (time_5us_cnt == time_5us_cnt_max))
spi_mosi <= curr_data[7];
else
spi_mosi <= spi_mosi;
end
default:spi_mosi <= 1'b0;
endcase
end
end
- 数据读取
将MISO的数据读进check(检查寄存器写入是否正常)以及24位的结果寄存器中即可。
always @(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)begin
register_check <= 32'd0;
ad1256_result <= 24'd0;
end
else begin
case(curr_state)
read_config_state:begin
if((state_cnt > 8'd37) && (state_cnt < 8'd101))begin
if((spi_sck == 1'b1) && (time_5us_cnt == time_5us_cnt_max))
register_check <= {register_check[30:0], spi_miso};
else
register_check <= register_check;
end
else
register_check <= register_check;
end
read_result_rdata_state:begin
if((state_cnt > 8'd20) && (state_cnt < 8'd69))begin
if((spi_sck == 1'b1) && (time_5us_cnt == time_5us_cnt_max))
ad1256_result <= {ad1256_result[22:0], spi_miso};
else
ad1256_result <= ad1256_result;
end
else
ad1256_result <= ad1256_result;
end
default:begin
register_check <= register_check;
ad1256_result <= ad1256_result;
end
endcase
end
end
结果测试
老规矩,这篇讲解代码,下一篇介绍一下工程测量结果并附上工程代码。