Hi, First post from a novice; hope that you can help with this as I have become bogged down.
Am using a Teensy 4.1 with 8MB PSRAM. It is connected to an external 10bit parallel ADC (Analogue Devices AD9201). The ADC is dual channel I and Q device intended for communication use and signal analysis. The ADC has an external clock pin to instruct the next IQ sample pair and a select pin to put either I or Q on parallel port. The 10bit parallel data is mapped to GPIO_1 (6) bits 16-25 as these are accessible on pins on Teensy4.1.
The input to the ADC will be a radar receiver. On receiving a trigger pulse (the interrupt) the Teensy acquires 1ms of I and Q data from the ADC at a rate of 2MSamples/s and runs a convolution process to compress the received chirp modulated pulses from 100us to 1us. The output will connect to parallel DAC and then a display monitor. The code for the output side is not yet written.
The convolution process is all ok; even using just the standard math library, I am pleased that Teensy processes 1ms of data in approx 0.25s. I will later expand the sketch to acquire, say, 32 x 1ms data sets and process these within an 8second period.
I have a sketch that, on interrupt, successfully clocks the ADC at 2MSample/s and toggles the Select line to read both I and Q. Thanks to research reading on this forum I am also reading the GPIO_6 register direct and then >>16 to get the correct value.
My ISR basically creates a pair of 2MHz square waves, one for ADC clock and one for ADC select. There is some NOPs built around the clock transitions to match the settling time and data-ready requirements of the ADC and to adjust the sampling rate to 2MHz which is critical for the signal processing. This was all good.
Having integrated the ISR with the signal processing routine I am finding that my stable sampling is now erratic. By running the ISR without the register read, I have determined that it is the register read that is responsible. I have attached scope grab to show 'holes' in the sampling pattern. Clearly there is some conflict or distraction going on. I am hoping that there is some setting that will force the register read to be consistent.
Thanks,
Paul
Am using a Teensy 4.1 with 8MB PSRAM. It is connected to an external 10bit parallel ADC (Analogue Devices AD9201). The ADC is dual channel I and Q device intended for communication use and signal analysis. The ADC has an external clock pin to instruct the next IQ sample pair and a select pin to put either I or Q on parallel port. The 10bit parallel data is mapped to GPIO_1 (6) bits 16-25 as these are accessible on pins on Teensy4.1.
The input to the ADC will be a radar receiver. On receiving a trigger pulse (the interrupt) the Teensy acquires 1ms of I and Q data from the ADC at a rate of 2MSamples/s and runs a convolution process to compress the received chirp modulated pulses from 100us to 1us. The output will connect to parallel DAC and then a display monitor. The code for the output side is not yet written.
The convolution process is all ok; even using just the standard math library, I am pleased that Teensy processes 1ms of data in approx 0.25s. I will later expand the sketch to acquire, say, 32 x 1ms data sets and process these within an 8second period.
I have a sketch that, on interrupt, successfully clocks the ADC at 2MSample/s and toggles the Select line to read both I and Q. Thanks to research reading on this forum I am also reading the GPIO_6 register direct and then >>16 to get the correct value.
My ISR basically creates a pair of 2MHz square waves, one for ADC clock and one for ADC select. There is some NOPs built around the clock transitions to match the settling time and data-ready requirements of the ADC and to adjust the sampling rate to 2MHz which is critical for the signal processing. This was all good.
Having integrated the ISR with the signal processing routine I am finding that my stable sampling is now erratic. By running the ISR without the register read, I have determined that it is the register read that is responsible. I have attached scope grab to show 'holes' in the sampling pattern. Clearly there is some conflict or distraction going on. I am hoping that there is some setting that will force the register read to be consistent.
Thanks,
Paul
Code:
#include <math.h>
#include <SD.h>
#include <SPI.h>
#define IMXRT_GPIO6_DIRECT (*(volatile uint32_t *)0x42000000)//32bit read direct from register
#define NOP __asm__ __volatile__ ("nop\n\t")
const int interruptPin = 28; //Trigger for sampling
const int clk = 31; //Read ADC I and Q channels at 2MSamples/s
const int sel = 30; //Select I or Q channel
volatile int sample = 0;
const float iAdcOffset = -0.048; //Set zero signal input of I chan
const float qAdcOffset = -0.036; //Set zero signal input of Q chan
unsigned long adcI; //10bit sample from ADC I chan
unsigned long adcQ; //10bit sample from ADC Q chan
const int nSamples = 2101; // For 1.05ms of data
volatile EXTMEM double I[2101]; //Scaled samples from I chan
volatile EXTMEM double Q[2101]; //Scaled samples from Q chan
double rxA[2101]; // Received signal Amplitude calc from I and Q
double rxPh[2101]; // Received signal Phase calc from I and Q
// Reference signal Amplitude and Phase for convolution
const double txA[301] //data set removed for clarity
const double txPh[301] //data set removed for clarity
double convPh[1801];//Convolution of received signal and reference signal phase
double convA[1801]; //Convolution of received signal and reference signal amplitude
double out[1801]; //Signal output (will go to external parallel DAC)
void setup() {
pinMode(clk, OUTPUT); //ADC clock
pinMode(sel, OUTPUT); //ADC Select I or Q
pinMode(19, INPUT); //ADC d0
pinMode(18, INPUT); //ADC d1
pinMode(14, INPUT); //ADC d2
pinMode(15, INPUT); //ADC d3
pinMode(40, INPUT); //ADC d4
pinMode(41, INPUT); //ADC d5
pinMode(17, INPUT); //ADC d6
pinMode(16, INPUT); //ADC d7
pinMode(22, INPUT); //ADC d8
pinMode(23, INPUT); //ADC d9
digitalWrite(clk, LOW);
attachInterrupt(interruptPin, readAdc, RISING);
}
void readAdc() {
for (sample = 0; sample < nSamples; sample++)
{
digitalWrite(clk, HIGH);
digitalWrite(sel, HIGH);
for (int wait = 0; wait < 6; wait ++) NOP;
adcI = IMXRT_GPIO6_DIRECT >>16;
I[sample] = (adcI / 511.5) - 1 + iAdcOffset;
for (int wait = 0; wait < 6; wait ++) NOP;
digitalWrite(clk, LOW);
digitalWrite(sel, LOW);
for (int wait = 0; wait < 6; wait ++) NOP;
adcQ = IMXRT_GPIO6_DIRECT >>16;
Q[sample] = (adcQ / 511.5) - 1 + qAdcOffset;
for (int wait = 0; wait < 6; wait ++) NOP;
}
}
void loop()
{
for(sample=0; sample < nSamples; sample++)
{
rxA[sample] = sqrt(I[sample]*I[sample] + Q[sample]*Q[sample]);
rxPh[sample] = atan2(Q[sample], I[sample]);
}
for(int k=0; k < 1801; k++)
{
for(int j=0; j < 301; j++)
{
convPh[j] = rxPh[j+k] - txPh[j];
convA[j] = rxA[j+k] * txA[j];
out[k] += convA[j] * cos(convPh[j]);
}
}
}
Last edited by a moderator: