Nix*_*xmd 5 c c++ gcc arm stm32
我已经使用 C++ 编写了一个在 ARM Cortex-M (STM32F0) 上运行的项目,但是我在将定义的缓冲区作为类成员访问时遇到了一些问题,尽管我通过将它们定义为全局变量来解决这个问题。
但是现在我完全被这个新问题困住了,我不知道该怎么办。
我有一个代码可以解锁闪存并向其中写入内容并关闭它。如果我在 C 文件中实现它并通过 C 自然运行它(从 main.c 调用)它工作完美。但是通过 C++ 文件(无论是写在 C 还是 C++ 源文件中)调用它会抛出一个 HardFault 异常。
static uint32_t waitForLastOperation(uint32_t msDelay)
{
while (READ_BIT(FLASH->SR, FLASH_SR_BSY) && msDelay)
{
LL_mDelay(1);
msDelay--;
}
/* Check FLASH End of Operation flag */
if (READ_BIT((FLASH->SR), (FLASH_SR_EOP)))
{
/* Clear FLASH End of Operation pending bit */
(FLASH->SR) = (FLASH_SR_EOP);
}
if (READ_BIT((FLASH->SR),
(FLASH_SR_WRPERR)) || READ_BIT((FLASH->SR), (FLASH_SR_PGERR)))
{
FLASH->SR = 0U;
return 0;
}
/* There is no error flag set */
return 1;
}
uint32_t programHalfWord(uint16_t data, uint32_t address)
{
uint32_t status;
/* Proceed to program the new data */
SET_BIT(FLASH->CR, FLASH_CR_PG);
/* Write data in the address */
*(__IO uint16_t*) address = data;
/* Wait for last operation to be completed */
status = waitForLastOperation(FLASH_TIMEOUT);
if (READ_BIT(FLASH->SR, FLASH_SR_EOP))
FLASH->SR = FLASH_SR_EOP;
/* If the program operation is completed, disable the PG Bit */
CLEAR_BIT(FLASH->CR, FLASH_CR_PG);
return status;
}
uint32_t flash_unlock()
{
if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) == RESET)
return 1;
/* Authorize the FLASH Registers access */
WRITE_REG(FLASH->KEYR, FLASH_KEY1);
WRITE_REG(FLASH->KEYR, FLASH_KEY2);
/* Verify Flash is unlocked */
if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != RESET)
return 0;
return 1;
}
Run Code Online (Sandbox Code Playgroud)
这就是我使用它的方式:
if(flash_unlock())
{
programHalfWord(0x11, 0x8007C00);
}
Run Code Online (Sandbox Code Playgroud)
它在执行后立即抛出异常*(__IO uint16_t*) address = data;。
Flash 在这个地址被擦除,地址对齐(它实际上是一个扇区的开始)。我已经检查了所有内容以确保闪存已解锁,但似乎用 C++ 编译的代码有些问题。
我正在使用 arm-none-eabi-gcc 和 arm-none-eabi-g++ 来编译我的代码。
提前致谢
更新:
以下是与 g++ 编译器一起使用的标志列表:
-mcpu=cortex-m0 -std=gnu++14 -g3 -DSTM32F030x6 -DHSE_STARTUP_TIMEOUT=100 -DLSE_STARTUP_TIMEOUT=5000 -DDEBUG -DLSE_VALUE=32768 -DDATA_CACHE_ENABLE=0 -DINSTRUCTION_CACHE_ENABLE=0 -DVDD_VALUE=3300 -DLSI_VALUE=40000 -DHSI_VALUE=8000000 -DUSE_FULL_LL_DRIVER -DPREFETCH_ENABLE=1 -DHSE_VALUE=2000000 -c -I../app/Inc -I../Inc -I../Drivers/STM32F0xx_HAL_Driver/Inc -I../Drivers/CMSIS/Include -I../Drivers/CMSIS/Device/ST/STM32F0xx/Include -I../app/Driver -Og -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-use-cxa-atexit -Wall -fno-short-enums -fstack-usage --specs=nano.specs -mfloat-abi=soft -mthumb
Run Code Online (Sandbox Code Playgroud)
这是针对 gcc 的:
-mcpu=cortex-m0 -std=gnu11 -g3 -DSTM32F030x6 -DHSE_STARTUP_TIMEOUT=100 -DLSE_STARTUP_TIMEOUT=5000 -DDEBUG -DLSE_VALUE=32768 -DDATA_CACHE_ENABLE=0 -DINSTRUCTION_CACHE_ENABLE=0 -DVDD_VALUE=3300 -DLSI_VALUE=40000 -DHSI_VALUE=8000000 -DUSE_FULL_LL_DRIVER -DPREFETCH_ENABLE=1 -DHSE_VALUE=2000000 -c -I../app/Inc -I../Inc -I../Drivers/STM32F0xx_HAL_Driver/Inc -I../Drivers/CMSIS/Include -I../Drivers/CMSIS/Device/ST/STM32F0xx/Include -I../app/Driver -Og -ffunction-sections -fdata-sections -Wall -fno-short-enums -fstack-usage --specs=nano.specs -mfloat-abi=soft -mthumb
Run Code Online (Sandbox Code Playgroud)
和 g++ 链接器:
-mcpu=cortex-m0 -T"./STM32F030K6TX_FLASH.ld" -Wl,-Map="${ProjName}.map" -Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb -Wl,--start-group -lc -lm -lstdc++ -lsupc++ -Wl,--end-group
Run Code Online (Sandbox Code Playgroud)
由于在无法访问您的硬件/软件设置的情况下很难分析问题,因此在最近在 STM32 闪存编程上遇到一些麻烦之后(在不同的 STM32 型号(STM32F215RET6)上),我只能做出疯狂的猜测并提供一些提示。- 但我根本不是这个领域的专家,到目前为止我只使用供应商提供的 HAL 驱动程序来访问内部闪存。
\n该错误可能是由内存总线错误引起的。
\n使用调试器验证是否属于这种情况会很有趣(例如,在错误发生后立即读取闪存状态寄存器(FLASH_SR))。
\n问题是:为什么你的 C 代码在使用 gcc 编译时可以工作,而在使用 g++ 编译时却不能工作?我想,这可能与技术细节有关,编译器“不知道”架构/内存模型的底层限制。
\nSTM32F030K6T参考手册 (RM0360)在“3.2.2 Flash 编程和擦除操作,主 Flash 存储器编程”部分中说道:
\n\n\n主闪存一次可以编程 16 位。当 CPU 将半字写入主 Flash 存储器地址且 FLASH_CR 寄存器的 PG 位置位时,编程操作开始。任何写入非半字长数据的尝试都会导致总线错误,从而产生硬故障中断。
\n
因此,对内部闪存的 32 位写访问将导致硬故障中断。
\n当您在启用程序集列表生成的情况下编译项目时,您可以分析 C++ 变体中到底发生了什么,并将其与 C 变体生成的机器代码进行比较。
\n由于我最近也一直在研究与 STM32 闪存相关的问题,因此我在我的案例中查找了供应商提供的闪存代码(stm32f2xx_hal_flash.c)中发生的情况,事实证明,对闪存的主要写入操作 ( *(__IO uint16_t*)Address = Data;) 被转换为匹配的 ARM 半字存储指令strh,如预期的那样:
strh r1, [r0] \nRun Code Online (Sandbox Code Playgroud)\n这可以通过查看 stm32f2xx_hal_flash.c 中 ST 提供的 FLASH_Program_HalfWord() 函数自动生成的汇编列表来验证。看起来像这样(用 GCC 编译,没有优化和调试信息-Og):
662:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** static void FLASH_Program_HalfWord(uint32_t Address, uint16_t Data)\n 663:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** {\n 140 .loc 1 663 1 is_stmt 1 view -0\n 141 .cfi_startproc\n 142 @ args = 0, pretend = 0, frame = 0\n 143 @ frame_needed = 0, uses_anonymous_args = 0\n 144 @ link register save eliminated.\n 664:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** /* Check the parameters */\n 665:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** assert_param(IS_FLASH_ADDRESS(Address));\n 145 .loc 1 665 3 view .LVU27\n 666:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** \n 667:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** /* If the previous operation is completed, proceed to program the new data */\n 668:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);\n 146 .loc 1 668 3 view .LVU28\n 147 0000 074B ldr r3, .L9\n 148 0002 1A69 ldr r2, [r3, #16]\n 149 0004 22F44072 bic r2, r2, #768\n 150 0008 1A61 str r2, [r3, #16]\n 669:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** FLASH->CR |= FLASH_PSIZE_HALF_WORD;\n 151 .loc 1 669 3 view .LVU29\n 152 .loc 1 669 13 is_stmt 0 view .LVU30\n 153 000a 1A69 ldr r2, [r3, #16]\n 154 000c 42F48072 orr r2, r2, #256\n 155 0010 1A61 str r2, [r3, #16]\n 670:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** FLASH->CR |= FLASH_CR_PG;\n 156 .loc 1 670 3 is_stmt 1 view .LVU31\n 157 .loc 1 670 13 is_stmt 0 view .LVU32\n 158 0012 1A69 ldr r2, [r3, #16]\n 159 0014 42F00102 orr r2, r2, #1\n 160 0018 1A61 str r2, [r3, #16]\n 671:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** \n 672:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** *(__IO uint16_t*)Address = Data;\n 161 .loc 1 672 3 is_stmt 1 view .LVU33\n 162 .loc 1 672 28 is_stmt 0 view .LVU34\n 163 001a 0180 strh r1, [r0] @ movhi\n 673:Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_flash.c **** }\n 164 .loc 1 673 1 view .LVU35\n 165 001c 7047 bx lr\n 166 .L10:\n 167 001e 00BF .align 2\n 168 .L9:\n 169 0020 003C0240 .word 1073888256\n 170 .cfi_endproc\nRun Code Online (Sandbox Code Playgroud)\n生成的机器代码可以使用 进行反汇编和检查objdump,无需所有注释,如下所示:
$ arm-none-eabi-objdump -d -j .text.FLASH_Program_HalfWord build/stm32f2xx_hal_flash.o\n\nbuild/stm32f2xx_hal_flash.o: file format elf32-littlearm\n\n\nDisassembly of section .text.FLASH_Program_HalfWord:\n\n00000000 <FLASH_Program_HalfWord>:\n 0: 4b07 ldr r3, [pc, #28] ; (20 <FLASH_Program_HalfWord+0x20>)\n 2: 691a ldr r2, [r3, #16]\n 4: f422 7240 bic.w r2, r2, #768 ; 0x300\n 8: 611a str r2, [r3, #16]\n a: 691a ldr r2, [r3, #16]\n c: f442 7280 orr.w r2, r2, #256 ; 0x100\n 10: 611a str r2, [r3, #16]\n 12: 691a ldr r2, [r3, #16]\n 14: f042 0201 orr.w r2, r2, #1\n 18: 611a str r2, [r3, #16]\n 1a: 8001 strh r1, [r0, #0]\n 1c: 4770 bx lr\n 1e: bf00 nop\n 20: 40023c00 .word 0x40023c00\nRun Code Online (Sandbox Code Playgroud)\n如果您能在编译为 C++ 的目标文件中找到它的样子,那将会很有趣。也是使用strh指令吗?
顺便说一句,所有 ARM 指令也以 ST 形式记录在STM32F0xxx Cortex-M0 编程手册 (PM0215)中:
\n\n\nCortex-M0处理器实现ARMv6-M架构,该架构基于16位Thumb\xc2\xae指令集并包含Thumb-2技术。
\n
\n\nSTRHRt, [Rn, <Rm|#imm>] 将寄存器存储为半字
\n
作为参考,当然也在ARM\xc2\xaev6-M Architecture Reference Manual中。
\n旁注1:
\n参考手册说地址0x8007C00位于闪存第 31 页的开头,在闪存扇区 7 中,假设使用 STM32F030K6Tx 芯片:\n
如果该扇区通过闪存选项字节进行写保护,则忘记这一点可能会导致问题(但显然情况并非如此,因为它在 C 变体中工作正常)。只是为了完整起见(您已经对此发表了评论),引用参考手册“4.1.3 写保护选项字节”:
\n\n\n这组寄存器用于对 Flash 存储器进行写保护。\n清除 WRPx 字段中的一位(同时设置 nWRPx 字段中的相应位)将对给定的存储器\n扇区进行写保护。对于 STM32F030x4、STM32F030x6、STM32F070x6、STM32F030x8 和 STM32F070xB 器件,从 0 到 31 的 WRP 位按 4 kB 扇区保护闪存。
\n
(可能不相关,但也值得一提:当心读保护 (RDP) 级别 2 或级别 3 处于活动状态时出现的不同条件。RDP 是一种不同的保护机制,通过闪存选项字节或锁定状态与扇区保护分开。当使用 RDP 级别 2 或 3 时,从调试器读取闪存或从 RAM 中执行时会导致硬故障。记录在参考手册的“3.3.1 读保护”部分中。)
\n旁注2:
\n您可以尝试将官方的 HAL C 驱动程序代码或您自己测试过的 flash 相关 C 代码与项目的新 C++ 部分混合在一起,然后检查问题是否仍然出现。
\nextern "C" { ... }(混合 C 和 C++ 时要小心,并始终使用相关帖子来处理命名管理: /sf/answers/72931631/)
旁注3:
\n正如已经提到的,我最近也遇到了与闪存编程无关的问题。并看到奇怪的总线错误(在硬故障后的状态寄存器中)。我还确保闪存已解锁,并且没有写保护。如果我没记错的话,我必须将其添加到擦除/写入操作之前(但我记不太清楚,现在找不到它)。这是一个必要但奇怪的修复,因为除了常规程序执行(来自闪存)之外,没有任何正在进行的操作。
\n while (FLASH_WaitForLastOperation(100) != HAL_OK) {\n HAL_IWDG_Refresh(&hiwdg);\n }\nRun Code Online (Sandbox Code Playgroud)\n这个问题可能与 STM32 使用带有预取缓冲区/等待状态/指令缓存和数据缓存的闪存的方式有关,如参考手册中所述(另请参阅:FLASH_ACR寄存器)。我没有进一步调查这个问题。只需确保启动写入/擦除访问时没有挂起/活动的闪存操作即可。
另外值得注意的是,编程/擦除操作将阻止对总线(闪存)的任何读访问,但它们不会导致错误,如参考手册“3.2.2 闪存编程和擦除操作”部分中所述:
\n\n\n只要 CPU 不访问闪存,正在进行的闪存操作就不会阻塞 CPU。
\n相反,在对闪存进行编程/擦除操作期间,任何读取闪存的尝试都会导致总线停止。编程/擦除操作完成后,读取操作将正确进行。这意味着在编程/擦除操作正在进行时无法进行代码或数据提取。
\n对于闪存上的编程和擦除操作(写入/擦除),内部 RC 振荡器 (HSI) 必须打开。
\n
编辑:
\n为了检查是否确实有足够的闪存可供写入,以及运行的二进制文件本身是否确实未使用该区域,这些命令可能会派上用场,作为将来的参考(使用我的测试二进制文件作为STM32F215RET 在这里):
\n$ arm-none-eabi-strip build/prj.elf \n$ arm-none-eabi-objdump -h build/prj.elf \n\nbuild/prj.elf: file format elf32-littlearm\n\nSections:\nIdx Name Size VMA LMA File off Algn\n 0 .isr_vector 00000184 08000000 08000000 00010000 2**0\n CONTENTS, ALLOC, LOAD, READONLY, DATA\n 1 .text 000134a0 08000188 08000188 00010188 2**3\n CONTENTS, ALLOC, LOAD, READONLY, CODE\n 2 .rodata 00002968 08013628 08013628 00023628 2**3\n CONTENTS, ALLOC, LOAD, READONLY, DATA\n 3 .ARM 00000008 08015f90 08015f90 00025f90 2**2\n CONTENTS, ALLOC, LOAD, READONLY, DATA\n 4 .init_array 00000004 08015f98 08015f98 00025f98 2**2\n CONTENTS, ALLOC, LOAD, DATA\n 5 .fini_array 00000004 08015f9c 08015f9c 00025f9c 2**2\n CONTENTS, ALLOC, LOAD, DATA\n 6 .data 000002c0 20000000 08015fa0 00030000 2**3\n CONTENTS, ALLOC, LOAD, DATA\n 7 .bss 0000149c 200002c0 08016260 000302c0 2**3\n ALLOC\n 8 ._user_heap_stack 00000604 2000175c 08016260 0003175c 2**0\n ALLOC\n 9 .ARM.attributes 00000029 00000000 00000000 000302c0 2**0\n CONTENTS, READONLY\n 10 .comment 0000001e 00000000 00000000 000302e9 2**0\n CONTENTS, READONLY\nRun Code Online (Sandbox Code Playgroud)\n0x08016260通过二进制标记所用闪存的结尾。
可以通过以下方式验证arm-none-eabi-size:
$ arm-none-eabi-size build/prj.elf \n text data bss dec hex filename\n 90004 712 6816 97532 17cfc build/prj.elf\n$ echo $((90004 + 712))\n90716\n$ echo $((0x08016260 - 0x08000000 - (90004 + 712)))\n4\nRun Code Online (Sandbox Code Playgroud)\n因此,通过 2**3 -> 8 字节对齐和闪存基地址0x08000000,这意味着二进制文件实际上使用了 90720 字节的闪存。
要找出哪些闪存扇区未使用,现在可以轻松地直接在参考手册中的“闪存组织”表中查找地址。
\n就我而言,修改了链接器脚本以确保仅使用一半的闪存,如下所示:
\n$ cat STM32F215RETx_FLASH.ld\n(...)\nMEMORY\n{\nRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K\nFLASH (rx) : ORIGIN = 0x8000000, LENGTH = 256K /* keep 256K free at the end */\n/* FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K */\n}\n(...)\nRun Code Online (Sandbox Code Playgroud)\n这样,如果二进制文件太大,您将收到链接器错误。
\n| 归档时间: |
|
| 查看次数: |
761 次 |
| 最近记录: |