├── .gitattributes ├── Digital PI Modules ├── Error_Calculator.v ├── PI_Controller.v ├── control_output.v └── deltaU.v ├── PMOD Controllers ├── dac_spi.v └── newadctest.v ├── README.md ├── Testbench └── final_tb.v └── Wrapper + Saturator Modules ├── comparator.v └── top.v /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Digital PI Modules/Error_Calculator.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // Company: 4 | // Engineer: 5 | // 6 | // Create Date: 08/21/2018 03:58:27 PM 7 | // Design Name: 8 | // Module Name: Error_Calculator 9 | // Project Name: 10 | // Target Devices: 11 | // Tool Versions: 12 | // Description: 13 | // 14 | // Dependencies: 15 | // 16 | // Revision: 17 | // Revision 0.01 - File Created 18 | // Additional Comments: 19 | // 20 | ////////////////////////////////////////////////////////////////////////////////// 21 | 22 | 23 | module Error_Calculator(clk,reset_b,adc_in_0,adc_in_1,error); 24 | input clk; 25 | input reset_b; 26 | input [15:0] adc_in_0; 27 | input [15:0] adc_in_1; 28 | output reg signed [15:0] error; 29 | 30 | 31 | always @ (posedge clk) 32 | begin 33 | if (!reset_b) 34 | error <= 0; 35 | else 36 | begin 37 | error <= adc_in_0 - adc_in_1; 38 | end 39 | end 40 | 41 | endmodule 42 | -------------------------------------------------------------------------------- /Digital PI Modules/PI_Controller.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | // Recursive Digital PI Implementation in Verilog 4 | 5 | //Methodology 6 | // - FSM to allow sequential processing of data samples from the ADC 7 | // - Uses an incremental PI form to compute increments to the control output 8 | // - Clips the integrator portion to a pre-determined voltage to prevent integrator windup 9 | 10 | 11 | 12 | module PI_Controller(clk,reset_b,adc_in_0,adc_in_1,adc_done,u,pi_done); 13 | 14 | parameter IDLE = 0000; 15 | parameter readADC = 0001; 16 | parameter computeError = 0002; 17 | parameter computeDeltaU = 0003; 18 | parameter computeU = 0004; 19 | parameter latchDAC = 0005; 20 | 21 | input clk; 22 | input reset_b; 23 | input [15:0] adc_in_0; 24 | input [15:0] adc_in_1; 25 | input adc_done; 26 | output reg signed [15:0] u; 27 | output reg pi_done; 28 | 29 | //Local Variables 30 | wire signed [15:0] error_bus; 31 | wire signed [15:0] deltaU_bus; 32 | wire signed [15:0] controlOutput_bus; 33 | wire signed [15:0] u_out_bus; 34 | 35 | 36 | reg [3:0] current_state; 37 | reg [3:0] next_state; 38 | reg signed [15:0] error; 39 | reg signed [15:0] error_prev; 40 | reg signed [15:0] deltaU; 41 | reg signed [15:0] u_prev; 42 | 43 | 44 | //Module Instantiations 45 | 46 | Error_Calculator u_ErrorCalculator 47 | ( 48 | .clk(clk), 49 | .reset_b(reset_b), 50 | .adc_in_0(adc_in_0), 51 | .adc_in_1(adc_in_1), 52 | .error(error_bus) 53 | ); 54 | 55 | deltaU u_deltaU 56 | ( 57 | .error(error), 58 | .errorPrev(error_prev), 59 | .deltaU(deltaU_bus) 60 | ); 61 | 62 | control_output u_control_output 63 | ( 64 | .clk(clk), 65 | .reset_b(reset_b), 66 | .state(current_state), 67 | .delta_u(deltaU), 68 | .u_prev(u_prev), 69 | .u_out(u_out_bus) 70 | ); 71 | 72 | //FSM 73 | always @ (posedge clk) 74 | begin 75 | if (!reset_b) 76 | current_state <= IDLE; 77 | else 78 | current_state <= next_state; 79 | end 80 | 81 | always @ (posedge clk) 82 | begin 83 | 84 | if (!reset_b) 85 | next_state <= IDLE; 86 | 87 | else 88 | begin 89 | 90 | case (current_state) 91 | 92 | IDLE: if (adc_done) 93 | begin 94 | next_state <= readADC; 95 | end 96 | else 97 | next_state <= IDLE; 98 | 99 | readADC: next_state <= computeError; 100 | computeError: next_state <= computeDeltaU; 101 | computeDeltaU: next_state <= computeU; 102 | computeU: next_state <= latchDAC; 103 | latchDAC: next_state <= IDLE; 104 | default: next_state <= IDLE; 105 | 106 | endcase 107 | end 108 | end 109 | 110 | always @ (posedge clk) 111 | begin 112 | 113 | if (!reset_b) 114 | begin 115 | pi_done <= 0; 116 | u <= 0; 117 | end 118 | 119 | else 120 | begin 121 | 122 | if (current_state == IDLE) 123 | begin 124 | pi_done <= 1; 125 | u <= u; 126 | end 127 | else if (current_state == readADC) 128 | begin 129 | pi_done <= 0; 130 | end 131 | else if (current_state == computeError) 132 | begin 133 | error <= error_bus; 134 | error_prev <= error; 135 | end 136 | else if (current_state == computeDeltaU) 137 | begin 138 | deltaU <= deltaU_bus; 139 | end 140 | else if (current_state == computeU) 141 | begin 142 | u <= u_out_bus; 143 | u_prev <= u; 144 | end 145 | else if (current_state <= latchDAC) 146 | begin 147 | pi_done <= 1'b1; 148 | end 149 | end 150 | end 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | endmodule 165 | -------------------------------------------------------------------------------- /Digital PI Modules/control_output.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // Company: 4 | // Engineer: 5 | // 6 | // Create Date: 08/31/2018 10:31:19 AM 7 | // Design Name: 8 | // Module Name: control_output 9 | // Project Name: 10 | // Target Devices: 11 | // Tool Versions: 12 | // Description: 13 | // 14 | // Dependencies: 15 | // 16 | // Revision: 17 | // Revision 0.01 - File Created 18 | // Additional Comments: 19 | // 20 | ////////////////////////////////////////////////////////////////////////////////// 21 | 22 | 23 | module control_output(clk,reset_b,state,delta_u,u_prev,u_out); 24 | 25 | input clk; 26 | input reset_b; 27 | input [3:0] state; 28 | input signed [15:0] delta_u; 29 | input signed [15:0] u_prev; 30 | output reg signed [15:0] u_out; 31 | 32 | parameter computeU = 0003; 33 | parameter integratorClip = 11'd181; 34 | 35 | always @ (posedge clk) 36 | begin 37 | 38 | if (!reset_b) 39 | u_out <= 0; 40 | 41 | else 42 | begin 43 | if (state == computeU) 44 | begin 45 | if (u_prev <= integratorClip && u_prev >= 0) 46 | u_out <= delta_u + u_prev; 47 | else if (u_prev < 0) 48 | u_out <= delta_u + 0; 49 | else if (u_prev > integratorClip) 50 | u_out <= delta_u + integratorClip; 51 | else 52 | u_out <= 0; 53 | end 54 | else 55 | u_out <= u_out; 56 | end 57 | end 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | endmodule 69 | -------------------------------------------------------------------------------- /Digital PI Modules/deltaU.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | ////////////////////////////////////////////////////////////////////////////////// 3 | // Company: 4 | // Engineer: 5 | // 6 | // Create Date: 08/29/2018 12:51:26 PM 7 | // Design Name: 8 | // Module Name: deltaU 9 | // Project Name: 10 | // Target Devices: 11 | // Tool Versions: 12 | // Description: 13 | // 14 | // Dependencies: 15 | // 16 | // Revision: 17 | // Revision 0.01 - File Created 18 | // Additional Comments: 19 | // 20 | ////////////////////////////////////////////////////////////////////////////////// 21 | 22 | 23 | module deltaU(error,errorPrev,deltaU); 24 | 25 | input signed [15:0] error; 26 | input signed [15:0] errorPrev; 27 | output signed [15:0] deltaU; 28 | 29 | parameter KpKi = 3; 30 | parameter KiKp = 1; 31 | 32 | assign deltaU = (KpKi * error) + (KiKp * errorPrev); 33 | 34 | endmodule 35 | -------------------------------------------------------------------------------- /PMOD Controllers/dac_spi.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | //Verilog Controller for a SPI-based Digital to Analog Converter 4 | 5 | //Functionality: 6 | // - Sends out Chip Select and Serial Clock signals to control the Converter 7 | // - Reads in 12 bit digital samples and shifts it out serially on the positive edge of the serial clock signal 8 | // - Finite State Machines to ensure that the controller only sends out samples that are fully read in and in a synchronized manner 9 | 10 | //Methodology 11 | //Uses a FSM to control the DAC 12 | // IDLE State 13 | // - The dac_cs signal is set high to pause DAC operations 14 | // - The parLoad_en signal is set high to read in digital samples from the outside modules 15 | // - The dac_done signal is set high to allow digital samples to be read in 16 | 17 | //SHIFT_OUT state 18 | // - The controller shifts out bits of the digital signal in a serial manner 19 | // - Operates on the positive edge of the serial clock signal, takes roughly 16 SClk signals to send a sample through 20 | // - The parLoad_en and dac_done signals are set low to prevent samples from being read in 21 | 22 | //SYNC_DATA 23 | // - This state acts as a buffer to allow the FFs to latch properly 24 | 25 | 26 | 27 | module spi2dac (clk,reset_b,dac_par_in, dac_cs, dac_sclk, dac_mosi); 28 | 29 | input clk; 30 | input reset_b; 31 | input [11:0] dac_par_in; 32 | output dac_cs; 33 | output dac_sclk; 34 | output dac_mosi; 35 | 36 | 37 | reg dac_cs; 38 | wire dac_out; 39 | 40 | reg clk_1MHZdac; 41 | reg [5:0] ctrdac; 42 | 43 | 44 | parameter TC = 6'd63; 45 | parameter control_vector = 4'b0000; 46 | 47 | //State Parameters 48 | parameter IDLE = 4'd0000; 49 | parameter SHIFT_OUT = 4'd0001; 50 | parameter SYNC_DATA = 4'd0002; 51 | reg tick_dac; 52 | 53 | //Clock Divider Module - Takes the FPGA's 100 MHZ clock and creates a derivative 1.2 MHZ clock to be used for the DAC 54 | always @ (posedge clk) 55 | if (!reset_b) 56 | begin 57 | ctrdac <= 0; 58 | tick_dac <= 0; 59 | clk_1MHZdac <= 0; 60 | end 61 | else 62 | begin 63 | ctrdac <= ctrdac - 1'b1; 64 | tick_dac <= 0; 65 | if (ctrdac == 63) 66 | begin 67 | tick_dac <= 1'b1; 68 | clk_1MHZdac <= ~clk_1MHZdac; 69 | end 70 | else if (ctrdac == 31) 71 | begin 72 | clk_1MHZdac <= ~clk_1MHZdac; 73 | end 74 | end 75 | 76 | assign dac_sclk = clk_1MHZdac; 77 | 78 | //Counter Module 79 | 80 | reg parLoad_en; 81 | reg shift_en; 82 | reg [15:0] shift_reg_dac1; 83 | reg data_val_out; 84 | reg [4:0] shift_ctr; 85 | 86 | always @ (posedge clk) 87 | if (tick_dac == 1) 88 | begin 89 | if (parLoad_en == 1) 90 | begin 91 | shift_ctr <= 4'b0000; 92 | shift_reg_dac1 <= {control_vector, dac_par_in}; 93 | end 94 | else if (shift_en == 1) 95 | begin 96 | shift_reg_dac1 <= {shift_reg_dac1[14:0], shift_reg_dac1[15]}; 97 | shift_ctr <= shift_ctr + 1; 98 | end 99 | data_val_out <= shift_reg_dac1[15]; 100 | end 101 | 102 | assign dac_mosi = shift_reg_dac1[15]; 103 | 104 | 105 | //Controller FSM 106 | reg [4:0] current_state; 107 | reg [4:0] next_state; 108 | 109 | //Synchronous state set 110 | always @ (posedge clk) 111 | if (tick_dac == 1) 112 | begin 113 | if (!reset_b) 114 | current_state <= IDLE; 115 | else 116 | current_state <= next_state; 117 | end 118 | 119 | //Unsynchronous Output Generation 120 | 121 | reg dac_done; 122 | always @ (posedge clk) 123 | begin 124 | 125 | if (current_state == IDLE) 126 | begin 127 | shift_en <= 0; 128 | dac_done <= 1; 129 | dac_cs <= 1; 130 | parLoad_en <= 1; 131 | end 132 | else if (current_state == SHIFT_OUT) 133 | begin 134 | shift_en <= 1; 135 | dac_done <= 0; 136 | dac_cs <= 0; 137 | parLoad_en <= 0; 138 | end 139 | else if (current_state == SYNC_DATA) 140 | begin 141 | shift_en <= 0; 142 | dac_done <= 0; 143 | dac_cs <= 1; 144 | parLoad_en <= 0; 145 | end 146 | 147 | end 148 | 149 | always @ (posedge clk) 150 | begin 151 | if (reset_b) 152 | begin 153 | 154 | case (current_state) 155 | 156 | IDLE : next_state <= SHIFT_OUT; 157 | SHIFT_OUT: if (shift_ctr == 4'hF) 158 | begin 159 | next_state <= SYNC_DATA; 160 | end 161 | SYNC_DATA: next_state <= IDLE; 162 | default : next_state <= IDLE; 163 | 164 | endcase 165 | end 166 | end 167 | 168 | 169 | 170 | endmodule 171 | -------------------------------------------------------------------------------- /PMOD Controllers/newadctest.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | //SPI-based controller for the PMOD AD1 4 | 5 | 6 | //Functionality: 7 | // - Sends out Chip Select and Serial Clock signals to control the Converter 8 | // - Reads in 12 bit digital samples and shifts it out serially on the positive edge of the serial clock signal 9 | // - Finite State Machines to ensure that the controller only sends out samples that are fully read in and in a synchronized manner 10 | 11 | //Methodology 12 | //Uses a FSM to control the DAC 13 | // IDLE State 14 | // - The dac_cs signal is set high to pause DAC operations 15 | // - The parLoad_en signal is set high to read in digital samples from the outside modules 16 | // - The dac_done signal is set high to allow digital samples to be read in 17 | 18 | //SHIFT_OUT state 19 | // - The controller shifts out bits of the digital signal in a serial manner 20 | // - Operates on the positive edge of the serial clock signal, takes roughly 16 SClk signals to send a sample through 21 | // - The parLoad_en and dac_done signals are set low to prevent samples from being read in 22 | 23 | //SYNC_DATA 24 | // - This state acts as a buffer to allow the FFs to latch properly 25 | 26 | 27 | module spi2adc (clk,reset_b,adc_cs_out,adc_sclk,adc_miso0,adc_miso1,setpoint_data, adc_par_out,adc_done_flag); 28 | 29 | input clk; 30 | input reset_b; 31 | output reg adc_cs_out; 32 | output adc_sclk; 33 | input adc_miso0; 34 | input adc_miso1; 35 | output adc_done_flag; 36 | output reg [11:0] setpoint_data; 37 | output reg [11:0] adc_par_out; 38 | 39 | reg adc_cs; 40 | 41 | parameter IDLE = 0000; 42 | parameter SHIFT_OUT = 0001; 43 | parameter LATCH = 0002; 44 | 45 | 46 | 47 | //Generate Serial CLK at 1 MHZ 48 | 49 | //reg [11:0] adc_par_out; 50 | reg clk_1MHZ; 51 | reg [5:0] ctr; 52 | parameter TC = 6'd63; 53 | reg tick; 54 | 55 | 56 | always @ (posedge clk) 57 | if (!reset_b) 58 | begin 59 | ctr <= TC; 60 | tick <= 0; 61 | clk_1MHZ <= 0; 62 | end 63 | else 64 | begin 65 | ctr <= ctr - 1'b1; 66 | tick <= 0; 67 | if (ctr == 63) 68 | begin 69 | tick <= 1'b1; 70 | clk_1MHZ <= ~clk_1MHZ; 71 | end 72 | else if (ctr == 31) 73 | begin 74 | clk_1MHZ <= ~clk_1MHZ; 75 | end 76 | end 77 | assign adc_sclk = clk_1MHZ; 78 | 79 | //END Clock Divide 80 | 81 | reg adc_done; 82 | reg shift_en; 83 | (*dont_touch = "true"*)reg [4:0] serial_ctr; 84 | (*dont_touch = "true"*)reg [15:0] shift_reg_adc; 85 | (*dont_touch = "true"*) reg [15:0] shift_reg_adc1; 86 | 87 | always @ (posedge clk) 88 | begin 89 | 90 | if (!reset_b) 91 | begin 92 | shift_reg_adc <= 0; 93 | shift_reg_adc1 <= 0; 94 | setpoint_data <= 0; 95 | adc_par_out <= 0; 96 | end 97 | else 98 | begin 99 | if (tick == 1) 100 | begin 101 | 102 | if ((shift_en == 1) && (!adc_cs_out)) 103 | begin 104 | shift_reg_adc <= {shift_reg_adc[14:0],adc_miso0}; 105 | shift_reg_adc1 <= {shift_reg_adc1 [14:0],adc_miso1}; 106 | serial_ctr <= serial_ctr + 1; 107 | end 108 | else if ((adc_done) && (adc_cs_out)) 109 | begin 110 | setpoint_data <= shift_reg_adc[11:0]; 111 | adc_par_out <= shift_reg_adc1[11:0]; 112 | serial_ctr <= 4'b000; 113 | end 114 | end 115 | end 116 | end 117 | //FSM 118 | reg [4:0] current_state; 119 | reg [4:0] next_state; 120 | 121 | always @ (posedge clk) 122 | if (!reset_b) 123 | current_state <= IDLE; 124 | else 125 | begin 126 | if (tick == 1) 127 | current_state <= next_state; 128 | end 129 | 130 | //Conditions for State Change 131 | always @ (posedge clk) 132 | begin 133 | if (!reset_b) 134 | next_state <= IDLE; 135 | 136 | else 137 | begin 138 | 139 | case (current_state) 140 | 141 | IDLE : next_state <= SHIFT_OUT; 142 | SHIFT_OUT: if (serial_ctr == 4'hE) //Corresponds to 16 Sclk cycles 143 | begin 144 | next_state <= LATCH; 145 | end 146 | LATCH: next_state <= IDLE; 147 | default : next_state <= IDLE; 148 | endcase 149 | end 150 | end 151 | 152 | always @ (posedge clk) //Signal Generation based on State 153 | begin 154 | 155 | if (!reset_b) 156 | begin 157 | shift_en <= 0; 158 | adc_done <= 1; 159 | adc_cs <=1; 160 | end 161 | 162 | else 163 | begin 164 | begin 165 | if (current_state == IDLE) 166 | begin 167 | shift_en <= 0; 168 | adc_done <= 1; 169 | adc_cs <= 1; 170 | end 171 | else if (current_state == SHIFT_OUT) 172 | begin 173 | shift_en <= 1; 174 | adc_done <= 0; 175 | adc_cs <= 0; 176 | end 177 | else if (current_state == LATCH) 178 | begin 179 | shift_en <= 0; 180 | adc_done <= 0; 181 | adc_cs <= 1; 182 | end 183 | end 184 | end 185 | end 186 | 187 | always @ (posedge clk) 188 | adc_cs_out <= adc_cs; 189 | 190 | reg adc_done_flag; 191 | 192 | always @ (posedge clk) 193 | adc_done_flag <= adc_done; 194 | 195 | 196 | endmodule 197 | 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Digital PID Controller implemented on Xilinx Artix-7 2 | 3 | Implemented in Verilog HDL, includes ADC/DAC drivers for Digilent PMOD AD1/DA2 4 | -------------------------------------------------------------------------------- /Testbench/final_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | 4 | //Verilog Testbench that mimics the serial communication of the PMOD AD1 and DA2 5 | //Meant to emulate the functionality of the PMOD interfaces and the algorithms implemented on the FPGA 6 | module tb1(); 7 | 8 | reg clk; 9 | wire adc_cs_out; 10 | wire adc_sclk; 11 | wire adc_miso0; 12 | wire adc_miso1; 13 | 14 | wire dac_cs; 15 | wire dac_sclk; 16 | wire dac_mosi; 17 | 18 | reg reset_b; 19 | reg [4:0] count1; 20 | reg [15:0] data_in; 21 | reg [15:0] setpoint_value; 22 | wire [3:0] idx; 23 | wire start; 24 | 25 | top uut ( 26 | .clk(clk), 27 | .reset_b(reset_b), 28 | .adc_miso0 (adc_miso0), 29 | .adc_miso1 (adc_miso1), 30 | .adc_cs_out (adc_cs_out), 31 | .adc_sclk (adc_sclk), 32 | .dac_cs (dac_cs), 33 | .dac_sclk (dac_sclk), 34 | .dac_mosi (dac_mosi) 35 | ); 36 | 37 | 38 | initial 39 | forever 40 | begin 41 | clk = 1; 42 | #5; 43 | clk = 0; 44 | #5; 45 | end 46 | 47 | initial 48 | begin 49 | 50 | reset_b = 0; 51 | #25 52 | reset_b = 1; 53 | end 54 | 55 | always @ (negedge adc_sclk) 56 | if (adc_cs_out) 57 | count1 <= 0; 58 | else 59 | count1 <= count1 + 1; 60 | 61 | assign start = ((count1 % 32) == 0); 62 | 63 | initial 64 | begin 65 | data_in = 12'b0; 66 | #20000 67 | data_in = 12'd1000; 68 | #20000 69 | data_in = 12'b010011011001; 70 | #40000 71 | data_in = 12'b100110110010; 72 | end 73 | 74 | initial setpoint_value = 12'b100110110010; 75 | 76 | 77 | assign idx = (15-count1); 78 | assign adc_miso0 = setpoint_value[idx]; 79 | assign adc_miso1 = data_in[idx]; 80 | 81 | 82 | 83 | 84 | 85 | endmodule 86 | -------------------------------------------------------------------------------- /Wrapper + Saturator Modules/comparator.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | //Clips the control output to the threshold voltages for the PMOD DA2 4 | 5 | module comparator(clk,reset_b,pi_done,data_in,data_out); 6 | 7 | input clk; 8 | input reset_b; 9 | input pi_done; 10 | input signed [15:0] data_in; 11 | output reg [15:0] data_out; 12 | 13 | reg [15:0] data; 14 | 15 | parameter uMAX = 16'd3723; //Cprresponds to roughly 3 Volts in Analog Voltages 16 | parameter uMIN = 16'd0; //Does not allow negative voltages to pass through 17 | 18 | always @(posedge clk) 19 | begin 20 | if (!reset_b) 21 | data <= 0; 22 | else 23 | begin 24 | if (pi_done) 25 | begin 26 | 27 | if ((data_in >= uMIN) && (data_in <= uMAX)) 28 | data <= data_in; 29 | else if (data_in > uMAX ) 30 | data <= uMAX; 31 | else 32 | data <= uMIN; 33 | end 34 | 35 | else 36 | data <= data; 37 | 38 | end 39 | end 40 | 41 | always @ (posedge clk) 42 | data_out <= data; 43 | 44 | endmodule -------------------------------------------------------------------------------- /Wrapper + Saturator Modules/top.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | //Verilog Modules for a functioning PI Controller 4 | //The algorithm used was derived from a continuous time PI equation discretized using a Bilinear Transform and a sampling rate of roughly 1.2 kHz 5 | //The discrete PI algorithm is shown below 6 | //u[k] = u[k-1] + (Kp+Ki)*e[k] + (Kp - Ki) * e[k-1] 7 | //The algorithm was converted into velocity form to account for Integrator windup and to prevent massive control outputs 8 | //deltaU = (Kp+Ki)*e[k] + (Kp - Ki) * e[k-1] 9 | //u[k] = u[k-1] + deltaU 10 | //The incremental algorithm accounts for the speed of calculations in the FPGA and does not let the recursive portion to overflow 11 | 12 | //The overall methodology used was to 13 | // 1. Read in the samples from the PMOD AD1 Analog-To-Digital Converter 14 | // 2. Zero-pad the samples to convert from 12-bit to 16-bit 15 | // 3. Feed the 16-bit samples to the PI module 16 | // 4. Take the control output from the PI module and run it through a saturator which clips the output at 3723 and 0 (these correspond to threshold voltages for the PMOD DA2 Digital To Analog converter) 17 | // 5. Feed the saturator output to the PMOD DA2 Digital-to-Analog Converter 18 | 19 | module top(clk,reset_b,adc_cs_out,adc_sclk,adc_miso0,adc_miso1,dac_cs,dac_sclk,dac_mosi); 20 | 21 | input clk; 22 | input reset_b; 23 | input adc_miso0; 24 | input adc_miso1; 25 | 26 | output adc_cs_out; 27 | output adc_sclk; 28 | output dac_cs; 29 | output dac_sclk; 30 | output dac_mosi; 31 | 32 | //Local Variables 33 | 34 | reg [15:0] adc_in_0_16bit; 35 | reg [15:0] adc_in_1_16bit; 36 | 37 | wire [11:0] adc_in_0; 38 | wire[11:0] adc_in_1; 39 | 40 | wire pi_done; 41 | wire adc_done_flag; 42 | wire signed [15:0] control_signal_out; 43 | wire [15:0] control_signal_saturated; 44 | 45 | //Module Instantiations 46 | 47 | spi2adc u_spi2adc 48 | ( 49 | .clk(clk), 50 | .reset_b(reset_b), 51 | .adc_cs_out(adc_cs_out), 52 | .adc_sclk(adc_sclk), 53 | .adc_miso0(adc_miso0), 54 | .adc_miso1(adc_miso1), 55 | .adc_done_flag(adc_done_flag), 56 | .setpoint_data(adc_in_0), 57 | .adc_par_out(adc_in_1) 58 | ); 59 | 60 | //Zero Pads the A/D samples 61 | always @ (posedge clk) 62 | begin 63 | 64 | if (!reset_b) 65 | begin 66 | adc_in_0_16bit <= 0; 67 | adc_in_1_16bit <= 0; 68 | end 69 | 70 | else 71 | begin 72 | 73 | if (adc_done_flag) 74 | begin 75 | adc_in_0_16bit <= {4'b0,adc_in_0}; 76 | adc_in_1_16bit <= {4'b0, adc_in_1}; 77 | end 78 | 79 | else 80 | begin 81 | adc_in_0_16bit <= adc_in_0_16bit; 82 | adc_in_1_16bit <= adc_in_1_16bit; 83 | end 84 | 85 | end 86 | end 87 | 88 | PI_Controller u_pi 89 | 90 | ( 91 | .clk(clk), 92 | .reset_b(reset_b), 93 | .adc_in_0(adc_in_0_16bit), 94 | .adc_in_1(adc_in_1_16bit), 95 | .adc_done(adc_done_flag), 96 | .pi_done(pi_done), 97 | .u(control_signal_out) 98 | ); 99 | 100 | comparator u_comparator 101 | ( 102 | .clk(clk), 103 | .reset_b(reset_b), 104 | .pi_done(pi_done), 105 | .data_in(control_signal_out), 106 | .data_out(control_signal_saturated) 107 | ); 108 | 109 | spi2dac u_spi2dac 110 | ( 111 | .clk(clk), 112 | .reset_b(reset_b), 113 | .dac_par_in(control_signal_saturated), 114 | .dac_cs(dac_cs), 115 | .dac_sclk(dac_sclk), 116 | .dac_mosi(dac_mosi) 117 | ); 118 | 119 | 120 | 121 | 122 | 123 | 124 | endmodule 125 | --------------------------------------------------------------------------------