"multiple definition of first defined here"。 STM32、AC6工作室

"multiple definition of first defined here". STM32, AC6 Studio

我遇到了一个愚蠢的问题,尽管一切都必须正常运行。我的任务是 - 使用联合和结构的数据类型初始化 I/O 寄存器。我在初始化时只使用了我的名字,这样我就可以避免因重新定义系统变量而导致的错误。但是,编译器仍然向我显示我创建的所有变量的错误,因为它们已在项目的其他地方初始化。有我的变量初始化代码:

volatile SYSCFG_t* mysyscfg=(volatile SYSCFG_t*)MYSYSCFG;

volatile RCC_AHB2ENR_t* myrcc_ahb2enr=(volatile RCC_AHB2ENR_t*)(MYRCC|MYAHB2ENR_OFFS);

volatile RCC_APB2ENR_t* myrcc_apb2enr=(volatile RCC_APB2ENR_t*)(MYRCC|MYAPB2ENR_OFFS);

volatile GPIO_t* mygpiob=(volatile GPIO_t*)MYGPIOB;

volatile GPIO_t* mygpioe=(volatile GPIO_t*)MYGPIOE;

volatile GPIO_t* mygpioa=(volatile GPIO_t*)MYGPIOA;

我犯的错误如下:

src/main.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:262: multiple definition of `mysyscfg'
src/init.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:262: first defined here
src/main.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:263: multiple definition of `myrcc_ahb2enr'
src/init.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:263: first defined here
src/main.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:264: multiple definition of `myrcc_apb2enr'
src/init.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:264: first defined here
src/main.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:265: multiple definition of `mygpiob'
src/init.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:265: first defined here
src/main.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:266: multiple definition of `mygpioe'
src/init.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:266: first defined here
src/main.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:267: multiple definition of `mygpioa'
src/init.o:/home/bogdan/workspace/timers/Debug/../src/lib.h:267: first defined here
collect2: error: ld returned 1 exit status
make: *** [makefile:62: timers.elf] Error 1

这些是链接器错误。你可能正在做这样的事情:

main.c:

#include "lib.h"

init.c:

#include "lib.h"

当链接器检查 main.c 和 init.c 中的符号和类型时,它发现它们中的每一个都定义了完全相同的符号,因此出现错误。通常 header 文件只声明符号,不定义它们,但是条件代码有一些创造性的用途可以解决这个问题:

lib.h(请为该文件想一个更具描述性的名称)

#if defined IN_INIT_C
  volatile SYSCFG_t* mysyscfg=(volatile SYSCFG_t*)MYSYSCFG; // Definition
#else
  extern volatile SYSCFG_T* mysyscfg; // Declaration
#endif

现在在 init.c 并且只在那个文件中:

#define IN_INIT_C
#include "lib.h"

或者您可以简单地将定义放在 init.c 中,并通过仅在其中放置声明来简化您的 header。

您还应该阅读此搜索提供的问答:

https://whosebug.com/search?q=%5Bc%5Ddifference+between+declaration+and+definition

在 include-file 中定义变量是个坏主意。原因是您无法将 include-file 包含在多个 c-file 中。如果这样做,您将得到现在的错误。

因此 您发布的代码应移至 c-file 并在 include-file 中您只需输入:

extern volatile SYSCFG_t* mysyscfg;

extern volatile RCC_AHB2ENR_t* myrcc_ahb2enr;

extern volatile RCC_APB2ENR_t* myrcc_apb2enr;

extern volatile GPIO_t* mygpiob;

extern volatile GPIO_t* mygpioe;

extern volatile GPIO_t* mygpioa;

然后 include-file 可以毫无问题地包含在多个 c-file 中。

正如其他人所说,不要在 header 文件中定义 object 或函数(static inline 除外)。

您还尝试重新发明轮子 - 所有寄存器定义都已经在 STM 提供的 CMSIS 文件中。

你的方法也很低效。当您访问您的变量时,它会生成不必要的代码。

#include <stdint.h>

#define __IO volatile

typedef struct
{
  __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
  __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
  __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
  __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
  __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
  __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
  __IO uint32_t BSRR;     /*!< GPIO port bit set/reset register,      Address offset: 0x18      */
  __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
  __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
} GPIO_TypeDef;

volatile GPIO_TypeDef *MYGPIOA = (volatile GPIO_TypeDef *)0x40000000;

#define GPIOA ((GPIO_TypeDef *)0x40000000)

void foo(uint32_t val)
{
    MYGPIOA -> MODER = val;
}

void bar(uint32_t val)
{
    GPIOA -> MODER = val;
}

如您所见,#define 方式生成的指令较少。这就是为什么在 CMSIS headers 中这样做的原因。您还不必要地为指针浪费内存。

foo:
        ldr     r3, .L3
        ldr     r3, [r3]
        str     r0, [r3]
        bx      lr
.L3:
        .word   .LANCHOR0
bar:
        mov     r3, #1073741824
        str     r0, [r3]
        bx      lr
MYGPIOA:
        .word   1073741824

https://godbolt.org/z/8K19js