На соседней странице мы разобрались с самым началом работы контроллера (ResetHandler и таблица векторов прерываний).
Теперь переходим к анализу startup_stm32fxxxxx.s файла.
Главные файлы проекта для настройки и анализа кода программы
Файл *.ld - тут память распределяется на области RAM и FLASH - это по сути исходник, такой же как и main.c.
Файл *.map - этот файл появляется в результате сборки и содержит полезную информацию что куда распределилось.
Файл *.list - файл тоже появляется в результате сборки крайне важен для анализа памяти.
startup_stm32f407xx.s
Этот файл чистой воды ассемблер и придется вспомнить , что делают ассемблерные инструкции. Очень рекомендую также пройти отладчиком по ассемблеру и посмотреть регистры.
Я замучился искать в интернете описание ассемблерных словечек . Вот где лучше сразу искать http://www.keil.com/support/man/docs/armclang_ref/armclang_ref_bpl1510589893923.htm.
Суть этого файла в том , что программа не сразу стартует с функции main.
.syntax unified //это говорит ассемблеру что можно использовать Thumb и не Thumb инструкции одновременно
.cpu cortex-m3 // тип нашего контроллера
.thumb // тип используемых инструкций Thumb
.global g_pfnVectors // это не просто объявление глобальной метки g_pfnVectors .
// Сюда ляжет таблица векторов (которая описана ниже) . Это при компиляции получится адрес х0000-х0184.
.global Default_Handler - объявление глобальной метки Default_Handler .
// это были только подготовительные объявления переменных
.word _sidata - ( LOADADDR(.data))
.word _sdata - start of FLASH data
.word _edata - end of FLASH data
.word _sbss - start of RAM bss
.word _ebss - end of RAM bss
// и это тоже были подготовительные объявления переменных
.section .text.Reset_Handler
// section (defines a logical memory section which contains program code or data) ,
// text - это тип данных = код прогаммы
// .Reset_Handler - это так логически обозвали этот первый участок памяти.
.weak Reset_Handler // weak означает , что метку можно переопределить в других файлах (если надо)
.type Reset_Handler, %function // указываем переменной тип функция
Reset_Handler: // это просто метка участка кода программы
ldr sp, =_estack // 0x20020000 , в этот момент pc = 0x80013bc
Важно для понимания основ : PC (program counter - адрес следующей инструкции выполнения) указывает на память FLASH, т.е. код программы записан и выполняется из FLASH , в RAM помещаются только изменяемые данные. Код программы константен.
Reset_Handler:
ldr sp, =_estack // сразу загружаем в регистр sp (stack pointer) верхнее значение RAM.
// Это важно , т.е. любое прерывание, любая функция сразу будет
// использовать стек для промежуточного хранения своих данных.
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0 // записать значение 0 в r1
b LoopCopyDataInit // безусловный переход на метку LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata // загрузка значения переменной _sidata в регистр r3 ( см. ld файл _sidata = LOADADDR(.data); )
// смотрим отладчиком _sidata = 0x800249c - это адрес FLASH
ldr r3, [r3, r1] // загрузка значения с адреса [r3 r1] в r3 с прединкрементом r1 (и это похоже их FLASH)
str r3, [r0, r1] // загрузка r3 по адресу [r0, r1]
// смещение задается в r1 - здесь происходит запись в память RAM по адресу r0 r1 .
// тут важно понимать , что для контроллера RAM это просто адрес 0x2000000 , FLASH просто адресс 0х8000000
// ему пофигу RAM там или FLASH или что-то еще
adds r1, r1, #4 : r1 = r1 4
LoopCopyDataInit:
ldr r0, = - start of FLASH data // смотрим отладчиком _sdata = 0x2000000 ,
//т.е. это адрес не FLASH , а RAM ! , т.е. куда копировать из FLASH
ldr r3, =_edata - end of FLASH data
// _edata =0x20000068 (маленькая у нас программка), т.е до какого адреса RAM
adds r2, r0, r1 // добавляет к r2 : r0 r1
cmp r2, r3 // сравниваем
bcc CopyDataInit // и если r2 != r3 переходим на CopyDataInit
// в результате блок data скопирован из FLASH в RAM
Таким образом с нулевого адреса RAM ( 0x2000000) у нас копия того, что во FLASH с адреса _sidata = 0x800249c .
А что это? В файле *.list это область .data. Здесь наши переменные данные.
Самое время посмотреть на *.list. Это распредение областей во FLASH.
Sections:
Idx Name Size VMA LMA File off Algn
0 .isr_vector 00000184 08000000 08000000 00010000 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .text 000021d4 08000184 08000184 00010184 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .rodata 00000134 08002358 08002358 00012358 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .ARM 00000008 0800248c 0800248c 0001248c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .init_array 00000004 08002494 08002494 00012494 2**2
CONTENTS, ALLOC, LOAD, DATA
5 .fini_array 00000004 08002498 08002498 00012498 2**2
CONTENTS, ALLOC, LOAD, DATA
6 .data 00000068 20000000 0800249c 00020000 2**2
CONTENTS, ALLOC, LOAD, DATA
.data первой копируется в RAM на адрес 0х2000000
7 .bss 00000070 20000068 08002504 00020068 2**2
.bss копируется в RAM на адрес 0х2000068 сразу после .data
ALLOC
8 ._user_heap_stack 00006000 200000d8 08002504 000200d8 2**0
ALLOC
Теперь внимание обратите на VMA и LMA колонки и станет понятно , что копируется в RAM , а что во FLASH.
.rodata - это данные с префиксом const , они тоже остаются во FLASH.
// Продолжаем изучение startup файла:
ldr r2, =_sbss - bss start RAM address // это у нас 0x20000068
b LoopFillZerobss безусловный переход на LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4 записываем значение r3 по адресу [r2] 4 // это у нас 0x20000068 4
LoopFillZerobss:
ldr r3, = _ebss bss end RAM address // 0x200000d8
cmp r2, r3
bcc FillZerobss
/* Call the clock system initialization function.*/
bl SystemInit // выполнить подпрограмму , из файла на языке C уже : см. system_stm32f2xx.c
// (там Reset the RCC clock configuration to the default reset state и Configure the Vector Table location)
/* Call static constructors */
bl __libc_init_array // здесь не совсем понятно
/* Call the application's entry point.*/
bl main // ну и переход на main
bx lr // Возврат из подпрограммы
.size Reset_Handler, .-Reset_Handler
Но самое интересное в самом конце файла.
Создание Таблицы веторов прерываний
isr_vector
.section .isr_vector,"a",%progbits
//.section name [, "flags" [, %type [, entry_size] [, group_name [, linkage]] [, link_order_symbol] [, unique, unique_id] ]]
// здесь мы создаем таблицу векторов прерываний
// флаги секции a :SHF_ALLOC: the section is allocatable , x: SHF_EXECINSTR: the section is executable.
// %progbits секция содержит инициализарованные данные(data) и/или код
.type g_pfnVectors, %object
// .type : directive sets the type of a symbol.
// g_pfnVectors symbol
// %object is a data object : The following types are accepted: %function,%object,%tls_object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
А можно ли здесь создать вторую таблицу векторов прерываний ? Можно ,читайте далее