Automatic level adjustment

I've been running in circles with this one all day...

I'm using a digital potentiometer (AD5207), which seems to work quite nicely when entering the entering the registers by hand.

What I want to do is to increase the volume automatically until an analog input reaches a certain level. If the volume is < than desired, I want to turn the digital pot up. If the volume is > than the desired amount I want to turn it down.

I am monitoring the sensor level on a LCD display, so I can see the sensor output in real time.

I have tried these inside the void loop():

The below simplified step (just to see if the level will increase) will turn the volume up by one step only - from 0 to 1.

if (sensor < 100) 
{
L++ 
volume(0,L); // this is tied to the void volume, where (channel,level) is written to the pot
}

So I've tried a for loop, which works to step the level up automatically.

for (L=0; L<50; L ++)
{
volume(0,L);
delay (5000); // strange, but I have to delay by at least 5 seconds in order for the level to change
}

Then I tried this (to control the volume). My hope in the code below was once the sensor read 100, the for loop would terminate. It doesn't though, it'll continue until '50' then start again at 0.

for (L=0; L<50; L ++)
{
  volume(0,L);
  delay (5000); 
  if (sensor >=100)
     {
     break;
     }
}

I have also tried putting a for loop inside an if statement, which acted similarly to the first example, giving me one step only.

if (sensor <100)
  {
  for (L=0; L<50; L ++)
    {
    volume(0,L);
    delay (5000); // strange, but I have to delay by at least 5 seconds in order for the level to change
    }
 }

Perhaps I am missing something, or is there a better way to do this?

I like the for loop, as I can set the maximum window to 50 - and not worry about the volume accidentally going higher than that in case of a sensor fail.

I am not quite sure why the first if statement only increases the level one time. I thought it would increase the level each time the void loop() went by if the level was less than 100.

Any comments or help are greatly appreciated.

Scott

What is happening inside volume(): Why does it need a five second delay for your change to take effect?

I'm not quite sure why it's taking so long.

void volume(int reg, int level)
 {
  digitalWrite(CS,LOW);
  SPI.transfer(reg);
  SPI.transfer(level);
  digitalWrite(CS,HIGH);

 }

If I do not delay for 5 seconds after volume(n,n), the chip doesn't appear to be able to keep up. I find this strange, because it's very similar to the AD5206 which I've seen used in tutorials with much faster increment intervals.

Thanks!

I have tried these inside the void loop():

The first one won't even compile. There is a missing ;. Depending on how L is defined, it may be being set to 0 on every pass through loop, so incrementing by one on each pass will result in getting to 1 each time. This appears to coincide with your symptoms, so the problem lies outside this snippet.

Any comments or help are greatly appreciated.

Post ALL of your code.

Thank you for your help. I did find that I was setting L = 0 in the void loop rather than globally, causing the L to be reset each time and only give me a single ++.

The code below seems to work, BUT - I have to have the long delay in there in order for the level to step. I'm monitoring the same signals via scope, and can see that the PP signal (which comes from an opamp peak detect circuit) is stable and reacting properly to level changes, when they happen. I can also watch L, and see it increase and decrease dependent on the current PP level - IF the delay is over 5 seconds.

#include <SPI.h>
int countOk = 9; // Output high when PSI is within range
int shdn = 3; // pin to mute volume if necessary, by shutting down the AD5207 chip
int PSI_Peak = 1; // from the opamp peak detect circuit - signal is stable and clean on scope
int L=3; // start volume at 3

void setup ()
{
analogReference(DEFAULT);
pinMode (SS, OUTPUT);
pinMode (shdn,OUTPUT);
pinMode (countOk, OUTPUT);
//pinMode (PSI_Peak, INPUT);
//tone (8,60);
//delay (50000);
//Serial.begin(57600);
SPI.begin();
}

void volume(int reg, int level)
{
digitalWrite(SS,LOW);
SPI.transfer(reg);
SPI.transfer(level);
digitalWrite(SS,HIGH);
}

