## Can you interpolate from PORT and PIN the pCINT values to use?

13 posts / 0 new
Author
Message

Hi

On the atmega's one can do this https://www.avrfreaks.net/forum/... . What I'd like to achieve is, to give in a port and the pin number, and set up the corresponding PCINT. Is there a mathematical method to do this, or should I just write a massive switch-case statement?

to give in a port and the pin number, and set up the corresponding PCINT

Why not just give the desired PCINT?   Making things more complex is not always simple.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

>Is there a mathematical method to do this

You can lay out the scene and see for yourself if there is a relationship that can be used, something like-

https://godbolt.org/z/b36dY9

randoh wrote:
or should I just write a massive switch-case statement?
I would have thought an Arduino-esque look-up table would do it. As far as I know the 0..7 pins always map to the corresponding bit in the group of 8 so the only thing you actually need to know is the B=0, C=1, D=2 relation (or whatever it is).

But how's this going to work anyway? You might usually have something like:

`pinHandlingClass pinSwitch(&PORTB, &PINB, &DDRB, 5);`

or, using the "3 adjacent" rule (which has mega128 as an exception by the way!) you only need one of those - perhaps just the PORT address? so it can be simplified as:

`pinHandlingClass pinSwitch(&PORTB, 5);`

But given that all you are being given here is &PORTB which might be something like 0x05 then how are you going to know that 0x05 really means "B" in order to then look up whether it is covered by the PCINT0, PCINT1 or PCINT2 group of interrupts? Or is your lookup table going to look something like:

```typedef struct {
volatile uint8_t * pPort,
void (*pcintCallback)();
} pcint_map_t;

pcint_map_t pcIntMap[] = {
{ 0x05, &PCMKS0, pcint0_handler },
{ 0x08, &PCMSK1, pcint1_handler },
{ 0x0B, &PCMSK2, pcint2_handler },
{ 0, 0 }
};```

Then you need to search this table for the given PORT IO address and then assign the given handler to it and set the 0..7 bit in the given PCMSKn register. To know which PCIE bit to set in PCICR it's simply the index number in the array where you found the port IO address. So if you were given 0x0B as the port you would find it at element 2 in the array so set (1 << 2) in PCICR.

EDIT 0x08 not 0x09 but the others weren't bad guesses!

Last Edited: Tue. Feb 2, 2021 - 09:41 AM

curtvm wrote:

You can lay out the scene and see for yourself if there is a relationship that can be used, something like-

https://godbolt.org/z/b36dY9

The key thing in that seems to be:

```//any port register to 0-2 (not required to be a seperate function, but here we are)
SI u8 getPortn(volatile u8* port){ return (port - &PINB) / 3; }```

but surely this is relying on adjacent groups of three? I know that holds true for something like the 328P:

but is this true across all traditional tiny/mega? (we already know the m128 breaks the group of 3 rule anyway) so I'm guessing there may be devices where the groups of three themselves are not adjacent and also not necessarily in ascending PCINT order? Perhaps I'm wrong and it is all very neat?

The reason I opted for the LUT approach above was for this very reason that you may not find a consistent mapping.

Last Edited: Tue. Feb 2, 2021 - 09:47 AM

clawson wrote:
we already know the m128 breaks the group of 3 rule

Top Tips:

1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...

What's the advantage of all of this?  To me it seems you have to give it something so that it can then figure out which PCINT you are trying to use.  But why not instead simply tell it what PCINT (the something) you want to use & then there is no chance of a problem in the first place?  Perhaps my viewpoint is too simplistic.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

avrcandies wrote:
What's the advantage of all of this?
Well I have to admit I used the same kind of "trick" in:

https://github.com/wrightflyer/s...

```///////////// PORT A //////////////
DualJoystick joy(0, 1, 64); // on A0/A1
// A2 - unused
Button  butJoy(&PORTA, 3, Button::NO_PULL_UP);
Button  but1(  &PORTA, 4, Button::NO_PULL_UP);
Button  but2(  &PORTA, 5, Button::NO_PULL_UP);
Button  but3(  &PORTA, 6, Button::NO_PULL_UP);
Button  but4(  &PORTA, 7, Button::NO_PULL_UP);

///////////// PORT B //////////////
Encoder enc1(  &PORTB, 0, 1);
Encoder enc2(  &PORTB, 2, 3);
Encoder enc3(  &PORTB, 4, 5); // SPI on 5/6/7 (with SS on 4)
// B6 / B7 - unused

///////////// PORT C //////////////
Encoder enc4(  &PORTC, 0, 1);
Button  but5(  &PORTC, 2, Button::NO_PULL_UP); // 2..5 are the JTAG pins
Button  but6(  &PORTC, 3, Button::NO_PULL_UP);
Button  but7(  &PORTC, 4, Button::NO_PULL_UP);
Button  but8(  &PORTC, 5, Button::NO_PULL_UP);
Encoder enc5(  &PORTC, 6, 7);

///////////// PORT D //////////////
Uart uart(9600); // on D0 / D1
Encoder enc6(  &PORTD, 2, 3);
Encoder enc7(  &PORTD, 4, 5);
Encoder enc8(  &PORTD, 6, 7);
```

In those I use the "group of 3" trick to determine DDR and PIN given only port. I guess could have passed everything:

```///////////// PORT A //////////////
DualJoystick joy(0, 1, 64); // on A0/A1
// A2 - unused
Button  butJoy(&PORTA, &PINA, &DDRA, 3, Button::NO_PULL_UP);
Button  but1(  &PORTA, &PINA, &DDRA, 4, Button::NO_PULL_UP);
Button  but2(  &PORTA, &PINA, &DDRA, 5, Button::NO_PULL_UP);
Button  but3(  &PORTA, &PINA, &DDRA, 6, Button::NO_PULL_UP);
Button  but4(  &PORTA, &PINA, &DDRA, 7, Button::NO_PULL_UP);

///////////// PORT B //////////////
Encoder enc1(  &PORTB, &PINB, &DDRB, 0, 1);
Encoder enc2(  &PORTB, &PINB, &DDRB, 2, 3);
Encoder enc3(  &PORTB, &PINB, &DDRB, 4, 5); // SPI on 5/6/7 (with SS on 4)
// B6 / B7 - unused

///////////// PORT C //////////////
Encoder enc4(  &PORTC, &PINC, &DDRC, 0, 1);
Button  but5(  &PORTC, &PINC, &DDRC, 2, Button::NO_PULL_UP); // 2..5 are the JTAG pins
Button  but6(  &PORTC, &PINC, &DDRC, 3, Button::NO_PULL_UP);
Button  but7(  &PORTC, &PINC, &DDRC, 4, Button::NO_PULL_UP);
Button  but8(  &PORTC, &PINC, &DDRC, 5, Button::NO_PULL_UP);
Encoder enc5(  &PORTC, &PINC, &DDRC, 6, 7);

///////////// PORT D //////////////
Uart uart(9600); // on D0 / D1
Encoder enc6(  &PORTD, &PIND, &DDRD, 2, 3);
Encoder enc7(  &PORTD, &PIND, &DDRD, 4, 5);
Encoder enc8(  &PORTD, &PIND, &DDRD, 6, 7);
```

but I guess it's really about less typing and making the code a little easier to read?

The counter argument is that it possibly obfuscates the code a little as there's a "hidden mechanism" going on inside that may leave the reader wondering "but how can these know the DDR and PIN addresses?". I do comment it in the implementing code, for example:

https://github.com/wrightflyer/s...

```Encoder::Encoder(volatile uint8_t * port, uint8_t pin1, uint8_t pin2) :
mPos(0)
{
mPin1 = (1 << pin1);
mPin2 = (1 << pin2);
mPORT = port;
mDDR  = port - 1;
mPIN  = port - 2;
// this uses the "trick" that GPIO ports in memory are laid out as:
// n + 0: PINx
// n + 1: DDRx
// n + 2: PORTx
//
// so given PORTx you can write DDR with (PORT-1) and PIN with (PORT-2)```

Last Edited: Tue. Feb 2, 2021 - 10:56 AM

clawson wrote:
but I guess it's really about less typing and making the code a little easier to read?

I can cheer on a build-time mechanism.  OP's mention of switch-case implied to me a run-time mechanism.  What -- the hardware configuration is going to change at some point while running?  So wouldn't a solution be rarely if ever used?

Now, w.r.t. this generic solution, no attention has yet been given here to the operational aspects of the triggered signals.  With a generic mechanism, the setup of the trigger mechanism is easy compared to now carrying out the correct action in the arbitrary ISR when you don't know which pin triggered the pin change.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Tue. Feb 2, 2021 - 12:22 PM

I saw this  and smiled

https://github.com/wrightflyer/s...

since I lived for several years about 2 miles from

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

When doing macros with ports,

I usually use the letter as one of the operands.

Concatenation can get complicated.

If you do not need contatenation with the result of an expansion,

'tain't too tough.

Iluvatar is the better part of Valar.

avrcandies wrote:
I saw this  and smiled
Yup "wrightflyer" is my ID on all kinds of things right across the internet - you can find me as @1903wrightflyer on Twitter for example.

My first interest has always been aviation and Dec 17th 1903 10:30am will always remain one of the most important points in time for me. On 17th Dec 2003 at 10:30 (admittedly local) I flew a wrighflyer kite from a beach in the Maldives in recognition of that special 100th anniversary - I am mad enough to have taken a model (well a kite) 5300 miles just to celebrate this event!

(oh and my second account on Freaks (the one I use to test a "user view") is Wrightflyer - some kind of double-personality thing going on in this thread: https://www.avrfreaks.net/forum/... )

Last Edited: Tue. Feb 2, 2021 - 06:01 PM