如何使用llvm生成的汇编语言打开stm32板上的LED?

Posted

技术标签:

【中文标题】如何使用llvm生成的汇编语言打开stm32板上的LED?【英文标题】:How to turn on LED on stm32 board using assembly language that generated by llvm? 【发布时间】:2021-02-04 06:25:35 【问题描述】:

我的英语水平很差,因为我不是以英语为母语的人。请理解。

我用 LLVM infra 编译了一些在 IAR 中运行良好的测试代码,但生成的代码没有在我的测试板上运行。 详情如下。

测试目标

我想看看使用 LLVM 生成的汇编代码的操作。

测试环境

    MCU:STM32L152VD (Cortex M3) IDE:IAR 8.2 调试器:Segger JLink LLVM 站点:http://ellcc.org/demo/index.cgi

测试步骤(摘要)

    创建可以在 IAR 中正常运行的测试代码。 将测试代码移动到http://ellcc.org/demo/index.cgi,选择Target后编译。 使用生成的汇编代码创建 test.s 文件。 创建makefile生成bin文件,用make程序执行makefile。 使用 JLink 程序将 bin 文件加载到目标板。

步骤 1

我编写了没有库的简单代码,如下所示。 此代码简单地打开 LED。

volatile int* _RCC = (int*)(0x40023800);
volatile int* _RCC_AHBENR = (int*)(0x4002381c);
volatile int* _GPIOE = (int*)0x40021000;
volatile int* _GPIOE_BSRR = (int*)(0x40021000 + 0x18);

void InitPort()

    const int _RCC_AHBENR_GPIOEEN = (0x00000010);
    int SetOutput = 0x00000600;

    *_RCC_AHBENR = _RCC_AHBENR_GPIOEEN;
    *_GPIOE = SetOutput;    // set mode to output

    *_GPIOE_BSRR = 0x00000020;  // set



int main()

    InitPort();

    *_GPIOE_BSRR = 0x00200000;  // reset
    
    while(1);

上面的代码在IAR中是正确的。

第二步

我将创建的测试代码移至http://ellcc.org/demo/index.cgi,并在选择以下选项后按下编译按钮。

第三步

我使用在站点中生成的汇编代码创建了 test.s 文件,如下所示。

    .text
    .syntax unified
    .eabi_attribute 67, "2.09"
    .cpu    cortex-m3
    .eabi_attribute 6, 10
    .eabi_attribute 7, 77
    .eabi_attribute 8, 0
    .eabi_attribute 9, 2
    .eabi_attribute 34, 1
    .eabi_attribute 17, 1
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 38, 1
    .eabi_attribute 18, 4
    .eabi_attribute 26, 2
    .eabi_attribute 14, 0
    .file   "_2376_0.c"
    .globl  InitPort
    .p2align    1
    .type   InitPort,%function
    .code   16
    .thumb_func
