1
votes

I'm new to STM32, not to embedded development (experience with PIC, dsPIC, MSP430).

Environment

  • CooCox CoIDE
  • arm-none-eabi-gcc
  • ST-LINK V2 (have encountered same with Segger J-Link) over SWD

Problem Description

I am trying to write a basic library using direct register manipulations (STMCube HAL is cool, but I found I had to read the datasheet anyway, so register manipulation is preferred). In essence, I'm just trying to blink an LED that is on the Blue Pill development board, which is pin C13.

I can compile, flash, and debug, but when I debug, the LED is not flashing and the registers aren't changing. The register values themselves don't actually matter that much, so you shouldn't need to go out and check the datasheet. I just need to be able to manipulate them!

Stepping through the debugger works just fine, and the variables watch window is also appropriately updated.

Things I've Tried

Although this isn't exhaustive (it has been several hours, so I may have forgotten to include a few), these are some of the things I have tried:

  • Changing the BOOTx pin configurations
  • Changing the debugger config (slow down, speed up, reset strategy).
  • Changing the debugger to SEGGER J-Link
  • Lock/Unlock GPIO pins

All have had same result. I suspect that I may be missing something in CMSIS or a necessary include file, I just can't seem to find it.

Another point that I may be missing deals with oscillator setup. As I understand it, there is a default configuration at startup that I don't have to worry about unless I want to change it. Perhaps this is flawed?

Edit: Works with STM32CubeMX HAL

When I try a basic toggle using the STM32CUBEMX HAL, it works. This makes me think that there is something fundamental that I am missing in the includes, project setup, oscillator setup... something?

Code

main.c

#include "gpio.h"

#define LED_PIN 13


int main(void)
{
    uint32_t i;

    // initialize the peripherals
    GPIO_init();
    GPIOC_setupOutput(LED_PIN, PUSH_PULL, PIN_SPEED_50MHz);

    while(1)
    {
        GPIOC_setPin(LED_PIN);
        for(i=0; i<4000000; i++);
        GPIOC_clearPin(LED_PIN);
        for(i=0; i<4000000; i++);
    }
}

gpio.h

#ifndef _GPIO_H
#define _GPIO_H

#include <stdint.h>
#include <stdbool.h>

typedef enum{
    ANALOG=0b00, FLOATING=0b01, PULL_UP_PULL_DOWN=0b10
}InputPinMode;

typedef enum{
    PUSH_PULL=0b00, OPEN_DRAIN=0b01, AF_PUSH_PULL=0b10, AF_OPEN_DRAIN=0b11
}OutputPinMode;

typedef enum{
    PIN_SPEED_2MHz=0b10, PIN_SPEED_10MHz=0b01, PIN_SPEED_50MHz=0b11
}PinSpeed;

void GPIO_init(void);
void GPIOA_setupInput(uint8_t pinNumber, InputPinMode mode);
void GPIOC_setupInput(uint8_t pinNumber, InputPinMode mode);
void GPIOA_setupOutput(uint8_t pinNumber, OutputPinMode mode, PinSpeed speed);
void GPIOC_setupOutput(uint8_t pinNumber, OutputPinMode mode, PinSpeed speed);

bool GPIOA_readPin(uint8_t pinNumber);


void GPIOA_setPin(uint8_t pinNumber);
void GPIOC_setPin(uint8_t pinNumber);

void GPIOA_clearPin(uint8_t pinNumber);
void GPIOC_clearPin(uint8_t pinNumber);

#endif

gpio.c

#include "stm32f1xx.h"
#include "gpio.h"

/**
 * @brief Initialize GPIO
 */
void GPIO_init(void){
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
}

/**
 * @brief Setup pin as an input
 * @param pinNumber the pin number
 * @param mode the input mode
 */
void GPIOA_setupInput(uint8_t pinNumber, InputPinMode mode){
    uint32_t pinNumberLocation, regValue;

    if(pinNumber < 8){
        pinNumberLocation = pinNumber << 2; // bit location
        regValue = (mode << 2) << pinNumberLocation;

        GPIOA->CRL &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOA->CRL = regValue;
    }else{
        pinNumberLocation = (pinNumber - 8) << 2;   // bit location
        regValue = (mode << 2) << pinNumberLocation;

        GPIOA->CRH &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOA->CRH = regValue;
    }
}

/**
 * @brief Setup port A pin as an output
 * @brief pinNumber the pin number
 * @brief mode the output mode
 * @brief speed the pin speed (lower results in less noise)
 */
void GPIOA_setupOutput(uint8_t pinNumber, OutputPinMode mode, PinSpeed speed){
    uint32_t pinNumberLocation, regValue;

    if(pinNumber < 8){
        pinNumberLocation = pinNumber << 2; // bit location
        regValue = ((mode << 2) << pinNumberLocation) + speed;

        GPIOA->CRL &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOA->CRL |= regValue;
    }else{
        pinNumberLocation = (pinNumber - 8) << 2;   // bit location
        regValue = ((mode << 2) << pinNumberLocation) + speed;

        GPIOA->CRH &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOA->CRH |= regValue;
    }
}

