Faster way to read in digital inputs into a byte value

strud

Well-known member
I have an application capturing both analog and digital data up to a rate of 128ksps.

At the moment the ISR is taking too much time and making the 128ksps rate unviable for streaming purposes.

I have determined that the digital input reading and byte assembly is by far the slowest part of the routine (below).

Note that these inputs are not readable as a single byte (unfortunately).

Code:
void adcDataReadyIsrStream(){

    adc.readData(&res);
    ultemp = micros() - loggerStatus.logStartTimeMicro;
    //mCurPosValue = myEnc1.read();     // taking 
    mCurPosValue++;
    
    // update digital inputs here as the ISRs for the digital inputs will be disabled 
    bitWrite(digitalInputs, 0,digitalReadFast(DIN0));
    bitWrite(digitalInputs, 1,digitalReadFast(DIN1));
    bitWrite(digitalInputs, 2,digitalReadFast(DIN2));
    bitWrite(digitalInputs, 3,digitalReadFast(DIN3));
    bitWrite(digitalInputs, 4,digitalReadFast(DIN4));
    bitWrite(digitalInputs, 5,digitalReadFast(DIN5));
    bitWrite(digitalInputs, 6,digitalReadFast(DIN6));
    bitWrite(digitalInputs, 7,digitalReadFast(DIN7));
  
    myQueueWrite(0xAA);
    myQueueWrite((uint8_t)(0xFF & ultemp));               // elapsed time
    myQueueWrite((uint8_t)(ultemp>>8)&0xFF);
    myQueueWrite((uint8_t)(ultemp>>16)&0xFF);
    myQueueWrite((uint8_t)(ultemp>>24)&0xFF);

    myQueueWrite((uint8_t)(res.chan1_16&0xFF));       // analog channel 1
    myQueueWrite((uint8_t)(res.chan1_16>>8)&0xFF);
    myQueueWrite((uint8_t)(res.chan2_16&0xFF));       // analog channel 1
    myQueueWrite((uint8_t)(res.chan2_16>>8)&0xFF);
    myQueueWrite((uint8_t)(res.chan3_16&0xFF));       // analog channel 1
    myQueueWrite((uint8_t)(res.chan3_16>>8)&0xFF);
    myQueueWrite((uint8_t)(res.chan4_16&0xFF));       // analog channel 1
    myQueueWrite((uint8_t)(res.chan4_16>>8)&0xFF);

    myQueueWrite((uint8_t)(mCurPosValue)&0xFF);      // 32 bit encoder counter
    myQueueWrite((uint8_t)(mCurPosValue>>8)&0xFF);
    myQueueWrite((uint8_t)(mCurPosValue>>16)&0xFF);
    myQueueWrite((uint8_t)(mCurPosValue>>24)&0xFF);
    myQueueWrite(digitalInputs);             // digital inputs, automatically update in own ISRs  
    // all added for debugging
   /* myQueueWrite((uint8_t)(myCircBuffer.length)&0xFF);      // 32 bit encoder counter
    myQueueWrite((uint8_t)(myCircBuffer.length>>8)&0xFF);
    myQueueWrite((uint8_t)(myCircBuffer.length>>16)&0xFF);
    myQueueWrite((uint8_t)(myCircBuffer.length>>24)&0xFF);

    myQueueWrite((uint8_t)(myCircBuffer.writeIndex)&0xFF);      // 32 bit encoder counter
    myQueueWrite((uint8_t)(myCircBuffer.writeIndex>>8)&0xFF);
    myQueueWrite((uint8_t)(myCircBuffer.writeIndex>>16)&0xFF);
    myQueueWrite((uint8_t)(myCircBuffer.writeIndex>>24)&0xFF);

    myQueueWrite((uint8_t)(myCircBuffer.readIndex)&0xFF);      // 32 bit encoder counter
    myQueueWrite((uint8_t)(myCircBuffer.readIndex>>8)&0xFF);
    myQueueWrite((uint8_t)(myCircBuffer.readIndex>>16)&0xFF);
    myQueueWrite((uint8_t)(myCircBuffer.readIndex>>24)&0xFF);
    */
    
}

The digitial input read and byte value assignment is taking an average of 206ns out of a total of 375ns so pretty horrible.

Obviously my code is terribly inefficient but I'm not sure which approach would be significantly faster?

I have not implemented ISRs to service the digital inputs individually as I need to keep the ADC ISR very high priority and low jitter.
 
