0
\$\begingroup\$

I'm following a bare metal course, I have managed to set up the project within IAR, my project compiles and runs. I'm attempting to get the user LED (LD2) to flash before I continue the course. I'm operating on a NUCLEO board with the STM32L053R8 chip. Trying to manipulate the chip registers with this User Manual

the LD2 on the NUCLEO board is connected to PA5.

#include "stm32l053xx.h"

void delayMs( uint32_t delay);

static volatile uint32_t ms_tick = 0 ;

int main()
{

  RCC->IOPENR |= 0x1;

  GPIOA->OTYPER &= ~(1UL << 5);
  GPIOA->MODER = (GPIOA->MODER & 0xEBFFFCFF) | 0x00000400;
  while(1)
   {

      GPIOA->ODR ^= (1UL << 5);
      delayMs(1000);
   }

  return 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 );
}

I'm very sad to admit that this has cost me somewhere around 10 hours of trying different things :) I was going to take a look at configuring the clock (HSI) but wanted to ask for some pointers before attempting that and possibly digging my hole deeper.

So what in the world am I doing wrong ? I have read the other threads about the subject, but can't get it to work regardsless.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Consider making a minimal example with the vendor HAL and tracing through it to see what is happening at register level and comparing that to your attempt. As a guess you're probably skipping necessary setup, something like enabling a peripheral clock or getting it to the GPIO block, but I don't really feel like verifying your unexplained constants one at a time against the manual - that's more your job. It's also possible that your timer-based delays aren't operating, and for that matter you've not said anything that proves your code is running at all. \$\endgroup\$ Commented Dec 4, 2020 at 21:04
  • \$\begingroup\$ first off, don't blink. just turn the LED on to be sure you have configured IO correctly. remember there is 3 different category in your simple blink code: 1)gpio registers 2) rcc (clock) configurations and 3) interrupts(for systick). then use the onboard debugger to be sure the ms_tick increases correctly \$\endgroup\$ Commented Dec 4, 2020 at 21:21
  • \$\begingroup\$ I love how the code uses magic numbers and no comments. To know what each line does, we still need to go to the Reference Manual and decipher what each line does. But frankly put, I don't know if your compiler configures and enables bus and peripheral clocks for you before executing main. If it does not, you need to configure them yourself. Note that while this bare metal stuff is to understand the MCU architecture, even the STM32L053 is far too complex to start bare metal programming from scratch and normally vendor startup libraries would be used to not waste 10 hours trying to blink a LED. \$\endgroup\$
    – Justme
    Commented Dec 4, 2020 at 21:23

1 Answer 1

0
\$\begingroup\$

Update - I have now gotten it to work, I took @ChrisStratton advice and found a light weight HAL demo, I started to compare the disassembly and register contents with the demo and found something interesting. The issue was with GPIOA->MODER which has the reset value I use 0xEBFFFCFF, however in the register view I could see I completely forgot to clear bit 11 - thereby setting the GPIO output as analog :') I also stumbled upon this gem GPIO configuration. I will update the code in the original question, once I clean it up.

/**
  ******************************************************************************
  * @file    main.c
  * @author  -
  * @brief   This file implements the following:
  *          - Baremetal implementation using the CMSIS library for Cortex-M0+ 
  *            of GPIO toggling, specifically the toggling of user LED (LD2) 
  *            via GPIO PA5 on the Nucleo-L053R8 STM32 development board.
  *          
  ******************************************************************************
 */ 

/* Includes ------------------------------------------------------------------*/
#include "stm32l0xx.h"

/* Private define ------------------------------------------------------------*/
#define RESET_VAL_PORTA_MODER 0xEBFFFCFF
#define RESET_VAL_PORTA_PUPDR 0x24000000
#define RESET_VAL_PORTA_SPEEDR 0x0C000000 

/* Private function prototypes -----------------------------------------------*/
void  ConfigureGPIO(void);

/**
  * Brief   Main program.
  * Param   None
  * Retval  None
  */
int main(void)
{
 
 /*!< At this stage the microcontroller clock setting is already configured, 
       this is done through SystemInit() function which is called from startup
       file (startup_stm32l053xx.s) before to branch to application main.
       To reconfigure the default setting of SystemInit() function, refer to
       system_stm32l0xx.c file
     */
  
 ConfigureGPIO();

  while (1)
  {
    // Atomic write - Set [Bit 5] [Val 1] to turn ON PA5 (Port A Pin 5 (LD2))
    GPIOA->BSRR = (1<<5); 
    
    // Atomic write - Set [Bit 21] [Val 1] to turn OFF PA5
    GPIOA->BSRR = (1<<21);   
  }
}

/**
  * Brief   This function enables the peripheral clocks on GPIO port A,
  *         configures GPIO PA5 in output mode push-pull enables internal pull-up 
  *         and sets the GPIO speed to medium.
  * Param   None
  * Retval  None
  */
static __INLINE void ConfigureGPIO(void)
{  
  /* (1) Enable the peripheral clock of GPIOA[Bit 0] [Val 1]*/
  /* (2) Select General purpose output mode [Bit 11:10] [Val 0:1] on GPIOA pin 5 */
  /* (3) Select Output type push-pull mode */
  /* (4) Select Pull-up (10) for PA5 */
  /* (5) Select Medium speed [Bit 11:10] [Val 0:1] for PA5 */
  
  RCC->IOPENR |= 0x1;  /* (1) */ 
  
  GPIOA->MODER = (GPIOA->MODER & RESET_VAL_PORTA_MODER) | (1<<10); /* (2) */
  GPIOA->MODER = (GPIOA->MODER & RESET_VAL_PORTA_MODER) & ~(1<<11); /* (2) */
 
  GPIOA->OTYPER &= ~(1<<5);  /* (3) */
  
  GPIOA->PUPDR = (GPIOA->PUPDR & RESET_VAL_PORTA_PUPDR) | 0x00000400;  /* (4) */
 
  GPIOA->OSPEEDR = (GPIOA->OSPEEDR & RESET_VAL_PORTA_SPEEDR) | 0x00000400;   /* (5) */
}

To sum up: This line saved me

GPIOA->MODER = (GPIOA->MODER & RESET_VAL_PORTA_MODER) & ~(1<<11); /* (2) */

/* https://www.st.com/resource/en/reference_manual/dm00095744-ultralowpower-stm32l0x3-advanced-armbased-32bit-mcus-stmicroelectronics.pdf */

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Very glad you solved your problem. Note however that solutions go in answers, so the repaired code (ideally a commented excerpt of the key issue) should be added as an edit to your answer, and not edited into the question. A question where the problem has been edited out wouldn't be much of a question ;-) Then when the self answer timer expires you can "accept" this so that the issue shows as resolved and doesn't get churned back up every six months. \$\endgroup\$ Commented Dec 5, 2020 at 18:59
  • \$\begingroup\$ Oh yeah! That makes sense Chris, don't want to delete the source material. \$\endgroup\$
    – Sorenp
    Commented Dec 5, 2020 at 19:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.