Когда уже имеется работающий копилятор, можно писать программы и собирать их с помощью командной строки, но гораздо удобнее делать все это с помощью любимой среды разработки (IDE, Integrated Development Environment).
Среда разработки
"-mthumb" - система команд, все вместе приведет к использованию семейства команд thumb-2, что в данном случае и нужно.
"-fno-exceptions" - позволяет в несколько раз уменьшить размер выходного файла, потому что выкидывается поддержка исключений для С++, которая в данном случае не нужна. Еще рекомендуют использовать параметр "-fno-rtti", который убирает накладные расходы, связанные с использованием идентификации типов во время исполнения (например, при наследовании).
"-nostartfiles" - отключается использование, например, crt0.o, где производится настройка многих вещей в самом начале работы программы: инициализация стека, настройка таблицы векторов прерываний, обнуление глобальных переменных, присвоение начальных значений. Если имеется свой собственный начальный файл. то стандартный лучше отключить.
"-nostdlib" - аналогично, если нежелательно использование стандартных библиотек libc, libm и libgcc.
Еще я на вкладке "Compiler settings-># defines" добавил следующие определения:
STM32F10X_HD
HSE_VALUE=12000000UL
Они нужны для указания параметров используемого процессора, используются стандартной библиотекой STM32.
На вкладке "Search directories" все поля пустые.
На вкладке "Pre/post build steps" в поле "Post-build steps" я добавил такие строки:
arm-none-eabi-size $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE) - чтобы получать информацию о размере используемой памяти,
arm-none-eabi-objcopy -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex - чтобы получить файл для прошивки,
arm-none-eabi-objcopy -O binary $(TARGET_OUTPUT_FILE) $TARGET_OUTPUT_FILE).bin - собственно, тоже можно использовать для прошивки процессора,
arm-none-eabi-objdump -h -S $(TARGET_OUTPUT_FILE) > $(TARGET_OUTPUT_FILE).lss - чтобы получить дизассемблерный листинг. Для листинга, по другим рекомендациям, можно использовать ключи -D -S - тогда листинг получается в несколько раз больше.
Создание проекта
Поскольку проект был пустой, то пришлось в нем создать новый файл main.cpp такого содержания:
SetSysClockTo72() все равно жестко используется умножение на 9:
В строке 412 и 413 файла stm32f10x.h видно, что нужны еще два файла:
Файлы core_cm3.h и core_cm3.c находятся в каталоге STM32F10x_StdPeriph_Lib_V3.3.0/Libraries/CMSIS/CM3/CoreSupport,
а system_stm32f10x.h и system_stm32f10x.c - рядом с файлом stm32f10x.h.
В core_cm3.* описывается то, что односится к ядру cortex-m3, а в остальных файлах - периферия.
Отладка
Для отладки нужно сделать следующие настройки проекта: перейти на пункт "Project->Properties...->Debugger". Здесь можно сделать настройки отладчика как для всего проекта, так и для отдельных целей сборки, я сделал для всего проекта.
Для этого в окне "Select target:" нужно выбрать "". На вкладке "Remote connection" выбрать тип "TCP", "IP address:" = localhost, "Port:" = 3333.
Перейти на вкладку "Additional GDB commands" и в поле "After connection:" вставить строки:
Для отладчика есть полезные глобальные настройки на "Settings->Compiler and debugger settings->Debugger settings". Например, такой полезный параметр: "Evaluate expression under cursor".
Среда разработки
В качестве IDE я выбрал Code::Blocks 10.05, потому что Eclipse мне разонравилось из-за своей тяжеловесности и приверженности Java.
Сначала попробовал воспользоваться встроенными в CB шаблонами проектов для ARM, но они оказались предназначенными для конкретныех отладочных плат, среди них даже не нашлось ни одной на нужном мне процессоре.
Поэтому пришлось создать пустой проект и настроить там все вручную.
Первым делом, создав пустой проект, нужно пойти на "Settings->Compiler and Debugger settings" и в разделе "Global compiler settings" выбрать GNU ARM Compilator.
Сначала нужно указать, где находятся исполняемые файлы компилятора и утилит, для этого надо перейти на вкладку "Toolchain executables" и указать там пути и названия. Автообнаружение компилятора не сработало, поэтому я руками вписал путь (/usr/arm) и ко всем утилитам ниже добавил приставку "arm-none-eabi-". Получилось очень красиво.
Еще на вкладке "Other settings" нужно выбрать значение для "Compiler logging": "Full command line", чтобы видеть в журнале компиляции все, что происходит.
На этом настройка глобальных параметров компилятора закончена.Теперь настройка сборки проекта: "Project->Build Options".
Здесь нужно в самом левом окошке выбрать название проекта, тогда настройки будут для всех вариантов сборки - и Debug, и Release.
На вкладке "Compiler settings->Other settings" вписал параметры "-mcpu=cortex-m3 -mthumb -fno-exceptions", больше никаких флагов трогать не стал. В процессорах STM32F103x нет сопроцессора, поэтому нужно использовать программный вариант арифметики с плавающей запятой (soft), что считается параметром по умолчанию.
"-mcpu=cortex-m3" - выбирается тип процессора,"-mthumb" - система команд, все вместе приведет к использованию семейства команд thumb-2, что в данном случае и нужно.
"-fno-exceptions" - позволяет в несколько раз уменьшить размер выходного файла, потому что выкидывается поддержка исключений для С++, которая в данном случае не нужна. Еще рекомендуют использовать параметр "-fno-rtti", который убирает накладные расходы, связанные с использованием идентификации типов во время исполнения (например, при наследовании).
В настроках компоновщика ("Linker settings") в поле Other linker options" нужно добавитьтакие параметры:
"-mcpu=cortex-m3 -mthumb
-Tstm32.ld
-Wl,-Map=$(TARGET_OUTPUT_FILE).map,--cref,--gc-sections".
"-Tstm32_flash.ld" - для размещения секций памяти, описанных в файле stm32_flash.ld,"-mcpu=cortex-m3 -mthumb
-Tstm32.ld
-Wl,-Map=$(TARGET_OUTPUT_FILE).map,--cref,--gc-sections".
"-Wl,-Map=$(TARGET_OUTPUT_FILE).map,--cref,--gc-sections" - чтобы получить карту памяти.
Часто встречается использование параметров
"-nostartfiles" и "-nostdlib", но это нужно делать сугубо индивидуально в зависимости от конкретной ситуации.Часто встречается использование параметров
"-nostartfiles" - отключается использование, например, crt0.o, где производится настройка многих вещей в самом начале работы программы: инициализация стека, настройка таблицы векторов прерываний, обнуление глобальных переменных, присвоение начальных значений. Если имеется свой собственный начальный файл. то стандартный лучше отключить.
"-nostdlib" - аналогично, если нежелательно использование стандартных библиотек libc, libm и libgcc.
Еще я на вкладке "Compiler settings-># defines" добавил следующие определения:
STM32F10X_HD
HSE_VALUE=12000000UL
Они нужны для указания параметров используемого процессора, используются стандартной библиотекой STM32.
На вкладке "Search directories" все поля пустые.
На вкладке "Pre/post build steps" в поле "Post-build steps" я добавил такие строки:
arm-none-eabi-size $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE) - чтобы получать информацию о размере используемой памяти,
arm-none-eabi-objcopy -O ihex $(TARGET_OUTPUT_FILE) $(TARGET_OUTPUT_FILE).hex - чтобы получить файл для прошивки,
arm-none-eabi-objcopy -O binary $(TARGET_OUTPUT_FILE) $TARGET_OUTPUT_FILE).bin - собственно, тоже можно использовать для прошивки процессора,
arm-none-eabi-objdump -h -S $(TARGET_OUTPUT_FILE) > $(TARGET_OUTPUT_FILE).lss - чтобы получить дизассемблерный листинг. Для листинга, по другим рекомендациям, можно использовать ключи -D -S - тогда листинг получается в несколько раз больше.
Создание проекта
Поскольку проект был пустой, то пришлось в нем создать новый файл main.cpp такого содержания:
#include "stm32f10x.h"
int main(void){uint32_t i = 0;while(true){for (uint32_t k = 0; k < 2000; k++)i = k;}return 0;}
Текст сделан так, чтобы компилятору можно было хоть что-то сделать. Для поддержки своих процессоров STMicroelectronics дает возможность загрузить со своего сайта библиотеку поддержки stm32f10x_stdperiph_lib.zip (примерно 14 МБ размером).
В этом пакете есть файл описания процессора STM32F10x_StdPeriph_Lib_V3.3.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h, в нем содержатся названия и привязки битов, регистров и всего прочего для процессора. В нем необходимо примерно в 50-й строке раскомментарить строчку, характеризующую используемый процессор, в моем случае это была строка 53:
или определить в командной строке компилятора параметр -DSTM32F10X_HD. Кроме того, необходимо указать частоту используемого кварца. По умолчанию считается, что используется кварц на 8 МГц, поэтому нужно указать свое значение (-DHSE_VALUE=12000000UL в моем случае, поскольку на плату установили кварц на 12 МГц). Но, к сожалению, этого недостаточно, потому что в функцииВ этом пакете есть файл описания процессора STM32F10x_StdPeriph_Lib_V3.3.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h, в нем содержатся названия и привязки битов, регистров и всего прочего для процессора. В нем необходимо примерно в 50-й строке раскомментарить строчку, характеризующую используемый процессор, в моем случае это была строка 53:
#define STM32F10X_HD /*!< STM32F10X_HD: STM32 High density devices */
SetSysClockTo72() все равно жестко используется умножение на 9:
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */что рассчитано только на использование кварца 8 МГц. В моем случае мне нужно было бы в этом файле (system_stm32f10x.h) вместо RCC_CFGR_PLLMULL9 вписать RCC_CFGR_PLLMULL6, либо написать собственную вариант настройки всех частот процессора.
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
В строке 412 и 413 файла stm32f10x.h видно, что нужны еще два файла:
поэтому их и соответствующие им С-файлы тоже нужно скопировать в свой проект.#include "core_cm3.h"#include "system_stm32f10x.h"
Файлы core_cm3.h и core_cm3.c находятся в каталоге STM32F10x_StdPeriph_Lib_V3.3.0/Libraries/CMSIS/CM3/CoreSupport,
а system_stm32f10x.h и system_stm32f10x.c - рядом с файлом stm32f10x.h.
В core_cm3.* описывается то, что односится к ядру cortex-m3, а в остальных файлах - периферия.
Периферийные описания, оказывается, можно брать и из другого каталога: STM32F10x_StdPeriph_Lib_V3.3.0/Libraries/STM32F10x_StdPeriph_Driver. Там находятся небольшие программные модули для обслуживания той или иной периферии. Но, чтобы не заморачиваться, еще и с интерфейсом этих вызовов, я использую базовую конфигурацию, а чтобы понять, как работать с тем или иным устройством процессора, заглядываю в этот каталог.
Для правильной инициализации некоторых вещей еще до передачи управления С-программе используется стартовый модуль на ассемблере, который можно взять из каталога STM32F10x_StdPeriph_Lib_V3.3.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/gcc_ride7 (потому что компилятор Raisonance построен на базе GCC) - для процессора STM32103VCT подходит файл startup_stm32f10x_hd.s.
Но я нашел примеры стартовых файлов на С и решил использовать именно такой файл:
После сброса или подачи питания работа начинается с Reset_Handler(), который сначала инициализирует области данных, вызывает статические конструкторы, если они есть, затем заносит в регистр SCB->VTOR адрес начала таблицы векторов - без этого у меня программа упорно не хотела работать с прерываниями, и именно по этой причине я отказался от стандартного стартового файла на ассемблере, потому что он не настраивал этот регистр.
Далее вызывается функция SystemInit(), которая настраивает тактовые частоты и параметры всей системы.
И только после этого управление передается функции main().
Программа не будет работать без еще одного важного файла: командного файла для компоновщика, который я взял готовым, только подправил фактические параметры памяти.
Но я нашел примеры стартовых файлов на С и решил использовать именно такой файл:
Этот файл я назвал startup.c и включил в состав проекта. Большую часть этого файла занимает определение векторов прерываний. Они специально объявляются с помощью "#pragma weak", чтобы вместо них потом могли быть вставлены адреса фактических функций обработки прерываний. Изначально все вектора прерываний указывают на Default_Handler(), куда попадает программа в случае неправильно оформленной обработки прерывания.
#include "stm32f10x.h"
typedef void( *const intfunc )( void );
#define WEAK __attribute__ ((weak))
extern unsigned long _etext;
extern unsigned long _sidata;
extern unsigned long _sdata;
extern unsigned long _edata;
extern unsigned long _sbss;
extern unsigned long _ebss;
extern unsigned long _estack;
void Reset_Handler(void) __attribute__((__interrupt__));
void __Init_Data(void);
void Default_Handler(void);
extern int main(void);
extern void __libc_init_array(void); /* calls CTORS of static objects */
void WEAK NMI_Handler(void);
void WEAK HardFault_Handler(void);
void WEAK MemManage_Handler(void);
void WEAK BusFault_Handler(void);
void WEAK UsageFault_Handler(void);
void WEAK MemManage_Handler(void);
void WEAK SVC_Handler(void);
void WEAK DebugMon_Handler(void);
void WEAK PendSV_Handler(void);
void WEAK SysTick_Handler(void);
void WEAK WWDG_IRQHandler(void);
void WEAK PVD_IRQHandler(void);
void WEAK TAMPER_IRQHandler(void);
void WEAK RTC_IRQHandler(void);
void WEAK FLASH_IRQHandler(void);
void WEAK RCC_IRQHandler(void);
void WEAK EXTI0_IRQHandler(void);
void WEAK EXTI1_IRQHandler(void);
void WEAK EXTI2_IRQHandler(void);
void WEAK EXTI3_IRQHandler(void);
void WEAK EXTI4_IRQHandler(void);
void WEAK DMA1_Channel1_IRQHandler(void);
void WEAK DMA1_Channel2_IRQHandler(void);
void WEAK DMA1_Channel3_IRQHandler(void);
void WEAK DMA1_Channel4_IRQHandler(void);
void WEAK DMA1_Channel5_IRQHandler(void);
void WEAK DMA1_Channel6_IRQHandler(void);
void WEAK DMA1_Channel7_IRQHandler(void);
void WEAK ADC1_2_IRQHandler(void);
void WEAK USB_HP_CAN1_TX_IRQHandler(void);
void WEAK USB_LP_CAN1_RX0_IRQHandler(void);
void WEAK CAN1_RX1_IRQHandler(void);
void WEAK CAN1_SCE_IRQHandler(void);
void WEAK EXTI9_5_IRQHandler(void);
void WEAK TIM1_BRK_IRQHandler(void);
void WEAK TIM1_UP_IRQHandler(void);
void WEAK TIM1_TRG_COM_IRQHandler(void);
void WEAK TIM1_CC_IRQHandler(void);
void WEAK TIM2_IRQHandler(void);
void WEAK TIM3_IRQHandler(void);
void WEAK TIM4_IRQHandler(void);
void WEAK I2C1_EV_IRQHandler(void);
void WEAK I2C1_ER_IRQHandler(void);
void WEAK I2C2_EV_IRQHandler(void);
void WEAK I2C2_ER_IRQHandler(void);
void WEAK SPI1_IRQHandler(void);
void WEAK SPI2_IRQHandler(void);
void WEAK USART1_IRQHandler(void);
void WEAK USART2_IRQHandler(void);
void WEAK USART3_IRQHandler(void);
void WEAK EXTI15_10_IRQHandler(void);
void WEAK RTCAlarm_IRQHandler(void);
void WEAK USBWakeUp_IRQHandler(void);
void WEAK TIM8_BRK_IRQHandler(void);
void WEAK TIM8_UP_IRQHandler(void);
void WEAK TIM8_TRG_COM_IRQHandler(void);
void WEAK TIM8_CC_IRQHandler(void);
void WEAK ADC3_IRQHandler(void);
void WEAK FSMC_IRQHandler(void);
void WEAK SDIO_IRQHandler(void);
void WEAK TIM5_IRQHandler(void);
void WEAK SPI3_IRQHandler(void);
void WEAK UART4_IRQHandler(void);
void WEAK UART5_IRQHandler(void);
void WEAK TIM6_IRQHandler(void);
void WEAK TIM7_IRQHandler(void);
void WEAK DMA2_Channel1_IRQHandler(void);
void WEAK DMA2_Channel2_IRQHandler(void);
void WEAK DMA2_Channel3_IRQHandler(void);
void WEAK DMA2_Channel4_5_IRQHandler(void);
__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
(intfunc)((unsigned long)&_estack), /* The stack pointer after relocation */
Reset_Handler, /* Reset Handler */
NMI_Handler, /* NMI Handler */
HardFault_Handler, /* Hard Fault Handler */
MemManage_Handler, /* MPU Fault Handler */
BusFault_Handler, /* Bus Fault Handler */
UsageFault_Handler, /* Usage Fault Handler */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
SVC_Handler, /* SVCall Handler */
DebugMon_Handler, /* Debug Monitor Handler */
0, /* Reserved */
PendSV_Handler, /* PendSV Handler */
SysTick_Handler, /* SysTick Handler */
/* External Interrupts */
WWDG_IRQHandler, /* Window Watchdog */
PVD_IRQHandler, /* PVD through EXTI Line detect */
TAMPER_IRQHandler, /* Tamper */
RTC_IRQHandler, /* RTC */
FLASH_IRQHandler, /* Flash */
RCC_IRQHandler, /* RCC */
EXTI0_IRQHandler, /* EXTI Line 0 */
EXTI1_IRQHandler, /* EXTI Line 1 */
EXTI2_IRQHandler, /* EXTI Line 2 */
EXTI3_IRQHandler, /* EXTI Line 3 */
EXTI4_IRQHandler, /* EXTI Line 4 */
DMA1_Channel1_IRQHandler, /* DMA1 Channel 1 */
DMA1_Channel2_IRQHandler, /* DMA1 Channel 2 */
DMA1_Channel3_IRQHandler, /* DMA1 Channel 3 */
DMA1_Channel4_IRQHandler, /* DMA1 Channel 4 */
DMA1_Channel5_IRQHandler, /* DMA1 Channel 5 */
DMA1_Channel6_IRQHandler, /* DMA1 Channel 6 */
DMA1_Channel7_IRQHandler, /* DMA1 Channel 7 */
ADC1_2_IRQHandler, /* ADC1 & ADC2 */
USB_HP_CAN1_TX_IRQHandler, /* USB High Priority or CAN1 TX */
USB_LP_CAN1_RX0_IRQHandler, /* USB Low Priority or CAN1 RX0 */
CAN1_RX1_IRQHandler, /* CAN1 RX1 */
CAN1_SCE_IRQHandler, /* CAN1 SCE */
EXTI9_5_IRQHandler, /* EXTI Line 9..5 */
TIM1_BRK_IRQHandler, /* TIM1 Break */
TIM1_UP_IRQHandler, /* TIM1 Update */
TIM1_TRG_COM_IRQHandler, /* TIM1 Trigger and Commutation */
TIM1_CC_IRQHandler, /* TIM1 Capture Compare */
TIM2_IRQHandler, /* TIM2 */
TIM3_IRQHandler, /* TIM3 */
TIM4_IRQHandler, /* TIM4 */
I2C1_EV_IRQHandler, /* I2C1 Event */
I2C1_ER_IRQHandler, /* I2C1 Error */
I2C2_EV_IRQHandler, /* I2C2 Event */
I2C2_ER_IRQHandler, /* I2C2 Error */
SPI1_IRQHandler, /* SPI1 */
SPI2_IRQHandler, /* SPI2 */
USART1_IRQHandler, /* USART1 */
USART2_IRQHandler, /* USART2 */
USART3_IRQHandler, /* USART3 */
EXTI15_10_IRQHandler, /* EXTI Line 15..10 */
RTCAlarm_IRQHandler, /* RTC Alarm through EXTI Line */
USBWakeUp_IRQHandler, /* USB Wakeup from suspend */
TIM8_BRK_IRQHandler,
TIM8_UP_IRQHandler,
TIM8_TRG_COM_IRQHandler,
TIM8_CC_IRQHandler,
ADC3_IRQHandler,
FSMC_IRQHandler,
SDIO_IRQHandler,
TIM5_IRQHandler,
SPI3_IRQHandler,
UART4_IRQHandler,
UART5_IRQHandler,
TIM6_IRQHandler,
TIM7_IRQHandler,
DMA2_Channel1_IRQHandler,
DMA2_Channel2_IRQHandler,
DMA2_Channel3_IRQHandler,
DMA2_Channel4_5_IRQHandler,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0,
(intfunc)0xF1E0F85F /* @0x1E0. This is for boot in RAM mode for STM32F10x High Density devices. */
};
void __Init_Data(void) {
unsigned long *src, *dst;
/* copy the data segment into ram */
src = &_sidata;
dst = &_sdata;
if (src != dst)
while(dst < &_edata)
*(dst++) = *(src++);
/* zero the bss segment */
dst = &_sbss;
while(dst < &_ebss)
*(dst++) = 0;
}
void Reset_Handler(void) {
/* Initialize data and bss */
__Init_Data();
__libc_init_array(); // call all static construcors in C++ (harmless in C programs)
SCB->VTOR = 0x08000000;
SystemInit();
main();
while(1) {}
}
#pragma weak MMI_Handler = Default_Handler
#pragma weak MemManage_Handler = Default_Handler
#pragma weak BusFault_Handler = Default_Handler
#pragma weak UsageFault_Handler = Default_Handler
#pragma weak SVC_Handler = Default_Handler
#pragma weak DebugMon_Handler = Default_Handler
#pragma weak PendSV_Handler = Default_Handler
#pragma weak SysTick_Handler = Default_Handler
#pragma weak WWDG_IRQHandler = Default_Handler
#pragma weak PVD_IRQHandler = Default_Handler
#pragma weak TAMPER_IRQHandler = Default_Handler
#pragma weak RTC_IRQHandler = Default_Handler
#pragma weak FLASH_IRQHandler = Default_Handler
#pragma weak RCC_IRQHandler = Default_Handler
#pragma weak EXTI0_IRQHandler = Default_Handler
#pragma weak EXTI1_IRQHandler = Default_Handler
#pragma weak EXTI2_IRQHandler = Default_Handler
#pragma weak EXTI3_IRQHandler = Default_Handler
#pragma weak EXTI4_IRQHandler = Default_Handler
#pragma weak DMA1_Channel1_IRQHandler = Default_Handler
#pragma weak DMA1_Channel2_IRQHandler = Default_Handler
#pragma weak DMA1_Channel3_IRQHandler = Default_Handler
#pragma weak DMA1_Channel4_IRQHandler = Default_Handler
#pragma weak DMA1_Channel5_IRQHandler = Default_Handler
#pragma weak DMA1_Channel6_IRQHandler = Default_Handler
#pragma weak DMA1_Channel7_IRQHandler = Default_Handler
#pragma weak ADC1_2_IRQHandler = Default_Handler
#pragma weak USB_HP_CAN1_TX_IRQHandler = Default_Handler
#pragma weak USB_LP_CAN1_RX0_IRQHandler = Default_Handler
#pragma weak CAN1_RX1_IRQHandler = Default_Handler
#pragma weak CAN1_SCE_IRQHandler = Default_Handler
#pragma weak EXTI9_5_IRQHandler = Default_Handler
#pragma weak TIM1_BRK_IRQHandler = Default_Handler
#pragma weak TIM1_UP_IRQHandler = Default_Handler
#pragma weak TIM1_TRG_COM_IRQHandler = Default_Handler
#pragma weak TIM1_CC_IRQHandler = Default_Handler
#pragma weak TIM2_IRQHandler = Default_Handler
#pragma weak TIM3_IRQHandler = Default_Handler
#pragma weak TIM4_IRQHandler = Default_Handler
#pragma weak I2C1_EV_IRQHandler = Default_Handler
#pragma weak I2C1_ER_IRQHandler = Default_Handler
#pragma weak I2C2_EV_IRQHandler = Default_Handler
#pragma weak I2C2_ER_IRQHandler = Default_Handler
#pragma weak SPI1_IRQHandler = Default_Handler
#pragma weak SPI2_IRQHandler = Default_Handler
#pragma weak USART1_IRQHandler = Default_Handler
#pragma weak USART2_IRQHandler = Default_Handler
#pragma weak USART3_IRQHandler = Default_Handler
#pragma weak EXTI15_10_IRQHandler = Default_Handler
#pragma weak RTCAlarm_IRQHandler = Default_Handler
#pragma weak USBWakeUp_IRQHandler = Default_Handler
#pragma weak TIM8_BRK_IRQHandler = Default_Handler
#pragma weak TIM8_UP_IRQHandler = Default_Handler
#pragma weak TIM8_TRG_COM_IRQHandler = Default_Handler
#pragma weak TIM8_CC_IRQHandler = Default_Handler
#pragma weak ADC3_IRQHandler = Default_Handler
#pragma weak FSMC_IRQHandler = Default_Handler
#pragma weak SDIO_IRQHandler = Default_Handler
#pragma weak TIM5_IRQHandler = Default_Handler
#pragma weak SPI3_IRQHandler = Default_Handler
#pragma weak UART4_IRQHandler = Default_Handler
#pragma weak UART5_IRQHandler = Default_Handler
#pragma weak TIM6_IRQHandler = Default_Handler
#pragma weak TIM7_IRQHandler = Default_Handler
#pragma weak DMA2_Channel1_IRQHandler = Default_Handler
#pragma weak DMA2_Channel2_IRQHandler = Default_Handler
#pragma weak DMA2_Channel3_IRQHandler = Default_Handler
#pragma weak DMA2_Channel4_5_IRQHandler = Default_Handler
void Default_Handler(void) {
while (1) {}
}
После сброса или подачи питания работа начинается с Reset_Handler(), который сначала инициализирует области данных, вызывает статические конструкторы, если они есть, затем заносит в регистр SCB->VTOR адрес начала таблицы векторов - без этого у меня программа упорно не хотела работать с прерываниями, и именно по этой причине я отказался от стандартного стартового файла на ассемблере, потому что он не настраивал этот регистр.
Далее вызывается функция SystemInit(), которая настраивает тактовые частоты и параметры всей системы.
И только после этого управление передается функции main().
Программа не будет работать без еще одного важного файла: командного файла для компоновщика, который я взял готовым, только подправил фактические параметры памяти.
Отладка
Для отладки нужно сделать следующие настройки проекта: перейти на пункт "Project->Properties...->Debugger". Здесь можно сделать настройки отладчика как для всего проекта, так и для отдельных целей сборки, я сделал для всего проекта.
Для этого в окне "Select target:" нужно выбрать "
Перейти на вкладку "Additional GDB commands" и в поле "After connection:" вставить строки:
monitor reset haltПосле этого все готово для отладки. Нужно запустить openocd, а после этого можно запускать отладку: "Debugger->Start".
break main
continue
Для отладчика есть полезные глобальные настройки на "Settings->Compiler and debugger settings->Debugger settings". Например, такой полезный параметр: "Evaluate expression under cursor".