RobTillaart / AD985X

Arduino library for AD9850 and AD9851 function generators.
MIT License
12 stars 2 forks source link

Multi-AD985X how to do #13

Closed RobTillaart closed 3 years ago

RobTillaart commented 3 years ago

In issue #9 (decimals in frequency) a discussion started how to connect multiple AD985X devices to one MCU.

Problem description

The problem identified is that the device does NOT have a Chip Select pin (CS) like many SPI devices do have. So sharing the DATA and CLOCK lines would result in preparing all AD985X devices with a new frequency.

How bad is that? As long as they do not get an update pulse it should not matter, however it is not a nice as an glitch of the FQ_UD line (also not nice) would update to an unexpected frequency. So we need better solutions.

Note the library already has a select pin in the begin() function to be "SPI compatible"

RobTillaart commented 3 years ago

Trivial solution

The trivial solution is to give every AD985X device a unique set of pins. By keeping the sets of pins disjunct there will be no interference between devices. It would mean that every device (besides VCC and GND) need 4 lines DATA, CLOCK, FQ_UD and RESET.

RobTillaart commented 3 years ago

Sharing lines

(this merely repeats what is said in #9 to keep all remarks together in one issue)

It is possible to share the DATA line without problems as long as these are only clocked in by the right device.

This implies that the CLOCK line should only reach the right device if the device is selected. This can be done by adding a logic port that ANDs the SELECT and the CLOCK.

This AND port effectively acts as a single gate in a multiplexer controlled by the devices SELECT line.

The RESET line can be multiplexed by the devices in the same way

The FQ_UD (frequency update) line can be multiplexed by the devices in the same way.

(update) The DATA line could also be multiplexed in this way, As 2 input AND port e.g. 74HC08 come per 4 in an IC this would mean one IC per device.

About SELECT active level

In- https://www.analog.com/en/analog-dialogue/articles/introduction-to-spi-interface.html# and other sources, it is described that SELECT LOW normally means that the device is selected / active.

This would imply that we need an INVERTER before applying the select signal to the AND port. As the select line is not really used yet this can be done in software. This way it simulates a SPI device with an active HIGH. This would not break the behavior when controlling a single device use, so acceptable.

RobTillaart commented 3 years ago

FQ_UD - frequency update

It needs to be investigated if the FQ_UD pin really needs to be multiplexed. If a device gets a FQ_UD pulse it will use the frequency as written in the register. As we do not write a new value in the devices (except 1) there is no change expected. The FQ_UD pulse should pulse in sync with the devices select line.

It would make the hardware simpler (which is good) and it would allow some extra functionality

  1. one could start a new frequency on multiple devices simultaneously
  2. one could delay the start of devices after "preloading" a new frequency quite quickly.

To use this a update() function should be implemented and a way to set the FQ_UD to manual mode.

_This manual mode will be implemented asap as (2) would be interesting for single devices too, and even when you have disjunct devices (trivial solution) that would share the FQUD pin.

RobTillaart commented 3 years ago

TODO prep 0.2.3

~- [ ] add a flag to update() to disable the select lines - to update all.~

~- [ ] add a flag to reset to disable the select lines - to reset all.~


update 2021-06-05

new insights in architecture will not add the flags. Will be discussed here soon.

CHEERScheers commented 3 years ago

Hi, Rob, Thank you very much for your method of controlling 3 devices. I followed by your method and code, but I fail to update the frequencys of 3 devices, the wave of which is not continuous, I think is the problem of frequency update wrong. below is my code, I will appreciate if you check it for me.

`#include "AD985X.h"

AD9850 freqGen;

uint32_t freq1 = 25000;
uint32_t freq2 = 30000;
uint32_t freq3 = 40000;
uint32_t prev = 10000;
uint32_t maxFreq = 30000000;
unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
unsigned long previousMillis3 = 0;
const long period1 = 0;
const long period2 = 0;
const long period3 = 0;
unsigned long currentMillis = millis();

void setup()
{
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("AD985X_LIB_VERSION: \t");
  Serial.println(AD985X_LIB_VERSION);

  freqGen.begin(2, 9, 10);
  freqGen.powerUp();
  maxFreq = freqGen.getMaxFrequency();
  Serial.println(maxFreq);
}

void loop()
{
 if (currentMillis - previousMillis1 >= period1) {
     int select = 2;
       if (prev != freq1){
           prev = freq1;
           freqGen.setFrequencyF(freq1 * 0.01);
           Serial.println(freq1);
           previousMillis1 = currentMillis;
       }
 }
 if (currentMillis - previousMillis2 >= period2) {
     int select = 3;
       if (prev != freq2){
           prev = freq2;
           freqGen.setFrequencyF(freq2 * 0.01);
           Serial.println(freq2);
           previousMillis2 = currentMillis;
       }
 }
 if (currentMillis - previousMillis3 >= period3) {
     int select = 3;
       if (prev != freq3){
           prev = freq3;
           freqGen.setFrequencyF(freq3 * 0.01);
           Serial.println(freq3);
           previousMillis3 = currentMillis;
       }
  }
 }

updated your post to do syntax highlighting.

RobTillaart commented 3 years ago

@CHEERScheers it is on my todo list to describe it well, but so are many other things. I will try to come back to this issue later this week

RobTillaart commented 3 years ago

@CHEERScheers had a quick look at your code and you use only 1 device that uses 3 different updaters.

You should use 3 devices when you want 3 devices....

CHEERScheers commented 3 years ago

Thank you Rob for your quick reply. But I am using 3 devices, I am confused about how to update. What I want to do is to let the 3 devices output 3 different frequency, need I use 3 Arduino? image

@CHEERScheers had a quick look at your code and you use only 1 device that uses 3 different updaters.

You should use 3 devices when you want 3 devices....

CHEERScheers commented 3 years ago

Hi, Rob, I tried like this. I can get 3 different frequency waves from 3 AD9850, but I have to flash the Arduino 3 times with 3 different frequencys one by one, if I power off the Arduino, then power on the Arduino, only one AD9850 works with the last flashed program. I think there's only one frequency in the register, I cannot control the 3 AD9850 with only one program...

CHEERScheers commented 3 years ago

I rewrite the codes, use the codes below can get different frequency waves perfectly.

#include "AD985X.h"

AD9850 freqGen;

uint32_t freq1 = 25000;
uint32_t freq2 = 30000;
uint32_t freq3 = 40000;
uint32_t prev = 0;
uint32_t maxFreq = 30000000;

void setup()
{
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("AD985X_LIB_VERSION: \t");
  Serial.println(AD985X_LIB_VERSION);

  freqGen.begin(2, 9, 10);
  freqGen.powerUp();
  freqGen.setFrequencyF(freq1 * 0.01);
  freqGen.begin(3, 9, 10);
  freqGen.powerUp();
  freqGen.setFrequencyF(freq2 * 0.01);
  freqGen.begin(4, 9, 10);
  freqGen.powerUp();
  freqGen.setFrequencyF(freq3 * 0.01);
  maxFreq = freqGen.getMaxFrequency();
  Serial.println(maxFreq);
}
void loop(){}
RobTillaart commented 3 years ago

@CHEERScheers you made a step in the right direction

please try this sketch (not tested beyond compilation)

!! disclaimer: I do not expect the code to work yet

//
//    FILE: AD985X_multi.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo multi device
//    DATE: 2021-06-04

#include "AD985X.h"

// we want to control three hardware devices
// so we declare three software objects
AD9850 freqGen0;
AD9850 freqGen1;
AD9850 freqGen2;

float    freq0 = 25000;
float    freq1 = 30000;
float    freq2 = 40000;
uint32_t maxFreq = 30000000;

uint32_t previousMillis0 = 0;
uint32_t previousMillis1 = 0;
uint32_t previousMillis2 = 0;

uint32_t period0 = 500;
uint32_t period1 = 1000;
uint32_t period2 = 2000;

uint32_t now;

void setup()
{
  // OPEN SERIAL for messages and debugging etc
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("AD985X_LIB_VERSION: \t");
  Serial.println(AD985X_LIB_VERSION);

  // initialize three devices
  freqGen0.begin(4, 9, 10);
  freqGen0.powerUp();
  freqGen1.begin(2, 9, 10);
  freqGen1.powerUp();
  freqGen2.begin(3, 9, 10);
  freqGen2.powerUp();

  // MAXFREQ is the same for all devices
  maxFreq = freqGen0.getMaxFrequency();
  Serial.println(maxFreq);
}

void loop()
{
  // get the time
  now = millis();

  // do we need to update 0
  if (now - previousMillis0 >= period0)
  {
    previousMillis0 = now;
    freq0 = freq0 * 0.01;
    if (freq0 >= maxFreq) freq0 = 500;
    freqGen0.setFrequencyF(freq0 * 0.01);
    Serial.println( (uint32_t) freq0);
  }

  // do we need to update 1
  if (now - previousMillis1 >= period1)
  {
    previousMillis1 = now;
    freq1 = freq1 * 0.01;
    if (freq1 >= maxFreq) freq1 = 500;
    freqGen1.setFrequencyF(freq1 * 0.01);
    Serial.println( (uint32_t) freq1);
  }

  // do we need to update 2
  if (now - previousMillis2 >= period2)
  {
    previousMillis2 = now;
    freq2 = freq2 * 0.01;
    if (freq2 >= maxFreq) freq2 = 500;
    freqGen2.setFrequencyF(freq2 * 0.01);
    Serial.println( (uint32_t) freq2);
  }

}

// -- END OF FILE --

As you see there is quite some repeating code and variables, To minimize that we can start using array's

RobTillaart commented 3 years ago

@CHEERScheers The array version, not tested beyond compilation

!! disclaimer: I do not expect the code to work yet

//
//    FILE: AD985X_array.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo multi device
//    DATE: 2021-06-04

#include "AD985X.h"

// three objects in an array
AD9850   freqGen[3];
uint8_t  PIN[3]      = { 2, 3, 4 };
float    freq[3]     = { 25000, 30000, 40000 };
uint32_t previous[3] = { 0, 0, 0 };
uint32_t period[3]   = { 500, 1000, 2000 };
uint32_t maxFreq     = 30000000;
uint32_t now;

void setup()
{
  // OPEN SERIAL for messages and debugging etc
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("AD985X_LIB_VERSION: \t");
  Serial.println(AD985X_LIB_VERSION);

  // initialize three devices
  for (int i = 0; i < 3; i++)
  {
    freqGen[i].begin(PIN[i], 9, 10);
    freqGen[i].powerUp();
    freqGen[i].setFrequencyF(freq[i]);
  }

  maxFreq = freqGen[0].getMaxFrequency();
  Serial.print("MAXFREQ:\t");
  Serial.println(maxFreq);
}

void loop()
{
  now = millis();

  // update any of the devices?
  for (int i = 0; i < 3; i++)
  {
    if ( now - previous[i] >= period[i] )
    {
      previous[i] = now;
      freq[i] = freq[i] * 0.01;
      freqGen[i].setFrequencyF(freq[i] * 0.01);
      Serial.print("FREQ ");
      Serial.print(i);
      Serial.print(" set to: ");
      Serial.println( (uint32_t) freq[i]);
    }
  }

}

// -- END OF FILE --
RobTillaart commented 3 years ago

Note: expect the code only works if the FQ_UD pin is behind an AND port that "opens" the port only for the FQ_UD signal to reach the selected device


     Arduino              AND             AD985X
    --------------------------------------------------

                       +--------+
        (2) select ----| A      |
                       |      Q |------- FQ_UD   AD985X[0]
        (9) FQ_UD  ----| B      |
                       +--------+

                       +--------+
        (3) select ----| A      |
                       |      Q |------- FQ_UD   AD985X[1]
        (9) FQ_UD  ----| B      |
                       +--------+

                       +--------+
        (4) select ----| A      |
                       |      Q |------- FQ_UD   AD985X[2]
        (9) FQ_UD  ----| B      |
                       +--------+

This way only the device selected will get an update pulse

CHEERScheers commented 3 years ago

@Rob I tried your code, but there's no waves output. The RX led is always twinkling. please see the video. RX

RobTillaart commented 3 years ago

Two questions:

1) you had it working with this code?

  freqGen.begin(2, 9, 10);
  freqGen.powerUp();
  freqGen.setFrequencyF(freq1 * 0.01);
  freqGen.begin(3, 9, 10);
  freqGen.powerUp();
  freqGen.setFrequencyF(freq2 * 0.01);
  freqGen.begin(4, 9, 10);
  freqGen.powerUp();
  freqGen.setFrequencyF(freq3 * 0.01);

2) can you post a drawing of your hardware used / how connected?

RobTillaart commented 3 years ago

code missed one + operator therefor all frequencies dropped to zero

please try this version (works here with one device)

//
//    FILE: AD985X_array.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo multi device
//    DATE: 2021-06-04

#include "AD985X.h"

// three objects in an array
AD9850   freqGen[3];
uint8_t  PIN[3]      = { 2, 3, 4 };
float    freq[3]     = { 25000, 30000, 40000 };
uint32_t previous[3] = { 0, 0, 0 };
uint32_t period[3]   = { 500, 1000, 2000 };
uint32_t maxFreq     = 30000000;
uint32_t now;

void setup()
{
  // OPEN SERIAL for messages and debugging etc
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("AD985X_LIB_VERSION: \t");
  Serial.println(AD985X_LIB_VERSION);

  // initialize three devices
  for (int i = 0; i < 3; i++)
  {
    freqGen[i].begin(PIN[i], 9, 10);
    freqGen[i].powerUp();
    freqGen[i].setFrequencyF(freq[i]);
  }

  maxFreq = freqGen[0].getMaxFrequency();
  Serial.print("MAXFREQ:\t");
  Serial.println(maxFreq);
}

void loop()
{
  now = millis();

  // update any of the devices?
  for (int i = 0; i < 3; i++)
  {
    if ( now - previous[i] >= period[i] )
    {
      previous[i] = now;
      freq[i] += freq[i] * 0.01;
      freqGen[i].setFrequencyF(freq[i]);
      Serial.print("FREQ ");
      Serial.print(i);
      Serial.print(" set to: ");
      Serial.println( (uint32_t) freq[i]);
    }
  }

}

// -- END OF FILE --
RobTillaart commented 3 years ago

image

CHEERScheers commented 3 years ago

Hi, Rob, I used your code, the frequency will increase to a large number with no regulation. So I delet 2 line of your code: freqGen[i].setFrequencyF(freq[i]); // this line is in void setup() freq[i] += freq[i] * 0.01;// this line is is in void loop()

With the code below which is based on your code, the 3 different frequency waves runs perfectly.

#include "AD985X.h"

// three objects in an array
AD9850   freqGen[3];
uint8_t  PIN[3]      = { 2, 3, 4 };
float    freq[3]     = { 25000, 30000, 40000 };
uint32_t previous[3] = { 0, 0, 0 };
uint32_t period[3]   = { 500, 1000, 2000 };
uint32_t maxFreq     = 30000000;
uint32_t now;

void setup()
{
  // OPEN SERIAL for messages and debugging etc
  Serial.begin(115200);
  Serial.println(__FILE__);
  Serial.print("AD985X_LIB_VERSION: \t");
  Serial.println(AD985X_LIB_VERSION);

  // initialize three devices
  for (int i = 0; i < 3; i++)
  {
    freqGen[i].begin(PIN[i], 9, 10);
    freqGen[i].powerUp();
  }

  maxFreq = freqGen[0].getMaxFrequency();
  Serial.print("MAXFREQ:\t");
  Serial.println(maxFreq);
}

void loop()
{
  now = millis();

  // update any of the devices?
  for (int i = 0; i < 3; i++)
  {
    if ( now - previous[i] >= period[i] )
    {
      previous[i] = now;
      freqGen[i].setFrequencyF(0.01*freq[i]);
      Serial.print("FREQ ");
      Serial.print(i);
      Serial.print(" set to: ");
      Serial.println( (uint32_t) freq[i]);
    }
  }
}
RobTillaart commented 3 years ago

updated your post to highlight the syntax

But good to hear you have it working. If there is no related question you may close the issue.

CHEERScheers commented 3 years ago

Thank you very much!

RobTillaart commented 3 years ago

reopened as I still need to add some documentation ....

rob commented 3 years ago

Thanks Rob.

On Fri, Jun 4, 2021 at 8:40 AM Rob Tillaart @.***> wrote:

reopened as I still need to add some documentation ....

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/RobTillaart/AD985X/issues/13#issuecomment-854685987, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAAD54HD75CDW3BWJXPOXLTRDCVJANCNFSM4WQNZ36A .

RobTillaart commented 3 years ago

@CHEERScheers released 0.3.0 including the document about multi device.

navypro1991 commented 2 years ago

Hi RobTillaart I have a project that generates 2 sine signals from 2 AD9850/9851 modules with the same frequency but different phase. Your library doesn't say this. In the AD9850 multi_sync library the solution for the SELECT pin is coupled to which pin? Can you explain it Thank!

RobTillaart commented 2 years ago

Thanks for the question.


You can use any free IO Pin of your processor as SELECT line You control the device with

pinMode(pinDeviceX, OUTPUT);
pinMode(pinDeviceY, OUTPUT);
...
digitalWrite(pinDeviceX, HIGH); 
set the deviceX settings
digitalWrite(pinDeviceX, LOW); 
...
digitalWrite(pinDeviceY, HIGH); 
set the deviceY settings
digitalWrite(pinDeviceY, LOW); 
...

For each extra device you need an additional IO pin. Be careful to select only one device at the time.

navypro1991 commented 2 years ago

We use an AND gate to create the SPI interface. But I don't know what pin of the AD9850 will be paired with the SELECT pin to control it. Can you help me explain. Thank you!

RobTillaart commented 2 years ago

It is all explained in the PDF. you MUST at least control the CLOCK pin with an AND port as this prevents that the non addressed devices are clocking in some data not meant for them.

navypro1991 commented 2 years ago

Thank you very much, I will test it and if there is any problem, I hope you can help

RobTillaart commented 2 years ago

Best is to find someone local with electronics experience (student or professional) that can help on-site. That is many times faster than online (and is probably more fun). Furthermore the Arduino forum has way more people on it that have the expertise to help.

But feel free to ask if questions remain.

Tip of the day: https://www.amazon.com/Practical-Electronics-Inventors-Fourth-Scherz/dp/1259587541

navypro1991 commented 2 years ago

Thank you so much!

Vào Th 2, 4 thg 7, 2022 vào lúc 15:22 Rob Tillaart < @.***> đã viết:

Best is to find someone local with electronics experience (student or professional) that can help on-site. That is many times faster than online (and is probably more fun). Furthermore the Arduino forum has way more people on it that have the expertise to help.

But feel free to ask if questions remain.

Tip of the day:

https://www.amazon.com/Practical-Electronics-Inventors-Fourth-Scherz/dp/1259587541

— Reply to this email directly, view it on GitHub https://github.com/RobTillaart/AD985X/issues/13#issuecomment-1173509244, or unsubscribe https://github.com/notifications/unsubscribe-auth/ARUYH7IXRNE5HDGPBUZSUBDVSKNOFANCNFSM4WQNZ36A . You are receiving this because you commented.Message ID: @.***>

-- С уважением, Нгуен Дык Ань, Вьетнамская Военно-Морская Академия Email: @.*** Tel: +84-984.660.342 +7-960.040.7328