2024-04-10 10:10 PM
Hi ST Community!
Warning, this is about to be a long one :)
I have tried following the LED on step by step, but for my board. In this instance, the LED I am flashing is LD2 on my board which is connected to pin D13, based on the schematic below (which I obtained from https://www.st.com/en/evaluation-tools/nucleo-f411re.html#cad-resources)
I have used the AHB1 clock channel peripherals as dictated by the architecture in the datasheet of my boards chip.
System architecture up close
Full View
Now, onto its reference manual (RM0383) I use the following Addresses
Boundary addresses used for RCC and GPIOD
Based on this data, my clock enable register has been set as 0x4002 3800 + 0x30 ==> 4002 3830
To enable the peripheral for Port D the value 0x08 was written through an OR shift (see final code)
With this data, my register of initialising port D is 0x4002 0C00 + 0x0 ==> 0x40020C00. Pin 13 was highlighted
To clear the register for the 26th and 27th position the value 0xF3FFFFFF was calculated to AND shift, and then set up with calculated hex value of 0x04000000 with the OR shift (see final code)
The output register determined to be was 0x4002 0C00 + 0x14 ==> 0x4002 0C14
The final output value 0x2000 based on Pin 13 was masked on the output register with an OR operation (see final code)
The final code I used was this
#include <stdint.h>
#if !defined(__SOFT_FP__) && defined(__ARM_FP)
#warning "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif
int main(void)
{
/* Loop forever */
// Create the pointer variables for the addresses
uint32_t *pClkctrlreg = (uint32_t*)0x40023830; // It will store as a pointer not a number
uint32_t *pPortDModeReg = (uint32_t*)0x40020C00; // It will store as a pointer not a number
uint32_t *pPortDOutReg = (uint32_t*)0x40020C14; // It will store as a pointer not a number
// Enable the clock for the relevant register (AHB1)
// uint32_t temp = *pClkctrlreg; // read
// temp = temp | 0x08; //modify
// *pClkctrlreg = temp; //write
*pClkctrlreg |= 0x08;
// clear the 26th and 27th bit positions to reset it initially
*pPortDModeReg &= 0xF3FFFFFF;
// Set the 26th bit as general purpose output mode
*pPortDModeReg |= 0x04000000;
// Output register
*pPortDOutReg |= 0x2000;
while (1);
}
No lights! and I can confirm my LED wasnt busted, as I used an old default LED dimming code which works
I went to debug to monitor the registers to make sure it was working too, and bridged the CN5 to see if it helped but no avail!
Solved! Go to Solution.
2024-04-11 12:01 AM
I've pulled a Nucleo411 from the stash of STM32 boards I have, copypasted your code, compiled, loaded, no LED.
But, observing registers, GPIOD_MODER register is zero. What gives?
I always compile with optimization, and your definitions don't define registers as volatile, so the compiler felt free - and rightly so - to reorganize the code and also omit some of the writes. This, together with poor readability, highlight the fact that not using symbols from the CMSIS-mandated device header is a bad idea.
Regardless, the root problem is elsewhere, and it is, surprisingly, in Arduino... the LED is *not* on pin PD13, but on the pin which is in Arduino world (hence the Arduino header used on Nucleo) called as D13, i.e. 13th Digital pin.
It is PA5.
Here is Nucleo411 blinky:
#include "stm32f4xx.h" // must be before common.h, where the OR macro is defined
#include "stm32f4xx_augment.h"
// 'F411 NUCLEO
// LED PA5
#define LED_PORT GPIOA
#define LED_PIN 5
// BUTTON PC13, switches to GND
#define BUTTON_PORT GPIOC
#define BUTTON_PIN 13
// default HSI 16MHz clock
#define CLOCK_MHZ 16
// -------- utils/frameworks
// trivial loopdelay
static void LoopDelay(volatile uint32_t n) {
while(n > 0) n--;
}
#define DELAY_CONSTANT 1000000
// ---------------------------- MAIN ------------------------------
int main(void) {
RCC->AHB1ENR |= 0
| RCC_AHB1ENR_GPIOAEN
| RCC_AHB1ENR_GPIOCEN
;
GPIOA->MODER = (GPIOA->MODER // assuming reset value
) | (0
| (GPIO_Mode_Out * GPIO_MODER_MODER5_0) // LED
);
// GPIOC - button is on PC13 - GPIO In is the default value, and there's an external pullup, so we don't need to set up here anything
while(1) {
PIN_SET(LED);
LoopDelay(DELAY_CONSTANT);
if (PIN_GET(BUTTON)) {
LoopDelay(3 * DELAY_CONSTANT);
}
PIN_CLR(LED);
LoopDelay(DELAY_CONSTANT);
}
}
// GPIOx_MODER - 2 bits per pin
#define GPIO_Mode_In 0x00 // GPIO Input Mode
#define GPIO_Mode_Out 0x01 // GPIO Output Mode
#define GPIO_Mode_AlternateFunction 0x02 // GPIO Alternate function Mode
#define GPIO_Mode_AF GPIO_Mode_AlternateFunction
#define GPIO_Mode_Analog 0x03 // GPIO Analog Mode
#define PIN_SET(pin) pin##_PORT->BSRR = (1 << (pin##_PIN + 0))
#define PIN_CLR(pin) pin##_PORT->BSRR = (1 << (pin##_PIN + 16))
#define PIN_GET(pin) ((pin##_PORT->IDR & (1 << pin##_PIN)) ? 1 : 0)
JW
2024-04-11 12:01 AM
I've pulled a Nucleo411 from the stash of STM32 boards I have, copypasted your code, compiled, loaded, no LED.
But, observing registers, GPIOD_MODER register is zero. What gives?
I always compile with optimization, and your definitions don't define registers as volatile, so the compiler felt free - and rightly so - to reorganize the code and also omit some of the writes. This, together with poor readability, highlight the fact that not using symbols from the CMSIS-mandated device header is a bad idea.
Regardless, the root problem is elsewhere, and it is, surprisingly, in Arduino... the LED is *not* on pin PD13, but on the pin which is in Arduino world (hence the Arduino header used on Nucleo) called as D13, i.e. 13th Digital pin.
It is PA5.
Here is Nucleo411 blinky:
#include "stm32f4xx.h" // must be before common.h, where the OR macro is defined
#include "stm32f4xx_augment.h"
// 'F411 NUCLEO
// LED PA5
#define LED_PORT GPIOA
#define LED_PIN 5
// BUTTON PC13, switches to GND
#define BUTTON_PORT GPIOC
#define BUTTON_PIN 13
// default HSI 16MHz clock
#define CLOCK_MHZ 16
// -------- utils/frameworks
// trivial loopdelay
static void LoopDelay(volatile uint32_t n) {
while(n > 0) n--;
}
#define DELAY_CONSTANT 1000000
// ---------------------------- MAIN ------------------------------
int main(void) {
RCC->AHB1ENR |= 0
| RCC_AHB1ENR_GPIOAEN
| RCC_AHB1ENR_GPIOCEN
;
GPIOA->MODER = (GPIOA->MODER // assuming reset value
) | (0
| (GPIO_Mode_Out * GPIO_MODER_MODER5_0) // LED
);
// GPIOC - button is on PC13 - GPIO In is the default value, and there's an external pullup, so we don't need to set up here anything
while(1) {
PIN_SET(LED);
LoopDelay(DELAY_CONSTANT);
if (PIN_GET(BUTTON)) {
LoopDelay(3 * DELAY_CONSTANT);
}
PIN_CLR(LED);
LoopDelay(DELAY_CONSTANT);
}
}
// GPIOx_MODER - 2 bits per pin
#define GPIO_Mode_In 0x00 // GPIO Input Mode
#define GPIO_Mode_Out 0x01 // GPIO Output Mode
#define GPIO_Mode_AlternateFunction 0x02 // GPIO Alternate function Mode
#define GPIO_Mode_AF GPIO_Mode_AlternateFunction
#define GPIO_Mode_Analog 0x03 // GPIO Analog Mode
#define PIN_SET(pin) pin##_PORT->BSRR = (1 << (pin##_PIN + 0))
#define PIN_CLR(pin) pin##_PORT->BSRR = (1 << (pin##_PIN + 16))
#define PIN_GET(pin) ((pin##_PORT->IDR & (1 << pin##_PIN)) ? 1 : 0)
JW
2024-04-11 04:06 AM
Thanks Jan! Yes turns out it was the wrong pin:face_with_tears_of_joy: its a very bare metal C programming (so no ST libraries), just standard C ones :) shouldve mentioned that.
2024-04-11 05:27 AM
The CMSIS-mandated device header is IMO a good least common denominator, which should be used even if you want to avoid using ST libraries.
IMO there's no benefit from not using it and also negligible risk in using it.
JW