Teensy 4.1 multiple continuous digital inputs

xenington

Well-known member
Hi Everyone,

Nice to see you all again after my time off.

I am revisiting the fluxgate gradiometer project again - this time using 8 sensors. The previous version used 4 sensors and interrupts to sample the square waves at about 10 times a second. Time considerations and worries about the accuracy of timing a single pulse in a 100KHz signal got me thinking about an alternative approach.

Is it possible for a Teensy 4.1 to continuously count the pulses of 8 waves simultaneously? I am imagining that each counter would start at zero and simply keep counting pulses until the end of the transect is reached. At set intervals (e.g. 10 per second), the total reached by each counter is saved to an array. Obviously, the inputs must not be interrupted by other things going on like saving to the array, emitting a beep every few seconds and updating a TFT screen. There are enough pins available on a Teensy 4.1 to use 8 sensors, but can it be done?

Thanks for your help.
 
Paul wrote a sketch called FreqCountMany that uses QuadTimer to count rising edges on 10 input pins (6, 9, 10, 11, 12, 13, 14, 15, 18, 19). The counting is all done in hardware, so there is lots of processing time available for whatever else you want to do.


 
Excellent. Thank you.

This looks like another version of something I wrote in 2020, except using the QUAD timers instead of the PIT timers and counting edges rather than timing a single pulse. The pins that the QUAD timers are attached to are a bit problematic - some of them are needed for my TFT and touch screen. The only way I can see this working for me is to use multiple timers. Paul left a TODO on line 43 - // TODO: can 6 more be used with XBAR1 and GPR6 ? I will have a look into it and report back.

https://forum.pjrc.com/threads/freqmeasuremulti-on-the-t4

The links to the code I posted no longer work unfortunately, so repost here:

Code:
/* 
 *   READ MAGNETIC SENSORS USING TIMER INTERRUPTS
 *   SAVE DATA TO ARRAY BUFFER
 *    Dean Smart 2020
 *   
 *  Pins: sensor teensy4
 *          LT      2
 *          LB      4
 *          RT      6
 *          RB      8
 */

void intervalSetup()
{ 
//  calculate PIT interval, PIT Timer p.2971
  const float PIT_Clock = 0.041666667;                                     // PIT clock cycle interval (24MHz = 41.666667nS/tick)
  const float readRateMicros = 100000000 / (float)(walkingPace * readRate);  // convert reads/sec to microseconds/read
  const uint32_t PIT_Interval = round(readRateMicros / PIT_Clock) - 1;       // PIT interval in clock cycles

  CCM_CCGR1 |= CCM_CCGR1_PIT(CCM_CCGR_ON);  // enable PIT clock, p.1085
  PIT_MCR = 0;                              // activate PIT module, p.2979
  PIT_TCTRL1 = 0;                           // reset timer control, p.2984
  PIT_LDVAL1 = PIT_Interval;                // load PIT_Interval, p.2982
  PIT_TFLG1 = PIT_TFLG_TIF;                 // reset interrupt flag, p.2985
  PIT_TCTRL1 = PIT_TCTRL_TIE;               // PIT interrupts enable, p.2984

  attachInterruptVector(IRQ_PIT, freqCapture);  // attach freqCapture isr
  NVIC_ENABLE_IRQ(IRQ_PIT);                     // enable PIT interrupt
  NVIC_SET_PRIORITY(IRQ_PIT, 0);                // prioritise freqCapture interrupt 
}

struct freqPinStruct {        // order of variables in freqPin[]
  IMXRT_FLEXPWM_t *Pflexpwm;  // pointer for pin flexpwm mode
  uint8_t subModule;          // pin sub-module number
  volatile uint32_t *Pmux;    // pointer for pin mux mode
  uint8_t muxVal;             // value to set mux mode on pin
  volatile uint32_t *Pinput;  // pointer for pin input select
  uint8_t inputVal;           // value to set input on pin
};

const struct freqPinStruct freqPin[] =
{
  {&IMXRT_FLEXPWM4, 2, &IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_04, 1, &IOMUXC_FLEXPWM4_PWMA2_SELECT_INPUT, 0},  // pin 2 LT
  {&IMXRT_FLEXPWM2, 0, &IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_06, 1, &IOMUXC_FLEXPWM2_PWMA0_SELECT_INPUT, 0},  // pin 4 LB
//  {&IMXRT_FLEXPWM2, 1, &IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_08, 1, &IOMUXC_FLEXPWM2_PWMA1_SELECT_INPUT, 0},  // pin 5 spare
  {&IMXRT_FLEXPWM2, 2, &IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_10, 2, &IOMUXC_FLEXPWM2_PWMA2_SELECT_INPUT, 1},   // pin 6 RT
  {&IMXRT_FLEXPWM1, 3, &IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00, 6, &IOMUXC_FLEXPWM1_PWMA3_SELECT_INPUT, 4},   // pin 8 RB
    // *Pflexpwm, subModule, Pmux, muxVal, Pinput, inputVal
};