void loop()
{
digitalWrite (shdn, HIGH); // enable AD5207
float PP = analogRead(PSI_Peak); // This is the volume level sensor - 0 to 5V

/*
// for serial testing only
Serial.print("L");
Serial.println(L);
Serial.print("PP");
Serial.println(PP);
*/

volume(0,L); // write channel 0, L to AD5207 via SPI

if (PP >= 0 && PP <= 75 && L <=50)
{
L++;
}
else if (PP >= 80 && L > 1)
{
L--;
}
delay(6000); // If I make this smaller, the AD5207 seems to get confused

//END

}

With a shorter delay, L will step anywhere from one to three times, then go to zero. sometimes after about ten minutes, it'll pop back up to some random number and go through another one to three cycles before going back to zero.

I was using the tone function, but I have it commented out this morning in case it was interfering with the SPI on pin 11. This doesn't seem to have changed it at all - but the possibility that it might allowed me to sleep last night!

I have also tried declaring pinMode for PSI-Peak and commenting it out - doesn't change anything.

Thanks for having a look!
Scott

Can you clarify - is the delay necessary for your output to go to the correct level, or for you input to read that level correctly?

Without the delay, the spi write to volume does not happen (or at least doesn't take), thus the volume does not increase or decrease.

I can verify this by removing the level requirements, and allowing L to increment from 0. With no delay, the increment might happen anywhere from one to three times.

just a simple code as below does not work.

void loop()
{

L++;
volume(0,L);

delay(50);
}

But this does!

void loop()
{

L++;
volume(0,L);

delay(5000);
}

Thanks again for trying to help, I really appreciate it.

Scott

float PP = analogRead(PSI_Peak); // This is the volume level sensor - 0 to 5V

The analogRead function returns an integer in the range 0 to 1023. Why are you storing it in a float?

if (PP >= 0 && PP <= 75 && L <=50)
{
  L++;
}
else if (PP >= 80 && L > 1)
{
  L--;
}

Then, you are comparing the float to a bunch of ints...

The code below seems to work, BUT - I have to have the long delay in there in order for the level to step. I'm monitoring the same signals via scope, and can see that the PP signal (which comes from an opamp peak detect circuit) is stable and reacting properly to level changes, when they happen. I can also watch L, and see it increase and decrease dependent on the current PP level - IF the delay is over 5 seconds.

If you comment out the SPI.transfer calls, and manually adjust the volume, is the PP value correctly, and quickly, following the volume? Or, is the problem really that the PP value change seriously lags an actual volume change?

In other words, are we solving a software or a hardware problem?

Thanks PaulS,

The float PP was something I was trying, it works the same if I replace float with int.

The PP value (which is the signal from the opamp peak detect circuit) follows the signal very quickly. The capacitor in the circuit is not very large. I can watch it on the scope,
When the level increases, PP increases almost instantly, when the level decreases the PP value follows within a few ms.

As mentioned above, when I remove PP all together from the code, and just tell the volume L to increase, the delay is required for the digital pot to change properly.

For what it's worth, I can see on the scope that SS is being asserted low then high no matter what the delay is (long or short) .

The signal that I am automatically adjusting the level for is a clean 60Hz sine wave. I need to keep the peak amplitude at a certain level.

Thanks

As mentioned above, when I remove PP all together from the code, and just tell the volume L to increase, the delay is required for the digital pot to change properly.

6 seconds, though? That seems like a very long time to me.

The data is shifted out to the device when the SPI.transfer() function ends, so the delay isn't required because of SPI.

One thing that I note, though, is that SPI.transfer() takes a byte, and you are passing it an int. In general, it is best to use the proper data type(s) throughout your code.

Thanks for all the help guys. I've fixed the problem by replacing the AD5207 chip. The code above that worked with the long delay now works well with a delay(50);

I've gone through my code, used byte for L and int for PP, which also works (of course)...

Thanks again.

Scott

Depending on the response time of the rest of the system, you may need to delay so that you have time for your change to take effect.

I once was trying to control a dryer in a ceramic machine. The heat would go up and down and up and down and sometimes would stabilize briefly. If I took more time between readings the system behaved much better. and would actually stabilize in a period of 15 to 20 minutes. WHat you need to do is have enough time for your changes to take effect before you attempt to make another change. If it takes 5 seconds for the system to respond to your changes then you need to slow down your control loop to match the system conditions.