1
\$\begingroup\$

I need to model a digital control command for a power converter. To do this, I need to find the fixed-point coefficients of the control command in hexadecimal format. In the code that I am using, the ADC result is configured to be left-aligned so I need a Pre Left Shift of 3 and a Post Left Shift of 1.

To achieve this, I have pre-calculated the coefficients of the control command, which are floating-point decimal numbers that I need to convert into fixed hexadecimal numbers. The software that I use to do this is called ST-WDS and it uses the following formulas to do this:

The basic formula for digital control command is : $$ H_{compensation} = \frac{B2*z^2+B1*z+B0}{-A2*z^2-A1*z+1} $$.

In the software, the floating-point decimal numbers B2, B1, and B0 are divided by $$\frac{2^1*2^3}{K} \space{\space\space\space\space\space(1)}$$ to convert them into fixed decimal, then they are multiplied by $2ドル^{15}$$ to convert them into fixed hexadecimal.

The floating-point decimal numbers A2 and A1 are divided by $2ドル^{1}$$ to convert them into fixed decimal, and then they are multiplied by $2ドル^{15}$$ to convert them into fixed hexadecimal.

Here you can see what the software is calculating:

enter image description here

(1) K is the gain which negate the additional gains of the ADC and DAC

Can you please explain me the reasoning behind these calculations?

toolic
10.8k11 gold badges31 silver badges35 bronze badges
asked Jun 25 at 12:06
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

The only reason I see to do it this way is to use integer calculations in the controller, instead of floating point. Note that integer and fixed point are equivalent - you'd use the same integer operations for both.

Most likely, the controller doesn't have floating point hardware, and can't efficiently emulate it. So fixed point is used instead.

As for why the particular scaling values are used - they were selected so that the intermediate results:

  • don't overflow the accumulator used for computations,
  • don't lose too much precision due to truncation of significant bits of coefficients and intermediate results.

Frankly, if I was designing this software, asking the user for shift factors is a cop-out. This stuff should be computed automatically for best precision without overflow, and a performance report should be provided to show what are the control loop errors with given coefficients and given ADC input range.

devnull
11k2 gold badges20 silver badges46 bronze badges
answered Jun 25 at 12:17
\$\endgroup\$
0
\$\begingroup\$

The "controller" shows a general-purpose "biquad" filter section.

I don't know, offhand, how reasonable or effective this is for typical "control" applications (where an integral-dominant characteristic is common), but beware of quantization error; you may need an extension register, or wider format (e.g. 8.24 or 1.31 fixed point), to address this.

For general signal purposes, at least, I found this solution / approach effective:

Formulas are available here for example:
Biquad calculator v2 | EarLevel Engineering
The values are ready to use in the direct algebraic formula, such as with precision floating point, but need some adjustment for use in a fixed-point system.

I used the following bookmarklet to prepare the coefficients for this following system:

javascript:(function() {
 var count = 0, ret = ["", ""], n;
 document.getElementById('biquad_coefsList')
 .innerHTML.split(" ").forEach(function(x) {
 n = Math.round(16384 * Number.parseFloat(x));
 if (n) {
 if (count >= 3) {
 n = -n;
 }
 ret[0] += ", " + n;
 ret[1] += " " + (
 "0000" +
 (n + 0x10000).toString(16)
 ).slice(-4);
 count++;
 }
 } );
 ret = "a0, a1, a2, b1, b2:\n" + "Dec: " + ret[0].slice(2)
 + '\n' +"Hex: " + ret[1].slice(1);
 console.log(ret);
 alert(ret);
} )();

(Excuse the terseness, it is a bookmarklet after all; this is at least expanded a bit for readability.)

(For those unaware, a bookmarklet incorporates code into a bookmarked URI, so that one can call a utility function on demand. They're not very common circulation these days, I think, more of an old-school phenomenon; but this has worked for me on Chrome/Firefox as of some years ago, at least. If nothing else, the self-executing anonymous function's contents can be copied into and executed in the Debug Console on any desktop browser.)

The system in question (links to my GitHub):
https://github.com/T3sl4co1l/Reverb

I won't quote code samples here (as they're somewhat lengthy), but the relevant function/procedure lies in dsp.c starting from line 292 (including doc comment), or the AVR assembly equivalent in asmdsp.S starting from line 528 (including comments). Various alternative statements are commented out, reflecting the development of the function (and ASM optimization); there is a (more or less) pure-C implementation, then 2's-complement and bit magic (likely making various use of UB on avr-gcc 8; this wasn't very carefully written I think), then helper functions (e.g. mac32p16p16 performs a bit better for this special case than the built-in / library __mulhisi3 and related support stubs the compiler uses), then finally the full ASM equivalent.

The internal representation here is 2.14 fixed point, or signed 1.14, depending on your perspective (i.e., in the sense that 2's-complement has a sign bit). This is efficient for the AVR I was writing for, and practical for the 12-bit converters on the project. I specifically avoided a biased unsigned representation, as offsets would get weird, or incur additional arithmetic to deal with them (perhaps analogous to single-supply analog circuits needing more coupling capacitors and bias resistors).

The AVR used also offers signed a fixed-point arithmetic variant (FMUL(S(U))), though I didn't use it here. I believe the outcome was, I found it easier/more performant this way, due to arithmetic quirks of the FMULs, and register allocation, hence needing the 24-bit, two-shift fixup on line 632.

There are some other quirks in this design, such as the apparent 15-bit input (dsp.c lines 368-388 implement a boxcar decimation filter), a 7-bit shift in dspReverbTaps(), and overall gain set by dspMix(). Some of these account for input format, dynamic range (managing overflow/clipping without sacrificing LSBs), and output format is also accounted for in writeDac() (>> 3).

As you can see, there are many opportunities to adjust gain; which ones are needed, where, is something you'll have to consider in the design; or provide enough spare bits to be able to ignore -- or float the whole thing and accept the slower execution time!

With 32-bit registers (I assume, given the mention of the ST tool, you're working on ARM / STM32 something or other), a wider format is trivial, and can provide excellent dynamic range (necessary for long time constants, such as the dominant integrator term for controllers). Execution speed will also be sufficient* not to need ASM optimizations for most power control purposes (say, 100s kSps * few iterations of the function).

*By itself anyway, as long as you aren't chewing CPU cycles doing something else overly important. All considerations/requirements of hard real-time design apply here, of course.

answered Jun 25 at 14:45
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.