3 years, 7 months ago.

Nucleo 144 boards and GPIO HAL - Not all ports work

Hello,

I'm trying to remove MBED generalized HAL abstractions from my STMicro Nucleo-144 board based project and am getting strange behavior. I'm moving from the platform agnostic DigitalIN, DigitalOut, PortIn, PortOut to STMicro port specific references such as PORTB->MODER.

For some ports, my GPIO initialization and use of GPIOs works(Port D seems to work correctly), but PORTB and PORTC are not working with the HAL abstractions. For example, setting PORTB->MODER has no effect and the value remains at 0x00000280(the reset value). In fact all of the other PORTB-> registers (OTYPER, OSPEEDR, PUPDR) all return 0x00000280, which is not correct. If I remove HAL abstractions and "poke" the register directly in this way:

  • (int*)0x40020400 |= 0x10004001; Set Nucleo LEDS as outputs

The write takes effect. This leads me to believe something is amiss in the HAL stack. For now, I'm just going to make my own HAL header file for the GPIO registers, but this seems like it should be addressed(pun intended).

I get the same behavior on both an F746 Nucleo and an F446 Nucleo board that I tested. It doesn't appear that any ports have their configuration locked.

UPDATE:

It appears that the HAL headers are not he problem. When I hard code the register address, the write takes effect, but after a short time, the register reverts back to it's reset value. Look at the example program below:

  1. include "mbed.h"

Serial pc(USBTX, USBRX);

  1. define GPIOB_MODER *(uint32_t*)0x40020400

int main(void) {

pc.printf("Before:\t%0.8x\r\n", GPIOB_MODER); GPIOB_MODER |= 0x10004001; pc.printf("After:\t%0.8x\r\n", GPIOB_MODER); pc.printf("After:\t%0.8x\r\n", GPIOB_MODER);

}

The output is:

Before: 00000280 After: 10004281 After: 00000280

If I use the already configured GPIOB->MODER for the register references, the extra processor time spend doing the redirection actually allows the register to revert before the first read completes, so I never see the written value stick.

Is there something under the mbed hood that is stepping on my register accesses? I see this behavior for both GPIOB and GPIOC, but GPIOD behaves as expected...

I am able to see the correct values using code similar to yours. I am still declaring my pins using mbed declarations.

#include "mbed.h"

DigitalOut myled1(LED1);
DigitalOut myled2(LED2);
DigitalOut myled3(LED3);
volatile int * _GPIOBp = (volatile int *) 0x40020400;
int main() {
    while(1) {
        myled1 = 1; // LED is ON
        wait(0.2); // 200 ms
        myled1 = 0; // LED is OFF
        myled2 = 1; // LED is OFF
        wait(0.2); // 200 ms
        myled2 = 0; // LED is OFF
        myled3 = 1; // LED is OFF
        wait(0.2); // 200 ms
        myled3 = 0; // LED is OFF
        wait(1.0); // 1 sec
        printf("My ports are %08X %08X %08X\r\n",_GPIOBp[0], _GPIOBp[1], _GPIOBp[2]);
        printf("loop\r\n");
    }
}

The resultant printf is "My ports are 10004281 00000000 200080C2"

posted by Bill Bellis 21 Sep 2017

Hi Bill,

Correct, with the DigitalOut declarations in place, the config occurs "under the hood" and the values stick. I'm trying to remove that layer of abstraction as I'm moving this project over to a chip down design coded with Keil's dev tools. I just wanted to do an intermediate STM specific mbed compiled build in the interim...

posted by Douglas S 21 Sep 2017

OK, I just did it your way and see your behavior....digging down because now I'm curious as to why this is not working...

posted by Bill Bellis 21 Sep 2017

GPIO Clock has to be enabled first :)

#include "mbed.h"

//DigitalOut myled1(LED1);
//DigitalOut myled2(LED2);
//DigitalOut myled3(LED3);
volatile int * _GPIOBp = (volatile int *) 0x40020400;
volatile int * _GPIOB_DATp = (volatile int *) 0x40020418;
int main() {
    SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
    for(int i=0; i < 10000; i++);
    _GPIOBp[0] = 0x10004281;
    _GPIOBp[1] = 0x00000000;
    _GPIOBp[2] = 0x200080C2;
    while(1) {
        *_GPIOB_DATp = (1 << 0); // LED is ON
        wait(0.2); // 200 ms
        *_GPIOB_DATp = (1 << 16); // LED is OFF
        *_GPIOB_DATp = (1 << 7); // LED is ON
        wait(0.2); // 200 ms
        *_GPIOB_DATp = (1 << 23); // LED is ON
        *_GPIOB_DATp = (1 << 14); // LED is ON
        wait(0.2); // 200 ms
        *_GPIOB_DATp = (1 << 30); // LED is ON
        wait(1.0); // 1 sec
        printf("My ports are %08X %08X %08X\r\n",_GPIOBp[0], _GPIOBp[1], _GPIOBp[2]);
        printf("loop\r\n");
    }
}
posted by Bill Bellis 21 Sep 2017

Thanks for the tip! I'll check that out. Although that doesn't explain why GPIOD works. I even did a bare metal project with no mbed.h and my LEDs on my shield behaved correctly... It also doesn't quite explain why the first readback of GPIOB->MODER works, unless I was simply reading back a "parked" data bus that was persisting from the write...

posted by Douglas S 21 Sep 2017

Ha! It works indeed! Very odd that when I read RCC_AHB1ENR, GPIOD is enabled already. I would assume mbed is doing this under the hood, but I swear my bare metal implementation could still light my shield LEDs correctly. There must still be some mbed "loader" of some sort that still gets executed even if your project does not include "mbed.h" apparently. Once I move over to Keil, I'll investigate further, but I'll explicitly enable GPIOD's clock regardless.

Can you re-paste your above comment/solution into the answer portion below, so I can accept it as the answer for the benefit of others who come across this issue? Thanks!

posted by Douglas S 21 Sep 2017

Yes! Glad I could help.

posted by Bill Bellis 21 Sep 2017

1 Answer

3 years, 7 months ago.

A couple of questions: Did you mean "GPIOB->MODER" instead of "PORTB->MODER"? The included HAL files from ST (in my version) use GPIOB instead of PORTB

The GPIOB->(reg name) should work fine as it references 0x40020400. I am using "mbed-dev" as my library. Are you using mbed-os, mbed-dev, or your own forked derivative? If you look at the Github, the definition is correct for the GPIO registers https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F446xE/device/stm32f446xx.h

BB

Accepted Answer

Hello, Sorry, yes, I meant GPIOB->xxxx. PORTB-> would not have compiled, of course. I'm assuming I'm using mbed-dev. Is there a way to see/select? All of my projects simply include "mbed.h" as the master header. GPIOB-> should work as exected. GPIOD works for me including revealing that the UART pins are configured as the alternate functions, but GPIOB returns the MODER register reset value for all registers I read, and my attempts to write different values has no effect. The same is true for GPIOC whose registers all return 0x00000000 and appear to not be written(actually, my F446 at the office, seemed to take config writes, but was still not working correctly, while my F746 at home is not taking config writes). In both cases, simple tests I did with hard addresses appeared to work on the config registers...

posted by Douglas S 21 Sep 2017

NOTE: See above reference to turning on GPIO clocks for the answer. Thanks again!

posted by Douglas S 21 Sep 2017