Как включить светодиод на плате stm32, используя язык ассемблера, созданный llvm?

Я плохо владею английским, потому что я не носитель английского языка. Пожалуйста пойми.

Я скомпилировал тестовый код, который нормально работал в IAR с помощью LLVM infra, но сгенерированный код не работал на моей тестовой плате. Детали заключаются в следующем.

Цель теста

Я хочу увидеть, как работает ассемблерный код, созданный с помощью 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. Создайте make-файл для создания bin-файла и выполните make-файл с помощью программы make.
  5. Загрузите bin-файл на целевую плату с помощью программы JLink.

Шаг 1

Я написал простой код без библиотеки, как показано ниже. Этот код просто включает светодиод.

       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 и нажал кнопку компиляции после выбора параметров, как показано ниже.

Шаг 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, [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

Шаг 4

Я создал make-файл для создания bin-файла, как показано ниже. Это содержимое make-файла.

       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-файл с помощью программы make и получил файлы test.o, test.elf, test.bin.

Шаг 5

Я загрузил файл bin с помощью JLink.exe (seggar) и выполнил его с помощью команды 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 к моей плате показано на рисунке.

Я также попытался загрузить файл bin, созданный инструментом EWARM вручную (функция EWARM не использовалась), как показано ниже.

Метод ниже - это тот же метод, что и загруженный bin-файл, созданный LLVM. в результате светодиодный индикатор bin-файла EWARM включается, а файл llvm - нет.

Я проверил, что значение регистра отличается при загрузке файла bin EWARM и при загрузке файла bin LLVM. (ПК, SP, MSP)

Ниже приведено значение регистра в начальной точке после загрузки bin-файла EWARM. (Это работает)

Ниже показано значение регистра в начальной точке после загрузки bin-файла LLVM. (Это не работает)

Я думаю, что причиной этой проблемы может быть неправильное значение счетчика программ, (основного) указателя стека. Если этот вывод верен, как настроить значение регистра в первый раз?

Если потребуется дополнительная информация, сообщите мне. Я искренне хочу решить эту проблему.

2 ответа

Решение

Значит, вы на правильном пути, кроме пары вещей. Вы используете bsrr для сброса, затем установки и немедленного сброса выходного контакта. Во-первых, нужен ли вывод на плате для включения светодиода: низкий или высокий? Если низкий, то ваш код 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, {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 + количество оперативной памяти. Этот крошечный пример почти не использует стек, а приложение довольно маленькое, так что 0x1000 байт более чем достаточно.

Следующее множество - это сами векторы, и они должны быть адресом обработчика ORRED с 1

08000040 <reset>:
08000046 <hang>:

Если вы этого не видите, то вещь не загружается, и игра уже окончена, не пытайтесь использовать двоичный файл, пока таблица векторов не будет связана для правильного адреса и не будет содержать как минимум первые два слова указателя стека init и reset обработчик.

Я включил много других векторов для перехвата ошибок, если ваш код не содержит ошибок и построен правильно, то они вам не нужны для чего-то вроде этого.

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 основан на опыте, уровень абстракции имеет много преимуществ, точка. Вы можете использовать изменчивый указатель, и я скоро это покажу.

Точно так же лучше всего читать, изменять-записывать эти регистры как привычку, эта часть и эти регистры хорошо документированы, и это код после сброса без другого кода перед ним (rtos, библиотеки и т. Д.), Поэтому можно с уверенностью предположить что вы можете просто вставить значение в регистры (не то, чтобы регистр включения часов сбрасывался на 0x00008000 с вашей стороны, и вы отключаете GPIOG, почему он включен? Кто знает)

0x00000020 vs (1<< 5) - это личное предпочтение, я использую либо сам, в зависимости от кода и ситуации, в этом случае я предпочитаю четко видеть пин-код.

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

Это простая задержка, которая не требует изменчивости, компилятор не может оптимизировать вне файла в этом случае, поэтому должен реализовать цикл. Значение было настроено вручную, не ожидайте, что это даст какой-либо надежный показатель, просто сделайте его достаточно битым, чтобы светодиод мигал не слишком быстро, не слишком медленно. Как только вы увидите, что он работает, измените значение вдвое, наполовину, перестройте, перезагрузите и увидите изменение частоты мигания светодиода, что является грубым тестом, чтобы увидеть, что код мигания - это код, который вы только что сгенерировали, а не что-то оставшееся от вас или кого-то другого, не хотите в конечном итоге делать неверные предположения о том, что какой-то код, который вы создали, работал, когда вместо этого инструменты подвели вас, и они не загрузили новую программу во флэш-память.

Подход с изменчивым указателем, и это может быть связано с вашей проблемой.

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;

Я не могу найти это утверждение в своем документе, но проблема в том, что, просто вставив значение в оба этих регистра, существует небольшое количество тактов между моментом включения периферийного устройства и началом попытки записи в него. Подход чтения-изменения-записи, в частности, использование функций абстракции, обеспечивает большую задержку. Итак, в этом случае я экспериментально добавил фиктивный вызов, чтобы прожечь некоторое время. И этого было адекватно на моем чипе.

Использование изменчивого чтения-изменения-записи также было адекватным.

RCC_AHB1ENR = 0x00100001;
GPIOA_MODER |= 0x400;

Исследуя это на другой части STM32, по какой-то причине, возможно, по этой причине, вы можете прочитать регистр модера или, возможно, значение сброса регистра модера перед включением часов, вообще не включая периферийное устройство, поэтому чтение происходит через это решение, то модифицирующая запись сжигает некоторое количество тактов между процессором и шиной, обеспечивая задержку, необходимую для того, чтобы запись могла работать. У вас может быть эта проблема с вашим кодом, и два компилятора могут генерировать код по-разному. Из исследований я знаю, что llvm/clang и gnu по-разному думают о том, что означает volatile. Мы увидим это через минуту.

Я намеренно сделал эту сборку так, чтобы main.s генерировался для случая gnu, хотя это ненужный шаг.

    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

Компилятор подготовил два хранилища впереди и сделал их вплотную, есть часы, связанные с шиной 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;

может быть без задержки, не из-за непостоянства, а из-за того, как компилятор решил упорядочить инструкции и какие инструкции он решил использовать.

Also understand this is gcc version 10.2.0, there is no reason to assume that prior/different versions produce the same code. Nor any reason whatsoever to assume that IAR if it doesn't use gnu or other toolchains would generate the same code. You need to examine the disassembly, understand where issues can arise, etc. You can easily see that someone who doesn't like my PUT32/GET32 read-modify-write and simply changes those few lines of code into volatile pointer can cause the program to break. With experience one should see the difference in the high level code as causing a possible race condition because the execution speed of those register modifications has changed, and timing does matter. Order certainly matters in a case like this so re-arranging them will fail, but also timing, trying to make your code faster, removing a printf that was there for debug and then everything breaks, first thought is did I change the code to something functionally equivalent, if that is true then next thought is timing, add lots of delays then start removing them.

You can now easily repeat all of this using my flash.ld and flash.s and your main.c turned into main.s, or take my main.c, one of them, and replace the three registers with the addresses from your datasheet.

So we might assume that since you are ideally only changing main.c/main.s then the vector table is not the problem, the binary is otherwise okay.

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

At least make the moder register a read-modify-write, or put a delay in to see if you are seeing a race condition as well.

int SetOutput = 0x00000600;

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

*_GPIOE_BSRR = 0x00000020;  // set

The bsrr value indicates your led is on pin5 (of port E), which is bit 10 being set in moder you have bits 10 and 11 being set with 0x600, was there a reason for that, will not hurt trying to get the led on.

And then essentially you have

*_GPIOE_BSRR = 0x00000020;  // set

followed very quickly by

*_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

Обычно для mcu, который вы хотите скопировать.data из флэш-памяти в оперативную память и нулевой.bss, вам нужен гораздо более сложный сценарий компоновщика для идентификации этих областей, а сценарий компоновщика и код начальной загрузки тесно связаны (и являются специфичными для инструментальной цепочки, а не порт на другие инструментальные средства). Я не использую.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 (как во флэш-памяти, так и в оперативной памяти) и.bss (в оперативной памяти), а также copy и zero, возможно, зашли так далеко, что сгенерировали 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
    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, попадут в этот случай, и если вы сначала накроете свой собственный поворотник, то это лишит вас возможности снова загружать деталь через песочницу. Но если вы стремились собрать весь их код, и это произошло, вы все равно были бы заблокированы (все еще можете попасть в части stm32 с boot0 и serial или usb, и т. Д. Или swd, некоторые детали поставщиков вы можете легко кирпичи и не сможете восстановить с помощью swd). (Используемая вами jlink использует swd (отладка последовательного кабеля), чтобы попасть в деталь и запрограммировать флеш-память).

Для правильного ответа требуется дополнительная информация.

  • Есть ли на плате какое-то другое ПО (загрузчик, ос)?
  • Адрес 0x08000000 на большинстве целевых устройств отображается на аппаратную шину, подключенную к загрузочной флэш-памяти. Jlink прошивает чип?
  • Ваша рабочая среда IAR работает в эмуляторе?

Похоже, вы работаете без загрузчика или операционной системы. В этом случае вам необходимо выполнить процедуру загрузки, описанную в руководстве для вашего чипа Cortex M3.

например.

  • Включить энергетические домены
  • Настроить часы
  • инициализировать свой стек

Более простой способ - узнать, есть ли поддержка uboot для вашей цели. Если это плата разработчика, то у большинства разработчиков есть загрузка программного обеспечения по умолчанию, которую вы можете использовать с ними. Как только ваше основное оборудование настроено, вы можете запускать свой код.

Другие вопросы по тегам