/**
 * @brief Setup port C pin as an output
 * @brief pinNumber the pin number
 * @brief mode the output mode
 * @brief speed the pin speed (lower results in less noise)
 */
void GPIOC_setupOutput(uint8_t pinNumber, OutputPinMode mode, PinSpeed speed){
    uint32_t pinNumberLocation, regValue;

    if(pinNumber < 8){
        pinNumberLocation = pinNumber << 2; // bit location
        regValue = ((mode << 2) << pinNumberLocation) + speed;

        GPIOC->CRL &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOC->CRL |= regValue;
    }else{
        pinNumberLocation = (pinNumber - 8) << 2;   // bit location
        regValue = ((mode << 2) << pinNumberLocation) + speed;

        GPIOC->CRH &= ~(0b1111 << pinNumberLocation);   // clear the register
        GPIOC->CRH |= regValue;
    }
}

bool GPIOA_readPin(uint8_t pinNumber){
    uint16_t mask = 1 << pinNumber;

    if(GPIOA->IDR & mask)
        return true;
    else
        return false;
}

void GPIOA_setPin(uint8_t pinNumber){ GPIOA->BSRR = (1 << pinNumber);}
void GPIOC_setPin(uint8_t pinNumber){ GPIOC->BSRR = (1 << pinNumber);}

void GPIOA_clearPin(uint8_t pinNumber){ GPIOA->BSRR = ~(1 << (pinNumber + 16));}
void GPIOC_clearPin(uint8_t pinNumber){ GPIOC->BSRR = ~(1 << (pinNumber + 16));}
2
GPIOA->CRL = GPIOB->CRL = GPIOC->CRL = GPIOD->CRL = GPIOE->CRL = crl; … - That's a very bad idea and even damage your hardware. Also .c is not a headder file and those are not just declarations, but definitions (which should also not be in a header file, except for inline functions. - too honest for this site
@olaf I realize that *.c is not a header file. I simply didn't place the header file here for the sake of brevity. I will edit to add. Also, why is setting all GPIOx->CRL and GPIOx->CRH a bad idea? The reference manual simply states that it places all pins in analog input mode. - slightlynybbled
You should get a better manual and set the GPIOs to the state the external hardware expects. - too honest for this site
@Olaf I have no external hardware associated at the moment. I'm learning about the device as I'm learning. I appreciate you taking the time to educate me, but I still fail to see the issue. Initializing all GPIO to a known input state at startup does not seem controversial to me and - frankly - is not even a pressing issue since register writes aren't working at all. Do you see anything above that would not allow me to write to the registers? - slightlynybbled
I wonder what you do with that device then. No JTAG, no LED, no button, no crystal, no anything. Why do you write code at all, if you have a dead brick? - too honest for this site

2 Answers

2
votes

Your problem is trivial.

You just forgot to enable the the peripheral clock. all peripheral clocks in the Cortex micros are off by default. Please read the reference manual not the data sheet. Look for the "peripheral clock enable registers" and enable GPIO port in the proper one.

PS in your case RCC_APB2ENR

you do a lots of another serious mistakes in your code. I have no time to read in full but BSRR BRR registers are write only and you cant do any operations which incur reading (ie |= or &=). probably there is much more. Read RM more carefully. another mistake - you write to the reserved part of BRR. (upper 16 bits are reserved) If you even write to the correct half of the registe your &= ~() operation does something different than you think (abstracting from illegal read operation) - it does nothing. Actually you did not read and understand the RM at all

So for the future: Cortex micros to have an access to the peripheral registers - start their clock. Even EXTI (external interrupts) is the peripheral as well, and its clock has to be enabled.

Some peripherals have even more than one clock to enable (for example ADC in STM32F3xx where analog part has more than one clock option and it has to be enabled separately + digital part)

2
votes

Diode on PB2 - working code

volatile uint32_t delay;
#include "stm32f10x.h"
int main(void)
{
  RCC->APB2ENR = RCC_APB2ENR_IOPBEN;
  GPIOB->CRL |= GPIO_CRL_MODE2_1;
  GPIOB->CRL &= ~GPIO_CRL_CNF2_0;

  while(1)
  {
    GPIOB->ODR |= GPIO_ODR_ODR2;   //or GPIO_ODR_2 depending of the .h header version 
    for(delay = 2000000; delay; delay--);
    GPIOB->ODR &= ~GPIO_ODR_ODR2;  //or GPIO_ODR_2 depending of the .h header version 
    for(delay = 2000000; delay; delay--);

    GPIOB->BSRR = 1 << 2;
    for(delay = 2000000; delay; delay--);
    GPIOB->BSRR = 1 << (2 + 16);
    for(delay = 2000000; delay; delay--);
  }
} // change