0

I am trying to write a bare metal program to blink green led. In fact, I am unable to turn any LEDs on or off. This is an off-the-shelf board. The board name is NUCLEO F429ZI. Board Image

I have gone through the schematics and I am sure that the pin is PA5 i.e. Port A and pin number 5. However, the led isn't blinking at all. I can see that my code is loaded onto STM32 board using uVision IDE.

I have tried setting blue LED i.e. PB7 but that also didn't work at all.

void delayMs(int delay);

int main(void)
{
    //enable clock access to A
    RCC->AHB1ENR |= 1;  //enable GPIO A clock
    GPIOA->MODER |= 0x400; //       PA-5 01   0000 - PA0
    while(1)
    {
          GPIOA->ODR |= 0x20;
         //delay
          delayMs(100);
          GPIOA->ODR &=~ 0x20;
          delayMs(100);
    }

}

void delayMs(int delay)
{
    int i = 0;
    for(; delay >0; delay--)
    {
     for(i=0; i<3195; i++)
        {
        }
    }
}

The green LED in the STM32F429ZI should blink.

Next, I tried turning on the blue LED that is also not working. As per my understanding by looking at schematics - PB7 should be turned on for blue LED. But this is also not working.

#include "stm32f4xx.h"
int main(void)
{
    RCC->AHB1ENR |= 1;
    // ob 01 00 00 00 00 00 00 00 // PB7
    GPIOB->MODER = 0x4000;
    for(;;)
    {
        GPIOB->ODR = 0x80;
    }
}
  • Where is the clock setup? Why `int main (void)`, STM32 is not a PC. – Lundin May 09 '19 at 07:00
  • 1
    What does the optimizer with your delay loop? Better use a timer. – Mike May 09 '19 at 07:01
  • 1
    Show the declaration of `GPIOA` and `RCC`, my bet is they are not `volatile`. – meaning-matters May 09 '19 at 07:10
  • 2
    Before you try blinking it, try just turning it on or off - if both those work (in separate programs) then it is your ill-advised delay implementation that is at fault (it is flawed at best in any case). If not then your GPIO set-up or pin. If it is an off-the-shelf board, then tell us what it is, then if you are uncertain about the schematic, someone can check your assumptions (or even read the documentation). – Clifford May 09 '19 at 14:20
  • @meaning-matters : They are defined in the vendor supplied header file for the part, so unlikely I think - I doubt he has defined them himself or used some broken third-party implementation. The delay loop counter is an issue however. – Clifford May 09 '19 at 14:25
  • @Clifford - I think you are correct earlier, it has nothing to do with delay. Even when I try to turn on the blue LED it doesn't work so I think there is some fundamental thing I am missing here. I have added the image of the board also now. –  May 09 '19 at 19:13
  • I am not sure a photo of the board is particularly useful, but the name allows us to find the documentation (which has pictures of the board - annotated). My answer is updated - the delay is an issue, but perhaps not the immediately issue in question. – Clifford May 09 '19 at 21:49
  • At least one of your RCC settings is wrong, as you are claiming that the same setting is the enable for both GPIOA and GPIOB, which cannot be true. – Chris Stratton May 09 '19 at 22:00
  • @Lundin - why not `int main(void)` It is not wrong and not harmful, many compilers or static analysis tools (and many SO denizens) will issue a warning for `void main`. It is possible even on a freestanding implementation for a customised runtime to make use of the main return value (or `exit(int n)` argument). – Clifford May 09 '19 at 22:21
  • @Clifford Most obviously because there is nothing to return to. But also because in most cases, this generates overhead on the stack. If the CRT is written in C, which is very likely nowadays, you'll stack the return address etc when main() is called. Which then sits there forever as dead waste of space for the lifetime of your program. Yes there are tricks to avoid that, pragmas, inline assembler etc etc, but why dirty up your code with such clutter, needlessly. Every embedded compiler I know supports implementation-defined form `void main (void)`. For example gcc `-ffreestanding`. – Lundin May 10 '19 at 06:31
  • Regarding the SO denizens, they are mostly PC programmers. – Lundin May 10 '19 at 06:32
  • @Lundin or embedded systems programmers using C++ ;-) – Clifford May 10 '19 at 06:51
  • @Clifford C++ is actually quite similar to C here. There is some confused FAQ written by Stroupstrup where he claims that other forms than `int main()` were never valid C++. What he failed to teach is that's only true if you speak of the formal term "strictly conforming program" - that is, a program without any implementation-defined behavior. What makes C++ stand out in stupid ways, is that if you name the function main(), it must have the hosted system form `int main ()`, since some PC programmer said so. Otherwise the compiler must give it an implementation-defined name (like WinMain). – Lundin May 10 '19 at 06:59
  • @Lundin I here what you are saying, but it is off topic and already widely discussed. My point was simply that it is not _wrong_ and not the first issue I'd choose to highlight on this question. – Clifford May 10 '19 at 07:05

