Author Topic: ADC with Altera Cyclone FPGA - internal and external (I2C) implementation  (Read 8447 times)

0 Members and 1 Guest are viewing this topic.

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1348
  • Country: ca
How would you implement the simplest and most ingenious ADC in Altera Cyclone II - IV, without using an external ADC?
It can be 8 - 10 bits, doesn't have to be super linear, just to read the approximate position of a linear potentiometer.
I will post my idea so nobody accuses me of asking others to think for me. It is not very ingenious though but, for me as a noob, is easier than reading an I2C ADC.

Edit: To add more details, a counter in the FPGA that gets latched when the output of the comparator goes high and some logic that discharges the cap and resets the counter.
« Last Edit: May 13, 2020, 12:24:30 am by Miti »
Fear does not stop death, it stops life.
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 22347
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: ADC in Altera Cyclone FPGA
« Reply #1 on: May 09, 2020, 11:04:58 pm »
I understand LVDS inputs can be reasonable comparators; YMMV.  (Varies by family; some FPGAs have shite receivers with just about zero common mode range.)  In combination with a CMOS output, you have a S-D ADC possible.  Preferably with the CMOS output buffered to VREF, else you're relying on VCCIO and pin output resistance for that.  Or in combination with a DAC (of whatever applicable type, parallel or serial), you have a SAR ADC.  That should be fine for, eh, maybe 8 bits give or take calibration.

Ramp, sure, can be done that way; beware of linearity though.  With care, that should be usable up to 10-12 bits, with INL being the worst offender and DNL being pretty smooth.  Slow -- low sample rate due to comparing it with a digital counter.  (S-D is slow too, but can dither pretty fast without much effort.)

As for your pot, are you sure you can't use an encoder instead? :P

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8065
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #2 on: May 10, 2020, 03:42:27 am »
but, for me as a noob, is easier than reading an I2C ADC.
Nope, it identically complicated as reading a fixed address I2C ADC.  You just need a little time to think and practice some Verilog coding.

Only if the chosen I2C ADC needs internal registers to be set in advance will using the I2C be any more complicated.

3 wire ADCs using SPI bus and I2S are even easier to interface with since you don't need to worry about a bidirectional IO pins.  Running multiple of these samplers in parallel is extra easy as each sampler uses 1 additional data input pin while the 2 control outputs are shared between the ADCs.  And in your Verilog code, you just add an additional serial capture register byte for each additional ADC.

(HINT: we are here to help and strengthening your Verilog skills for IO bus controls will have a bigger payout in the long run...)
« Last Edit: May 10, 2020, 05:17:31 am by BrianHG »
 

Offline Wiljan

  • Regular Contributor
  • *
  • Posts: 230
  • Country: dk
Re: ADC in Altera Cyclone FPGA
« Reply #3 on: May 10, 2020, 05:25:02 am »
Why not use the Max10 from Intel? it does have ADC build-in and are cheap. It also have build-in flash and only need 1 voldtage supply
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8065
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #4 on: May 10, 2020, 07:29:33 am »
Why not use the Max10 from Intel? it does have ADC build-in and are cheap. It also have build-in flash and only need 1 voldtage supply
Dollar for Dollar, the Max10 has a lower density than the Cyclone's.  However, for smaller designs,  or where budget isn't a concern, it can be very useful.
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1348
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #5 on: May 10, 2020, 07:49:40 pm »
Nope, it identically complicated as reading a fixed address I2C ADC.  You just need a little time to think and practice some Verilog coding.
...
Only if the chosen I2C ADC needs internal registers to be set in advance will using the I2C be any more complicated.
...
(HINT: we are here to help and strengthening your Verilog skills for IO bus controls will have a bigger payout in the long run...)

I really want to add I2C to my project and I thought that's for a later date and higher level.
But with that very generous offer to help me improve my Verilog coding skills, you convinced me. Thank you!

