I am developing my own driver for a STM32 MCU, however I always get the same error when I try to compile the code. Here is the entire code from all the files I developed.
main.c
#include "stm32f103xx.h"
void delay(void);
int main(void){
GPIO_Handle_t GPIOLed;
GPIOLed.pGPIOx = GPIOA;
GPIOLed.GPIO_PinConfig.GPIO_PinNumber = GPIO_PIN_5;
GPIOLed.GPIO_PinConfig.GPIO_PinMode = GPIO_MODE_OUTPUT_10MHZ;
GPIO_PeriClockControl(GPIOA, ENABLE);
GPIO_Init(&GPIOLed);
while(1){
GPIO_ToggleOutputPin(GPIOA, GPIO_PIN_5);
delay();
}
return 0;
}
void delay(void){
for(uint32_t i=0;i<500000;i++){
;
}
}
stm32f103xx.h
#ifndef INC_STM32F103XX_H_
#define INC_STM32F103XX_H_
#include <stdint.h> /* Native library to use unsigned integers of 8, 16 and 32 bits */
#define FLASH_BASEADDR 0x08000000U /* Start address of the embedded Flash memory */
#define SRAM1_BASEADDR 0x20000000U /* Start address of the embedded SRAM memory */
#define PERIPH_BASE 0x40000000U /* Start address of the peripheral bus */
#define APB1PERIPH_BASE PERIPH_BASE /* Start address of the APB1 bus */
#define APB2PERIPH_BASE 0x40010000U /* Start address of the APB2 bus */
#define APHPERIPH_BASE 0x40018000U /* Start address of the AHB bus */
#define RCC_BASEADDR (APHPERIPH_BASE + 0x9000)
/* Address from APB2 Peripherals APB2 */
#define GPIOA_BASEADDR (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASEADDR (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASEADDR (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASEADDR (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASEADDR (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASEADDR (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASEADDR (APB2PERIPH_BASE + 0x2000)
#define EXTI_BASEADDR 0x40010400 /* Start address of the EXTI bus */
/* Address from APB1 Peripherals APB1 */
#define SPI2_BASEADDR (APB1PERIPH_BASE + 0x3800)
#define SPI3_BASEADDR (APB1PERIPH_BASE + 0x3C00)
#define USART2_BASEADDR (APB1PERIPH_BASE + 0x4400)
#define USART3_BASEADDR (APB1PERIPH_BASE + 0x4800)
#define UART4_BASEADDR (APB1PERIPH_BASE + 0x4C00)
#define UART5_BASEADDR (APB1PERIPH_BASE + 0x5000)
#define I2C1_BASEADDR (APB1PERIPH_BASE + 0x5400)
#define I2C2_BASEADDR (APB1PERIPH_BASE + 0x5800)
/* Structure to hold the registers from GPIOs on STM32F103xx */
typedef struct{
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
}GPIO_RegDef_t;
/* Structure to hold the registers from RCC on STM32F103xx*/
typedef struct{
volatile uint32_t CR;
volatile uint32_t CFGR;
volatile uint32_t CIR;
volatile uint32_t APB2RSTR;
volatile uint32_t APB1RSTR;
volatile uint32_t AHBENR;
volatile uint32_t APB2ENR;
volatile uint32_t APB1ENR;
volatile uint32_t BDCR;
volatile uint32_t CSR;
volatile uint32_t AHBSTR;
volatile uint32_t CFGR2;
}RCC_RegDef_t;
/* Macro for GPIOs memory positions */
# define GPIOA ((GPIO_RegDef_t*) GPIOA_BASEADDR)
# define GPIOB ((GPIO_RegDef_t*) GPIOB_BASEADDR)
# define GPIOC ((GPIO_RegDef_t*) GPIOC_BASEADDR)
# define GPIOD ((GPIO_RegDef_t*) GPIOD_BASEADDR)
# define GPIOE ((GPIO_RegDef_t*) GPIOE_BASEADDR)
# define GPIOF ((GPIO_RegDef_t*) GPIOF_BASEADDR)
# define GPIOG ((GPIO_RegDef_t*) GPIOG_BASEADDR)
GPIO_RegDef_t *pGPIOA = GPIOA;
GPIO_RegDef_t *pGPIOB = GPIOB;
GPIO_RegDef_t *pGPIOC = GPIOC;
GPIO_RegDef_t *pGPIOD = GPIOD;
GPIO_RegDef_t *pGPIOE = GPIOE;
GPIO_RegDef_t *pGPIOF = GPIOF;
GPIO_RegDef_t *pGPIOG = GPIOG;
/* Macro for RCC memory position */
#define RCC ((RCC_RegDef_t*) RCC_BASEADDR)
/* Macro for clock enable on APB2 bus peripherals */
#define GPIOA_PCLK_EN (RCC->APB2ENR |= (1 << 2))
#define GPIOB_PCLK_EN (RCC->APB2ENR |= (1 << 3))
#define GPIOC_PCLK_EN (RCC->APB2ENR |= (1 << 4))
#define GPIOD_PCLK_EN (RCC->APB2ENR |= (1 << 5))
#define GPIOE_PCLK_EN (RCC->APB2ENR |= (1 << 6))
/* Macro for clock enable on APB1 bus peripherals */
#define SPI2_PCLK_EN (RCC->APB1ENR |= (1 << 14))
#define SPI3_PCLK_EN (RCC->APB1ENR |= (1 << 15))
#define USART2_PCLK_EN (RCC->APB1ENR |= (1 << 17))
#define USART3_PCLK_EN (RCC->APB1ENR |= (1 << 18))
#define UART4_PCLK_EN (RCC->APB1ENR |= (1 << 19))
#define UART5_PCLK_EN (RCC->APB1ENR |= (1 << 20))
#define I2C1_PCLK_EN (RCC->APB1ENR |= (1 << 21))
#define I2C2_PCLK_EN (RCC->APB1ENR |= (1 << 22))
/* Macro for clock disable on APB2 bus peripherals */
#define GPIOA_PCLK_DI (RCC->APB2ENR &= ~(1 << 2))
#define GPIOB_PCLK_DI (RCC->APB2ENR &= ~(1 << 3))
#define GPIOC_PCLK_DI (RCC->APB2ENR &= ~(1 << 4))
#define GPIOD_PCLK_DI (RCC->APB2ENR &= ~(1 << 5))
#define GPIOE_PCLK_DI (RCC->APB2ENR &= ~(1 << 6))
/* Macro for clock disable on APB1 bus peripherals */
#define SPI2_PCLK_DI RCC->APB1ENR &= ~(1 << 14)
#define SPI3_PCLK_DI RCC->APB1ENR &= ~(1 << 15)
#define USART2_PCLK_DI RCC->APB1ENR &= ~(1 << 17)
#define USART3_PCLK_DI RCC->APB1ENR &= ~(1 << 18)
#define UART4_PCLK_DI RCC->APB1ENR &= ~(1 << 19)
#define UART5_PCLK_DI RCC->APB1ENR &= ~(1 << 20)
#define I2C1_PCLK_DI RCC->APB1ENR &= ~(1 << 21)
#define I2C2_PCLK_DI RCC->APB1ENR &= ~(1 << 22)
/* Macro to reset the GPIOx registers */
#define GPIOA_REG_RESET() do {(RCC->APB2RSTR |= (1 << 2)); (RCC->APB2RSTR &= (1 << 2));} while(0)
#define GPIOB_REG_RESET() do {(RCC->APB2RSTR |= (1 << 3)); (RCC->APB2RSTR &= (1 << 3));} while(0)
#define GPIOC_REG_RESET() do {(RCC->APB2RSTR |= (1 << 4)); (RCC->APB2RSTR &= (1 << 4));} while(0)
#define GPIOD_REG_RESET() do {(RCC->APB2RSTR |= (1 << 5)); (RCC->APB2RSTR &= (1 << 5));} while(0)
#define GPIOE_REG_RESET() do {(RCC->APB2RSTR |= (1 << 6)); (RCC->APB2RSTR &= (1 << 6));} while(0)
#define ENABLE 1
#define DISABLE 0
#define SET ENABLE
#define RESET DISABLE
#define GPIO_PIN_SET SET
#define GPIO_PIN_RESET RESET
#include "stm32f103xx_gpio_driver.h"
#endif
stm32f103xx_gpio_driver.h
#ifndef INC_STM32F103XX_GPIO_DRIVER_H_
#define INC_STM32F103XX_GPIO_DRIVER_H_
#include "stm32f103xx.h"
typedef struct{
uint8_t GPIO_PinNumber;
uint8_t GPIO_PinMode;
uint8_t GPIO_PinSpeed;
uint8_t GPIO_PinPuPdControl;
uint8_t GPIO_PinOPType;
uint8_t GPIO_PinAltFunMode;
}GPIO_PinConfig_t;
typedef struct{
GPIO_RegDef_t *pGPIOx; /* Holds the base address of the GPIO port to which the pin belongs */
GPIO_PinConfig_t GPIO_PinConfig; /* Holds the GPIO pin configuration settings */
}GPIO_Handle_t;
/* GPIO possible modes */
#define GPIO_MODE_INPUT 0
#define GPIO_MODE_OUTPUT_10MHZ 1
#define GPIO_MODE_OUTPUT_2MHZ 2
#define GPIO_MODE_OUTPUT_50MHZ 3
/* GPIO output mode */
#define GPIO_OUT_PP 0
#define GPIO_OUT_OD 1
#define GPIO_ALT_PP 2
#define GPIO_ALT_OD 3
/* GPIO input mode */
#define GPIO_IN_ANL 0
#define GPIO_IN_FI 1
#define GPIO_IN_PUPD 2
/* GPIO pin number */
#define GPIO_PIN_0 0
#define GPIO_PIN_1 1
#define GPIO_PIN_2 2
#define GPIO_PIN_3 3
#define GPIO_PIN_4 4
#define GPIO_PIN_5 5
#define GPIO_PIN_6 6
#define GPIO_PIN_7 7
#define GPIO_PIN_8 8
#define GPIO_PIN_9 9
#define GPIO_PIN_10 10
#define GPIO_PIN_11 11
#define GPIO_PIN_12 12
#define GPIO_PIN_13 13
#define GPIO_PIN_14 14
#define GPIO_PIN_15 15
/***************************************************************************************************
* APIs supported by this driver
* For more informations about the APIs check the function definitions
************************************************************************************************** */
/*
* Peripheral clock setup
*/
void GPIO_PeriClockControl(GPIO_RegDef_t *pGPIOx, uint8_t EnorDi);
/*
* Initialize and Disable Initialization
*/
void GPIO_Init(GPIO_Handle_t *pGPIOHandle);
void GPIO_Deinit(GPIO_RegDef_t *pGPIOx);
/*
* Data read and write
*/
uint8_t GPIO_ReadFromInputPin(GPIO_RegDef_t *pGPIOx, uint8_t PinNumber);
uint16_t GPIO_ReadFromInputPort(GPIO_RegDef_t *pGPIOx);
void GPIO_WriteToOutputPin(GPIO_RegDef_t *pGPIOx, uint8_t PinNumber, uint8_t Value);
void GPIO_WriteToOutputPort(GPIO_RegDef_t *pGPIOx, uint8_t Value);
void GPIO_ToggleOutputPin(GPIO_RegDef_t *pGPIOx, uint8_t PinNumber);
/*
* IRQ Configuration and ISR Handling
*/
void GPIO_IRQConfig(uint8_t IRQNumber, uint8_t IRQPriority, uint8_t EnorDi);
void GPIO_IRQHandling(uint8_t PinNumber);
#endif
stm32f103xx_gpio_driver.c
#include "stm32f103xx_gpio_driver.h"
/*
* Peripheral clock setup
*/
void GPIO_PeriClockControl(GPIO_RegDef_t *pGPIOx, uint8_t EnorDi){
if(EnorDi == ENABLE){
if(pGPIOx == GPIOA)
GPIOA_PCLK_EN;
else if(pGPIOx == GPIOB)
GPIOB_PCLK_EN;
else if(pGPIOx == GPIOC)
GPIOC_PCLK_EN;
else if(pGPIOx == GPIOD)
GPIOD_PCLK_EN;
else if(pGPIOx == GPIOE)
GPIOE_PCLK_EN;
}
else{
if(pGPIOx == GPIOA)
GPIOA_PCLK_DI;
else if(pGPIOx == GPIOB)
GPIOB_PCLK_DI;
else if(pGPIOx == GPIOC)
GPIOC_PCLK_DI;
else if(pGPIOx == GPIOD)
GPIOD_PCLK_DI;
else if(pGPIOx == GPIOE)
GPIOE_PCLK_DI;
}
}
/*
* Initialize and Disable Initialization
*/
void GPIO_Init(GPIO_Handle_t *pGPIOHandle){
uint32_t temp; /* Temporary variable to set the bits on GPIO registers */
/* Configuration of the GPIO pin register depending from the port selected */
temp = (pGPIOHandle->GPIO_PinConfig.GPIO_PinMode << (2*pGPIOHandle->GPIO_PinConfig.GPIO_PinNumber));
if(pGPIOHandle->GPIO_PinConfig.GPIO_PinNumber <= 7)
pGPIOHandle->pGPIOx->CRL |= temp;
else
pGPIOHandle->pGPIOx->CRH |= temp;
temp = 0;
/* Configuration of the GPIO input/output and speed */
temp = (pGPIOHandle->GPIO_PinConfig.GPIO_PinSpeed << (2*(pGPIOHandle->GPIO_PinConfig.GPIO_PinNumber-7)));
/* Configuration of the GPIO Pull-up/Pull-down */
/* Configuration of the output/input */
/* Configuration of the alternative functionality */
}
void GPIO_Deinit(GPIO_RegDef_t *pGPIOx){
if(pGPIOx == GPIOA)
GPIOA_REG_RESET();
else if(pGPIOx == GPIOB)
GPIOB_REG_RESET();
else if(pGPIOx == GPIOC)
GPIOC_REG_RESET();
else if(pGPIOx == GPIOD)
GPIOD_REG_RESET();
else if(pGPIOx == GPIOE)
GPIOE_REG_RESET();
}
/*
* Data read and write
*/
uint8_t GPIO_ReadFromInputPin(GPIO_RegDef_t *pGPIOx, uint8_t PinNumber){
uint8_t value;
value = ((pGPIOx->IDR >> PinNumber) & 0x00000001);
return value;
}
uint16_t GPIO_ReadFromInputPort(GPIO_RegDef_t *pGPIOx){
uint8_t value;
value = pGPIOx->IDR;
return value;
}
void GPIO_WriteToOutputPin(GPIO_RegDef_t *pGPIOx, uint8_t PinNumber, uint8_t value){
if(value == GPIO_PIN_SET) /* Write 1 to the output data register at the bit field corresponding pinNumber */
pGPIOx->ODR |= (1 << PinNumber);
else
pGPIOx->ODR &= ~(1 << PinNumber);
}
void GPIO_WriteToOutputPort(GPIO_RegDef_t *pGPIOx, uint8_t value){
pGPIOx->ODR = value;
}
void GPIO_ToggleOutputPin(GPIO_RegDef_t *pGPIOx, uint8_t PinNumber){
pGPIOx->ODR = pGPIOx->ODR ^= (1 << PinNumber);
}
/*
* IRQ Configuration and ISR Handling
*/
void GPIO_IRQConfig(uint8_t IRQNumber, uint8_t IRQPriority, uint8_t EnorDi){
}
void GPIO_IRQHandling(uint8_t PinNumber){
}
There are always 8 errors, saying that pGPIOA, pGPIOB, pGPIOC, pGPIOD, pGPIOE , pGPIOF and pGPIOG have multiple definitions.
GPIO_RegDef_t *pGPIOA = GPIOA;
GPIO_RegDef_t *pGPIOB = GPIOB;
GPIO_RegDef_t *pGPIOC = GPIOC;
GPIO_RegDef_t *pGPIOD = GPIOD;
GPIO_RegDef_t *pGPIOE = GPIOE;
GPIO_RegDef_t *pGPIOF = GPIOF;
GPIO_RegDef_t *pGPIOG = GPIOG;
This is not supposed to be in the stm32*.h. You can declare variables multiple times, but you can't define them multiple times (give value at initiation). And you define them each time the file stm32*.h is included in every .c, because they have a value set.
If you want them as global variables, which seems to be the goal, you must define them only once, otherwise every .c file that includes that header, will have its own global variable with identical name, so it errors out. Therefore, it's best to define global variable in a .c file, and all other files need to have "extern" declaration of the same variable (or they can all include some header file that has that "extern" line - it's literally the same thing after preprocessor simply pastes the contents of .h files into .c files upon every "#include").
For example, you can put your pGPIOx definitions above main() in main.c, and if your stm32*.h has extern GPIO_RegDef_t *pGPIOA;
, then every file that includes it, will have access to the variables declared in main.c.
However, it's best not to alter default library files. You can create global_var.c (which, in turn, is free to include stm32*.h, so you can use the same types) and put definitions with values into there, and create gloval_var.h and put "extern" declarations into there. Every .c that includes global_var.h will have access to them. It will keep your library files in their original form and give you all advantages.
global_var.c:
#include "global_var.h" //includes all the definitions for MCU types
GPIO_RegDef_t *pGPIOA = GPIOA; //definition
GPIO_RegDef_t *pGPIOB = GPIOB;
GPIO_RegDef_t *pGPIOC = GPIOC;
GPIO_RegDef_t *pGPIOD = GPIOD;
GPIO_RegDef_t *pGPIOE = GPIOE;
GPIO_RegDef_t *pGPIOF = GPIOF;
GPIO_RegDef_t *pGPIOG = GPIOG;
global_var.h:
#include "whatever.h"
#include "stm32f103xx.h"
extern GPIO_RegDef_t *pGPIOA; //declaration
extern GPIO_RegDef_t *pGPIOB;
extern GPIO_RegDef_t *pGPIOC;
extern GPIO_RegDef_t *pGPIOD;
extern GPIO_RegDef_t *pGPIOE;
extern GPIO_RegDef_t *pGPIOF;
extern GPIO_RegDef_t *pGPIOG;
Now every .c file that includes global_var.h, will literally copy paste this in place of #include (because that's what #include does), so they will have a variable declaration with "extern", which says "look for that variable outside this translation unit". Or you can simply have these lines straight in the .c file that needs to use them. After preprocessor pastes #include into .c, it will be completely identical thing.
As "further reading", I would recommend covering materials on compilation preprocessor (how "#include" and "#define" work, what a "translation unit" is, among other things, nothing difficult really), "extern" and global variables. Preferably in this order.
A quality discussion on global variables can be found here, first half of the accepted answer is relevant to you: stackoverflow