3 Answers3

1

Rather unhelpfully the Nucleo F429ZI UM1974 user manual download link at https://www.st.com/en/evaluation-tools/nucleo-f429zi.html#resource appears to be broken (at time of writing), but I have found a copy elsewhere, and the pin assignments of the three user LEDs is described thus:

enter image description here

The schematic suggests that the factory state of solder-bridges SB120 and SB119 are set for LD1 on PB0 rather then PA5. The problem with your attempt to set LD2 (blue) is that you did not enable GPIOB in the RCC - it enables GPIOA as in the LD1(green) attempt.

Another issue is that your setting of GPIO MODER assumes that the reset state for the port is zero. That is not the case (although it is for the specific pins in question - so you "get away with it" in this case):

enter image description here

It is a good idea to define all the port/pin specific constants in one place so you can switch or add outputs easily and with less chance of error:

#define LED_PORT              GPIOB
#define LED_PORT_RCC_EN       0x2u 
#define GPIO_MODE_MASK       ~0x3u
#define GPIO_MODE_OUTPUT      0x1u

#define GREEN_LED_PIN         0u
#define BLUE_LED_PIN          7u
#define RED_LED_PIN           14u
#define FLASH_LED             GREEN_LED_PIN

int main(void)
{
    RCC->AHB1ENR |= LED_PORT_RCC_EN ;
    LED_PORT->MODER &= GPIO_MODE_MASK << (FLASH_LED << 1) ;
    LED_PORT->MODER |= GPIO_MODE_OUTPUT << (FLASH_LED << 1) ;

    for(;;)
    {
          LED_PORT->ODR |= 0x1 << FLASH_LED ;
          delayMs( 100 ) ;
          LED_PORT->ODR &= ~(0x1 << FLASH_LED) ;
          delayMs( 100 ) ;
    }

    return 0 ;
}

Your delay function is seriously flawed, and likely to be optimised out to "do nothing". Your loop counter needs to be declared volatile to avoid being optimised out. If that happens, the indicator will not visibly blink but will be pulsed at a very high frequency and be on but not at full brightness.

The following will prevent the loops being optimised out.

void delayMs( unsigned delay )
{
    for( volatile unsigned d = delay; d > 0; d--)
    {
        for( volatile int i = 0; i < 3195; i++ )
        {
        }
    }
}

However it is a 180MHz part; if you are running it at full-speed 3195 iterations it will probably not take 1ms. More like a few tens of microseconds. Even if running at the start-up HSI frequency of 16MHz, it is likely to of the order of a few 100s of microseconds, and will in any case vary with optimisation settings and time spent in any interrupt handlers running. Much better to use the Cortex-M SYSTICK counter as follows:

static volatile uint32_t ms_tick = 0 ;

void SysTick_Init(void) 
{
    SysTick_Config( SystemCoreClock / 1000 ) ;  
}

void SysTick_Handler(void)
{
    ms_tick++;
}

void delayMs( uint32_t delay)
{
    uint32_t start_tick = ms_tick ;
    while( (ms_tick - start_tick) < delay );
}

