скрытое меню

startupxxxx.s старт программы

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

Надо расслабится и посмотреть на проблему со стороны.

Обратимся к истокам , к исходному старту программы.

В общем надо начинать с самого начала запуска контроллера. А где он запускается ?

Запуск микроконтроллера от А до ...

Главные файлы настройки программы

Файл *.ld - тут память распределяется на области RAM и FLASH - это по сути исходник, такой же как и main.c.
Файл *.map - этот файл появляется в результате сборки и содержит полезную информацию что куда распределилось.
Файл *.list - файл тоже появляется в результате сборки крайне важен для анализа памяти.

startup_stm32f407xx.s

Этот файл чистой воды ассемблер и придется вспомнить , что делают ассемблерные инструкции. Очень рекомендую также пройти отладчиком по ассемблеру и посмотреть регистры.

Суть этого файла в том , что программа не сразу стартует с функции 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 - здесь происходит запись в память <strong>RAM</strong>   по адресу 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

Остается понять куда попадет isr_vector и что такое init_array и fini_array (со странно малым размером). Теперь внимание обратите на VMA и LMA колонки и станет понятно , что больше в RAM ничего не копируется, кроме _user_heap_stack.

.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    // выполнить подпрограмму , из файла на языке с уже : 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