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
BOOTxpin 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));}
GPIOA->CRL = GPIOB->CRL = GPIOC->CRL = GPIOD->CRL = GPIOE->CRL = crl; …- That's a very bad idea and even damage your hardware. Also.cis not a headder file and those are not just declarations, but definitions (which should also not be in a header file, except forinlinefunctions. - too honest for this site*.cis 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 allGPIOx->CRLandGPIOx->CRHa bad idea? The reference manual simply states that it places all pins in analog input mode. - slightlynybbled