Then the delay will be accurate regardless of the clock speed you run your processor at or interrupt load.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • not sure but the code doesn't work if I give #define FLASH_LED to BLUE_LED_PIN –  May 10 '19 at 03:39
  • @kurramkurramD : Try it now - I have corrected the MODER mask shift - it is two bits per pin, not 1. Sorry; coding it blind and untested. – Clifford May 10 '19 at 16:48
0

You should not modify ODR directly as it does a read/write on the entire port. Instead you should use the BSRR register where you can affect one bit only. Note that some STM32 part uses a single 32-bit BSRR register to set and clear the bits (one operation is shifted 16 bit left) whereas others use two 16-bit registers so consult the reference manual or header file for details.

Your MODER setting is incorrect. You need to clear the 2 bits for PA5 first, before OR'ing 01 in, e.g.

GOPIOA->MODER &= ~0b1100000000;
GPIOA->MODER |= 0b0100000000;
Clifford
  • 88,407
  • 13
  • 85
  • 165
  • 1
    While using BSRR is probably a better method in this case, using a read-write-modify of ODR is not _wrong_ per se, but may be unsafe if the port is also being modified in different thread or interrupt handler. It is not the cause of the problem in this question however. – Clifford May 09 '19 at 22:54
  • @ChrisStratton : That is I think the recommended state to reduce power consumption for unused pins but not the reset state which for this part is 0xA800 0000 for port A, 0x0000 0280 for port B and 0x0000 0000 for other ports. The board's LEDs are on PB0, PB7 and PB14, so in this case you "get away with it". Good point from Richard though. – Clifford May 09 '19 at 22:58
  • Huh, okay, I admit I checked the RM for an L0 not an F4. But if that is the case, then neither of the points made in this response have any actual bearing on the problem, but are merely tangential commentary. – Chris Stratton May 09 '19 at 23:13
0
This is complete solution for Nucleo 144 - STM32F429ZI.

#include "stm32f4xx.h"

#define LED_PORT              GPIOB
#define LED_PORT_RCC_EN       0x2u 
#define GPIO_MODE_MASK       ~0x3u
#define GPIO_MODE_OUTPUT      0x1u

#define GREEN_LED_PIN         0u
#define BLUE_LED_PIN          7u
#define RED_LED_PIN           14u
#define FLASH_LED             BLUE_LED_PIN


void delayMs( unsigned delay );

int main(void)
{
    RCC->AHB1ENR |= LED_PORT_RCC_EN ; // enable for PORT B
    //LED_PORT->MODER &= GPIO_MODE_MASK << FLASH_LED ;
    //LED_PORT->MODER |= GPIO_MODE_OUTPUT << FLASH_LED ;
      GPIOB->MODER |= 0x4000;  // blue LED PB7
      GPIOB->MODER |= 0x1;  // green LED PB0
      GPIOB->ODR |= 0x1; // green LED ODR
      GPIOB->MODER |=  0x1 << 28; // red LED PB14
      GPIOB->ODR |= 0x1 <<14; // RED LED ODR
      //GPIOB->BSRR |= 0x4000; // 7th pin 
    // connected to pull up resistor -- when we press button , it goes low
        //B1 user button is connected to PC13 - i.e. blue button
      // Enable clock to PC13 i.e. Port C
      RCC->AHB1ENR |=  0x1 << 2;
      //GPIOC->MODER |=     //Input mode PC13- 13th pin 0
       // by default pins are input only
      GPIOC->MODER |=  0;
    for(;;)
    {
               // pressed blue button
               if( (GPIOC->IDR & (1<<13)) ) // PC13
                 {
                      LED_PORT->ODR |= 0x1 << FLASH_LED ;

                 }
                 else
                 {
            LED_PORT->ODR &= ~(0x1 << FLASH_LED) ;

                 }
    }

    return 0 ;
}

void delayMs( unsigned delay )
{
    for( volatile unsigned d = delay; d > 0; d--)
    {
        for( volatile int i = 0; i < 3195; i++ )
        {
        }
    }
}