Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 7 of 7

Thread: Teensy 4.1 GPIO_6 Register Read Erratic Timing

  1. #1
    Junior Member
    Join Date
    Dec 2020
    Posts
    4

    Teensy 4.1 GPIO_6 Register Read Erratic Timing

    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

    Click image for larger version. 

Name:	Sampling.jpg 
Views:	17 
Size:	91.3 KB 
ID:	22855




    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 defragster; 12-16-2020 at 04:42 PM. Reason: edited for CODE marking

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    24,005
    Quote Originally Posted by Paul G View Post
    I have attached scope grab to show 'holes' in the sampling pattern. Clearly there is some conflict or distraction going on.
    Maybe another interrupt is happening? I don't see any use of Serial.print() in your program, so the only one that should be running is Systick every 1ms.

    Anyway, if it is another interrupt, usually the solution is to increase the priority setting for the interrupt you're using, so others can't interrupt it.

  3. #3
    Junior Member
    Join Date
    Dec 2020
    Posts
    4
    Just been looking closely at this on the scope and observed that the 'holes' are punched around ~20us into my 1ms block and then again at around ~520us! So I was thinking there is a clue here about something at 1ms perhaps! Sounds like there is a solution. By the way, I get the same problem with multiple digitalReadFast and assembling my 10bit word from them; thought it was worth a try.

    I don't yet know how to set interrupt priority but I guess a search here will help.
    Many thanks for this.

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,762
    Noting this code uses PSRAM:
    Code:
    volatile EXTMEM double I[2101]; //Scaled samples from I chan
    volatile EXTMEM double Q[2101]; //Scaled samples from Q chan
    Does the QSPI hardware perhaps 'blip' the CPU processing order to complete that I/O during the _isr >> void readAdc() when it does the writes to I[] and Q[]

    That is only 33K of memory - what happens if those EXTMEM's move to DMAMEM's?

    @Paul G : there is a '#' code marker on the edit toolbar that puts [C0DE] // code here [/C0DE] to preserve indenting for readability { note it is actually CODE (cap letter O) - a 0/Zero was inserted instead to stop it getting parsed }

  5. #5
    Junior Member
    Join Date
    Dec 2020
    Posts
    4
    Does the QSPI hardware perhaps 'blip' the CPU processing order to complete that I/O during the _isr >> void readAdc() when it does the writes to I[] and Q[]

    That is only 33K of memory - what happens if those EXTMEM's move to DMAMEM's?
    I'll keep this is mind but it looks like the interrupt priority may be the issue. Have set IRQ_GPIO6789 at highest priority (as not sure what SysTick is called in the list to check its priority) and verified with NVIC_GET_PRIORITY(IRQ_GPIO6789). So far so good.

    The EXTMEM is for when this is scaled up by 32x although even then I see that I could retain DMAMEM for the capture and use EXTMEM for the intermediate arrays in the processing routine.

    Thanks and apologies for missing the # option when posting my code.

  6. #6
    Senior Member
    Join Date
    Feb 2018
    Location
    Corvallis, OR
    Posts
    331
    I would go even further and move those I and Q arrays into DTCM to avoid any possibility of cache interference with the collection. As defragster points out, it is only 33K of memory, but that is 1KB past what I think the cache can handle at one time. Moving those arrays to DTCM may require moving some other variables into DMAMEM, but if the processing is done in the foreground, it is less likely to present timing issues.

    Setting a higher interrupt priority won't protect you from the Systick interrupt, which runs at the highest priority. Only masking all interrupts for the 1mSec of collection will do that, but it will also have an effect on the micros() and millis() functions.

  7. #7
    Junior Member
    Join Date
    Dec 2020
    Posts
    4
    Quote Originally Posted by mborgerson View Post

    Setting a higher interrupt priority won't protect you from the Systick interrupt, which runs at the highest priority. Only masking all interrupts for the 1mSec of collection will do that, but it will also have an effect on the micros() and millis() functions.
    Ok; understood. Will look at masking all other interrupts and explore recommended memory plan. Thanks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •