A given subgroup (810, 811 or 812) has its different versions! So, for example, the 812 version can be 16-pin, and it can also be 20-pin, so then PIO0.7 will be on a different leg depending on the variant of the microcontroller - not always on pin 17 (if we buy it as a single microcontroller, not on the entire devboard of course). So how to distinguish in the code, to which physical pin/leg of a microcontroller a PIO0_7 belongs, since the code operates on GPIO numbers, and not on the numbers of physical pins?
First to say: good thinking and you’re on the right track. Indeed, in software you work with PORT and PIN numbers (NXP calls them PIO, other may call them GPIO). And indeed, where they connect on the physical package is different per package. If I understand your question right, then you’re wondering how do the two connect? And if you use a different package on a different project, then what happens?
Thankfully, the manufacturer gives you the information you need in the Data Sheet or Reference Manual.
Look here for the datasheet of the LPC 81xOn page 6, chapter 7, you will see these figures. They show you for each package on which physical pin each PIO is connected.

There is also this table starting on page 8, that lists all the PIO (and some other) functions, and which pin they connect to for a given package. And if you look at either or both, you can see for example that PIO0_1 is physical pin 12 on the TSSOP-20 package, but pin 9 on the TSSOP-16 package.
You may notice that some pins have more functions than only PIO. That is common, as a microcontroller can do many things. And you can choose what you use a certain physical pin for. It usually can only do one thing at a time, and there are ways to tell the code which function to use. That also means that after the microcontroller starts up, after reset, there will be function that it goes to automatically. Usually, but not always(!) that is PIO.
What if you want to use a different package for your controller?Now to the question: you have written some code, and now you want to use it on a different package where the physical pins are linked to a different PIO. This is where you need to do some work. On one hand you need to decide which pin you want to use for what. If you make your own board that is simple, you can choose almost freely. Almost, because not all functions are available on every pin.
But you as the programmer must look up which PIO connects to which physical pin, and then use the correct PIO in your code.So say, you have the LPC812 in a TSSOP-16 package. And you want to connect a LED, and you’d like it to connect to physical pin 8. For example, maybe that’s the easiest for you for doing your PCB layout.
Now you go to the diagrams and the table in the data sheet, and you look up which PIO you need to use in your code. If you look in the datasheet I linked above, you will find that you need to use PIO0_10 for your LED now. So in your code you will now need to use “(1<<10)” when you need to do something with your LED.
But what if you wrote all your code and then later decided that you actually wanted to use the TSSOP-20 package. But you still want to connect the LED to physical pin 8. Well, again you need to do the same thing: you go to the table in the data sheet, and now you see that in your code you must use PIO0_11. So now you need to change every single use of (1<<10) in your code and make it use (1<<11) instead.
Easier with MacrosBut can’t that be done easier then, because now you need to change your code everywhere you used PIO_10? And perhaps you used other PIO as well that also need to change. It’s a bit difficult to make sure that you change
every instance of PIO to the correct number. Well, that’s where it is easier to use “macros” in C:
What if you could just, for example, write “MY_LED” everywhere. And somewhere at the top of the code is where you tell the compiler that “MY_LED” means “use PIO0_10”?
This is how:
#define MY_LED (1<<10)
Now instead of
LPC_GPIO_PORT->DIR0 |= (1<<10);
LPC_GPIO_PORT->SET0 |= (1<<10);
You can write:
LPC_GPIO_PORT->DIR0 |= MY_LED;
LPC_GPIO_PORT->SET0 |= MY_LED;
And if you need to change which PIO you want/need to use, the only thing you need to do is change the macro:
#define MY_LED (1<<11)
And the rest of your code has now changed as well to use PIO0_11 everywhere it says MY_LED.
If you apply this to the code you shared in your first post, is looks like this:
#define RED_LED (1<<7)
#define BLUE_LED (1<<16)
#define GREEN_LED (1<<17)
void LED_Init()
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);
//red led
LPC_GPIO_PORT->DIR0 |= RED_LED;
LPC_GPIO_PORT->SET0 |= RED_LED;
//blue led
LPC_GPIO_PORT->DIR0 |= BLUE_LED;
LPC_GPIO_PORT->SET0 |= BLUE_LED;
//green led
LPC_GPIO_PORT->DIR0 |= GREEN_LED;
LPC_GPIO_PORT->SET0 |= GREEN_LED;
return;
}
TL:DR So in summary: you as the programmer need to know which PIO you must use if you want to use a certain physical pin. If you change the package, then you need to change some things in your code too. But you can use the table and diagrams in the Data Sheet to tell you what you need to know. And you can use macros in C to make your life easier if you need to change something.