I will go with an I2C ADC, for example MCP3021 plus MCP6021 input buffer.
I have a Terasic DE1 Altera Cyclone II development board and for the beginning I will be using an EEPROM with known content. If I can get to reading the content of any address in that EEPROM and display the address and the content on the 4 x 7 segment displays, I can read from the ADC.
I've attached a sample code for that board and an I2C library that I found the most appropriate and readable, in my opinion, for this application. I will try to understand and add comments to the code.
For the beginning, I will strip the code to the DE1_Default top level only and I will try adding the I2C module "i2c_master.v" and "read_eeprom.v" as higher level that uses the i2c_master.v to read from an address and display on the LED 7 segment.
Fear does not stop death, it stops life.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8065
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #6 on: May 11, 2020, 01:30:17 am »
Ok Miti,
You have a very complete development board.
The example I2C source you provided is written by someone else.  Since you do not want to debug and reverse understand their code, we will not be using it.

You will be writing an ultra simple I2C interface in Verilog specifically for the MCP3021 so you may actually gain programming skills instead of plug in and tie together third party modules skills.

Now before we begin, I need to know a few things:
1. Which Quartus are you using?
2. Have you ever written anything in Verilog in Quartus?
3. Created a 'Sheet Symbol' for such code?
4. Placed that generated symbol on a top level block diagram schematic?
5. Wired that module to IO pins on your developement board?
6. Have you ever simulated your own code before?

« Last Edit: May 11, 2020, 03:13:58 am by BrianHG »
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2812
  • Country: nz
Re: ADC in Altera Cyclone FPGA
« Reply #7 on: May 11, 2020, 12:22:07 pm »
Not saying that this is the right way , but lease complex way for such a simple device as the MCP3021, and with limited HDL skills you could consider stripping things down, making the problem less of a coding issue and more of a data issue.

Just use long shift registers to:
- control the SCLK
- control the tristate on the SDAT line
- collect the data bits from from the ADC
- control when the data from the serial input shift register are transferred to the serial output (and possibly strobe a "data enable" line).

If you then have the correct length shift registers, with the correct values in, the code becomes mundane without any tricky logic traps. All you need is the right length shift registers with the correct patterns in it (and to adjust your constants to match).

Not saying that these shift registers below are the correct length, or the correct values are there for anything in particular, but it generates something resembling an I2C waveform and shows the idea.