InitPort:
    .fnstart
    sub sp, #8
    movs    r0, #16
    str r0, [sp, #4]
    mov.w   r1, #1536
    str r1, [sp]
    movw    r1, :lower16:_RCC_AHBENR
    movt    r1, :upper16:_RCC_AHBENR
    ldr r1, [r1]
    str r0, [r1]
    ldr r0, [sp]
    movw    r1, :lower16:_GPIOE
    movt    r1, :upper16:_GPIOE
    ldr r1, [r1]
    str r0, [r1]
    movw    r0, :lower16:_GPIOE_BSRR
    movt    r0, :upper16:_GPIOE_BSRR
    ldr r0, [r0]
    movs    r1, #32
    str r1, [r0]
    add sp, #8
    bx  lr
.Lfunc_end0:
    .size   InitPort, .Lfunc_end0-InitPort
    .cantunwind
    .fnend

    .globl  main
    .p2align    1
    .type   main,%function
    .code   16
    .thumb_func
main:
    .fnstart
    push    r7, lr
    mov r7, sp
    sub sp, #8
    movs    r0, #0
    str r0, [sp, #4]
    bl  InitPort
    movw    r0, :lower16:_GPIOE_BSRR
    movt    r0, :upper16:_GPIOE_BSRR
    ldr r0, [r0]
    mov.w   lr, #2097152
    str.w   lr, [r0]
    b   .LBB1_1
.LBB1_1:
    b   .LBB1_1
.Lfunc_end1:
    .size   main, .Lfunc_end1-main
    .cantunwind
    .fnend

    .type   _RCC,%object
    .data
    .globl  _RCC
    .p2align    2
_RCC:
    .long   1073887232
    .size   _RCC, 4

    .type   _RCC_AHBENR,%object
    .globl  _RCC_AHBENR
    .p2align    2
_RCC_AHBENR:
    .long   1073887260
    .size   _RCC_AHBENR, 4

    .type   _GPIOE,%object
    .globl  _GPIOE
    .p2align    2
_GPIOE:
    .long   1073876992
    .size   _GPIOE, 4

    .type   _GPIOE_BSRR,%object
    .globl  _GPIOE_BSRR
    .p2align    2
_GPIOE_BSRR:
    .long   1073877016
    .size   _GPIOE_BSRR, 4


    .ident  "ecc version 2017-08-23 (http://ellcc.org) based on clang version 6.0.0 (trunk 311547)"
    .section    ".note.GNU-stack","",%progbits

第四步

我创建了 makefile 来生成 bin 文件,如下所示。 这是makefile的内容。

bin: test.s
    @echo "Running target all"
    arm-none-eabi-as c:/backend/files/test.s -o c:/backend/files/test.o
    arm-none-eabi-ld -Ttext=0x08000000 c:/backend/files/test.o -o c:/backend/files/test.elf
    arm-none-eabi-objdump -D c:/backend/files/test.elf
    arm-none-eabi-objcopy c:/backend/files/test.elf -O binary c:/backend/files/test.bin

clean:
    @echo "Running target clean"
    rm -f *.o
    rm -f *.elf
    rm -f *.bin

我用make程序执行了上面的makefile,得到了一个test.o、test.elf、test.bin文件。

第 5 步

我使用 JLink.exe (seggar) 加载了 bin 文件并使用 go 命令执行,但注意到发生在船上。 (我在加载bin文件时使用的命令是“loadbin C:\backend\files\test.bin, 0x08000000”)

结论

到这里就是我所做的一切。 我按照上面的方法做了,但是 LLVM infra 生成的汇编代码与 IAR 生成的代码不同。 我想知道我做错了什么以及如何解决以实现目标。 任何帮助将不胜感激。

谢谢。


补充信息

船上没有像 RTOS 这样的软件。 下图是我用来测试的整个结构。 只有 main.cpp 文件是源代码。其他文件是由 EWARM IDE 生成的。

地图文件内容如下。

###############################################################################
#
# IAR ELF Linker V8.22.2.15995/W32 for ARM                24/Oct/2020  19:22:32
# Copyright 2007-2018 IAR Systems AB.
#
#    Output file  =  C:\Users\jjw\Desktop\hobby\Test\Debug\Exe\Test.out
#    Map file     =  C:\Users\jjw\Desktop\hobby\Test\Debug\List\Test.map
#    Command line =  
#        -f C:\Users\jjw\AppData\Local\Temp\EW7E50.tmp
#        (C:\Users\jjw\Desktop\hobby\Test\Debug\Obj\main.o -o
#        C:\Users\jjw\Desktop\hobby\Test\Debug\Exe\Test.out --redirect
#        _Printf=_PrintfFullNoMb --redirect _Scanf=_ScanfFullNoMb --map
#        C:\Users\jjw\Desktop\hobby\Test\Debug\List\Test.map --config
#        "C:\Program Files (x86)\IAR Systems\Embedded Workbench
#        8.0\arm\CONFIG\generic_cortex.icf" --semihosting --entry
#        __iar_program_start --redirect __iar_sh_stdout=__iar_sh_stdout_swo
#        --vfe --text_out locale)
#
###############################################################################

*******************************************************************************
*** RUNTIME MODEL ATTRIBUTES
***

CppFlavor        = *
__CPP_Exceptions = Disabled
__CPP_Language   = C++14
__SystemLibrary  = DLib
__dlib_version   = 6


*******************************************************************************
*** HEAP SELECTION
***

The basic heap was selected because no calls to memory allocation
functions were found in the application outside of system library
functions, and there are calls to deallocation functions in the
application.


*******************************************************************************
*** PLACEMENT SUMMARY
***

"A0":  place at 0x00000000  ro section .intvec ;
"P1":  place in [from 0x00000000 to 0x0007ffff]  ro ;
define block CSTACK with size = 1K, alignment = 8  ;
define block PROC_STACK with size = 0M, alignment = 8  ;
define block HEAP with size = 2K, alignment = 8  ;
"P2":  place in [from 0x20000000 to 0x2000ffff] 
          rw, block CSTACK, block PROC_STACK, block HEAP ;
initialize by copy  rw ;

  Section            Kind        Address   Size  Object
  -------            ----        -------   ----  ------
"A0":                                      0x40
  .intvec            ro code  0x00000000   0x40  vector_table_M.o [4]
                            - 0x00000040   0x40

"P1":                                     0x104
  .text              ro code  0x00000040   0x3c  main.o [1]
  .text              ro code  0x0000007c   0x2c  copy_init3.o [4]
  .text              ro code  0x000000a8   0x28  data_init.o [4]
  .iar.init_table    const    0x000000d0   0x14  - Linker created -
  .text              ro code  0x000000e4   0x1e  cmain.o [4]
  .text              ro code  0x00000102    0x4  low_level_init.o [3]
  .text              ro code  0x00000106    0x4  exit.o [3]
  .text              ro code  0x0000010a    0x2  vector_table_M.o [4]
  .text              ro code  0x0000010c    0xa  cexit.o [4]
  .rodata            const    0x00000116    0x1  unwind_debug.o [5]
  .text              ro code  0x00000118   0x14  exit.o [5]
  .text              ro code  0x0000012c    0xc  cstartup_M.o [4]
  Initializer bytes  const    0x00000138    0xc  <for P2-1>
  .rodata            const    0x00000144    0x0  copy_init3.o [4]
                            - 0x00000144  0x104

"P2", part 1 of 2:                          0xc
  P2-1                        0x20000000    0xc  <Init block>
    .data            inited   0x20000000    0x4  main.o [1]
    .data            inited   0x20000004    0x4  main.o [1]
    .data            inited   0x20000008    0x4  main.o [1]
                            - 0x2000000c    0xc

"P2", part 2 of 2:                        0x400
  CSTACK                      0x20000010  0x400  <Block>
    CSTACK           uninit   0x20000010  0x400  <Block tail>
                            - 0x20000410  0x400


*******************************************************************************
*** INIT TABLE
***

          Address     Size
          -------     ----
Copy (__iar_copy_init3)
    1 source range, total size 0xc:
          0x00000138   0xc
    1 destination range, total size 0xc:
          0x20000000   0xc



*******************************************************************************
*** MODULE SUMMARY
***

    Module            ro code  ro data  rw data
    ------            -------  -------  -------
C:\Users\jjw\Desktop\hobby\Test\Debug\Obj: [1]
    main.o                 60       12       12
    -------------------------------------------
    Total:                 60       12       12

command line: [2]
    -------------------------------------------
    Total:

dl7M_tln.a: [3]
    exit.o                  4
    low_level_init.o        4
    -------------------------------------------
    Total:                  8

rt7M_tl.a: [4]
    cexit.o                10
    cmain.o                30
    copy_init3.o           44
    cstartup_M.o           12
    data_init.o            40
    vector_table_M.o       66
    -------------------------------------------
    Total:                202

shb_l.a: [5]
    exit.o                 20
    unwind_debug.o                   1
    -------------------------------------------
    Total:                 20        1

    Gaps                    1
    Linker created                  20    1 024
-----------------------------------------------
    Grand Total:          291       33    1 036


*******************************************************************************
*** ENTRY LIST
***

Entry                      Address  Size  Type      Object
-----                      -------  ----  ----      ------
.iar.init_table$$Base   0x000000d0         --   Gb  - Linker created -
.iar.init_table$$Limit  0x000000e4         --   Gb  - Linker created -
?main                   0x000000e5        Code  Gb  cmain.o [4]
CSTACK$$Base            0x20000010         --   Gb  - Linker created -
CSTACK$$Limit           0x20000410         --   Gb  - Linker created -
InitPort()              0x00000041  0x1e  Code  Gb  main.o [1]
Region$$Table$$Base     0x000000d0         --   Gb  - Linker created -
Region$$Table$$Limit    0x000000e4         --   Gb  - Linker created -
_GPIOE                  0x20000004   0x4  Data  Gb  main.o [1]
_GPIOE_BSRR             0x20000008   0x4  Data  Gb  main.o [1]
_RCC_AHBENR             0x20000000   0x4  Data  Gb  main.o [1]
__cmain                 0x000000e5        Code  Gb  cmain.o [4]
__exit                  0x00000119  0x14  Code  Gb  exit.o [5]
__iar_copy_init3        0x0000007d  0x2c  Code  Gb  copy_init3.o [4]
__iar_data_init3        0x000000a9  0x28  Code  Gb  data_init.o [4]
__iar_debug_exceptions  0x00000116   0x1  Data  Gb  unwind_debug.o [5]
__iar_program_start     0x0000012d        Code  Gb  cstartup_M.o [4]
__iar_systems$$module Abs
                        0x00000001        Data  Gb  command line/config [2]
__low_level_init        0x00000103   0x4  Code  Gb  low_level_init.o [3]
__vector_table          0x00000000        Data  Gb  vector_table_M.o [4]
_call_main              0x000000f1        Code  Gb  cmain.o [4]
_exit                   0x0000010d        Code  Gb  cexit.o [4]
_main                   0x000000ff        Code  Gb  cmain.o [4]
exit                    0x00000107   0x4  Code  Gb  exit.o [3]
main                    0x0000005f  0x12  Code  Gb  main.o [1]


[1] = C:\Users\jjw\Desktop\hobby\Test\Debug\Obj
[2] = command line
[3] = dl7M_tln.a
[4] = rt7M_tl.a
[5] = shb_l.a

    291 bytes of readonly  code memory
     33 bytes of readonly  data memory
  1 036 bytes of readwrite data memory

Errors: none
Warnings: none

icf文件内容如下。

/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_4.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x00000000;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_IROM1_start__ = 0x00000000;
define symbol __ICFEDIT_region_IROM1_end__   = 0x0007FFFF;
define symbol __ICFEDIT_region_IROM2_start__ = 0x0;
define symbol __ICFEDIT_region_IROM2_end__   = 0x0;
define symbol __ICFEDIT_region_EROM1_start__ = 0x0;
define symbol __ICFEDIT_region_EROM1_end__   = 0x0;
define symbol __ICFEDIT_region_EROM2_start__ = 0x0;
define symbol __ICFEDIT_region_EROM2_end__   = 0x0;
define symbol __ICFEDIT_region_EROM3_start__ = 0x0;
define symbol __ICFEDIT_region_EROM3_end__   = 0x0;
define symbol __ICFEDIT_region_IRAM1_start__ = 0x20000000;
define symbol __ICFEDIT_region_IRAM1_end__   = 0x2000FFFF;
define symbol __ICFEDIT_region_IRAM2_start__ = 0x0;
define symbol __ICFEDIT_region_IRAM2_end__   = 0x0;
define symbol __ICFEDIT_region_ERAM1_start__ = 0x0;
define symbol __ICFEDIT_region_ERAM1_end__   = 0x0;
define symbol __ICFEDIT_region_ERAM2_start__ = 0x0;
define symbol __ICFEDIT_region_ERAM2_end__   = 0x0;
define symbol __ICFEDIT_region_ERAM3_start__ = 0x0;
define symbol __ICFEDIT_region_ERAM3_end__   = 0x0;
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__     = 0x400;
define symbol __ICFEDIT_size_proc_stack__ = 0x0;
define symbol __ICFEDIT_size_heap__       = 0x800;
/**** End of ICF editor section. ###ICF###*/

define memory mem with size = 4G;
define symbol use_IROM1 = (__ICFEDIT_region_IROM1_start__ != 0x0 || __ICFEDIT_region_IROM1_end__ != 0x0);
define symbol use_IROM2 = (__ICFEDIT_region_IROM2_start__ != 0x0 || __ICFEDIT_region_IROM2_end__ != 0x0);
define symbol use_EROM1 = (__ICFEDIT_region_EROM1_start__ != 0x0 || __ICFEDIT_region_EROM1_end__ != 0x0);
define symbol use_EROM2 = (__ICFEDIT_region_EROM2_start__ != 0x0 || __ICFEDIT_region_EROM2_end__ != 0x0);
define symbol use_EROM3 = (__ICFEDIT_region_EROM3_start__ != 0x0 || __ICFEDIT_region_EROM3_end__ != 0x0);
define symbol use_IRAM1 = (__ICFEDIT_region_IRAM1_start__ != 0x0 || __ICFEDIT_region_IRAM1_end__ != 0x0);
define symbol use_IRAM2 = (__ICFEDIT_region_IRAM2_start__ != 0x0 || __ICFEDIT_region_IRAM2_end__ != 0x0);
define symbol use_ERAM1 = (__ICFEDIT_region_ERAM1_start__ != 0x0 || __ICFEDIT_region_ERAM1_end__ != 0x0);
define symbol use_ERAM2 = (__ICFEDIT_region_ERAM2_start__ != 0x0 || __ICFEDIT_region_ERAM2_end__ != 0x0);
define symbol use_ERAM3 = (__ICFEDIT_region_ERAM3_start__ != 0x0 || __ICFEDIT_region_ERAM3_end__ != 0x0);

if (use_IROM1)

  define region IROM1_region = mem:[from __ICFEDIT_region_IROM1_start__ to __ICFEDIT_region_IROM1_end__];

else

  define region IROM1_region = [];


if (use_IROM2)

  define region IROM2_region = mem:[from __ICFEDIT_region_IROM2_start__ to __ICFEDIT_region_IROM2_end__];

else

  define region IROM2_region = [];

define region IROM_region = IROM1_region | IROM2_region;

if (use_EROM1)

  define region EROM1_region = mem:[from __ICFEDIT_region_EROM1_start__ to __ICFEDIT_region_EROM1_end__];

else

  define region EROM1_region = [];

if (use_EROM2)

  define region EROM2_region = mem:[from __ICFEDIT_region_EROM2_start__ to __ICFEDIT_region_EROM2_end__];

else

  define region EROM2_region = [];

if (use_EROM3)

  define region EROM3_region = mem:[from __ICFEDIT_region_EROM3_start__ to __ICFEDIT_region_EROM3_end__];

else

  define region EROM3_region = [];

define region EROM_region = EROM1_region | EROM2_region | EROM3_region;

if (use_IRAM1)

  define region IRAM1_region = mem:[from __ICFEDIT_region_IRAM1_start__ to __ICFEDIT_region_IRAM1_end__];

else

  define region IRAM1_region = [];

if (use_IRAM2)

  define region IRAM2_region = mem:[from __ICFEDIT_region_IRAM2_start__ to __ICFEDIT_region_IRAM2_end__];

else

  define region IRAM2_region = [];

define region IRAM_region = IRAM1_region | IRAM2_region;

if (use_ERAM1)

  define region ERAM1_region = mem:[from __ICFEDIT_region_ERAM1_start__ to __ICFEDIT_region_ERAM1_end__];

else

  define region ERAM1_region = [];

if (use_ERAM2)

  define region ERAM2_region = mem:[from __ICFEDIT_region_ERAM2_start__ to __ICFEDIT_region_ERAM2_end__];

else

  define region ERAM2_region = [];

if (use_ERAM3)

  define region ERAM3_region = mem:[from __ICFEDIT_region_ERAM3_start__ to __ICFEDIT_region_ERAM3_end__];

else

  define region ERAM3_region = [];

define region ERAM_region = ERAM1_region | ERAM2_region | ERAM3_region;

do not initialize   section .noinit ;
initialize by copy  readwrite ;
if (isdefinedsymbol(__USE_DLIB_PERTHREAD))

  // Required in a multi-threaded application
  initialize by copy with packing = none  section __DLIB_PERTHREAD ;


place at address mem:__ICFEDIT_intvec_start__  readonly section .intvec ;

if (!isempty(IROM_region))

  place in IROM_region   readonly ;


if (!isempty(EROM_region))

  place in EROM_region   readonly section application_specific_ro ;


if (!isempty(IRAM_region))

  define block CSTACK     with alignment = 8, size = __ICFEDIT_size_cstack__      ;
  define block PROC_STACK with alignment = 8, size = __ICFEDIT_size_proc_stack__  ;
  define block HEAP       with alignment = 8, size = __ICFEDIT_size_heap__        ;
  place in IRAM_region   readwrite, block CSTACK, block PROC_STACK, block HEAP ;


if (!isempty(ERAM_region))

  place in ERAM_region   readwrite section application_specific_rw ;

在 EWARM 工具中,我使用 JLink 调试器上传了上述源代码。 JLink 调试器和我的板子的连接如图。

我也尝试手动上传 EWARM 工具生成的 bin 文件(不使用 EWARM 功能),如下所示。

以下方法与LLVM生成的上传bin文件的方法相同。 结果,EWARM bin 文件是 LED 亮,而 llvm 文件不是。

我在上传 EWARM bin 文件和上传 LLVM bin 文件时检查了不同的寄存器值。 (PC、SP、MSP)

以下是上传 EWARM bin 文件后起始点的寄存器值。 (这是操作)

以下是上传 LLVM bin 文件后起始点的寄存器值。 (这个不操作)

我认为这个问题的原因是程序计数器的值,(主)堆栈指针不正确。如果这个推论是对的,那么如何配置第一次的寄存器值呢?

如果需要其他信息,请告诉我。我要认真解决这个问题。

【问题讨论】:

【参考方案1】:

因此,除了几件事之外,您正走在正确的道路上。您正在使用 bsrr 重置然后设置然后立即重置输出引脚。首先,要打开 LED,您的电路板设计需要引脚低还是高?如果低,那么你的 main.c 代码很好,如果高,那么它应该闪烁得如此之快,以至于你需要一个范围或类似的东西,你的眼睛不会看到它。

我有许多带有许多不同芯片的 stm32 板。我没有来自这个家族的一个或一个,但这很好,我会通过一些事情来寻找,展示如何完全控制所有代码,然后你可以回到你的工具中并检查输出并查看问题是否出在二进制文件或您如何将其加载到零件中。有人会假设,如果您可以构建一种方式并使用相同的工具/命令进行加载,并且它“有效”但构建了另一种方式并且它不起作用,那么它不是二进制文件的加载,而是构建/软件的加载。

我使用的是 NUCLEO-F446RE 板。 PA5上有一个LED。你有 gnu 工具,我有 gnu 工具,所以你可以使用这些工具来构建这个项目(如果你选择这样做,可以根据你的需要进行修改)。

flash.ld

MEMORY

    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000

SECTIONS

    .text   :  *(.text*)    > rom

flash.s

.cpu cortex-m3
.thumb

.thumb_func
.global _start
_start:
.word 0x20001000
.word reset
.word hang
.word hang

.word hang
.word hang
.word hang
.word hang

.word hang
.word hang
.word hang
.word hang

.word hang
.word hang
.word hang
.word hang

.thumb_func
reset:
    bl main
    b hang
.thumb_func
hang:   b .

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.thumb_func
.globl bounce
bounce:
    bx lr

main.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void bounce ( unsigned int );

#define RCCBASE         0x40023800
#define RCC_AHB1ENR     (RCCBASE+0x30)
#define RCC_APB1ENR     (RCCBASE+0x40)

#define GPIOABASE       0x40020000
#define GPIOA_MODER     (GPIOABASE+0x00)
#define GPIOA_BSRR      (GPIOABASE+0x18)

static void led_init ( void )

    unsigned int ra;

    ra=GET32(RCC_AHB1ENR);
    ra|=1<<0; //enable GPIOA
    PUT32(RCC_AHB1ENR,ra);

    ra=GET32(GPIOA_MODER);
    ra&=~(3<<(5<<1)); //PA5
    ra|= (1<<(5<<1)); //PA5
    PUT32(GPIOA_MODER,ra);


static void led_on ( void )

    PUT32(GPIOA_BSRR,((1<<5)<< 0));


static void led_off ( void )

    PUT32(GPIOA_BSRR,((1<<5)<<16));


int main ( void )

    unsigned int rx;

    led_init();
    while(1)
    
        led_on();
        for(rx=0;rx<400000;rx++) bounce(rx);
        led_off();
        for(rx=0;rx<400000;rx++) bounce(rx);
   
    return(0);

构建

arm-linux-gnueabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-linux-gnueabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m3 -mthumb -S main.c -o main.s
arm-linux-gnueabi-as --warn --fatal-warnings -mcpu=cortex-m3 main.s -o main.o
arm-linux-gnueabi-ld -nostdlib -nostartfiles -T flash.ld flash.o main.o -o blinker.elf
arm-linux-gnueabi-objdump -D blinker.elf > blinker.list
arm-linux-gnueabi-objcopy -O binary blinker.elf blinker.bin

您不一定必须使用所有这些命令行选项,进行实验(但检查输出)。

在使用二进制文件之前检查它

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000041    stmdaeq r0, r0, r6
 8000008:   08000047    stmdaeq r0, r0, r1, r2, r6
 800000c:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000010:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000014:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000018:   08000047    stmdaeq r0, r0, r1, r2, r6
 800001c:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000020:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000024:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000028:   08000047    stmdaeq r0, r0, r1, r2, r6
 800002c:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000030:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000034:   08000047    stmdaeq r0, r0, r1, r2, r6
 8000038:   08000047    stmdaeq r0, r0, r1, r2, r6
 800003c:   08000047    stmdaeq r0, r0, r1, r2, r6

08000040 <reset>:
 8000040:   f000 f808   bl  8000054 <main>
 8000044:   e7ff        b.n 8000046 <hang>

08000046 <hang>:
 8000046:   e7fe        b.n 8000046 <hang>

第一部分是向量表,它需要在 0x08000000

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000041    stmdaeq r0, r0, r6
 8000008:   08000047    stmdaeq r0, r0, r1, r2, r6
 800000c:   08000047    stmdaeq r0, r0, r1, r2, r6

我使用 objdump 来生成它,所以无论如何它都会尝试反汇编这些字节。所以当你看到上面的内容时,重要的是这个

08000000 <_start>:
 8000000:   20001000
 8000004:   08000041
 8000008:   08000047
 800000c:   08000047

第一项是堆栈指针的初始值,你可能有更多的内存,简单地将堆栈指针设置为最大地址加一个或 0x20000000 + ram 的数量并不少见。这个小例子几乎不使用堆栈,而且应用程序非常小,所以 0x1000 字节绰绰有余。

接下来的这么多是向量本身,它们需要是处理程序地址 ORRED 与 1

08000040 <reset>:
08000046 <hang>:

如果你没有看到,那么这个东西将无法启动并且游戏已经结束,在向量表链接到正确的地址并且至少包含前两个字堆栈指针之前,不要尝试使用二进制文件初始化和重置处理程序。

我包含了许多其他向量来捕获错误,如果您的代码没有错误并且构建正确,那么您不需要它们来完成类似的事情。

08000054 <main>:
 8000054:   b570        push    r4, r5, r6, lr
 8000056:   4816        ldr r0, [pc, #88]   ; (80000b0 <main+0x5c>)
 8000058:   f7ff fff8   bl  800004c <GET32>
 800005c:   f040 0101   orr.w   r1, r0, #1
 8000060:   4813        ldr r0, [pc, #76]   ; (80000b0 <main+0x5c>)

orr.w 指令表明这是为 thumb2、armv7-m 构建的。这对我的板子(cortex-m4)和你的板子(cortex-m3)都很好,但如果这是一个 cortex-m0 或 cortex-m0+,这个代码会失败并导致一个需要故障处理程序的故障,即使它是一个无限循环(而不是向量条目是进一步扰乱内核并可能使尝试使用调试器进行调试变得更糟的指令)。不幸的是,arm 所做的事情(包括统一语法)的副作用是,您无法从汇编语言中确切知道您将获得什么,好吧,通过练习,但查看它的最佳方式是反汇编。

所以这段代码有可能会起作用。这个 nucleo 板是 mbed 样式,因此它呈现为可移动驱动器,您只需将 .bin 文件复制过来。

PUT32/GET32 是基于经验的,抽象层有很多好处。你可以使用 volatile 指针,我很快就会展示出来。

同样,最好习惯性地读取-修改-写入这些寄存器,这部分和这些寄存器都有很好的文档记录,这是重置后代码,前面没有其他代码(rtos、库等),所以它是可以安全地假设您可以简单地将值插入寄存器(不是时钟启用寄存器重置为 0x00008000 并且您正在禁用 GPIOG,为什么启用它?谁知道)

0x00000020 vs (1

for(rx=0;rx<400000;rx++) bounce(rx);

这是一个不需要 volatile 的简单延迟,在这种情况下编译器无法在文件外部进行优化,因此必须实现循环。该值是手动调整的,不要指望这会产生任何类型的可靠速率,只需使其足够小以看到 LED 闪烁不会太快也不会太慢。一旦你看到它工作,然后将值更改为两倍,一半,重新构建,重新加载并查看 LED 闪烁率变化,这是一个粗略的测试,看看闪烁器代码是你刚刚生成的代码,而不是剩下的代码从您或其他人那里,不要最终假设您编写的某些代码有效,而这些工具却使您失败并且他们没有将新程序加载到闪存中。

易失性指针方法,这可能与您的问题有关。

void bounce ( unsigned int );

#define RCCBASE         0x40023800
#define RCC_AHB1ENR (*((volatile unsigned int *)(RCCBASE+0x30)))

#define GPIOABASE       0x40020000
#define GPIOA_MODER (*((volatile unsigned int *)(GPIOABASE+0x00)))
#define GPIOA_BSRR  (*((volatile unsigned int *)(GPIOABASE+0x18)))

static void led_init ( void )

    RCC_AHB1ENR = 0x00100001;
    bounce(0);
    GPIOA_MODER = 0xA8000400;


static void led_on ( void )

    GPIOA_BSRR = 0x00000020;


static void led_off ( void )

    GPIOA_BSRR = 0x00200000;


int main ( void )

    unsigned int rx;

    led_init();
    while(1)
    
        led_on();
        for(rx=0;rx<400000;rx++) bounce(rx);
        led_off();
        for(rx=0;rx<400000;rx++) bounce(rx);
   
    return(0);

这是怎么回事:

    RCC_AHB1ENR = 0x00100001;
    bounce(0);
    GPIOA_MODER = 0xA8000400;

我在我的文档中找不到该语句,但问题是通过将值插入这两个寄存器中,在启用外设和我们开始尝试写入之间存在少量时钟给它。 read-modify-write 方法,特别是使用抽象函数提供了大量的延迟。所以在这种情况下,我实验性地添加了一个虚拟调用来燃烧一些时间。这在我的芯片上已经足够了。

使用 volatile read-modify-write 也足够了。

RCC_AHB1ENR = 0x00100001;
GPIOA_MODER |= 0x400;

在对其他 STM32 部件进行研究时,无论出于何种原因,您都可以在时钟启用之前读取调制解调器寄存器或调制解调器寄存器的复位值,而根本不启用外设,因此读取通过该解决方案发生,然后修改写入会在处理器和总线之间消耗一些时钟,从而提供允许写入工作所需的延迟。您的代码可能存在此问题,并且两个编译器生成的代码可能不同。我从研究中知道 llvm/clang 和 gnu 对 volatile 的含义有不同的看法。我们可以在一分钟内看到。

我有意进行此构建,以便为 gnu 案例生成 main.s,即使这是不必要的步骤。

    RCC_AHB1ENR = 0x00100001;

 8000060:   4b0d        ldr r3, [pc, #52]   ; (8000098 <main+0x44>)
 8000062:   490e        ldr r1, [pc, #56]   ; (800009c <main+0x48>)
 8000064:   4a0e        ldr r2, [pc, #56]   ; (80000a0 <main+0x4c>)
 8000066:   6019        str r1, [r3, #0]

    GPIOA_MODER |= 0x400;

 8000068:   6813        ldr r3, [r2, #0]
 800006a:   4e0e        ldr r6, [pc, #56]   ; (80000a4 <main+0x50>)
 800006c:   f443 6380   orr.w   r3, r3, #1024   ; 0x400
 8000070:   4d0d        ldr r5, [pc, #52]   ; (80000a8 <main+0x54>)
 8000072:   6013        str r3, [r2, #0]

 8000098:   40023830
 800009c:   00100001
 80000a0:   40020000
 80000a4:   40020018

这是比赛条件:

 8000060:   490c        ldr r1, [pc, #48]   ; (8000094 <main+0x40>)
 8000062:   480d        ldr r0, [pc, #52]   ; (8000098 <main+0x44>)
 8000064:   4b0d        ldr r3, [pc, #52]   ; (800009c <main+0x48>)
 8000066:   4a0e        ldr r2, [pc, #56]   ; (80000a0 <main+0x4c>)
 8000068:   4e0e        ldr r6, [pc, #56]   ; (80000a4 <main+0x50>)
 800006a:   4d0f        ldr r5, [pc, #60]   ; (80000a8 <main+0x54>)

    RCC_AHB1ENR = 0x00100001;

 800006c:   6008        str r0, [r1, #0]

    GPIOA_MODER = 0xA8000400;

 800006e:   601a        str r2, [r3, #0]

 8000094:   40023830    andmi   r3, r2, r0, lsr r8
 8000098:   00100001    andseq  r0, r0, r1
 800009c:   40020000    andmi   r0, r2, r0
 80000a0:   a8000400    stmdage r0, sl
 80000a4:   40020018    andmi   r0, r2, r8, lsl r0

编译器把前面的两个store都准备好,背靠背做,有ahb总线相关的时钟,但是明显不够。

我没有看到你正在使用的网页,我“简单地”(即使在一台快速的计算机上也需要很长时间)为这些目标构建一个 llvm/clang 的交叉编译器(这些天这是我能做到的唯一方法让它正常工作,带有三元组的 apt-gotten 不适用于版本 10 或 11,无论我最后尝试的是什么)。我也从源头推出自己的 gnu 工具,但无论如何。

llvm

 8000062:   f641 2680   movw    r6, #6784   ; 0x1a80
 8000066:   f04f 0820   mov.w   r8, #32
 800006a:   f44f 1900   mov.w   r9, #2097152    ; 0x200000
 800006e:   f2c4 0002   movt    r0, #16386  ; 0x4002
 8000072:   f2c0 0110   movt    r1, #16
 8000076:   f2c4 0502   movt    r5, #16386  ; 0x4002
 800007a:   f2c0 0606   movt    r6, #6
 800007e:   6001        str r1, [r0, #0]
 8000080:   f240 4000   movw    r0, #1024   ; 0x400
 8000084:   f6ca 0000   movt    r0, #43008  ; 0xa800
 8000088:   f845 0c18   str.w   r0, [r5, #-24]

llvm 也是如此

RCC_AHB1ENR = 0x00100001;
GPIOA_MODER = 0xA8000400;

可以毫无延迟地背靠背,不一定是因为易失性,而是因为编译器如何选择排列指令以及它选择使用什么指令。

同时了解这是 gcc 版本 10.2.0,没有理由假设先前/不同版本会产生相同的代码。也没有任何理由假设 IAR 如果不使用 gnu 或其他工具链会生成相同的代码。您需要检查反汇编,了解可能出现问题的位置等。您可以很容易地看到不喜欢我的 PUT32/GET32 读取-修改-写入并简单地将那几行代码更改为易失性指针的人可能会导致程序打破。凭经验,人们应该看到高级代码中的差异导致可能的竞争条件,因为这些寄存器修改的执行速度已经改变,而且时间确实很重要。在这种情况下,顺序当然很重要,所以重新安排它们会失败,还有时间,试图让你的代码更快,删除一个用于调试的 printf,然后一切都坏了,首先想到的是我是否将代码更改为某些东西功能上等效,如果这是真的,那么下一个想法是时间,添加大量延迟,然后开始删除它们。

您现在可以使用我的 flash.ld 和 flash.s 轻松重复所有这些操作,并将您的 main.c 转换为 main.s,或者使用我的 main.c,其中之一,并将三个寄存器替换为地址来自您的数据表。

因此我们可能会假设,由于理想情况下您只是更改 main.c/main.s,因此向量表不是问题,二进制文件也可以。

*_RCC_AHBENR = _RCC_AHBENR_GPIOEEN;
*_GPIOE = SetOutput;    // set mode to output

至少让调制解调器注册一个读-修改-写,或者延迟看看你是否也看到了竞争条件。

int SetOutput = 0x00000600;

*_RCC_AHBENR = _RCC_AHBENR_GPIOEEN;
*_GPIOE = SetOutput;    // set mode to output

*_GPIOE_BSRR = 0x00000020;  // set

bsrr 值表明您的 LED 位于 pin5 (端口 E)上,这是在现代设置的第 10 位,您将第 10 位和第 11 位设置为 0x600,是否有原因,尝试获取不会有任何伤害领导。

然后基本上你有

*_GPIOE_BSRR = 0x00000020;  // set

很快就跟进了

*_GPIOE_BSRR = 0x00200000;  // reset

然后你进入一个无限循环,它不再改变任何东西 PE5 应该永远为低电平,或者直到你重置,然后它会在几个/几十个时钟长的时间内发出信号。

你可能在 llvm 网页代码中没有竞态条件:

movw    r1, :lower16:_RCC_AHBENR
movt    r1, :upper16:_RCC_AHBENR
ldr r1, [r1]
str r0, [r1]
ldr r0, [sp]
movw    r1, :lower16:_GPIOE
movt    r1, :upper16:_GPIOE
ldr r1, [r1]
str r0, [r1]

它仍然是工具。

arm-none-eabi-as c:/backend/files/test.s -o c:/backend/files/test.o
arm-none-eabi-ld -Ttext=0x08000000 c:/backend/files/test.o -o c:/backend/files/test.elf

这对我来说是你生成的汇编语言

arm-none-eabi-as main.s -o main.o
arm-none-eabi-ld -Ttext=0x08000000 main.o -o main.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000008000000
arm-none-eabi-objdump -D main.elf 

main.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <InitPort>:
 8000000:   b082        sub sp, #8
 8000002:   2010        movs    r0, #16
 8000004:   9001        str r0, [sp, #4]
 8000006:   f44f 61c0   mov.w   r1, #1536   ; 0x600
 800000a:   9100        str r1, [sp, #0]
 800000c:   f240 0168   movw    r1, #104    ; 0x68
 8000010:   f6c0 0101   movt    r1, #2049   ; 0x801

所以最初的问题就在您的屏幕上。

 8000000:   b082        sub sp, #8
 8000002:   2010        movs    r0, #16
 8000004:   9001        str r0, [sp, #4]
 8000006:   f44f 61c0   mov.w   r1, #1536   ; 0x600

arm-none-eabi-objcopy main.elf -O binary main.bin
hexdump -C main.bin 
00000000  82 b0 10 20 01 90 4f f4  c0 61 00 91 40 f2 68 01  |... ..O..a..@.h.|
00000010  c0 f6 01 01 09 68 08 60  00 98 40 f2 6c 01 c0 f6  |.....h.`..@.l...|
00000020  01 01 09 68 08 60 40 f2  70 00 c0 f6 01 00 00 68  |...h.`@.p......h|

向量表如下所示:

0x08000000: 0x2010b082
0x08000004: 0xF44F9001

这根本行不通,它甚至可能会尝试从该地址获取,但游戏立即结束。

因此,非常简短的回答是您没有提供向量表或引导程序。

现在了解我的情况,这是我的引导程序:

bl main

通常,对于要将 .data 从闪存复制到 ram 和零 .bss 的 mcu,您需要一个更复杂的链接描述文件来识别这些区域,并且链接描述文件和引导代码密切相关(并且是特定于工具链的,不假定移植到其他工具链)。我不使用 .data 也不使用也不关心 .bss 项目为零,所以我的链接器脚本很简单,我的引导程序是设置堆栈指针并输入 C 代码,因为 cortex-m 负责堆栈指针 我所要做的就是调用 C 入口点。由于 cortex-m 的工作原理,您实际上可以这样做:

flash.s

.cpu cortex-m3
.thumb

.thumb_func
.global _start
_start:
.word 0x20001000
.word main

.thumb_func
.globl bounce
bounce:
    bx lr

但这只有在您不依赖 .data 或 .bss 或上帝禁止您认为可以在 C 代码中初始化它们而不是在引导程序中(当然是用 asm 编写)时才有效。

通用 C 支持的正确答案是借用/修改/创建一个复杂的链接器脚本,您可以使用它来获取工具来帮助您创建变量,以识别 .data 的开头和结尾或开头和大小(都在闪存中和 ram) 和 .bss (在 ram 中) 和复制和零,可能会生成一个 int argc (of 1) 和 argv[0] 以防万一用户觉得需要。

C 库实现通常包括更多的链接器脚本内容,尽管这不是必需的,这只是一些人倾向于这样做的方式,同样还有更多的引导程序内容,当然,这是其中一些内容的正确位置。如果可以避免的话,我不会使用 C 库,它会使项目立即变得更大,并且其中许多需要一个假系统,然后你必须实施假系统才能让它们工作。

很明显,我的非常微不足道的例子就是,它对你可以用它做什么有严格的限制,但它证明了成功,将你与任何可能干扰成功的库代码完全隔离(通过尝试做绕过的事情库代码或库代码及其引导项可能会干扰您直接访问寄存器的成功)。

还要注意,在我的实现中,我依靠命令行来预先获取向量表,很多人会:

.cpu cortex-m3
.thumb

.section .vectors

.thumb_func
.global _start
_start:
.word 0x20001000
.word reset

.text

.thumb_func
reset:
    bl main
    b hang
.thumb_func
hang:   b .

然后是类似的东西

MEMORY

    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000

SECTIONS

    .romx : 
        *(.vectors*)
        *(.text*)
      > rom

注意

MEMORY

    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000

SECTIONS

    .bob :  *(.vectors*)  > rom
    .ted :  *(.text*)    > rom

各种坏了:

Disassembly of section .bob:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000001    stmdaeq r0, r0

Disassembly of section .ted:

08000000 <reset>:
 8000000:   f000 f808   bl  8000014 <main>
 8000004:   e7ff        b.n 8000006 <hang>

08000006 <hang>:
 8000006:   e7fe        b.n 8000006 <hang>

并且不会启动。在尝试对零件进行编程之前,请务必检查 cortex-m 构建上的向量表。不是您的情况,也不是我的情况,但是有一些/许多解决方案,其中重新编程零件的能力在很大程度上依赖于零件上的二进制文件,其中包含所有加载程序代码并且没有任何挂起或损坏,有像这样的板子的洗衣清单我不会提到任何名字。

许多在 Arduino 环境下工作的人都会陷入这种情况,首先,如果您像这样滚动自己的闪光灯,这将破坏您再次通过沙箱加载零件的能力。但是,如果您的目标是在其中构建他们的所有代码并且发生这种情况,您仍然会变砖(仍然可以使用 boot0 和串行或 USB 等或 swd 进入 stm32 部件,某些供应商部件您可以轻松变砖但无法用 swd 恢复)。 (您使用的 jlink 是使用 swd(串行线调试)进入部件并对闪存进行编程)。

【讨论】:

我现在看到这篇文章了。对不起。我会看到这篇文章的详细信息,我会发表评论。感谢您发自内心的文章。 啊,我做错了。我试图奖励你...对不起...有办法回头吗?我想给你点,因为你发自内心的回答。 我会在 23 小时后给你 200 分。请稍候。 我在这里不是为了积分,如果你想出来并想给他们那很好,如果你不这样做,我认为他们只是过期了,你把它们拿回来。 我确实找到并证明了您的具体问题,如果该问题或其他任何问题有用,然后付款,去回答一些其他问题....【参考方案2】:

需要更多信息才能正确回答..

板上是否有其他软件(引导加载程序、操作系统)? 大多数 arm 目标上的地址 0x08000000 映射到连接到 bootflash 的硬件总线。 Jlink 是在刷芯片吗? 您的 IAR 工作台是否在模拟器中运行?

听起来您在没有任何引导加载程序或操作系统的情况下运行。在这种情况下,您需要按照您的 cortex M3 芯片手册中的启动程序进行操作。

例如。

启用电源域 设置时钟 初始化堆栈

更简单的方法可能是查看您的目标是否支持 uboot。如果它是一个开发板,那么大多数开发板都有一些默认的软件负载,您可以使用它们。设置好核心硬件后,您就可以开始运行代码了。

【讨论】:

感谢您的回答。我会审核您的建议并补充我的文章。 0x08000000是stm32芯片特有的,不是一般arm的东西,和arm无关。一些 stm32 芯片在不同的地址有更快的路径,而其他品牌使用不同的解决方案或地址。手臂本身在 0x00000000 处获取,如果芯片供应商有任何镜像,他们就会进行镜像。

以上是关于如何使用llvm生成的汇编语言打开stm32板上的LED?的主要内容,如果未能解决你的问题,请参考以下文章

stm32如何使用llvm+clang编译

如何通过串口烧写STM32程序

如何通过串口烧写STM32程序

无法使用 STM32F429I-Disco 板上的 FMC 读取/写入外部 SRAM

请问关于STM32,用yagarto生成的.bin文件,怎样烧录?

STM32CubeIDE 上的空目标选择窗口