Post Go back to editing

Real Time Control TUTORIAL: SAFELOAD, Fixing and Breaking variables

Hi There, how are you all?

I've seen around here many people having trouble to control Sigma DSPs in real time. So I decided to write a little "walkthrough" guide. Let's get started:

Now that you SOMEHOW came up with your IIR filter coefficients, you surely would like to load them into the DSP, right? GOOD!

If you don’t know how to come up with the coefficients, you can do it by hitting the books and doing the math, using  Matlab’s FDATOOL, using GNU Octave or being struck by lightning. If you know any other method, please let me know!

Three steps are necessary:

1 – Conversion from Floating point to 5.23 Fixed point

2 – Breaking of 32-bit values into 4x 8-bit values that can be sent via I2C / SPI

3 – Safeload the content, so it can be dynamically updated in real time.

STEP ONE:

You must convert the floating point numbers, like 1.2934645 to FIXED point, simply because SigmaDSPs are fixed-point.

That’s EASY! All you have to do is multiply it by 0x00800000 (hexadecimal) or 8388608 (decimal). Yep, they’re the same.

So, let’s say you just did 1.2934645 * 8388608 = 10,850,366.

Floating point: 1.2934645

5.23 Fixed point: 10,850,366.

Happy!?  Well, I’m not, I wish these were dollars in my bank account.

STEP TWO:

Now you see, this is a BIG number. Big numbers need big variables to hold them, and you’re likely to be using a 32-bit long one to hold it. So, we must break this 32-bit big fat variable in four skinny 8-bit variables. That is necessary because the I2C / SPI buses are 8-bit wide.

What we need:

0x00, 0xA5, 0x90, 0x3E

Several methods exists to break 32-bit variable into 4x 8-bit variables. There’s one bellow (works only in BYTE ADDRESSABLE systems), yeah, the code looks dumb for the sake of simplicity, so it has no loops to automate the process:

#include //Everything you need. For the program!

float Value32=1.254789; //Random float value

unsigned char *pValue32; //Pointer to that value

unsigned char Value8_pt1=0;  // 4 x Chars to "carry" the broken Float value

unsigned char Value8_pt2=0;

unsigned char Value8_pt3=0;

unsigned char Value8_pt4=0;

void main (void)

{

//Lets break the initial float value in 4x 8-bit parts:

pValue32 = (unsigned char *) &Value32; //Receives the address of the initial Float variable

Value8_pt1 = *pValue32; //Receives bits 0 to 7

pValue32++; //Increment pointer

Value8_pt2 = *pValue32;//Receives bits 8 to 15

pValue32++; //Increment pointer

Value8_pt3 = *pValue32;//Receives bits 16 to 23

pValue32++; //Increment pointer

Value8_pt4 = *pValue32;//Receives bits 24 to 31

}

STEP THREE:

In order to load this data into SigmaDSP in real time without causing clicks, noises and unstable behavior, we must use the SAFELOAD method.

For ADAU1442/5/6, Software Safeload method must be used. You can find it, kindly posted by our friend Brett, here:

http://ez.analog.com/message/59251#59251

If you’re using older ones, like ADAU1701 for example, you must utilize the Hardware Safeload method that I’ll describe now:

The Safeload can load up to FIVE sections at once. Why? Because an IIR filter, the so called biquad, needs five coefficients: B0, B1, B2, A1, A2.  5 sections =  5 x 4 bytes. So up to 20-bytes can be safeloaded in a single shot.

For each section there is a Safeload ADDRESS register and a Safeload DATA register that are directly related to each other.  Let’s call them “The parents”. They can only work together.

When you want to load a coefficient, you want that to reach a certain memory space in the parameter ram memory of the DSP.

If you perform a write directly to that desired memory space, unwanted effects will happen, such as clicks and pop noises.

The DSP Core Control Register. It has a bit that you must “flip” in order to trig the safeload transfer. So the whole deal is:

2- Write the Safeload DATA register with the data that you want to load into the Parameter RAM memory space

3- Trigger the Safeload transfer by flipping the Bit 5 of the Core Control Register. ONLY AFTER YOU’RE DONE LOADING ALL SECTIONS.

Let me make a note here because it sounds confusing:

You write the Safeload DATA register with THE DATA you would like to load TO the Parameter RAM memory. So THIS write contains the "safeload data register" address and the DATA itself, that can't be more than 4 bytes long. Actually it is 28-bit long, but that doesn't really matter... By the way, attention to the "Dummy" byte 3, it MUST be there ok?

As I said before, we have 5 safeload sections, each section can safeload 4-bytes that corresponds to ONE coefficient, so, FOR EACH coefficient of a biquad, you must perform the procedure above. As the very last part, trigger the Safeload action by flipping the DSP Core Control Register bit. Here it is:

You can write the whole register, which is 16-bit long, no problem. The DSP will continue the operation normally during the write and it will not reset or cause any noises. After you're finished writing this register, you should immediately see the effect of your new coefficients loaded into the DSP!

That's all folks! I sincerely hope it's not much overwhelming and that it is clear enough. Please point out any mistakes (even grammar mistakes) because I did not revised this yet so I'm pretty sure it's far from perfect. Another thing is, if it lacks in clarity please show me where and why so I can fix it.

Cheers!!!

• Hi,

Thanks for the help.  I'm just venturing into those microcontroller ==> ADAU1701 writes as we speak, and your tutorial has made the process a lot more understandable.

• Great! Post progress... It's not really difficult, just a little tricky. If you are able to write your own I2C functions, everything else is just a matter of time and patience.

•      Hi,

It turns out you need not get hit by lightning to obtain the bi quad coefficients -- I shamelessly lifted the formulas from Brett G.'s spreadsheet available here:  http://ez.analog.com/docs/DOC-1819 .

It was fairly easy to code these into C, then with help from your tutorial I converted the resulting floating point numbers into bytes.  The entire function is less than a page long but it barely fits into a PIC16F877A since it calls log and trig functions from my compiler's math lib.  Fortunately we plan on using a PIC18 series micro for the actual product.

The one thing I haven't done is the code to actually load the hex bytes into the DSP -- but a friend of mine here is expert with such things so I'll let him take care of that.

Again, thanks so much for your tutorial!!!

Bob

• Hey Robert, you're very welcome!

Neither PIC16 or PIC18 are well suited for the job. Why don't you go for a PIC24F that has lots of memory and costs about the same, sometimes even less than the PIC18?

Hey, BTW, I've been having some troubles with these formulas from Brett, weird gain variations! I created a post here:

Are you having the same problem?

Thanks a lot!

•      Hi Fernando,

Thanks for the idea of switching to a larger PIC.  Since my previous post I substituted a look-up table with pre-calculated values  for the sine and cosine functions; that cut my ROM usage in half.

So far I haven't found any gain-shifting problems with the parametric filter I've been working with, however I just started to explore its operation.

Best regards,

bob

• Hi!

Thanks for the great tutorial. I was wondering how would you convert the negative coefficients of IIR filters?

Thanks!

Vincent

•      Hi,

Often you need not do anything special to handle negative coefficients.  SigmaDSPs represent negative numbers as two's complement, the same as many C compilers.  Thus going from one to the other is seamless.  For info on number formats in SigmaDSP, see What are the number formats for SigmaDSP?

For example, this early version of a parametric EQ coefficient function works without any logic to separate negative from positive coefficients:

Best regards,

Bob