Go Down

### Topic: Reading PIND and PINB (Read 4290 times)previous topic - next topic

#### swoshie

##### Jan 02, 2013, 09:05 pm
Hi,

I've been trying to use pulseIn() for quite some time now but I want a faster way of reading the inputs and preferably at the same time.

My scenario:

To read 8 inputs and return the pin with the highest frequency. On every input there will be a sine wave between 5-7 KHz that is converted to a square wave via a schmitt trigger. I want to measure HIGH to LOW to HIGH or equivalent.

The problem with pulseIn is that all the pins are read after each other while PINx reads all the inputs as a whole byte.

When trying to read the inputs with
Code: [Select]
`int inputPin = 5;int inputByte;void setup() {  pinMode(inputPin, INPUT);  Serial.begin(9600);}void loop() {  inputByte = PIND;  Serial.println(inputByte);  delay(1000);}`

I just get the number 7 when nothing is attached. Fare enough I thought but how should I continue if I want to read which pin has the highest frequency?

Maybe it's a lot to ask for but I seriously don't understand how this works as most of the posts at this forums only describes OUTPUTS. I hope someone has the time to help me out!

Best regards,
Swoshie

#### PaulS

#1
##### Jan 02, 2013, 09:19 pm
Quote
I just get the number 7 when nothing is attached.

Why are you reading pins with nothing attached?

Quote
Fare enough I thought but how should I continue if I want to read which pin has the highest frequency?
Seems to me like you need to compare the time each pin on the port goes high with when that pin goes low. The time taken doing that will far exceed the time required for a pin to go HIGH or LOW. So, I don't think there is a way for you to measure the frequency of all the pins simultaneously.

#### Grumpy_Mike

#2
##### Jan 02, 2013, 09:24 pm
What you want to do is not easy.
Code: [Select]
`inputByte = PIND;`
Returns a single instant snap shot of the logic states of the pins on port D. There is no way it can tell you anything about the frequency of the signals on the pins.
To do what you want you would have to repeatedly look at the pins and analise what pins are high and what are low. You would then have to make a note of the time when each pin makes a transition from one state to another. When you see the opposite transition, make a note of the time again and subtract it from the first transition time to see how long it has spent in one state.

This is difficult to code and perhaps if you said what you actually want to do, as opposed to how you think you should do it, there would be a better way of doing it.

#### retrolefty

#3
##### Jan 02, 2013, 09:59 pm
I just get the number 7 when nothing is attached.
With nothing wired up to pins 0-7 (which is port D) one just reads floating input pins which can be random values, except for pins 0 and 1 which should read high as they are being effectively pulled high by the USB serial converter chip.
Fare enough I thought but how should I continue if I want to read which pin has the highest frequency?

Variable = PIND only reads the input pins as a single snap shoot reading. To determine frequency you will have to continously read the bits using PIND and measure the time between transitions of each specific bit from low to high and to the next high to low transition. That will give you a time measurement of half the waveform's period, so double that and calculate the reciprocal will give you it's frequency. Keeping track of passing time can be done using the micros() command to save a starting time from the low to high transition and then again using micros() to save the ending time that the following high to low transition occurs. Then subtract the starting time from the ending time and you have the half period time in microseconds. And of course you have to do that for all bits you are monitoring at the same time.

Hope that helps, it's not a particular easy program structure to have to write depending on how many signals you are trying to measure, but the principle is pretty simple.

Lefty

Maybe it's a lot to ask for but I seriously don't understand how this works as most of the posts at this forums only describes OUTPUTS. I hope someone has the time to help me out!

#### swoshie

#4
##### Jan 02, 2013, 10:07 pm
Wow, it sounds more difficult then I thought!

As you say Lefty, the principle is pretty simple but that code just seems too hard for me to write. Maybe I'll have too stick to pulseIn after all..

#### WizenedEE

#5
##### Jan 02, 2013, 10:14 pm
This is actually fairly easy if you assume that the frequency is constant. The highest frequency pin has the lowest period. Thus, you want to figure out which pin changes first.

So you can store the "starting value" of the port¸ then enter a while loop that checks the current value against the starting value. When one of the pins changes, you return that as the pin with the highest frequency
Code: [Select]
`uint8_t getHighestFrequency(volatile uint8_t* port) {  uint8_t startingD = PIND;  uint8_t whichPin;  do {    whichPin = startingD ^ PIND;  } while (whichPin == 0);  // whichPin has a 1 in the bit that has changed. Want to find out which one that is  uint8_t res;  for (res = 0; (whichPin >>= 1) != 0; res++);  return res;}`

