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

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

我的英语能力很差,因为我不会说英语。请理解。

我编译了一些测试代码,这些代码可以在LLVM下文的IAR中正常运行,但是生成代码未在我的测试板上运行。 详细信息如下。

测试目标

我想看一下使用LLVM生成的汇编代码

测试环境

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

测试步骤(摘要

  1. 创建可以在IAR中正常运行的测试代码
  2. 将测试代码移至http://ellcc.org/demo/index.cgi并在选择Target后进行编译。
  3. 使用生成的汇编代码创建test.s文件
  4. 创建makefile来生成bin文件并使用make程序执行makefile。
  5. 使用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中运行。

步骤2

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

enter image description here

步骤3

我用以下在站点生成的汇编代码创建了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,:lower16:_GPIOE
    movt    r1,:upper16:_GPIOE
    ldr r1,[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,#0
    str r0,#4]
    bl  InitPort
    movw    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

步骤4

我创建了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”)

结论

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

谢谢。


附加信息

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

enter image description here

地图文件内容如下。

###############################################################################
#
# 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 HEAP };
}

if (!isempty(ERAM_region))
{
  place in ERAM_region  { readwrite section application_specific_rw };
}

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

enter image description here

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

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

enter image description here

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

以下是上载EWARM bin文件后起点处的寄存器值。 (已操作)

enter image description here

以下是上载LLVM bin文件后起点处的寄存器值。 (未操作)

enter image description here

我认为导致此问题的原因是程序计数器(主)堆栈指针的值不正确。如果此推断正确,那么如何首次配置寄存器值?

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

解决方法

需要更多信息才能正确回答此问题。

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

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

例如

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

一条更简单的方法可能是查看目标是否支持uboot。如果是devboard,则大多数devbords都可以使用一些默认软件加载。设置核心硬件后,即可开始运行代码。

,

因此,除了几件事情之外,您还在正确的道路上。您正在使用bsrr进行复位然后置位,然后立即复位输出引脚。首先,要打开LED,您的电路板设计需要将引脚设为低电平还是低电平?如果为低电平,则main.c代码很好;如果为高,则它应该闪烁得如此之快,以至于您需要一个示波器或类似的东西,您的眼睛将看不到它。

我有许多带有许多不同芯片的stm32板。我没有这个家族的一个或一个,但这很好,将逐步查找一些内容,展示如何完全控制所有代码,然后可以向后使用工具并检查输出并查看问题是否出在二进制文件上,或者如何将其加载到零件中。有人会假设,如果您可以构建一个方法并使用相同的工具/命令加载,并且它“起作用”但构建另一种方法却不起作用,则不是加载二进制文件而是构建/软件。>

我正在使用NUCLEO-F446RE板。 PA5上有一个指示灯。您有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,r1,r2,r6}
 800000c:   08000047    stmdaeq r0,r6}
 8000010:   08000047    stmdaeq r0,r6}
 8000014:   08000047    stmdaeq r0,r6}
 8000018:   08000047    stmdaeq r0,r6}
 800001c:   08000047    stmdaeq r0,r6}
 8000020:   08000047    stmdaeq r0,r6}
 8000024:   08000047    stmdaeq r0,r6}
 8000028:   08000047    stmdaeq r0,r6}
 800002c:   08000047    stmdaeq r0,r6}
 8000030:   08000047    stmdaeq r0,r6}
 8000034:   08000047    stmdaeq r0,r6}
 8000038:   08000047    stmdaeq r0,r6}
 800003c:   08000047    stmdaeq r0,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,r6}

我使用objdump生成了此文件,因此无论如何它都会尝试反汇编这些字节。因此,当您看到上面的内容时,这很重要

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

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

接下来的很多向量本身就是它们,它们必须是与1对应的处理程序地址ORRED。

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,#1
 8000060:   4813        ldr r0,#76]   ; (80000b0 <main+0x5c>)

orr.w指令指示这是为thumb2 armv7-m构建的。这对我的电路板(cortex-m4)和您的电路板(cortex-m3)都很好,但是如果这是cortex-m0或cortex-m0 +,则此代码将失败并导致出现错误,即使要无限循环(而不是向量入口会进一步破坏内核,并使其更难尝试使用调试器进行调试)。手臂如何做包括统一语法在内的事情的不幸副作用是,您无法从汇编语言中确切地知道要通过实践获得的结果,但是查看它的最佳方法是反汇编。

因此该代码有可能会起作用。此核子板为mbed样式,因此可将其呈现为可移动驱动器,您只需复制.bin文件即可。

PUT32 / GET32是基于经验的,抽象层有很多好处。您可以使用volatile指针,我将在稍后展示。

同样,最好习惯将这些寄存器读-修改-写,这部分和这些寄存器都有据可查,这是复位后的代码,前面没有其他代码(rtos,库等),因此可以安全地假定您可以将值简单地塞入寄存器中(不是时钟使能寄存器部分复位为0x00008000且您正在禁用GPIOG,为什么启用了GPIOG?谁知道)

0x00000020与(1

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

这是一个简单的延迟,不需要volatile,在这种情况下编译器无法在文件外部进行优化,因此必须实现循环。该值是手动调整的,不要指望它能产生任何可靠的速率,只需使其足够小即可看到led闪烁不是太快就不会太慢。一旦看到它起作用,则将其值更改为两倍,一半,重新构建,重新加载,并查看LED闪烁速率的变化,这是一个粗略的测试,以了解闪烁代码是您刚刚生成的代码,而不是剩下的东西从您或其他人那里得到的结果,我们不想以错误的假设来结束,即当工具使您无法使用并且它们没有将新程序加载到Flash中时,您编写的某些代码可以工作。

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

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也足够。

RCC_AHB1ENR = 0x00100001;
GPIOA_MODER |= 0x400;

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

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

    RCC_AHB1ENR = 0x00100001;

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

    GPIOA_MODER |= 0x400;

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

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

以下是比赛条件:

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

    RCC_AHB1ENR = 0x00100001;

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

    GPIOA_MODER = 0xA8000400;

 800006e:   601a        str r2,#0]

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

编译器在前面准备了两个存储,然后将它们做得背靠背,有一些与ahb总线相关的时钟,但显然不够。

我没有看到您正在使用的网页内容,我“简单地”(即使在快速的计算机上也需要花很长时间)为这些目标构建llvm / clang的交叉编译器(这是我唯一的方法)使其正常工作,三合一的apt不适用于版本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位于(端口E的)引脚5上,这是在现代模式中设置的第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]

有可能仍然是工具。

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,#0]
 800000c:   f240 0168   movw    r1,#104    ; 0x68
 8000010:   f6c0 0101   movt    r1,#2049   ; 0x801

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

 8000000:   b082        sub sp,#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

通常,对于要从Flash复制.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的开头和结尾或开头和大小的变量(均在flash中)和ram)和.bss(在ram中),然后复制并为零,以防万一用户感到有需要时,会生成一个int argc(为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
}
SECTIONS
{
    .romx : {
        *(.vectors*)
        *(.text*)
     } > rom
}

请注意

MEMORY
{
    rom : ORIGIN = 0x08000000,LENGTH = 0x1000
}
SECTIONS
{
    .bob : { *(.vectors*) } > rom
    .ted : { *(.text*)   } > rom
}

是各种各样的坏事

Disassembly of section .bob:

08000000 <_start>:
 8000000:   20001000    andcs   r1,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和serial或usb等进入sw32部件,或者使用swd,某些供应商的部件可以轻松实现砖砌而无法用swd恢复)。 (您正在使用的jlink东西是使用swd(串行线调试)进入零件并对闪存进行编程的。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?