Firmware issue with STM32F40x LQFP100 vs LQFP64

The Cerbuino uses a 64pin STMF4 chip. My custom board uses the same chip in a 100pin layout. My firmware runs, but all the pins are mapped incorrectly.

For example, PB12 is (Cpu.Pin)33 and PA1 is (Cpu.Pin)1. I have an LED on PA1 and buzzer on PB12, and I can work with them but the pins should like 51 and 24 not 33 and 1.

I assumed there was a header file or something I’d need to change in the firmware for the 100pin vs 64pin layout. Has anyone else already figured this out? I’m a newbie. Any help is appreciated.

The porting ebook under support page is a good start.

1 Like

So, after a little more digging (and testing) it doesn’t seem to matter. There’s a file in the firmware called CPU.cs which has entries like this…

public static class Pins
{
public const Cpu.Pin GPIO_PIN_A_0 = (Cpu.Pin)0;

    public const Cpu.Pin GPIO_PIN_A_1 = (Cpu.Pin)1;

…Etc.

While the code says “Cpu.Pin” it isn’t actually representing physical CPU pins. (Cpu.Pin)1 is physical CPU pin 25 on my big 100 pin chip and pin 15 on the 64pin Cerbuino board.

It seems to me like this is a non-issue. I don’t have to change any code. So far I just code to the pin based on the CPU.cs file, then look at my diagram of the CPU to see where PA1, PB12, etc. are connected.

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?)