#### retrolefty

#6
##### Jan 02, 2013, 10:19 pm
Quote
So you can store the "starting value" of the port¸ then enter a while loop that checks the current value against the starting value. When one of the pins changes, you return that as the pin with the highest frequency

I would think that would only work if all the individual signals were in phase with each other such that they all had the same 'starting time', and I would not think one could think or even wish that to be the case?

Lefty

#### swoshie

#7
##### Jan 02, 2013, 10:51 pm

I would think that would only work if all the individual signals were in phase with each other such that they all had the same 'starting time', and I would not think one could think or even wish that to be the case?

Lefty

Exactly, the 8 inputs can vary. While 1 input is chosen another input can start and if that one is better, the arduino should choose that one.

#### PeterH

#8
##### Jan 03, 2013, 12:08 am
If you want to detect the highest frequency of a set of digital inputs, you could maintain a count of the number of transitions for each input and then select the input with the highest count over a given period, or the input counter that overflows first. Depending what sort of frequency you need to detect, this might be easy to achieve or completely impossible. However, it does mean that you don't actually need to time the inputs or measure the input frequency, so it's not as hard a problem as you're currently making it.

#### Grumpy_Mike

#9
##### Jan 03, 2013, 12:11 am
Yes you can do it like that but it will take much longer to get the answer than reading a single cycle's period.

#### dhenry

#10
##### Jan 03, 2013, 12:52 am
Quote
So you can store the "starting value" of the port¸ then enter a while loop that checks the current value against the starting value. When one of the pins changes, you return that as the pin with the highest frequency

That piece tells you which is the first pin to change, since the start of that code's execution, not which pin has the fastest pulse train.

For example, if you have two pulses coming into two pins, one slow and one fast. However, if the code is called just before the slow pin changes, it will return that pin.

Code: [Select]
`  for (res = 0; (whichPin >>= 1) != 0; res++);`

1) you can reuse one of the variables declared earlier for res.
2) you can speed it up considerably with a look-up table.

#### PeterH

#11
##### Jan 03, 2013, 02:14 am

Yes you can do it like that but it will take much longer to get the answer than reading a single cycle's period.

Agreed, but it would be feasible up to higher frequencies than could be supported by timing each input event separately.

#### MarkT

#12
##### Jan 03, 2013, 02:55 am
There are various complications, if you assume the frequency is stable but the duty-cycle varies you
have to keep timestamp and count of transitions per pin.  Perhaps the frequency varies and you want
to track the current frequency?

Code: [Select]
`byte prev_pins  ;unsigned long periods [8] ;unsigned long timestamp [8] ;unsigned long prev_timestamp [8] ;void sample (){  byte pins = PIND ;        // read 8 pins  unsigned long now = micros () ;  byte transitions = pins ^ prev_pins ;  // see which pins changed since last time sample() called  prev_pins = pins ;  for (byte i = 0 ; i < 8 ; i++)  {    if ((transitions & (1 << i)) != 0)   // for each pin that changed update timestamps and period    {      periods [i] = now - prev_timestamp [i] ;      prev_timestamp [i] = timestamp [i] ;      timestamp [i] = now ;    }  }}void setup (){  prev_pins = PIND ;}void loop (){  sample () ;  // check periods[] here but don't take too long to do so}`

But that will read wrong until at least two edges have come in on each pin...

It could be turned into a pin change interrupt handler quite nicely though.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

#### retrolefty

#13
##### Jan 03, 2013, 03:14 amLast Edit: Jan 03, 2013, 03:25 am by retrolefty Reason: 1
Quote
There are various complications, if you assume the frequency is stable but the duty-cycle varies you
have to keep timestamp and count of transitions per pin.

Well he did state the source signals were sinewave converted to square wave so 50/50 duty cycle could be assumed, so just a transition to transition timing should be enough information to work with?

Quote
" On every input there will be a sine wave between 5-7 KHz that is converted to a square wave via a schmitt trigger."

Perhaps if the signals could all be wired to the same port then pinchange interrupt would signal some signal had a transition and compare it to the last port reading would tell which pin caused the change. Might not be that hard a task using pinchange Ints and timestamps from micros() for each bit ? Heck even this hardware guy can see a light at the end of this tunnel. Throw in a couple of long arrays and time to rock and roll. I bet PaulS could crank this out between his main meal and his dessert.

Lefty

Go Up