Go Down

Topic: [Solved] How arduino read the digital pins? (Read 511 times) previous topic - next topic

cristobalmr

Sep 08, 2017, 05:41 pm Last Edit: Sep 16, 2017, 05:37 pm by cristobalmr
Hi!

I'm working in a project where I have to read two or more pins at the same time (so I can search for coincidences). To do this I'm using PIO_PDSR.

I have a detector that will emit a square signal with width of 90 ns when something pass through it. I know I can read it because I'm getting the frecuency of detection that the detector is set to (15 Hz).

My question is how PIO_PDSR works, in terms of measuring the status of the pin, i.e., when I call the function the arduino will be measuring the whole time PIO_PDSR is executing (like 1 us) or is like instant. Because if it measure in an instant (by probabilities) the arduino shouldn't get the right frecuency (I should get like 1 if I'm lucky). So the only option is that is reading the whole time and if in that time there is a change of state I will recive the value of that state (if is the normal state is LOW and it goes to HIGH I'll recive HIGH).

I hope I made my self clear. Thanks in advance!

MasterT

You are right, it reads instantly. Instead of polling a pin, use attachinterrupt. https://www.arduino.cc/en/Reference/AttachInterrupt

MorganS

No, it's not instant. Input pins have capacitance. There's also programmable de-glitch and debounce filters available on the Due. These delay the detection of a change on a pin.

90ns is about 7 clock cycles of the 84MHz Due. You can't read a pin or PIO_SDSR and do anything useful in 7 clocks. You could not detect that pin1 has gone high and then look at pin2. Even though both of those events (if simultaneous) will be captured by one read of PIO_SRSR, you can't look at the contents in time before you have to read it again.

An interrupt on one (or both) pins won't find the other pin is active because it takes more than 7 clocks to switch to the interrupt handler and read PIO_SDSR.

I would put interrupts on both pins with attachInterrupt(). Fortunately you can do this on any pin on the Due. Then have both of them record the microseconds when the event occurred. Then define how many microseconds apart you consider to be "coincident". Two truly simultaneous inputs will create two interrupts, one after the other, so you could set this threshold as low as 1us.

90ns is really short. You might consider a pulse-stretcher circuit to expand the duration of each pulse into a size that the Arduino can read more comfortably.
"The problem is in the code you didn't post."

ard_newbie

#3
Sep 09, 2017, 08:36 am Last Edit: Sep 10, 2017, 06:31 am by ard_newbie
An attachInterrupt won't give you a good precision since each of these interrupts  need at least 8 clock cycles to enter and 8 clock cycles to exit.

You will have a much better precision with a polling code because an I/O pin will detect a high level if a pulse duration is more than 12 ns (1 clock cycle) and sometimes even less than 12 ns.

You could try this code:
Code: [Select]

// Example skecth to detect a glitch coincidence
// between PD0(pin25) and PD2(pin27).
const uint32_t MaskPin25 = (1 << 0);
const uint32_t MaskPin27 = (1 << 2);
const uint32_t MaskPins2527 = MaskPin25 | MaskPin27;

void setup() {

  PMC->PMC_PCER0 |= PMC_PCER0_PID14; // PIOD power ON

}

void loop() {
  register uint32_t status;
  
  status = PIOD->PIO_PDSR;
  while ((status & MaskPins2527)!= MaskPins2527)  // Wait until a coincidence detection
  {
    status = PIOD->PIO_PDSR;
  }
  // Do whatever you want with this information
}





Connect Pull down resistors to the two selected pins to avoid erratic results due to EMI.

cristobalmr

Hi!

Thanks for the replies. I tested it with one attachInterrupt long ago and it didn't work, but never tried with two so I'll be doing that.

And I'll be also testing the code and see wich method works better. I'll be doing it on monday (The stuff is at my lab and I'm far away from it until monday).

Thanks!

cristobalmr

Hi!

The PIO_PDSR will be taking just 1 clock cycle or does it have like a "boot" time + medition time (I suppose 1 cycle?) + exit time.

Thanks!

MorganS

Code: [Select]
int MyVar = PIO_PDSR;
This will take only one clock cycle. (The full details are in the datasheet but this will certainly be compiled as a register-to-register copy.)

But you can't do anything useful in one clock cycle. If you wanted to inspect the value and check for the values you're looking for, that will take more clocks.
"The problem is in the code you didn't post."

HermannSW

Due needs 3 clock cycles to read digital pins, but that allows to read 32 pins in parallel, which allows to build a 28Msps (Million samples per second) logic analyzer with Due (for a short amount of time due to only 96KB of memory, 32CH for 714μs or 8CH for 3ms):
https://forum.arduino.cc/index.php?topic=417851.msg2883182#msg2883182

Hermann.

cristobalmr

Hi!

I tested the code and it worked fine, I'm having better results. The attachInterrupt in both pins is not so good, I had better results with the polling code.

My final question is how long does it takes the while to execute, i.e., to test the condition, then decide to enter and then to exit. I need to know this because I have to get a dead time out of it, so my medition time is going to be PIO_PDSR (3 or 1 clocks), and the dead time is what takes the while to do its thing.

Also I'm checking the post posted by HermannSW

Thanks!

ard_newbie

#9
Sep 12, 2017, 05:39 am Last Edit: Sep 15, 2017, 05:42 am by ard_newbie
The only way to count a few clock cycles is to use SysTick->VAL. Systick is a timer counter used by the core which counts down from SystemCoreClock/1000 (84000000/1000 if your DUE is clocked at 84 MHz), to zero within 1 ms then starts again at 84000 and so on. Each tick of SysTick->VAL equals one clock cycle.

Note that this method is valid only if the timestamp is smaller than 1 ms ( less than 84000 clock cycles).

To measure the while loop, set pins 25 and 27 to a high level, then run this sketch:

Code: [Select]

// Example skecth to detect a glitch coincidence
// between PD0(pin25) and PD2(pin27).
const uint32_t MaskPin25 = (1 << 0);
const uint32_t MaskPin27 = (1 << 2);
const uint32_t MaskPins2527 = MaskPin25 | MaskPin27;

void setup() {
  PMC->PMC_PCER0 |= PMC_PCER0_PID14; // PIOD power ON
}
void loop() {
  register uint32_t status;
  int32_t timestamp;
  const uint32_t SCC = SystemCoreClock;

  status = PIOD->PIO_PDSR;
  // Assuming the coincidence condition is valid at that point
  // between pin25 and pin27
  timestamp = SysTick->VAL; // Takes 1 clock cycle

  while ((status & MaskPins2527) != MaskPins2527) // Wait until a coincidence detection
  {
    status = PIOD->PIO_PDSR;
  }

  timestamp -= SysTick->VAL ; // takes 1 clock cycle

  timestamp = (timestamp < 0) ? timestamp + SCC - 2 : timestamp - 2;
  Serial.print(" Number of clock cycles =" ); Serial.println(timestamp);
}





Go Up