Reading Full Ports and Combining

Status
Not open for further replies.

jkoffman

Well-known member
Hi all,

I'm still fairly new to the Teensy and to C in general, so I apologize if this question is a bit rudimentary.

Here's the situation. I have a Teensy 3.2 and an 8 position DIP switch. Because of everything else going on in this project, the first 6 positions are connected to pins 14 through 19 on the Teensy, then the last two are connected to pins 10 and 11. My goal here is to come up with a single 8 bit number.

I could do this via a bunch of individual pin reads, but that seems less elegant. I think that position 1 through 6 line up with PortA 0 through 5. Does that mean I could do something like:
addresstemp = PORTA;
and then mask off the bits I want? The documentation here: https://www.pjrc.com/teensy/pins.html suggests that in input mode the PORTX object controls the internal pull up.

The second part of the question is that I believe the best way to do this in C would be to work things out so that I end up with everything in two registers, aligned correctly, masked off, then OR them together. Does that sound about right?

Thank you!
 
Unless you have severe memory issues I'd do it as follows:

1) define a const uint8_t array with all the pins in it.
2) use a for next loop to read the positions with a digital read that uses the array for the pin number
3) store the data in a uint8_t where each on state is added as a left shifted 1. The number of left shifts = the position in the array.

Voila, all 8 states are captured in a uint8_t and you minimize SRAM usage. Especially if the data is only collected once at setup and the result is stored in a global variable.
 
Unless you have severe memory issues I'd do it as follows:

1) define a const uint8_t array with all the pins in it.
2) use a for next loop to read the positions with a digital read that uses the array for the pin number
3) store the data in a uint8_t where each on state is added as a left shifted 1. The number of left shifts = the position in the array.

Voila, all 8 states are captured in a uint8_t and you minimize SRAM usage. Especially if the data is only collected once at setup and the result is stored in a global variable.

Hi Constantin,

Hm, I'm already doing something similar to update my PWM output pins in a loop. Makes sense to me, though I find it a bit weird there's no way to read more than a single bit at a time.

Oh well, time to get coding!

Thank you!
 
Right the registers you're interested in are GPIOx_PDIR where x is either A,B,C,D or E

Only ports C and D have 8 bits in parallel so these are the only ones you can use.
Check the schematic for what these pins are numbered as on the Teensy

So connect your input to Port C0-7 or D0-7 then use:
Code:
uint8_t received = GPIOx_PDIR & 0xFF
Where x = C ⊕ D

The 0xFF is because the GPIO register is 32bits and you only want 8 bits of it
 
I could do this via a bunch of individual pin reads, but that seems less elegant.

The depends on what you consider elegant.

Perhaps your idea of elegance is code which compiled to as few instructions or bytes/words as possible? Or maybe code which executes in as few cycles as possible?

Or maybe elegance is fewer lines or characters in the source code? Or perhaps fewer lines, but not making use of function calls?

From your question, I'm getting the impression your concept of elegance isn't readable, self-documenting, and portable source code. Really, that's perfectly fine. You certainly are welcome to have your own preference. But I do hope you can at least understand that many other people consider elegant code to be the way that's easiest to read and maintain and port to other platforms, and above all other considerations, the least likely to have an unexpected bug or side effect or compatibility issue when reused with any other arbitrary code.


though I find it a bit weird there's no way to read more than a single bit at a time.

Well, you certainly can read the raw hardware registers. But unless you've carefully selected the pins based on which actual hardware register they use, you'd need to read 2 or more of those registers, and then add code to pick out and rearrange only the bits you want from each register. If you want to do that, you certainly can.

However, the general recommended way is to read the pins individually, using the easy to understand functions.
 
The depends on what you consider elegant.

Perhaps your idea of elegance is code which compiled to as few instructions or bytes/words as possible? Or maybe code which executes in as few cycles as possible?

Or maybe elegance is fewer lines or characters in the source code? Or perhaps fewer lines, but not making use of function calls?

From your question, I'm getting the impression your concept of elegance isn't readable, self-documenting, and portable source code. Really, that's perfectly fine. You certainly are welcome to have your own preference. But I do hope you can at least understand that many other people consider elegant code to be the way that's easiest to read and maintain and port to other platforms, and above all other considerations, the least likely to have an unexpected bug or side effect or compatibility issue when reused with any other arbitrary code.

Point taken. It would probably help if I explained that I've spent all my time programming in assembly on processors far more limited than what you've put into the Teensy. On those micros reading individual pins and combining them would be a time consuming process that consumed a bunch of resources. I definitely see the merit of reading individual pins and combining them, I just need to get over my prejudice against this sort of construct.


Well, you certainly can read the raw hardware registers. But unless you've carefully selected the pins based on which actual hardware register they use, you'd need to read 2 or more of those registers, and then add code to pick out and rearrange only the bits you want from each register. If you want to do that, you certainly can.

However, the general recommended way is to read the pins individually, using the easy to understand functions.

I did actually arrange things so that while I didn't have 8 bits available continuously on the same port, I managed to keep the arrangement to two chunks and arranged correctly to make a byte. That said, the more I think about it, the concept of picking out individual pins is rather liberating. Here's what I've come up with, comments welcome:

Code:
const int addrpin[] = {12, 11, 10, 19, 18, 17, 16, 15, 14};  

void getaddress(void)  // Get the address
{ // This version is for DIP switches
  uint16_t addresstemp = 0; // Clear the temp variable
  for(int acnt = 0; acnt < 8; acnt++)
  { // Loop through all input pins
    if(digitalRead(addrpin[acnt]) == 0)
    {
      addresstemp = addresstemp | 0x01;   // If pin is low then the switch is closed, so we want to
    }                                     //    shift a 1 into the address

    addresstemp = addresstemp <<1;         // Shift over one and repeat!       
  }

  RXaddr = addresstemp;
}

Thoughts?

Thank you!
 
I'm using a self created class to read DIP switches or jumpers from 1 to 8 (consecutive!) pins into a uint8_t variable (inPort) and to write out 1 to 8 bits to (consecutive!) Pins, too :

The thfpio.h file
Code:
#ifndef THFPIO_H_
#define THFPIO_H_

#include <Arduino.h>
#include <FreqMeasureMulti.h>

class outPort {
public:
	bool begin(uint8_t pinStart, uint8_t numPins);
	void write(uint8_t val);
private:
	uint8_t pS, nP;
};

class inPort {
public:
	bool begin(uint8_t pinStart, uint8_t numPins);
	uint8_t read(void);
private:
	uint8_t pS, nP;
};

#endif /* THFPIO_H_ */

and the thfpio.cpp file
Code:
#include "thfpio.h"

bool outPort::begin(uint8_t pinStart, uint8_t numPins) {
	int myPin;
	pS = pinStart;
	nP = numPins;
	if((pS + nP >= CORE_NUM_DIGITAL) || (nPins > 8) || (nPins < 1)) return false;

	for(int i = 0; i < nP; i++) {
		myPin = pS + i;
		pinMode(myPin, OUTPUT);
		digitalWrite(myPin, 0);
	}
	return true;
}

void outPort::write(uint8_t val) {
	int myPin;
	for(int i = 0; i< nP; i++) {
		myPin = pS + i;
		digitalWrite(myPin, val & (1 << i));
	}
}

/* inPort */

bool inPort::begin(uint8_t pinStart, uint8_t numPins) {
	int myPin;
	pS = pinStart;
	nP = numPins;
	if((pS + nP >= CORE_NUM_DIGITAL) || (nPins > 8) || (nPins < 1)) return false;

	for(int i = 0; i < nP; i++) {
		myPin = pS + i;
		pinMode(myPin, INPUT_PULLUP);
		digitalWrite(myPin, 1);
	}
	return true;
}

uint8_t inPort::read(void) {
	int myPin;
	uint8_t myVal = 0;
	for(int i = 0; i< nP; i++) {
		myPin = pS + i;
		myVal |= (digitalRead(myPin) << i);
	}
	return myVal;
}
 
Code:
const int addrpin[] = {12, 11, 10, 19, 18, 17, 16, 15, 14};  

void getaddress(void)  // Get the address
{ // This version is for DIP switches
  uint16_t addresstemp = 0; // Clear the temp variable
  for(int acnt = 0; acnt < 8; acnt++)
  { // Loop through all input pins
    if(digitalRead(addrpin[acnt]) == 0)
    {
      addresstemp = addresstemp | 0x01;   // If pin is low then the switch is closed, so we want to
    }                                     //    shift a 1 into the address

    addresstemp = addresstemp <<1;         // Shift over one and repeat!       
  }

  RXaddr = addresstemp;
}

One note on the above code. The left shift needs to come before pin is read, not after.
 
Status
Not open for further replies.
Back
Top