2
\$\begingroup\$

I have a question about connecting a potentiometer to a FPGA.

I am connecting a potentiometer to an external 8 bit ADC chip that is outside the FPGA. I want the pot to represent a speed setpoint ranging from a min to max value, for example say 0 rpm to 100 rpm of a motor.

Now since I have no ALU on my FPGA, I am creating my own ALU which is no problem. However I was planning on working out the RPM setpoint based on the following equation below: $$Setpoint~RPM = ADC_{Pot} \cdot \frac{Range_{RPM}}{2^N-1}$$

For example pot is set to ~50% and motor speed range is 100rpm: 128 ( 100 / 255 ) = 50.1 RPM.

Now the problem I run into is I require floating point numbers for my calculations. Am I overcomplicating this process of converting the pot value into an RPM setpoint by using the ALU? Could this simply be done in some code in VHDL?

I would appreciate any help, thanks.

EDIT

Ok it might be useful if I provide some information about what I am doing.

So I am making my own processing core on an FPGA. I wish to display this rpm setpoint on a LCD display. So I am working in binary with registers completely. So my pot value is inputted with 8 bit digital resolution. And say I want to represent on my LCD display a value between 0 and 100 rpm. I was going to use the above calculation with my designed ALU.

So for the calculation I was going to store the (RPM Range / Bit Size) value in memory to avoid needing to divide in my ALU and ca just multiply it with the inputted pot value but this will require a decimal point which is confusing me.

\$\endgroup\$
11
  • 2
    \$\begingroup\$ You can avoid floating point calculations by working with units of 0.1 or even 0.01 rpm \$\endgroup\$ Nov 8 '20 at 18:12
  • 1
    \$\begingroup\$ @David777 Displaying is not the same as processing or execution. Your calculator processes in binary, yet displays in decimal. If you are trying to get your FPGA to output RPM in a human readable decimal format on a display instead of perform speed control as your question implies, then you have not said so. \$\endgroup\$
    – DKNguyen
    Nov 8 '20 at 18:17
  • 3
    \$\begingroup\$ Look up fixed point math. It is just integer math, but you, yourself keep track of where the decimel is. So for 16-bits, the 8MSB could be left of the radix and 8LSB could be right of the radix. Or wherever you want. You left/right shift prior to addition/subtraction to align the radix and after multiplication/divisions so the radix is in the right spot. You may change the radix too throughout calculations as long as you account for it in the next calculation. \$\endgroup\$
    – DKNguyen
    Nov 8 '20 at 18:29
  • 1
    \$\begingroup\$ If the resolution is only 8 bits you could consider a simple lookup table. \$\endgroup\$ Nov 8 '20 at 19:23
  • 1
    \$\begingroup\$ Well 8 bits get you 256 states so you could have intermediate RPM values. If you through away the LSB you have 128 states. \$\endgroup\$ Nov 8 '20 at 19:32
2
\$\begingroup\$

From your original equation we can elimate the division like this:

$$ \begin{align} Setpoint_{RPM}&=ADC_{POT}\cdot\frac{Range_{RPM}}{2^N-1}\tag{1}\\\\ Setpoint_{RPM}&=ADC_{POT}\cdot K\tag{2} \end{align} $$

where K is a constant that can be pre-calculated to get rid of the division:

$$ \frac{100}{255}=0.3921568627450980392156862745098\tag{3} $$

For fixed-point binary (aka Q-notation), we multiply the top and bottom by \$2^Q\$:

$$ \begin{align} Setpoint_{RPM}=\frac{2^Q\left(ADC_{POT}\cdot K\right)}{2^Q}\tag{4}\\\\ Setpoint_{RPM}=\frac{2^Q\cdot ADC_{POT}\cdot K}{2^Q}\tag{5} \end{align} $$

In binary, \$2^Q\$ is simply a shift by Q bits: left for multiply; right for divide.

Since,

$$ 2^Q\cdot K\tag{6} $$

is also a constant, we can pre-calculate that as well.

Let’s choose 16 for Q which means 16 binary places after the binary point:

$$ 2^Q\cdot K=2^{16}\cdot\frac{100}{255}=25700\tag{7} $$

rounded to the nearest integer.

So now we have:

$$ Setpoint_{RPM}=\frac{ADC_{POT}\cdot25700}{2^Q}\tag{8} $$

There is a snag, however. When shifting right by Q bits, all the binary places after the binary point get chopped off which introduces a rounding error, but there’s a simple fix by adding one half in the Q domain before performing the shift right:

$$ Setpoint_{RPM}=\frac{ADC_{POT}\cdot25700+\frac{2^Q}{2}}{2^Q}\tag{9} $$

And now for the VHDL, which in essence is:

setpoint_rpm <= (adc_pot * Q_K + Q_HALF) srl Q;

And here's a fuller example:

function "srl"(constant a, q: in natural) return natural is
    variable u: unsigned(30 downto 0);
begin
    u := to_unsigned(a, u'length);
    return to_integer(u(30 downto q));
end function;

constant RANGE_RPM: natural := 100;
constant ADC_BITS: natural := 8;
constant Q: natural := 16;
constant Q_RANGE_RPM: natural := 2**Q * RANGE_RPM;
constant Q_K: natural := natural(real(2**Q) * (real(RANGE_RPM) / real(2**ADC_BITS - 1)));
constant Q_HALF: natural := natural(real(2**Q * 0.5));

signal adc_pot: natural := 128;
signal setpoint_rpm: natural;

...

setpoint_rpm <= (adc_pot * Q_K + Q_HALF) srl Q;
\$\endgroup\$
1
  • \$\begingroup\$ Thank you very much for your detailed answer. These math steps above have cleared up my design problem I think. If I have anymore questions I will report back. Thanks once again! I never thought of shifting right and left to multiply / divide by 2 every time. \$\endgroup\$
    – David777
    Nov 10 '20 at 11:18
1
\$\begingroup\$

Why waste FPGA logic on such calculation? Use an SPI / I2C based potentiometer. Only two potential limitations would be that the speed of change of resistance and the rate at which the SPI / I2C can update the potentiometer value the second limitation being low wattage for this option.

I'm sure it would reduce BOM cost and also PCB space, along with better reliability.

Here is a SPI based pot from Microchip/

https://www.microchip.com/wwwproducts/en/MCP4131

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.