I know you’ve probably figured this out, but just for others who may stumble upon this topic:
Cpu.Pin corresponds to port pins, not physical package pins, which is consistent with every other development environment out there.
Consecutive port pins (say, A0 through A15) rarely map to consecutive pins on the package, and parallel interfaces used to be fairly common (so you’d do things like PORTA = 0xFF to output 0xFF – all 1s – on the port). If your GPIO functions used physical device pins, you wouldn’t be able to do nice parallel operations. And you’d have to have a lot of nasty switch/case statements to make sure you weren’t trying to write to a special-function pin.
If you think about how the processor is actually implemented (in terms of register-based GPIO), you’ll begin to realize how odd it would be to implement pin functions that way – you’d need 3 16-bit registers to configure a 48-pin chip, even if it only had 30 or so I/O (with the rest of the pins dedicated to power/ground/clock/whatever). That’s a huge waste!
The other issue is that often times the same die is installed in multiple packages and bonded out differently for each package. So, an STM32F4 in a BGA package has a different pinout than in an LQFP package.
Since ARM processors are 32-bit, and you typically need (at least) two bits to configure a pin (direction and output level/pull-up resistor state), they typically have 16 I/O per “port” – so you end up with A0-A15, B0-B15, C0-C15, etc.
In .NETMF, Cpu.Pin is just an integer mapping of those. For example A0 = 0, A15 = 15, B0 = 16, B15=31, etc. The C# functions that work with pins always take a Cpu.Pin as an argument; you can safely cast any arbitrary integer to it; so you tend to see a lot of:
Cpu.Pin led = (Cpu.Pin)0 // LED is on pin A0
vs this:
Cpu.Pin led = Pins.GPIO_PIN_A_0
though obviously both are functionally identical(with the first one perhaps executing a bit faster? Maybe? No?)