digital-to-analog conversion: DAC, analog-to-digital conversion: ADC – Xilinx Risc-V Board Tutorial : AD, DA Experiment – FII-PRX100 FPGA Board Experiment 13
Experiment 13 AD, DA Experiment
1.Experiment Objective
Since in the real world, all naturally occurring signals are analog signals, and all that are read and processed in actual engineering are digital signals. There is a process of mutual conversion between natural and industrial signals (digital-to-analog conversion: DAC, analog-to-digital conversion: ADC). The purpose of this experiment is twofold:
- Learning the theory of AD conversion
- Read the value of AD acquisition from PCF8591, and convert the value obtained into actual value, display it with segment decoders
2.Experiment Requirement
Perform analog-to-digital conversion using the ADC port of the chip and display the collected voltage value through the segment decoders.
Board downloading verification for comparison
Introduction to PCF8591: The PCF8591 uses the IIC bus protocol to communicate with the controller (FPGA). Please refer to the previous experiment for the contents of the IIC bus protocol. The first four bits of the device address are 1001, and the last three bits are determined by the actual circuit connection (here the circuit is grounded, so the device address is 7’b1001000). The LSB is the read/write control signal. After sending the device address information and the read/write control word are done, the control word information is sent. The specific control word information is shown in Fig 13. 1.
Fig 13. 1 PCF8591 Control address
Here, the experiment uses the DIP switch (SW1, SW0) input channel as the AD acquisition input channel. Configure the control information as (8’h40). For more details, refer to the datasheet of PCF8591.
SW1,SW0 | Channel Selection | Collection Object |
00 | 0 | Photosensitive Resistor Voltage Value |
01 | 1 | Thermistor Voltage Value |
10 | 2 | Adjustable Voltage Value |
3.Experiment Design
- Program design and review the top-down design method used before.
- The top-level entity is divided into three parts: the segment decoder driver part, the AD sampling part of the PCF and the IIC serial port driver part.
IIC serial driver part code is as follows:
module iic_com (
clk,rst_n, data, sw1,sw2, scl,sda, iic_done, dis_data ); input clk; // 50MHz input rst_n; input sw1,sw2; inout scl; inout sda; output reg [7:0] dis_data=8’h00; input [7:0] data ; output reg iic_done =0 ; reg [7:0] data_tep; reg scl_link ;
reg [19:0] cnt_5ms ; reg sw1_r,sw2_r; reg[19:0] cnt_20ms; always @ (posedge clk or negedge rst_n) if(!rst_n) cnt_20ms <= 20’d0; else cnt_20ms <= cnt_20ms+1’b1; always @ (posedge clk or negedge rst_n) if(!rst_n) begin sw1_r <= 1’b1; sw2_r <= 1’b1; end else if(cnt_20ms == 20’hfffff) begin sw1_r <= sw1; sw2_r <= sw2; end reg[2:0] cnt; reg[8:0] cnt_delay; reg scl_r; reg [7:0] read_data_temp [15:0]; reg [11:0] dis_data_temp ; always @ (posedge clk ) dis_data_temp<=read_data_temp[0]+read_data_temp[1]+read_data_temp[2]+read_data_temp[3]+read_data_temp[4]+read_data_temp[5]+read_data_temp[6]+read_data_temp[7]+read_data_temp[8]+read_data_temp[9]+read_data_temp[10]+read_data_temp[11]+read_data_temp[12]+read_data_temp[13]+read_data_temp[14]+read_data_temp[15];
always @ (posedge clk ) dis_data <= dis_data_temp>>4 ; integer i; always @ (posedge clk or negedge rst_n) if(!rst_n) begin for (i=0;i<16;i=i+1) read_data_temp[i]<=8’h00; end else if (iic_done) begin for (i=0;i<15;i=i+1) read_data_temp[i+1]<=read_data_temp[i]; read_data_temp[0] <= read_data ; end else begin for (i=0;i<16;i=i+1) read_data_temp[i]<=read_data_temp[i]; end always @ (posedge clk or negedge rst_n) if(!rst_n) cnt_delay <= 9’d0; else if(cnt_delay == 9’d499) cnt_delay <= 9’d0; else cnt_delay <= cnt_delay+1’b1; always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 3’d5; else begin case (cnt_delay) 9’d124: cnt <= 3’d1; //cnt=1:scl 9’d249: cnt <= 3’d2; //cnt=2:scl 9’d374: cnt <= 3’d3; //cnt=3:scl 9’d499: cnt <= 3’d0; //cnt=0:scl default: cnt<=3’d5; endcase end end `define SCL_POS (cnt==3’d0) //cnt=0:scl `define SCL_HIG (cnt==3’d1) //cnt=1:scl `define SCL_NEG (cnt==3’d2) //cnt=2:scl `define SCL_LOW (cnt==3’d3) //cnt=3:scl always @ (posedge clk or negedge rst_n) if(!rst_n) data_tep <= 8’h00; else data_tep<= data ; // always @ (posedge clk or negedge rst_n) if(!rst_n) scl_r <= 1’b0; else if(cnt==3’d0) scl_r <= 1’b1; //scl else if(cnt==3’d2) scl_r <= 1’b0; //scl assign scl = scl_link?scl_r: 1’bz ; `define DEVICE_READ {7’h48,1’b1} `define DEVICE_WRITE {7’h48,1’b0} `define WRITE_DATA 8’b1000_0001 `define BYTE_ADDR 8’b0000_0011 reg[7:0] db_r; reg[7:0] read_data; parameter IDLE = 4’d0; parameter START1 = 4’d1; parameter ADD1 = 4’d2; parameter ACK1 = 4’d3; parameter ADD2 = 4’d4; parameter ACK2 = 4’d5; parameter START2 = 4’d6; parameter ADD3 = 4’d7; parameter ACK3 = 4’d8; parameter DATA = 4’d9; parameter ACK4 = 4’d10; parameter STOP1 = 4’d11; parameter STOP2 = 4’d12; reg[3:0] cstate; reg sda_r; reg sda_link; reg[3:0] num; always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin cstate <= IDLE; sda_r <= 1’b1; scl_link <= 1’b1; sda_link <= 1’b1; num <= 4’d0; read_data <= 8’b0000_0000; cnt_5ms <=20’h00000 ; iic_done<=1’b0 ; end else case (cstate) IDLE: begin sda_link <= 1’b1; scl_link <= 1’b1; iic_done<=1’b0 ; if(!sw1_r || !sw2_r) begin db_r <= `DEVICE_WRITE; cstate <= START1; end else cstate <= IDLE; end START1: begin if(`SCL_HIG) begin sda_link <= 1’b1; sda_r <= 1’b0; cstate <= ADD1; num <= 4’d0; end else cstate <= START1; end ADD1: begin if(`SCL_LOW) begin if(num == 4’d8) begin num <= 4’d0; sda_r <= 1’b1; sda_link <= 1’b0; cstate <= ACK1; end else begin cstate <= ADD1; num <= num+1’b1; case (num) 4’d0: sda_r <= db_r[7]; 4’d1: sda_r <= db_r[6]; 4’d2: sda_r <= db_r[5]; 4’d3: sda_r <= db_r[4]; 4’d4: sda_r <= db_r[3]; 4’d5: sda_r <= db_r[2]; 4’d6: sda_r <= db_r[1]; 4’d7: sda_r <= db_r[0]; default: ; endcase // sda_r <= db_r[4’d7-num]; end end // else if(`SCL_POS) db_r <= {db_r[6:0],1’b0}; else cstate <= ADD1; end ACK1: begin if(/*!sda*/`SCL_NEG) begin cstate <= ADD2; db_r <= {6’b0100_00,data_tep[1:0]}; end else cstate <= ACK1; end ADD2: begin if(`SCL_LOW) begin if(num==4’d8) begin num <= 4’d0; sda_r <= 1’b1; sda_link <= 1’b0; cstate <= ACK2;
end else begin sda_link <= 1’b1; num <= num+1’b1; case (num) 4’d0: sda_r <= db_r[7]; 4’d1: sda_r <= db_r[6]; 4’d2: sda_r <= db_r[5]; 4’d3: sda_r <= db_r[4]; 4’d4: sda_r <= db_r[3]; 4’d5: sda_r <= db_r[2]; 4’d6: sda_r <= db_r[1]; 4’d7: sda_r <= db_r[0]; default: ; endcase // sda_r <= db_r[4’d7-num]; cstate <= ADD2; end end // else if(`SCL_POS) db_r <= {db_r[6:0],1’b0}; else cstate <= ADD2; end ACK2: begin if(/*!sda*/`SCL_NEG) begin if(!sw1_r) begin cstate <= DATA; db_r <= data_tep; end else if(!sw2_r) begin db_r <= `DEVICE_READ; cstate <= START2; end end else cstate <= ACK2; end START2: begin if(`SCL_LOW) begin sda_link <= 1’b1; sda_r <= 1’b1; cstate <= START2; end else if(`SCL_HIG) begin sda_r <= 1’b0; cstate <= ADD3; end else cstate <= START2; end ADD3: begin if(`SCL_LOW) begin if(num==4’d8) begin num <= 4’d0; sda_r <= 1’b1; sda_link <= 1’b0; cstate <= ACK3; end else begin num <= num+1’b1; case (num) 4’d0: sda_r <= db_r[7]; 4’d1: sda_r <= db_r[6]; 4’d2: sda_r <= db_r[5]; 4’d3: sda_r <= db_r[4]; 4’d4: sda_r <= db_r[3]; 4’d5: sda_r <= db_r[2]; 4’d6: sda_r <= db_r[1]; 4’d7: sda_r <= db_r[0]; default: ; endcase // sda_r <= db_r[4’d7-num]; cstate <= ADD3; end end // else if(`SCL_POS) db_r <= {db_r[6:0],1’b0}; else cstate <= ADD3; end ACK3: begin if(/*!sda*/`SCL_NEG) begin cstate <= DATA; sda_link <= 1’b0; end else cstate <= ACK3; end DATA: begin if(!sw2_r) begin if(num<=4’d7) begin cstate <= DATA; if(`SCL_HIG) begin num <= num+1’b1; case (num) 4’d0: read_data[7] <= sda; 4’d1: read_data[6] <= sda; 4’d2: read_data[5] <= sda; 4’d3: read_data[4] <= sda; 4’d4: read_data[3] <= sda; 4’d5: read_data[2] <= sda; 4’d6: read_data[1] <= sda; 4’d7: read_data[0] <= sda; default: ; endcase // read_data[4’d7-num] <= sda; end // else if(`SCL_NEG) read_data <= {read_data[6:0],read_data[7]}; end else if((`SCL_LOW) && (num==4’d8)) begin num <= 4’d0; cstate <= ACK4; end else cstate <= DATA; end else if(!sw1_r) begin sda_link <= 1’b1; if(num<=4’d7) begin cstate <= DATA; if(`SCL_LOW) begin sda_link <= 1’b1; num <= num+1’b1; case (num) 4’d0: sda_r <= db_r[7]; 4’d1: sda_r <= db_r[6]; 4’d2: sda_r <= db_r[5]; 4’d3: sda_r <= db_r[4]; 4’d4: sda_r <= db_r[3]; 4’d5: sda_r <= db_r[2]; 4’d6: sda_r <= db_r[1]; 4’d7: sda_r <= db_r[0]; default: ; endcase // sda_r <= db_r[4’d7-num]; end // else if(`SCL_POS) db_r <= {db_r[6:0],1’b0}; end else if((`SCL_LOW) && (num==4’d8)) begin num <= 4’d0; sda_r <= 1’b1; sda_link <= 1’b0; cstate <= ACK4; end else cstate <= DATA; end end ACK4: begin if(/*!sda*/`SCL_NEG) begin // sda_r <= 1’b1; cstate <= STOP1; end else cstate <= ACK4; end STOP1: begin if(`SCL_LOW) begin sda_link <= 1’b1; sda_r <= 1’b0; cstate <= STOP1; end else if(`SCL_HIG) begin sda_r <= 1’b1; cstate <= STOP2; end else cstate <= STOP1; end STOP2: begin if(`SCL_NEG) begin sda_link <= 1’b0; scl_link <= 1’b0; end else if(cnt_5ms==20’h3fffc) begin cstate <= IDLE; cnt_5ms<=20’h00000; iic_done<=1 ; end else begin cstate <= STOP2 ; cnt_5ms<=cnt_5ms+1 ; end end default: cstate <= IDLE; endcase end assign sda = sda_link ? sda_r:1’bz; endmodule |
Segment decoder driver part is as follow:
module led_seg7(
clk,rst_n, dis_data, point1 , sel,sm_db ); input clk; input rst_n; input[7:0] dis_data; output reg [5:0] sel; output[6:0] sm_db; output reg point1 ; reg[11:0] cnt1; reg[2:0] cnt; reg [3:0] num; wire en=(cnt1==12’hfff) ?1:0 ; parameter V_REF = 12’d3300 ; reg [19:0] num_t ; reg [19:0] num1 ; always @ (posedge clk) num_t <= V_REF *dis_data ; wire [3:0] data0 ; wire [3:0] data1 ; wire [3:0] data2 ; wire [3:0] data3 ; wire [3:0] data4 ; wire [3:0] data5 ; assign data5 = num1 / 17’d100000; assign data4 = num1 / 14’d10000 % 4’d10; assign data3 = num1 / 10’d1000 % 4’d10 ; assign data2 = num1 / 7’d100 % 4’d10 ; assign data1 = num1 / 4’d10 % 4’d10 ; assign data0 = num1 % 4’d10; always @(posedge clk or negedge rst_n) begin if(rst_n == 1’b0) begin num1 <= 20’d0; end else num1 <= num_t >> 4’d8; end always @ (posedge clk or negedge rst_n) if(!rst_n) cnt1 <= 4’d0; else cnt1 <= cnt1+1’b1;
parameter seg0 = 7’h3f, seg1 = 7’h06, seg2 = 7’h5b, seg3 = 7’h4f, seg4 = 7’h66, seg5 = 7’h6d, seg6 = 7’h7d, seg7 = 7’h07, seg8 = 7’h7f, seg9 = 7’h6f, sega = 7’h77, segb = 7’h7c, segc = 7’h39, segd = 7’h5e, sege = 7’h79, segf = 7’h71; reg[6:0] sm_dbr;
always @ (*) case (num) 4’h0: sm_dbr = seg0; 4’h1: sm_dbr = seg1; 4’h2: sm_dbr = seg2; 4’h3: sm_dbr = seg3; 4’h4: sm_dbr = seg4; 4’h5: sm_dbr = seg5; 4’h6: sm_dbr = seg6; 4’h7: sm_dbr = seg7; 4’h8: sm_dbr = seg8; 4’h9: sm_dbr = seg9; 4’ha: sm_dbr = sega; 4’hb: sm_dbr = segb; 4’hc: sm_dbr = segc; 4’hd: sm_dbr = segd; 4’he: sm_dbr = sege; 4’hf: sm_dbr = segf; default: ; endcase assign sm_db = sm_dbr; always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin sel <= 6’b000000; num <= 4’b0; cnt<=3’b000; end else begin
case (cnt) 3’d0 :begin sel <= 6’b111111; num <= data5 ; point1 <= 1’b1 ; if(en) cnt<=3’d1 ; end 3’d1 :begin sel <= 6’b111111; num <= data4 ; point1 <=1’b1 ; if(en) cnt<=3’d2 ;
end 3’d2 :begin sel <= 6’b111011; num <= data3; point1 <= 1’b0 ; if(en) cnt<=3’d3 ;
end 3’d3 :begin sel <= 6’b110111; num <= data2; point1 <= 1’b1 ; if(en) cnt<=3’d4 ;
end 3’d4 :begin sel <= 6’b101111; num <= data1; point1 <=1’b1;
if(en) cnt<=3’d5 ;
end 3’d5 :begin sel <= 6’b011111; num <= data0; point1 <=1’b1; if(en) cnt<=3’d0 ;
end default :begin sel <= 6’b000000; num <= 4’h0; point1 <= 1’b1; end endcase
end end endmodule |
4.Downloading to The Board
-
-
- Lock the pins
-
SignaL Name | Port Description | Network Label | FPGA Pin |
clk | System clock, 50 MHz | C10_50MCLK | U22 |
rst_n | Reset, high by default | KEY1 | M4 |
sm_db[0] | Segment a | SEG_PA | K26 |
sm_db [1] | Segment b | SEG_PB | M20 |
sm_db [2] | Segment c | SEG_PC | L20 |
sm_db [3] | Segment d | SEG_PD | N21 |
sm_db [4] | Segment e | SEG_PE | N22 |
sm_db [5] | Segment f | SEG_PF | P21 |
sm_db [6] | Segment g | SEG_PG | P23 |
sm_db [7] | Segment h | SEG_DP | P24 |
data[0] | Switch input | GPIO_DIP_SW0 | N8 |
data[1] | Switch input | GPIO_DIP_SW1 | M5 |
data[2] | Switch input | GPIO_DIP_SW2 | P4 |
data[3] | Switch input | GPIO_DIP_SW3 | N4 |
data[4] | Switch input | GPIO_DIP_SW4 | U6 |
data[5] | Switch input | GPIO_DIP_SW5 | U5 |
data[6] | Switch input | GPIO_DIP_SW6 | R8 |
data[7] | Switch input | GPIO_DIP_SW7 | P8 |
scl | PCF8591 clock | ADDA_I2C_SCl | E20 |
sda | PCF8591 data line | ADDA_I2C_SDA | C19 |
sel[0] | Segment decoder position selection | SEG_3V3_D0 | R16 |
sel[1] | Segment decoder position selection | SEG_3V3_D1 | R17 |
sel[2] | Segment decoder position selection | SEG_3V3_D2 | N18 |
sel[3] | Segment decoder position selection | SEG_3V3_D3 | K25 |
sel[4] | Segment decoder position selection | SEG_3V3_D4 | R25 |
sel[5] | Segment decoder position selection | SEG_3V3_D5 | T24 |
-
-
- Testing by selecting SW0 and SW1 to change the measurement objects.
-
Fig 13. 2 Test result