Как включить светодиод на плате stm32, используя язык ассемблера, созданный llvm?
Я плохо владею английским, потому что я не носитель английского языка. Пожалуйста пойми.
Я скомпилировал тестовый код, который нормально работал в IAR с помощью LLVM infra, но сгенерированный код не работал на моей тестовой плате. Детали заключаются в следующем.
Цель теста
Я хочу увидеть, как работает ассемблерный код, созданный с помощью 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 со сгенерированным кодом сборки.
- Создайте make-файл для создания bin-файла и выполните make-файл с помощью программы make.
- Загрузите 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 для вашей цели. Если это плата разработчика, то у большинства разработчиков есть загрузка программного обеспечения по умолчанию, которую вы можете использовать с ними. Как только ваше основное оборудование настроено, вы можете запускать свой код.