This is roughly the code I'll try out this evening

Code:
#define IMXRT_GPIO6_DIRECT  (*(volatile uint32_t *)0x42000000)
    // read in raw values from single port GPIO6
    register uint32_t rawdata  = IMXRT_GPIO6_DIRECT;  // 0B11111111111111110011000000000000
    
    // Pin #	bit position	value	  hex value
    // 14	      18	        131072	  20000
    // 15	      19	        262144	  40000
    // 16	      23	        4194304	  400000
    // 17	      22	        2097152	  200000
    // 18	      17	        65536	    10000
    // 19	      16	        32768	    8000
    // 20	      26	        33554432	2000000
    // 21	      27	        67108864	4000000

    // and with the bit mapped to each pin
    digitalInputs = ((rawdata & 0x20000) >> CORE_PIN14_BIT);  // DIN0 is PIN14
    digitalInputs += ((rawdata & 0x40000) >> CORE_PIN15_BIT-1);  // DIN1 is PIN15
    digitalInputs += ((rawdata & 0x400000) >> CORE_PIN16_BIT-2);  // DIN1 is PIN16
    digitalInputs += ((rawdata & 0x200000) >> CORE_PIN17_BIT-3);  // DIN1 is PIN17
    digitalInputs += ((rawdata & 0x10000) >> CORE_PIN18_BIT-4);  // DIN1 is PIN18
    digitalInputs += ((rawdata & 0x80000) >> CORE_PIN19_BIT-5);  // DIN1 is PIN19
    digitalInputs += ((rawdata & 0x2000000) >> CORE_PIN20_BIT-6);  // DIN1 is PIN20
    digitalInputs += ((rawdata & 0x4000000) >> CORE_PIN21_BIT-7);  // DIN1 is PIN21
 
Code:
//  T4.1       bit pin
//01 <-> GPIO6-16 19/A5  AD_B1_00  Data   BIDIR                   Teensy Data BUS 0-7 INPUT & OUTPUT
//02 <-> GPIO6-17 18/A4  AD_B1_01
//03 <-> GPIO6-18 14/A0  AD_B1_02
//04 <-> GPIO6-19 15/A1  AD_B1_03
//05 <-> GPIO6-20 40/A16 AD_B1_04
//06 <-> GPIO6-21 41/A17 AD_B1_05
//07 <-> GPIO6-22 17/A3  AD_B1_06
//08 <-> GPIO6-23 16/A2  AD_B1_07  Data   BIDIR                   Teensy Data BUS 0-7 INPUT & OUTPUT

volatile uint32_t GPIO_cs = 0;

GPIO_cs = GPIO6_PSR >> 16; //sample and shift the bits. ;)
https://www.pjrc.com/teensy/schematic.html
 
Last edited:
Hi Chris O

I am still getting my head around (very slowly) the nomenclature and the different labelling levels.

In the schematic you linked to, what is the 'port' AD_B1 ?

I need to map my signals in the odd way I've listed as they are wired that way ie in order of the PINS broken o
 
In the schematic you linked to, what is the 'port' AD_B1 ?
AD_B1_00 basically PORT name similar to Arduino Uno: PORTA, PORTB, PORTC.
Direct port manipulation will yeld the best performance but you need to connect the wires (pins) correctly so you can read the bits in one go without too many calculations (bit shifting).
 
For me, the AD_B1_ is the pin name, the GPIO number is closer to the PORTA, B, C, D of the AVR processors.
For example if you look at my excel sheet in GPIO pin order.
Screenshot.jpg
You will see a column with name... the AD_B1_ to me is a hint that the pin is likely to be an anlog input pin. some of them that start with SD_... are often used for SDIO (SD), EMC_ for memory...

Fastest/Best way to do what you want to do? It may depend on how you setup everything. like:

can you use DMA to do any of this? For example the analog input? The ADC library has examples for doing this. Either as fast as it can go, or by using a clock to govern the interval the conversions happen.

Digital pins - As Chris mentioned, if you can get the pins in the right order, you might be able to do it with a read in the PSR register and a shift.

If there is a gap between some of the pins. you might be able to do it with a couple of shifts and an or (might need an and or two).

If you can get all 8 pins into a consecutive 8 bits but in a scrambled order. you might be able to do it by doing the read in/shift to get them into 8 bits and then use a 256 byte translation (look up) table to convert to the real value.

And probably lots of other solutions as well.
 
Back
Top