Code: [Select]
`timescale 1ns / 1ps
module dummy_i2c(
    input clk,

    output i2c_sclk,
    inout  i2c_dat,  // Externally pulled up

    output [3:0] leds
    );

   // Shift registers
   reg [79:0] data_in_sr;
   reg [79:0] tristate_sr;
   reg [79:0] sclk_sr;
   reg [79:0] data_ready_sr;

   // For driving the output buffer
   wire pin_value;
   wire pin_tristate;

   // For dividing the clock
   reg [3:0]  counter;

   // Holds the last read data value
   reg [3:0]  led_reg;

   // Attach the input/output pins to the internal registers
   assign i2c_sclk     = sclk_sr[39];
   assign pin_tristate = tristate_sr[39];
   assign leds         = led_reg;

initial begin
   // Patterns for waggling the pins to generate the I2C transaction
                    //  77777777776666666666555555555544444444443333333333222222222211111111110000000000
                    //  98765432109876543210987654321098765432109876543210987654321098765432109876543210
   tristate_sr   <= 80'b10011110000111100001111000000000000111111111111111111111111111111111111111100111;
   sclk_sr       <= 80'b11001100110011001100110011001100110011001100110011001100110011001100110011001111;
   data_ready_sr <= 80'b00000000000000000000000000000000000000000000000000000000000000000000000000000001;
   counter       <=  4'b0000;
end


always @ (posedge clk) begin
   // Divide the system CLK by 5, making the serial clk 1/20th the system clock.
if(counter == 4'b0101) begin
  // If we should have valid data in the shift register then save it.
  if(data_ready_sr[39] == 1'b1) begin
     led_reg = { data_in_sr[14], data_in_sr[10], data_in_sr[6], data_in_sr[2] };
  end
  // Move all the shift registers along on bit.
  tristate_sr    <= { tristate_sr[78:0],   tristate_sr[79] };
  sclk_sr        <= { sclk_sr[78:0],       sclk_sr[79] };
  data_ready_sr  <= { data_ready_sr[78:0], data_ready_sr[79] };
  // Capture incoming data in the input shift register
  data_in_sr     <= { data_in_sr[78:0],    pin_value };
  counter        <= 4'b0;
end else begin
   counter <= counter + 1;
end
end

  // This is for an Xilinx part, but the equivilent will exist for Altera/Intel
IOBUF #(
      .DRIVE(12), // Specify the output drive strength
      .IOSTANDARD("DEFAULT"), // Specify the I/O standard
      .SLEW("SLOW") // Specify the output slew rate
) IOBUF_inst (
      .O(pin_value),     // Buffer output
      .IO(i2c_dat),      // Buffer inout port (connect directly to top-level port)
      .I(0),  // Buffer input
      .T(pin_tristate)   // 3-state enable input, high=input, low=output
   );
endmodule

« Last Edit: May 11, 2020, 12:24:02 pm by hamster_nz »
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: Miti

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1348
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #8 on: May 11, 2020, 12:44:42 pm »
Ok Miti,
You have a very complete development board.
The example I2C source you provided is written by someone else.  Since you do not want to debug and reverse understand their code, we will not be using it.

You will be writing an ultra simple I2C interface in Verilog specifically for the MCP3021 so you may actually gain programming skills instead of plug in and tie together third party modules skills.

Now before we begin, I need to know a few things:
1. Which Quartus are you using? 13.0SP1, this is the latest that support Cyclone II.
2. Have you ever written anything in Verilog in Quartus? Basic things like examples from web, small counters and dividers, LED drivers.
3. Created a 'Sheet Symbol' for such code? Yes, as I said above, basic.
4. Placed that generated symbol on a top level block diagram schematic? Yes.
5. Wired that module to IO pins on your developement board? Yes.
6. Have you ever simulated your own code before? I tried to and it works if I don't use mega functions, altcounter, altpll, etc.

See here for simulation issues:
https://www.eevblog.com/forum/fpga/which-cyclone-pll-implementation-is-better/
Fear does not stop death, it stops life.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8065
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #9 on: May 11, 2020, 10:01:20 pm »
Ok, lets start.
     We will begin doing this 'Extra Dumb' style, so smart experienced ones out there please be patient as we will convert all the 'IF's to case statements and improve sequencing lateron.  The goal is first to make Miti write this first interface himself.

Step 1:

Make the Verilog module (MCP3021_reader) inst with inputs and outputs, IE
INPUTS -> clk, reset, conv, SCL_in, SDA_in
Outputs regs -> SCL_oe, SCL_out, SDA_oe, SDA_out, adc_val (10bit) , adc_rdy (strobes high when ready)

(I listed the SCL and SDA as ins and outs, + bidir pin tristate controls out for both since we may allow full I2C bus release.)

Since we need to run the I2C at a lower frequency, we want 2 adjustable parameter for the module:
#1- CLK_IN_HZ
#2- I2C_SCL_HZ

Start you module, use as an example setup my RS232 transceiver posted here:
https://www.eevblog.com/forum/fpga/verilog-rs232-uart-and-rs232-debugger-source-code-and-educational-tutorial/msg2801394/#msg2801394
(We will be programming with similar, yet even simpler coding techniques to make this work)

For now,
1. Make a synchronous reset defaults for the output pins.
2. Make a period counter (call it I2C_period[23:0] ) with enough bits to operate as slow as 2 cycles per second if you use you dev board's 25Mhz clock.
3. Make the 'adc_rdy' output reg cycle/invert it's value once every period count.
4. Make a symbol for the 'MCP3021_reader', and wire it into your dev-board's Cyclone so that the reset is button actuated and the adc_rdy will cycle an led.
5. Test and post the .sv file here and we will add the sequencer to I2C communicate with the MCP3021.

When I say test, 2 steps,
A) set the 2 parameters so the led will blink as a speed you can visually see
B) simulate so that you can see the 'adc_rdy' cycle, BUT change the parameters so that this cycling happens around every 4-16 clocks, not once every 25million clock cycles.

make your code similar to :
Code: [Select]
always @ (posedge clk) begin

if (reset) begin

end else begin

end

end // posedge clk
endmodule...

« Last Edit: May 11, 2020, 10:24:49 pm by BrianHG »
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15244
  • Country: fr
Re: ADC in Altera Cyclone FPGA
« Reply #10 on: May 12, 2020, 12:07:04 am »
I would personally probably implement that as a sigma-delta ADC. I already have a generic sigma-delta modulator in VHDL with which I have implemented DACs. Implementing an ADC on top of that would be relatively simple. All I'd need would be a couple external parts: typically a comparator and a couple passives.
 
The following users thanked this post: Miti

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8065
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #11 on: May 12, 2020, 01:31:32 am »
Not saying that this is the right way , but lease complex way for such a simple device as the MCP3021, and with limited HDL skills you could consider stripping things down, making the problem less of a coding issue and more of a data issue.

Just use long shift registers to:
- control the SCLK
- control the tristate on the SDAT line
- collect the data bits from from the ADC
- control when the data from the serial input shift register are transferred to the serial output (and possibly strobe a "data enable" line).

If you then have the correct length shift registers, with the correct values in, the code becomes mundane without any tricky logic traps. All you need is the right length shift registers with the correct patterns in it (and to adjust your constants to match).

Not saying that these shift registers below are the correct length, or the correct values are there for anything in particular, but it generates something resembling an I2C waveform and shows the idea.

Code: [Select]
`timescale 1ns / 1ps
module dummy_i2c(
    input clk,

    output i2c_sclk,
    inout  i2c_dat,  // Externally pulled up

    output [3:0] leds
    );

   // Shift registers
   reg [79:0] data_in_sr;
   reg [79:0] tristate_sr;
   reg [79:0] sclk_sr;
   reg [79:0] data_ready_sr;

   // For driving the output buffer
   wire pin_value;
   wire pin_tristate;

   // For dividing the clock
   reg [3:0]  counter;

   // Holds the last read data value
   reg [3:0]  led_reg;

   // Attach the input/output pins to the internal registers
   assign i2c_sclk     = sclk_sr[39];
   assign pin_tristate = tristate_sr[39];
   assign leds         = led_reg;

