Transducer Matrix Delay

I am creating a 2x2 transducer matrix where each transducer should generate a 40kHz signal. I want the matrix to be able to produce a delay in all directions, starting from left to right and from right to left. This means that for example, the first column would produce a signal simultaneously, then after 200us, the second column will produce a signal simultaneously.

Here is my current code:

const int transducerPins[2][2] = {{9, 10}, {11, 12}};
const int numRows = 2;
const int numCols = 2;

// Edit delay required here
const unsigned int delayMicros = 200; 

// Variables to track delay, current row, current column, and direction
volatile unsigned long nextToggleTime = 0; 
volatile int currentRow = 0;
volatile int currentCol = 0;
volatile bool directionRight = false; // true for left-to-right, false for right-to-left

void setup() {
  // Set the transducer pins as output
  for (int i = 0; i < numRows; i++) {
    for (int j = 0; j < numCols; j++) {
      pinMode(transducerPins[i][j], OUTPUT);
    }
  }

  // Configure timer1 for 40kHz
  noInterrupts();           // Disable all interrupts
  TCCR1A = 0;               // Clear Timer1
  TCCR1B = 0;
  TCNT1  = 0;               // Initialize counter value to 0
  OCR1A = 200;              // Set compare register (16MHz / 200 = 80kHz square wave -> 40kHz full wave)
 
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS10);    // Set prescaler to 1 -> no prescaling
  TIMSK1 |= (1 << OCIE1A);  // Enable Timer1 compare interrupt
  
  interrupts();             // Enable all interrupts
}

ISR(TIMER1_COMPA_vect) {    // At each interrupt, do the following
  unsigned long currentTime = micros();
  
  // Check if the current time has reached or passed the nextToggleTime
  if (currentTime >= nextToggleTime) {
    // Toggle the current pin
    int pinToToggle = transducerPins[currentRow][currentCol];
    // PORTB manipulation
    if (pinToToggle >= 8 && pinToToggle <= 13) {  // pins 8 to 13 are on PORTB
      pinToToggle -= 8;  // Convert to PORTB bit position
      PORTB ^= (1 << pinToToggle);  // Toggle the specific pin
    }
    
    // Set the next toggle time
    nextToggleTime = currentTime + delayMicros;
    
    // Move to the next pin based on the current direction
    if (directionRight) {
      currentCol++;
      if (currentCol >= numCols) {
        currentCol = numCols - 1;
        currentRow++;
        if (currentRow >= numRows) {
          currentRow = numRows - 1;
          directionRight = false;
        }
      }
    } else {
      currentCol--;
      if (currentCol < 0) {
        currentCol = 0;
        currentRow--;
        if (currentRow < 0) {
          currentRow = 0;
          directionRight = true;
        }
      }
    }
  }
}

void loop() {

}

Why do I have 2 problems:

  1. The signal generated is not a constant 40kHz amongst the pair of transducers generating a signal at the same time
  2. The pair generating at the same time is not simultaneous. There is a slight delay among them. I used direct port manipulation to prevent this but the problem still persists.

That is a long ISR. It seems unlikely that all the operations can be completed in less than 12.5 usec, minus interrupt latency.

You probably need a faster MCU, or a much more efficient style of coding.

Consider something along these lines, which will toggle PORTB, pins 0 to 3 successively, in successive timer interrupts (may need different transducer wiring):

ISR(TIMER1_COMPA_vect) {    // At each interrupt, do the following
static byte bit_to_toggle = 1;
PINB = bit_to_toggle; //toggle that bit (works on AVR MCUs)
bit_to_toggle <<= 1; //point to next higher bit
if (bit_to_toggle & 32) bit_to_toggle=1; //recycle
}

Here is what the output on PORTB looks like for that code, using your timer settings:

If you use a timer to trigger the interrupt you don't need to check if the time has passed to change the state of the pins.
That's probably why the frequency is not constant.

With each interrupt the state change must be done.

Thank you for your reply! What would that code look like?

Thank you! When I use your code as the ISR, why isn't the frequency 40kHz? Also, what software are you using and how did you set it up with the arduino?

Same as #1 but without using micros(), currentTime and nextToggleTime.

Because only one of four port pins is toggled per timer interrupt, every 12.5 usec. You should be able to work out the timing from that. Change this line to suit:

OCR1A = 200;              // Set compare register (16MHz / 200 = 80kHz square wave -> 40kHz full wave)

Edit: there is an error in the ISR as posted above, and it addressed 5 port pins. The code should instead be the following, then the frequency produced by each pin will be 10 kHz.

ISR(TIMER1_COMPA_vect) {    // At each interrupt, do the following
static byte bit_to_toggle = 1;
PINB = bit_to_toggle; //toggle that bit (works on AVR MCUs)
bit_to_toggle <<= 1; //point to next higher bit
if (bit_to_toggle & 16) bit_to_toggle=1; //recycle
}

I used an inexpensive logic analyzer and PulseView to sample, record and display the output of port B.

I'm now just trying to get a delay in a row of transducers at 40kHz. Here is my current code:

const int transducerPins[] = {9, 10, 11, 12}; 

void setup() {
  // Set the transducer pins as output
  for (int i = 0; i < 4; i++) {
    pinMode(transducerPins[i], OUTPUT);
    digitalWrite(transducerPins[i], LOW); // Initialize pins to LOW
  }

  // Configure Timer1 for 40kHz
  noInterrupts();           // Disable all interrupts
  TCCR1A = 0;               // Clear Timer1
  TCCR1B = 0;
  TCNT1  = 0;               // Initialize counter value to 0

  // Set compare match register for 40kHz increments
  OCR1A = 199;              // (16MHz / (40kHz * 2)) - 1 = 199

  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS10);    // No prescaling
  TIMSK1 |= (1 << OCIE1A);  // Enable Timer1 compare interrupt

  interrupts();             // Enable all interrupts
}

ISR(TIMER1_COMPA_vect) {
  static byte bit_to_toggle = 1; // Start with the first bit (corresponding to pin 8)
  PINB = bit_to_toggle;          // Toggle that bit (works on AVR MCUs)
  bit_to_toggle <<= 1;           // Point to the next higher bit
  if (bit_to_toggle & 32)        // Recycle when bit goes beyond bit 3 (pin 11)
    bit_to_toggle = 1;           // Reset to the first bit
}

void loop() {
  // Main loop does nothing, all action is in the ISR
}

However, it seems like this output is 10kHz. Could it be a limitation of the arduino?

I also have this code:

const int transducerPins[] = {9, 10, 11, 12};
const int numPins = 4;

// Edit delay required here
const unsigned int delayMicros = 50; 

// Variables to track delay and current pin
volatile unsigned long nextToggleTime = 0; 
volatile int currentPin = 0;


void setup() {
  // Set the transducer pins as output
  for (int i = 0; i < numPins; i++) {
    pinMode(transducerPins[i], OUTPUT);
  }

  // Configure timer1 for 40kHz
  noInterrupts();           // Disable all interrupts
  TCCR1A = 0;               // Clear Timer1
  TCCR1B = 0;
  TCNT1  = 0;               // Initialize counter value to 0
  OCR1A = 200;              // 
 
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS10);    // Set prescaler to 1 -> no prescaling
  TIMSK1 |= (1 << OCIE1A);  // Enable Timer1 compare interrupt
  
  interrupts();             // Enable all interrupts
}

ISR(TIMER1_COMPA_vect) {    // At each interrupt, do the following
    unsigned long currentTime = micros();
  
  // Check if the current time has reached or passed the nextToggleTime
  if (currentTime >= nextToggleTime) {
    // Toggle the current pin
    PORTB ^= (1 << (currentPin + 1)); // Toggle the specific pin (pins 9, 10, 11, and 12 are PORTB bits 1, 2, 3, 4 respectively)
    
    // Set the next toggle time
    nextToggleTime = currentTime + delayMicros;
    
    // Move to the next pin, wrapping around to the first pin after the last one
    currentPin++;
    if (currentPin >= numPins) {
      currentPin = 0;
    }
  }
}

void loop() {

}

shouldn't that be PORTB which is the output register?

No, use of PINB to toggle an output bit (or several) is a special feature of AVR processors, described in the data sheet.

1 Like

Replace the 32 with 16, as described in post #7. With OCR1A set to 200, the output is 10 kHz on each pin, as explained in that post.

I see, I want each transducer to produce a 40kHz signal though, with a delay between each of them. It seems like there is already a 2.8us in built delay with this code perhaps due to the sequential nature of the code?

On a 16 MHz AVR-based Arduino, the following code should produce 40 kHz on all transducers, phase shifted as described above.

void setup() {
  
DDRB = 0x0F;
  // put your setup code here, to run once:

  // Configure timer1 for 40kHz
  noInterrupts();           // Disable all interrupts
  TCCR1A = 0;               // Clear Timer1
  TCCR1B = 0;
  TCNT1  = 0;               // Initialize counter value to 0
  OCR1A = 49;              // Set compare register for 320 kHz interrupt rate
 
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS10);    // Set prescaler to 1 -> no prescaling
  TIMSK1 |= (1 << OCIE1A);  // Enable Timer1 compare interrupt
  
  interrupts();             // Enable all interrupts
}

ISR(TIMER1_COMPA_vect) {
static byte bit_to_toggle = 1;
PINB = bit_to_toggle; //toggle that bit (works on AVR MCUs)
bit_to_toggle <<= 1; //point to next higher bit
if (bit_to_toggle & 16) bit_to_toggle=1; //recycle
}
void loop() {}

I see, thank you it looks good on my oscilloscope! What is the reason/math behind the number 49?
Also, I want to be able to change and input how much delay I have between one transducer signal to the next. I modified my code to get this:

const int transducerPins[] = {9, 10, 11, 12};
volatile int delayBetweenSignals = 100; // Initial delay in microseconds, can be adjusted as needed

void setup() {
  // Set pins 9-12 as outputs
  for (int i = 0; i < 4; i++) {
    pinMode(transducerPins[i], OUTPUT);
  }

  // Configure timer1 for 40kHz
  noInterrupts();           // Disable all interrupts
  TCCR1A = 0;               // Clear Timer1
  TCCR1B = 0;
  TCNT1  = 0;               // Initialize counter value to 0
  OCR1A = 49;              // Set compare register for 40 kHz interrupt rate
  
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS10);    // Set prescaler to 1 -> no prescaling
  TIMSK1 |= (1 << OCIE1A);  // Enable Timer1 compare interrupt
  
  interrupts();             // Enable all interrupts
}

ISR(TIMER1_COMPA_vect) {
  static byte bit_to_toggle = 1;
  static unsigned long lastToggleTime = 0;
  unsigned long currentTime = micros();

  if (currentTime - lastToggleTime >= delayBetweenSignals) {
    PORTB ^= bit_to_toggle; // Toggle the corresponding bit
    bit_to_toggle <<= 1; // Point to next higher bit
    if (bit_to_toggle & 16) bit_to_toggle = 1; // Recycle
    lastToggleTime = currentTime; // Update the last toggle time
  }
}

void loop() {
  // delayBetweenSignals = ... (set to the desired value)
}

However, again the frequency of each signal changed. I want to keep all at 40kHz. Is there something I am not understanding?

Check the processor data sheet, timer section, for the exact formula. The need to subtract 1 comes from the details of timer action on compare/match.

I modified my code to get this:

As you have noticed, those modifications will change both the phase shift and the output frequency.

This is not a simple challenge, and my code example was merely intended to show you a different approach to thinking about the problem.

I want to make a phase delay in a transducer array. I want to use a the AD9833 to generate a 40kHz digital signal which would then be sent to an arduino UNO. I then want to use the Arduino UNO to take the 40kHz signal from the AD9833 and then send the signal with delays to four transducers. this means that transducer 1 would start the signal at time t, transducer 2 would start the signal at time t + delta, transducer 3 at time t+2delta etc.

Is it possible to generate a signal this fast? This is what I currently have but there are several problems:

  1. the signal is only 7kHz
  2. the signal is not uniform with a 50% duty cycle
  3. there is no delay in where the signal starts
#include <SPI.h>
#include "AD9833.h"

#define FSYNC_PIN 10

AD9833 ad9833(FSYNC_PIN);

const int transducer1 = 2;
const int transducer2 = 3;
const int transducer3 = 4;
const int transducer4 = 5;

const int delayDelta = 25;  // Adjust this value to set the delta time

void setup() {
  Serial.begin(9600);
  pinMode(transducer1, OUTPUT);
  pinMode(transducer2, OUTPUT);
  pinMode(transducer3, OUTPUT);
  pinMode(transducer4, OUTPUT);

  ad9833.begin();
  ad9833.setWave(AD9833_SINE);
  ad9833.setFrequency(40000);  // Set frequency to 40kHz
}

void loop() {
  digitalWrite(transducer1, HIGH);
  delayMicroseconds(delayDelta);
  digitalWrite(transducer2, HIGH);
  delayMicroseconds(delayDelta);
  digitalWrite(transducer3, HIGH);
  delayMicroseconds(delayDelta);
  digitalWrite(transducer4, HIGH);

  delayMicroseconds(10);  // Adjust this value to control signal duration
  
  digitalWrite(transducer1, LOW);
  digitalWrite(transducer2, LOW);
  digitalWrite(transducer3, LOW);
  digitalWrite(transducer4, LOW);
  
  delayMicroseconds(25);  // Adjust this value for the rest
}

I doubt if a UNO would suitable for this project
a ESP32 can generate a 40KHz digital signal
if you require four 40Khz digital signals what is the relationship between them?

I want to create a transducer matrix where I have a phase delay between the signals at each transducer.

would the phase delays be fixed, e.g. 90degrees or variable?

I want a fixed delay between each transducer but I want to be able to vary that delay in microseconds