我们正在为STM32F103 MCU开发.我们在ARM GCC工具链中使用裸机C++代码.经过几个小时的可疑表达,我们发现constant关键字触发了该表达式的不同结果.使用x86 GCC工具链测试同一段代码时,问题就不存在了.
我们使用STM的GPIOS进行调试.
这是完全重现问题的代码:
#include "stm32f10x.h"
#include "system_stm32f10x.h"
#include "stdlib.h"
#include "stdio.h"
const unsigned short RTC_FREQ = 62500;
unsigned short prescaler_1ms = RTC_FREQ/1000;
int main()
{
//********** Clock Init **********
RCC->CFGR |= RCC_CFGR_ADCPRE_0 | RCC_CFGR_ADCPRE_1; // ADC prescaler
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // Alternate Function I/O clock enable
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // I/O port C clock enable
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // I/O port A clock enable
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // ADC 1 interface clock enable
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Timer 2 clock enable
RCC->AHBENR = RCC_AHBENR_DMA1EN; // DMA1 clock enable
RCC->CSR = RCC_CSR_LSION; // Internal Low Speed oscillator enable
//********************************
/* GPIO Configuration */
GPIOC->CRH = GPIO_CRH_MODE12_0; //GPIO Port C Pin 12
GPIOC->CRH |= GPIO_CRH_MODE13_1 | GPIO_CRH_MODE13_0;
GPIOC->CRH |= GPIO_CRH_MODE10_0;
GPIOC->CRH |= GPIO_CRH_MODE9_0;
GPIOC->CRH |= GPIO_CRH_MODE8_0;
GPIOC->CRL = GPIO_CRL_MODE7_0;
GPIOC->CRL |= GPIO_CRL_MODE6_0;
GPIOC->CRL |= GPIO_CRL_MODE4_0;
GPIOC->CRL |= GPIO_CRL_MODE3_0;
while(1){
if(prescaler_1ms & (1<<0))GPIOC->BSRR |= GPIO_BSRR_BR13;
else GPIOC->BSRR |= GPIO_BSRR_BS13;
if(prescaler_1ms & (1<<1))GPIOC->BSRR |= GPIO_BSRR_BR12;
else GPIOC->BSRR |= GPIO_BSRR_BS12;
if(prescaler_1ms & (1<<2))GPIOC->BSRR |= GPIO_BSRR_BR10;
else GPIOC->BSRR |= GPIO_BSRR_BS10;
if(prescaler_1ms & (1<<3))GPIOC->BSRR |= GPIO_BSRR_BR9;
else GPIOC->BSRR |= GPIO_BSRR_BS9;
if(prescaler_1ms & (1<<4))GPIOC->BSRR |= GPIO_BSRR_BR8;
else GPIOC->BSRR |= GPIO_BSRR_BS8;
if(prescaler_1ms & (1<<5))GPIOC->BSRR |= GPIO_BSRR_BR7;
else GPIOC->BSRR |= GPIO_BSRR_BS7;
if(prescaler_1ms & (1<<6))GPIOC->BSRR |= GPIO_BSRR_BR6;
else GPIOC->BSRR |= GPIO_BSRR_BS6;
if(prescaler_1ms & (1<<7))GPIOC->BSRR |= GPIO_BSRR_BR4;
else GPIOC->BSRR |= GPIO_BSRR_BS4;
if(prescaler_1ms & (1<<8))GPIOC->BSRR |= GPIO_BSRR_BR3;
else GPIOC->BSRR |= GPIO_BSRR_BS3;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当该代码编译时,我们期望GPIOS上的结果为0b111110.当我们改变
const unsigned short RTC_FREQ = 62500;
Run Code Online (Sandbox Code Playgroud)
至
unsigned short RTC_FREQ = 62500;
Run Code Online (Sandbox Code Playgroud)
我们得到0b111111111.
这是我们使用的Makefile:
EABI_PATH=$(ROOT_DIR)"arm_toolchain/gcc-arm-none-eabi-6-2017-q2-update/arm-none-eabi/"
CMSIS_INC_PATH=$(ROOT_DIR)"STMLib/STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/"
PROJECT_INC=$(ROOT_DIR)
CXXINCS = -I$(EABI_PATH)"include" -I$(CMSIS_INC_PATH)"CoreSupport" -I$(CMSIS_INC_PATH)"DeviceSupport/ST/STM32F10x" -I$(PROJECT_INC)"Source" -I$(PROJECT_INC)"Includes"
CXXLIBS = -L$(EABI_PATH)"lib" -L$(EABI_PATH)"6.3.1"
CXXFLAGS = --specs=nosys.specs -DSTM32F10X_MD -DVECT_TAB_FLASH -fdata-sections -ffunction-sections -fno-exceptions -mthumb -mcpu=cortex-m3 -march=armv7-m -O2
LDFLAGS = -lstdc++ -Wl,--gc-sections
CC = $(EABI_PATH)"../bin/arm-none-eabi-gcc"
CXX = $(EABI_PATH)"../bin/arm-none-eabi-g++"
LD = $(EABI_PATH)"../bin/arm-none-eabi-ld"
STRIP = $(EABI_PATH)"../bin/arm-none-eabi-strip"
all:
$(CC) $(CXXINCS) -c $(PROJECT_INC)"Source/syscalls.c" $(PROJECT_INC)"Source/startup.c" $(CXXFLAGS)
$(CXX) $(CXXINCS) -c $(PROJECT_INC)"Source/main.cpp" $(CMSIS_INC_PATH)"DeviceSupport/ST/STM32F10x/system_stm32f10x.c" $(CXXFLAGS)
$(CXX) $(CXXLIBS) -o main syscalls.o main.o startup.o -T linker.ld system_stm32f10x.o $(LDFLAGS)
$(STRIP) --strip-all main
$(EABI_PATH)"bin/objcopy" -O binary main app
$(EABI_PATH)"bin/objdump" -b binary -m arm_any -D app > app_disasm
rm -f *.o main adc timer task solenoid dma startup syscalls system_stm32f10x
Run Code Online (Sandbox Code Playgroud)
有没有人知道是什么原因导致这样的问题?这是编译器错误吗?我们错过了什么吗?
将我的理论推广到答案,因为它由启动代码和LD脚本确认.
prescaler_1ms不会调用C++初始化代码,它应该将62复制到其中.当你定义RTC_FREQ为const,这个计算的结果在编译时是已知的,62存在于flash中并且不需要初始化.
C++初始化由许多生成的函数执行,其名称类似于_Z41__static_initialization_and_destruction_0ii.编译器在.init_array和.pre_init_array部分中收集指向这些函数的指针.在main()调用之前,启动代码应该迭代这些指针并调用它们中的每一个.这些指针数组的边界对于启动代码是已知的,因为这些特殊符号由链接描述文件定义:
__preinit_array_start, __preinit_array_end
__init_array_start, __init_array_end
Run Code Online (Sandbox Code Playgroud)
之间的区别_preinit_array和__init_array尚不清楚给我.在调用_init函数之前调用前一部分,之后调用后者.在我的项目中_init,gcc提供的函数似乎不是一个有效的函数,所以我没有调用它.
程序终止时有一个对称的过程,当使用__fini_array_start和调用全局对象的C++析构函数时__fini_array_end.但是,对于嵌入式系统,它可能不相关.
使项目调用C++初始化的最小步骤是:
.init_array在链接描述文件中包含该部分.从您提供的文档中,该.init_array部分似乎已定义为:
. = ALIGN(4);
__preinit_array_start = .;
KEEP(*(.preinit_array))
__preinit_array_end = .;
. = ALIGN(4);
__init_array_start = .;
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
__init_array_end = .;
Run Code Online (Sandbox Code Playgroud)
你可以将下面的代码(或类似)添加到__Init_Data()函数startup.c:
// usually these are defined with __attribute__((weak)) but I prefer to get errors when required things are missing
extern void (*__preinit_array_start[])(void);
extern void (*__preinit_array_end[])(void);
extern void (*__init_array_start[])(void);
extern void (*__init_array_end[])(void);
void __Init_Data(void) {
// copying initialized data from flash to RAM
...
// zeroing bss segment
...
// calling C++ initializers
void (**f)(void);
for (f = __preinit_array_start; f != __preinit_array_end; f++)
(*f)();
// init(); // _init and _fini do not work for me
for (f = __init_array_start; f != __init_array_end; f++)
(*f)();
}
Run Code Online (Sandbox Code Playgroud)
同样,我不确定这个_init功能,所以在这里注释掉了.我可能会在一段时间后问自己的问题.
| 归档时间: |
|
| 查看次数: |
139 次 |
| 最近记录: |