initial begin
   // Patterns for waggling the pins to generate the I2C transaction
                    //  77777777776666666666555555555544444444443333333333222222222211111111110000000000
                    //  98765432109876543210987654321098765432109876543210987654321098765432109876543210
   tristate_sr   <= 80'b10011110000111100001111000000000000111111111111111111111111111111111111111100111;
   sclk_sr       <= 80'b11001100110011001100110011001100110011001100110011001100110011001100110011001111;
   data_ready_sr <= 80'b00000000000000000000000000000000000000000000000000000000000000000000000000000001;
   counter       <=  4'b0000;
end


always @ (posedge clk) begin
   // Divide the system CLK by 5, making the serial clk 1/20th the system clock.
if(counter == 4'b0101) begin
  // If we should have valid data in the shift register then save it.
  if(data_ready_sr[39] == 1'b1) begin
     led_reg = { data_in_sr[14], data_in_sr[10], data_in_sr[6], data_in_sr[2] };
  end
  // Move all the shift registers along on bit.
  tristate_sr    <= { tristate_sr[78:0],   tristate_sr[79] };
  sclk_sr        <= { sclk_sr[78:0],       sclk_sr[79] };
  data_ready_sr  <= { data_ready_sr[78:0], data_ready_sr[79] };
  // Capture incoming data in the input shift register
  data_in_sr     <= { data_in_sr[78:0],    pin_value };
  counter        <= 4'b0;
end else begin
   counter <= counter + 1;
end
end

  // This is for an Xilinx part, but the equivilent will exist for Altera/Intel
IOBUF #(
      .DRIVE(12), // Specify the output drive strength
      .IOSTANDARD("DEFAULT"), // Specify the I/O standard
      .SLEW("SLOW") // Specify the output slew rate
) IOBUF_inst (
      .O(pin_value),     // Buffer output
      .IO(i2c_dat),      // Buffer inout port (connect directly to top-level port)
      .I(0),  // Buffer input
      .T(pin_tristate)   // 3-state enable input, high=input, low=output
   );
endmodule
My first step if a little more complex above a simple 1 unified serial pattern.  Though, I did say above that interfacing with a MCP3021 would be just as simple, or even simpler than making a sigma-delta ADC converter from scratch.

We begin with a period 100/400/1000/any KHz programmable clk with configurable shift segments, then we will separate the segments into the I2C protocol (Start/Stop/ACK/read/write) into separate sequences which will be driven by a master function sequencer so Miti will be able to address any I2C device, including flash I2C eeprom finally adding clock stretching/write eedata timer.

« Last Edit: May 12, 2020, 02:29:00 am by BrianHG »
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1348
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #12 on: May 12, 2020, 11:57:57 am »
I'm not only looking to get the job done and go away, I also want to learn in the process so I think Brian's approach is good.

Thanks Brian, I'll work on it in the next few days, life permitted.
Fear does not stop death, it stops life.
 

Online AndyC_772

  • Super Contributor
  • ***
  • Posts: 4278
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: ADC in Altera Cyclone FPGA
« Reply #13 on: May 12, 2020, 12:44:12 pm »
The simplest way to achieve an ADC-like input is to connect an R-C network to a single-ended digital output, so you can generate a (relatively) slowly rising voltage. Connect this to one half of a differential input, and the variable voltage from your potentiometer to the other half.

To measure voltage, drive the output low for long enough to discharge the cap fully, then drive it high and start a fast timer. Stop or capture the value in the timer register when the differential input changes state.

It's crude, of course, but you should get something approaching reasonable linearity if you restrict the range of the input to about 0.5x the final capacitor voltage. Best of all, it only takes up 3 pins and has essentially zero component cost.

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15244
  • Country: fr
Re: ADC in Altera Cyclone FPGA
« Reply #14 on: May 12, 2020, 02:12:23 pm »
I'm not only looking to get the job done and go away, I also want to learn in the process so I think Brian's approach is good.

Unless I missed something, he talked about interfacing an external ADC? That's certainly something you'll also learn from, but I thought your initial goal was to *implement* an ADC, and not use an external one?

Sorry if I missed something in what Brian suggested, though.
 

Offline langwadt

  • Super Contributor
  • ***
  • Posts: 4682
  • Country: dk
Re: ADC in Altera Cyclone FPGA
« Reply #15 on: May 12, 2020, 03:54:02 pm »
The simplest way to achieve an ADC-like input is to connect an R-C network to a single-ended digital output, so you can generate a (relatively) slowly rising voltage. Connect this to one half of a differential input, and the variable voltage from your potentiometer to the other half.

To measure voltage, drive the output low for long enough to discharge the cap fully, then drive it high and start a fast timer. Stop or capture the value in the timer register when the differential input changes state.

It's crude, of course, but you should get something approaching reasonable linearity if you restrict the range of the input to about 0.5x the final capacitor voltage. Best of all, it only takes up 3 pins and has essentially zero component cost.

if it is only for a potmeter input it can be even simpler, similar to the joystick port on old computers.

potmeter(+resistor to set a minimum) from Vcc to a cap, cap voltage to a tri-stateble input, when it reaches thresh-hold pulse the pin low to discharge cap

 
The following users thanked this post: Siwastaja

Offline langwadt

  • Super Contributor
  • ***
  • Posts: 4682
  • Country: dk
Re: ADC in Altera Cyclone FPGA
« Reply #16 on: May 12, 2020, 04:05:40 pm »
I understand LVDS inputs can be reasonable comparators; YMMV.  (Varies by family; some FPGAs have shite receivers with just about zero common mode range.)  In combination with a CMOS output, you have a S-D ADC possible.  Preferably with the CMOS output buffered to VREF, else you're relying on VCCIO and pin output resistance for that.  Or in combination with a DAC (of whatever applicable type, parallel or serial), you have a SAR ADC.  That should be fine for, eh, maybe 8 bits give or take calibration.

Ramp, sure, can be done that way; beware of linearity though.  With care, that should be usable up to 10-12 bits, with INL being the worst offender and DNL being pretty smooth.  Slow -- low sample rate due to comparing it with a digital counter.  (S-D is slow too, but can dither pretty fast without much effort.)

As for your pot, are you sure you can't use an encoder instead? :P

Tim

the comperator for an S-D ADC shouldn't need much common mode, the input is centered +/- hysteresis



in a pinch you might be able to get away without the opamp and just use a cap to ground (and an invert of the output)

 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8749
  • Country: fi
Re: ADC in Altera Cyclone FPGA
« Reply #17 on: May 12, 2020, 04:16:57 pm »
if it is only for a potmeter input it can be even simpler, similar to the joystick port on old computers.

potmeter(+resistor to set a minimum) from Vcc to a cap, cap voltage to a tri-stateble input, when it reaches thresh-hold pulse the pin low to discharge cap

Yes, I have done this on Cyclone II, don't have the VHDL around here now but I think it was a ridiculously easy one-hour job or so. It worked very well. Controlled a "throttle" signal with a potentiometer on a DIY conversion EV inverter.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8065
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #18 on: May 12, 2020, 09:33:47 pm »
I'm not only looking to get the job done and go away, I also want to learn in the process so I think Brian's approach is good.

Thanks Brian, I'll work on it in the next few days, life permitted.
I recommend editing your first opening post and change the thread title to "I2C interface for MCP3021 ADC with Altera Cyclone FPGA" so this thread doesn't get filled with 2 groups of posts making all our heads spin.
 
The following users thanked this post: SiliconWizard

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1348
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #19 on: May 13, 2020, 12:08:56 am »
I'm not only looking to get the job done and go away, I also want to learn in the process so I think Brian's approach is good.

Unless I missed something, he talked about interfacing an external ADC? That's certainly something you'll also learn from, but I thought your initial goal was to *implement* an ADC, and not use an external one?

Sorry if I missed something in what Brian suggested, though.

Yes, you are absolutely right, I started this thinking that I want something easy to implement inside the FPGA, something that I can take on as a beginner in FPGAs. I did not exclude an external I2C device but I saw it as a next level project, and it is next level on my own.
With some help from the community, however, it seems more doable, and thanks to Brian for offering that help.
I'm still interested in picking the community brains for ideas that I know are abundant and may be handy some time.
For example I googled "joystick port on old computers" and Dave's video #1054 came up.

So yes, I figured that people would be puzzled and I apologise.
Fear does not stop death, it stops life.
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1348
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #20 on: May 13, 2020, 12:16:21 am »
The simplest way to achieve an ADC-like input is to connect an R-C network to a single-ended digital output, so you can generate a (relatively) slowly rising voltage. Connect this to one half of a differential input, and the variable voltage from your potentiometer to the other half.

To measure voltage, drive the output low for long enough to discharge the cap fully, then drive it high and start a fast timer. Stop or capture the value in the timer register when the differential input changes state.

It's crude, of course, but you should get something approaching reasonable linearity if you restrict the range of the input to about 0.5x the final capacitor voltage. Best of all, it only takes up 3 pins and has essentially zero component cost.

I read somewhere that in the newer Cyclones, the differential inputs don't like common mode but the older ones had really fast comparators that accepted common mode.
Fear does not stop death, it stops life.
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1348
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #21 on: May 13, 2020, 12:21:20 am »
The simplest way to achieve an ADC-like input is to connect an R-C network to a single-ended digital output, so you can generate a (relatively) slowly rising voltage. Connect this to one half of a differential input, and the variable voltage from your potentiometer to the other half.

To measure voltage, drive the output low for long enough to discharge the cap fully, then drive it high and start a fast timer. Stop or capture the value in the timer register when the differential input changes state.

It's crude, of course, but you should get something approaching reasonable linearity if you restrict the range of the input to about 0.5x the final capacitor voltage. Best of all, it only takes up 3 pins and has essentially zero component cost.

if it is only for a potmeter input it can be even simpler, similar to the joystick port on old computers.

potmeter(+resistor to set a minimum) from Vcc to a cap, cap voltage to a tri-stateble input, when it reaches thresh-hold pulse the pin low to discharge cap

The Cyclone IOs don't have Schmitt trigger inputs so I wonder how clean and repeatable that threshold level is.
Fear does not stop death, it stops life.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8065
  • Country: ca
Re: ADC in Altera Cyclone FPGA
« Reply #22 on: May 13, 2020, 03:17:38 am »
The simplest way to achieve an ADC-like input is to connect an R-C network to a single-ended digital output, so you can generate a (relatively) slowly rising voltage. Connect this to one half of a differential input, and the variable voltage from your potentiometer to the other half.

To measure voltage, drive the output low for long enough to discharge the cap fully, then drive it high and start a fast timer. Stop or capture the value in the timer register when the differential input changes state.

It's crude, of course, but you should get something approaching reasonable linearity if you restrict the range of the input to about 0.5x the final capacitor voltage. Best of all, it only takes up 3 pins and has essentially zero component cost.

if it is only for a potmeter input it can be even simpler, similar to the joystick port on old computers.

potmeter(+resistor to set a minimum) from Vcc to a cap, cap voltage to a tri-stateble input, when it reaches thresh-hold pulse the pin low to discharge cap

The Cyclone IOs don't have Schmitt trigger inputs so I wonder how clean and repeatable that threshold level is.
The threshold moves with the VCCIO voltage and ever so slightly with the die temperature.

Though Schmitt trigger inputs may prevent bounce or oscillation on a slow rising or slow falling input signal, the truth is that timing and edge wise, Schmitt trigger inputs are actually noisier than regular inputs.
 

Offline MitiTopic starter

  • Super Contributor
  • ***
  • Posts: 1348
  • Country: ca
Hey Brian,

Here's the module you requested. I can confirm it blinks the LEDR[0] at 2Hz and it resets and stays in reset when I press KEY[0].
Simulation to follow...  :box:
Fear does not stop death, it stops life.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8065
  • Country: ca
Ok, perfect, excellent.
It is ok to work on solely the 'MCP3021_reader.v' as ascii text here on the forum using the 'Insert Code' icon...

Here are my first corrections/updates:

Code: [Select]
module MCP3021_Reader (
Clk, // System clock
Rst_N, // General reset active low
SCL_in, // I2C serial cloc input. Is this necessary for single master?
SDA_in, // I2C serial data input
Conv, // Start acquisition

SCL_out, // Serial clock output
SDA_out, // Serial data output
SCL_oe, // Output enable for SCL
SDA_oe, // Output enable for SDA
ADC_rdy, // ADC_rdy output - strobes high when data is available
ADC_val // 10 bit ADC register. ADC data is available in this register when ADC_Rdy
);

input Clk;
input Rst_N;
input SCL_in;
input SDA_in;
input Conv;

output reg SCL_out;
output reg SDA_out;
output reg SCL_oe;
output reg SDA_oe;
output reg ADC_rdy;
output reg [9:0] ADC_val;

reg[23:0] I2C_Period = 24'd0; // I2C clock divider

parameter   CLK_IN_HZ =  16;    //25'd24000000; // System clock frequency
parameter   I2C_SCL_HZ = 4; // I2C SCL frequency (= 120 for 100KHz or 30 for 400KHz)
localparam  I2C_PERIOD_SIZE = (CLK_IN_HZ/I2C_SCL_HZ)-1 ;

always @ (posedge Clk)
begin
if (!Rst_N)
begin
I2C_Period <= I2C_PERIOD_SIZE ;
ADC_rdy    <= 0;

SCL_out    <= 1;  // I2C default state is all HIGH
SCL_oe     <= 0;  // make I2C tristate by default.
SDA_out    <= 1;  // I2C default state is all HIGH
SDA_oe     <= 0;  // make I2C tristate by default.

ADC_rdy    <= 0;
end

else
begin
if ( I2C_Period == 0 )
begin

SCL_out    <= 1;  // I2C default state is all HIGH
SCL_oe     <= 1;  // make I2C tristate by default.
SDA_out    <= 1;  // I2C default state is all HIGH
SDA_oe     <= 1;  // make I2C tristate by default.

ADC_rdy <= ~ADC_rdy;
I2C_Period <= I2C_PERIOD_SIZE;
end

else
begin
I2C_Period <= I2C_Period - 1;
end
end

end // posedge clk

endmodule

Ok, now for the changes I made:
#1:
parameter   CLK_IN_HZ =  16;    //25'd24000000;      // System clock frequency
parameter   I2C_SCL_HZ = 4;            // I2C SCL frequency (= 120 for 100KHz or 30 for 400KHz)
localparam  I2C_PERIOD_SIZE = (CLK_IN_HZ/I2C_SCL_HZ)-1 ;

In the parameter, removing the " 25'd### " in Quartus changes this:



to this:



The 'localparam I2C_PERIOD_SIZE' is preferable as we will use a number of localparams to specify the structure and timing of the I2C protocol and it is preferable that all these controls are grouped right at the top of the source code.  It's good to avoid any main structural mathematics in the middle of the code.
 
As for the reset, I added all the defaults for the IO pins.
As for the loop, I made the test for ' if ( I2C_Period == 0 ) ' so that the loop begins right after the reset without delay.
Then I make ' I2C_Period <= I2C_PERIOD_SIZE ; ', and while waiting for the period, I subtract 1 at a time.

I setup a simple Quartus 9.1 project using Quartus' built in simulator as seen here:

993630-2

I've attached the project (minus the sub folders and report & programming binaries so that the .zip is tiny).  It will open, compile and regenerate the missing files in any Quartus version.  Note that it is a virgin project just for simulation.  It is not connected to your development board.

If you are using a more modern proper simulator like Modelsim, continue to build your own simulation.

Next, we will attack the simple 1 dimensional sequencer almost exactly like hamster_nz 's recommendation.  Then, break that one down into 2 dimensions where we make use of a higher dimension calling 6 sub dimension RX/TX _I2Cbyte, TX_I2C_start/stop/ack/nak sequences.

(The 2 dimension version will then be expandable to do a selection of higher main sequences used for addressing I2C eeproms.  Like write_byte and read_byte providing an r_address & w_address and data_in/out bus, then even further if you really want to get advanced like write_block, read_block & block_size for even faster access.)
« Last Edit: May 18, 2020, 05:29:22 am by BrianHG »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf