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:
The signal generated is not a constant 40kHz amongst the pair of transducers generating a signal at the same time
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! 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?
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'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() {
}
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?
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?
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:
the signal is only 7kHz
the signal is not uniform with a 50% duty cycle
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?