void freqSetup()  // set up the 4 pins, runs once, FlexPWM p.3029
{ 
  for (byte i = 0; i < 4; i ++) {   
    
    *freqPin[i].Pmux = freqPin[i].muxVal;      // select mux mode, p.432
    *freqPin[i].Pinput = freqPin[i].inputVal;  // select input mode, p.817
    
    IMXRT_FLEXPWM_t *Pflexpwm = freqPin[i].Pflexpwm;  // get Pflexpwm
    uint8_t subModule = freqPin[i].subModule;         // get subModule number
    uint8_t subModuleBit = 1 << subModule;            // convert subModule to 4 bits
    
    Pflexpwm->MCTRL |= FLEXPWM_MCTRL_CLDOK(subModuleBit);       // clear master control, p.3134
    Pflexpwm->SM[subModule].CTRL2 = FLEXPWM_SMCTRL2_CLK_SEL(0); // select IPBus clock, 150MHz = 6.66667nS/tick, p.3080
    Pflexpwm->SM[subModule].INIT = 0;                           // initial counter & reset value, p.3079
    Pflexpwm->SM[subModule].VAL1 = 65535;                       // maximum counter value/overflow, p.3087
    Pflexpwm->MCTRL |= FLEXPWM_MCTRL_LDOK(subModuleBit) | FLEXPWM_MCTRL_RUN(subModuleBit);  // LDOK loads setup into buffer, RUN enable PWM clock, p.3134
    
    Pflexpwm->SM[subModule].CAPTCTRLA &= 0x00;    // reset capture control register to zero
    Pflexpwm->SM[subModule].CAPTCTRLA = 0xA8;     
    Pflexpwm->SM[subModule].CAPTCOMPA |= FLEXPWM_SMCAPTCOMPA_EDGCMPA(40);   // set edge counter compare value to 40 edges (= 20 pulses), p.3112
  }
}

/*  Convert readings to magnetic flux (nanoTesla)
 *    typical readings in computer room = 1000 - 1250 ticks
 *    1000 - 1250 ticks = 6666.67 - 8333.34 nS (IPBus = 150MHz, 1 tick = 6.66667 nS)
 *    6666.67 - 8333.34 nS = 0.00015 - 0.00012 GHz (= 150000 - 120000 Hz)
 *    150000 - 120000 Hz = 90000 - 30000 nT (from Zagidulin 2018)
 *    1 Hz change = 2 nT, 1 nT change = 0.5 Hz
 *    1 nS change = 36 nT, 1 nT change = 0.028 nS
 *    1 tick change = 240 nT, 1 nT change = 0.0042 ticks
 *    formula y = 2x - 210000
 */

volatile uint32_t freqBuff[] = {0, 0, 0, 0};  // volatile buffer for isr, 0 = LT, 1 = LB, 2 = RT, 3 = RB
volatile uint8_t freqPulseCounter = 0;        // counts number of pulses sampled

bool freqAvailable()  // check that reading was successful
{
  for (byte i = 0; i < 4; i ++) {
    noInterrupts();
    freqBuffer[i] = freqBuff[i];  // copy volatile to non-volatile
    freqBuff[i] = 0;              // reset freqBuff[i] to 0
    interrupts();
    if (freqBuffer[i] == 0) {
      return false;
    }
  }
  return true;
}

void freqCapture()  // isr to capture frequencies
{
//  freqtime1 = PIT_CVAL1;        // TESTING use to time isr, 1 tick = 41.666667nS
 
  PIT_TFLG1 = PIT_TFLG_TIF;     // reset PIT interrupt flag, p.2985

  for (byte i = 0; i < 4; i ++) {                     // repeat for each sensor
    IMXRT_FLEXPWM_t *Pflexpwm = freqPin[i].Pflexpwm;  // get Pflexpwm
    uint8_t subModule = freqPin[i].subModule;         // get subModule number
    uint16_t capture1, capture2, captureDiff;
    Pflexpwm->SM[subModule].CAPTCTRLA |= 0x01;        // ARMA = 1 (start capture), p.3110   
    
    while (freqPulseCounter < freqNumberSamples) {    // repeat capture for freqNumberSamples
      if ((Pflexpwm->SM[subModule].STS) &= 0x800) {   // perform capture if values are available
        capture1 = Pflexpwm->SM[subModule].CVAL2;     // 1st edge capture value (number IPBus ticks), p.3121
        capture2 = Pflexpwm->SM[subModule].CVAL3;     // 2nd edge capture value (number IPBus ticks), p.3123
        captureDiff = capture2 - capture1;            // calculate difference between captures
        freqBuff[i] = freqBuff[i] + captureDiff;      // store readings in buffer
        freqPulseCounter ++;                          // advance pulse counter
//        Serial.print(captureDiff);
//        Serial.print(" ");
      }
    } 
    Pflexpwm->SM[subModule].STS |= 0x0C00;        // clear status, CFA1 + CFA0 = 1, p.3099
    Pflexpwm->SM[subModule].CAPTCTRLA &= 0xA8;    // ARMA = 0 (stop capture), p.3110     
    freqPulseCounter = 0;                         // reset pulse counter
//    Serial.println();
  } 
  asm volatile ("dsb");
  asm volatile ("isb");   
    
//  freqtime2 = PIT_CVAL1;      // TESTING use to time isr, 250 samples time = c.400000 ticks = 16666667 nS = 16.67 mS
}
 
Your original message says you want to "count the square waves", but your code does input capture and period measurement, so you probably want FreqMeasureMulti instead of FreqCountMany. The T4.1 pins available for FreqMeasureMulti are 0-9, 22-25, 28-29, 36-37, 42-47, 51, 54

I don't understand what your code is doing, but perhaps you could use FreqMeasureMulti and avoid the polling.
 
Back
Top