STM32F4를 시작하는 데 가장 효율적인 방법 / 최소 코드는 무엇입니까? ST에서 나온 시작 파일에는 불필요한 코드가 많이있는 것 같습니다.
STM32F4를 시작하는 데 가장 효율적인 방법 / 최소 코드는 무엇입니까? ST에서 나온 시작 파일에는 불필요한 코드가 많이있는 것 같습니다.
답변:
공급 업체가 제공 한 시작 코드를 사용하지 않을 수 있습니다. 사람들이 이것을하는 이유는 거의 없습니다 :
보다 효율적이거나 덜 부풀린 코드를 작성하십시오. 공급 업체 코드가 충족되지 않는 특별한 요구 사항이 있습니다. 물건이 어떻게 작동하는지 알고 싶습니다. 많은 MCU에서 사용할 수있는 범용 코드가 필요합니다. 프로세스를 완벽하게 제어하기를 원합니다. 기타..
다음은 C 프로그램 (C ++, 예외 등 없음) 및 Cortex M 마이크로 컨트롤러 (제조 / 모델에 관계없이)에만 적용됩니다. 또한 다른 컴파일러와 거의 다르지 않지만 GCC를 사용한다고 가정합니다. 마지막으로 newlib를 사용합니다.
가장 먼저 할 일은 링커 스크립트를 만드는 것입니다. 컴파일러에게 메모리에 물건을 배열하는 방법을 알려 주어야합니다. 링커 스크립트 자체에 대한 주제이므로 링커 스크립트에 대해 자세히 설명하지 않습니다.
/*
* Linker script.
*/
/*
* Set the output format. Currently set for Cortex M architectures,
* may need to be modified if the library has to support other MCUs,
* or completelly removed.
*/
OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
/*
* Just refering a function included in the vector table, and that
* it is defined in the same file with it, so the vector table does
* not get optimized out.
*/
EXTERN(Reset_Handler)
/*
* ST32F103x8 memory setup.
*/
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64k
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20k
}
/*
* Necessary group so the newlib stubs provided in the library,
* will correctly be linked with the appropriate newlib functions,
* and not optimized out, giving errors for undefined symbols.
* This way the libraries can be fed to the linker in any order.
*/
GROUP(
libgcc.a
libg.a
libc.a
libm.a
libnosys.a
)
/*
* Stack start pointer. Here set to the end of the stack
* memory, as in most architectures (including all the
* new ARM ones), the stack starts from the maximum address
* and grows towards the bottom.
*/
__stack = ORIGIN(RAM) + LENGTH(RAM);
/*
* Programm entry function. Used by the debugger only.
*/
ENTRY(_start)
/*
* Memory Allocation Sections
*/
SECTIONS
{
/*
* For normal programs should evaluate to 0, for placing the vector
* table at the correct position.
*/
. = ORIGIN(FLASH);
/*
* First link the vector table.
*/
.vectors : ALIGN(4)
{
FILL(0xFF)
__vectors_start__ = ABSOLUTE(.);
KEEP(*(.vectors))
*(.after_vectors .after_vectors.*)
} > FLASH
/*
* Start of text.
*/
_text = .;
/*
* Text section
*/
.text : ALIGN(4)
{
*(.text)
*(.text.*)
*(.glue_7t)
*(.glue_7)
*(.gcc*)
} > FLASH
/*
* Arm section unwinding.
* If removed may cause random crashes.
*/
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
/*
* Arm stack unwinding.
* If removed may cause random crashes.
*/
.ARM.exidx :
{
__exidx_start = .;
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
__exidx_end = .;
} > FLASH
/*
* Section used by C++ to access eh_frame.
* Generaly not used, but it doesn't harm to be there.
*/
.eh_frame_hdr :
{
*(.eh_frame_hdr)
} > FLASH
/*
* Stack unwinding code.
* Generaly not used, but it doesn't harm to be there.
*/
.eh_frame : ONLY_IF_RO
{
*(.eh_frame)
} > FLASH
/*
* Read-only data. Consts should also be here.
*/
.rodata : ALIGN(4)
{
. = ALIGN(4);
__rodata_start__ = .;
*(.rodata)
*(.rodata.*)
. = ALIGN(4);
__rodata_end__ = .;
} > FLASH
/*
* End of text.
*/
_etext = .;
/*
* Data section.
*/
.data : ALIGN(4)
{
FILL(0xFF)
. = ALIGN(4);
PROVIDE(__textdata__ = LOADADDR(.data));
PROVIDE(__data_start__ = .);
*(.data)
*(.data.*)
*(.ramtext)
. = ALIGN(4);
PROVIDE(__data_end__ = .);
} > RAM AT > FLASH
/*
* BSS section.
*/
.bss (NOLOAD) : ALIGN(4)
{
. = ALIGN(4);
PROVIDE(_bss_start = .);
__bss_start__ = .;
*(.bss)
*(.bss.*)
*(COMMON)
. = ALIGN(4);
PROVIDE(_bss_end = .);
__bss_end__ = .;
PROVIDE(end = .);
} > RAM
/*
* Non-initialized variables section.
* A variable should be explicitly placed
* here, aiming in speeding-up boot time.
*/
.noinit (NOLOAD) : ALIGN(4)
{
__noinit_start__ = .;
*(.noinit .noinit.*)
. = ALIGN(4) ;
__noinit_end__ = .;
} > RAM
/*
* Heap section.
*/
.heap (NOLOAD) :
{
. = ALIGN(4);
__heap_start__ = .;
__heap_base__ = .;
. = ORIGIN(HEAP_RAM) + LENGTH(HEAP_RAM);
__heap_end__ = .;
} > RAM
}
제공된 링커 스크립트를 직접 사용할 수 있습니다. 참고할 사항 :
이것은 내가 사용하는 링커 스크립트의 단순화 된 버전입니다. 제거하는 동안 코드에 버그가 생길 수 있으므로 다시 확인하십시오.
내가 아닌 다른 MCU에 사용하기 때문에 MEMORY 레이아웃을 자신의 필요에 맞게 변경해야합니다.
아래 링크 된 라이브러리를 변경하여 자신과 링크해야 할 수도 있습니다. 여기에서 newlib와 연결됩니다.
코드에 벡터 테이블을 포함시켜야합니다. 이것은 단순히 함수 포인터의 룩업 테이블이며, 인터럽트가 발생하면 하드웨어가 자동으로 점프합니다. 이것은 C에서 매우 쉽습니다.
다음 파일을보십시오. STM32F103C8 MCU 용이지만 필요에 따라 변경하기가 매우 쉽습니다.
#include "stm32f10x.h"
#include "debug.h"
//Start-up code.
extern void __attribute__((noreturn, weak)) _start (void);
// Default interrupt handler
void __attribute__ ((section(".after_vectors"), noreturn)) __Default_Handler(void);
// Reset handler
void __attribute__ ((section(".after_vectors"), noreturn)) Reset_Handler (void);
/** Non-maskable interrupt (RCC clock security system) */
void NMI_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** All class of fault */
void HardFault_Handler(void) __attribute__ ((interrupt, weak));
/** Memory management */
void MemManage_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Pre-fetch fault, memory access fault */
void BusFault_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Undefined instruction or illegal state */
void UsageFault_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** System service call via SWI instruction */
void SVC_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Debug monitor */
void DebugMon_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Pendable request for system service */
void PendSV_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** System tick timer */
void SysTick_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Window watchdog interrupt */
void WWDG_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** PVD through EXTI line detection interrupt */
void PVD_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Tamper interrupt */
void TAMPER_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** RTC global interrupt */
void RTC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Flash global interrupt */
void FLASH_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** RCC global interrupt */
void RCC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** EXTI Line0 interrupt */
void EXTI0_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** EXTI Line1 interrupt */
void EXTI1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** EXTI Line2 interrupt */
void EXTI2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** EXTI Line3 interrupt */
void EXTI3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** EXTI Line4 interrupt */
void EXTI4_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA1 Channel1 global interrupt */
void DMA1_Channel1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA1 Channel2 global interrupt */
void DMA1_Channel2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA1 Channel3 global interrupt */
void DMA1_Channel3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA1 Channel4 global interrupt */
void DMA1_Channel4_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA1 Channel5 global interrupt */
void DMA1_Channel5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA1 Channel6 global interrupt */
void DMA1_Channel6_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA1 Channel7 global interrupt */
void DMA1_Channel7_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** ADC1 and ADC2 global interrupt */
void ADC1_2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** USB high priority or CAN TX interrupts */
void USB_HP_CAN_TX_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** USB low priority or CAN RX0 interrupts */
void USB_LP_CAN_RX0_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** CAN RX1 interrupt */
void CAN_RX1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** CAN SCE interrupt */
void CAN_SCE_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** EXTI Line[9:5] interrupts */
void EXTI9_5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM1 break interrupt */
void TIM1_BRK_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM1 update interrupt */
void TIM1_UP_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM1 trigger and commutation interrupts */
void TIM1_TRG_COM_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM1 capture compare interrupt */
void TIM1_CC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM2 global interrupt */
void TIM2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM3 global interrupt */
void TIM3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM4 global interrupt */
void TIM4_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** I2C1 event interrupt */
void I2C1_EV_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** I2C1 error interrupt */
void I2C1_ER_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** I2C2 event interrupt */
void I2C2_EV_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** I2C2 error interrupt */
void I2C2_ER_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** SPI1 global interrupt */
void SPI1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** SPI2 global interrupt */
void SPI2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** USART1 global interrupt */
void USART1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** USART2 global interrupt */
void USART2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** USART3 global interrupt */
void USART3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** EXTI Line[15:10] interrupts */
void EXTI15_10_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** RTC alarm through EXTI line interrupt */
void RTCAlarm_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** USB wakeup from suspend through EXTI line interrupt */
void USBWakeup_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM8 break interrupt */
void TIM8_BRK_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM8 update interrupt */
void TIM8_UP_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM8 trigger and commutation interrupts */
void TIM8_TRG_COM_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM8 capture compare interrupt */
void TIM8_CC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** ADC3 global interrupt */
void ADC3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** FSMC global interrupt */
void FSMC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** SDIO global interrupt */
void SDIO_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM5 global interrupt */
void TIM5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** SPI3 global interrupt */
void SPI3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** UART4 global interrupt */
void UART4_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** UART5 global interrupt */
void UART5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM6 global interrupt */
void TIM6_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** TIM7 global interrupt */
void TIM7_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA2 Channel1 global interrupt */
void DMA2_Channel1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA2 Channel2 global interrupt */
void DMA2_Channel2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA2 Channel3 global interrupt */
void DMA2_Channel3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** DMA2 Channel4 and DMA2 Channel5 global interrupts */
void DMA2_Channel4_5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
// Stack start variable, needed in the vector table.
extern unsigned int __stack;
// Typedef for the vector table entries.
typedef void (* const pHandler)(void);
/** STM32F103 Vector Table */
__attribute__ ((section(".vectors"), used)) pHandler vectors[] =
{
(pHandler) &__stack, // The initial stack pointer
Reset_Handler, // The reset handler
NMI_Handler, // The NMI handler
HardFault_Handler, // The hard fault handler
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
MemManage_Handler, // The MPU fault handler
BusFault_Handler,// The bus fault handler
UsageFault_Handler,// The usage fault handler
#else
0, 0, 0, // Reserved
#endif
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
SVC_Handler, // SVCall handler
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
DebugMon_Handler, // Debug monitor handler
#else
0, // Reserved
#endif
0, // Reserved
PendSV_Handler, // The PendSV handler
SysTick_Handler, // The SysTick handler
// ----------------------------------------------------------------------
WWDG_IRQHandler, // Window watchdog interrupt
PVD_IRQHandler, // PVD through EXTI line detection interrupt
TAMPER_IRQHandler, // Tamper interrupt
RTC_IRQHandler, // RTC global interrupt
FLASH_IRQHandler, // Flash global interrupt
RCC_IRQHandler, // RCC global interrupt
EXTI0_IRQHandler, // EXTI Line0 interrupt
EXTI1_IRQHandler, // EXTI Line1 interrupt
EXTI2_IRQHandler, // EXTI Line2 interrupt
EXTI3_IRQHandler, // EXTI Line3 interrupt
EXTI4_IRQHandler, // EXTI Line4 interrupt
DMA1_Channel1_IRQHandler, // DMA1 Channel1 global interrupt
DMA1_Channel2_IRQHandler, // DMA1 Channel2 global interrupt
DMA1_Channel3_IRQHandler, // DMA1 Channel3 global interrupt
DMA1_Channel4_IRQHandler, // DMA1 Channel4 global interrupt
DMA1_Channel5_IRQHandler, // DMA1 Channel5 global interrupt
DMA1_Channel6_IRQHandler, // DMA1 Channel6 global interrupt
DMA1_Channel7_IRQHandler, // DMA1 Channel7 global interrupt
ADC1_2_IRQHandler, // ADC1 and ADC2 global interrupt
USB_HP_CAN_TX_IRQHandler, // USB high priority or CAN TX interrupts
USB_LP_CAN_RX0_IRQHandler, // USB low priority or CAN RX0 interrupts
CAN_RX1_IRQHandler, // CAN RX1 interrupt
CAN_SCE_IRQHandler, // CAN SCE interrupt
EXTI9_5_IRQHandler, // EXTI Line[9:5] interrupts
TIM1_BRK_IRQHandler, // TIM1 break interrupt
TIM1_UP_IRQHandler, // TIM1 update interrupt
TIM1_TRG_COM_IRQHandler, // TIM1 trigger and commutation interrupts
TIM1_CC_IRQHandler, // TIM1 capture compare interrupt
TIM2_IRQHandler, // TIM2 global interrupt
TIM3_IRQHandler, // TIM3 global interrupt
TIM4_IRQHandler, // TIM4 global interrupt
I2C1_EV_IRQHandler, // I2C1 event interrupt
I2C1_ER_IRQHandler, // I2C1 error interrupt
I2C2_EV_IRQHandler, // I2C2 event interrupt
I2C2_ER_IRQHandler, // I2C2 error interrupt
SPI1_IRQHandler, // SPI1 global interrupt
SPI2_IRQHandler, // SPI2 global interrupt
USART1_IRQHandler, // USART1 global interrupt
USART2_IRQHandler, // USART2 global interrupt
USART3_IRQHandler, // USART3 global interrupt
EXTI15_10_IRQHandler, // EXTI Line[15:10] interrupts
RTCAlarm_IRQHandler, // RTC alarm through EXTI line interrupt
USBWakeup_IRQHandler, // USB wakeup from suspend through EXTI line interrupt
TIM8_BRK_IRQHandler, // TIM8 break interrupt
TIM8_UP_IRQHandler, // TIM8 update interrupt
TIM8_TRG_COM_IRQHandler, // TIM8 trigger and commutation interrupts
TIM8_CC_IRQHandler, // TIM8 capture compare interrupt
ADC3_IRQHandler, // ADC3 global interrupt
FSMC_IRQHandler, // FSMC global interrupt
SDIO_IRQHandler, // SDIO global interrupt
TIM5_IRQHandler, // TIM5 global interrupt
SPI3_IRQHandler, // SPI3 global interrupt
UART4_IRQHandler, // UART4 global interrupt
UART5_IRQHandler, // UART5 global interrupt
TIM6_IRQHandler, // TIM6 global interrupt
TIM7_IRQHandler, // TIM7 global interrupt
DMA2_Channel1_IRQHandler, // DMA2 Channel1 global interrupt
DMA2_Channel2_IRQHandler, // DMA2 Channel2 global interrupt
DMA2_Channel3_IRQHandler, // DMA2 Channel3 global interrupt
DMA2_Channel4_5_IRQHandler // DMA2 Channel4 and DMA2 Channel5 global interrupts
};
/** Default exception/interrupt handler */
void __attribute__ ((section(".after_vectors"), noreturn)) __Default_Handler(void)
{
#ifdef DEBUG
while (1);
#else
NVIC_SystemReset();
while(1);
#endif
}
/** Reset handler */
void __attribute__ ((section(".after_vectors"), noreturn)) Reset_Handler(void)
{
_start();
while(1);
}
여기서 일어나고있는 일. -먼저 _start 함수를 선언하여 다음과 같이 사용할 수 있습니다. -모든 인터럽트에 대한 기본 핸들러를 선언하고 리셋 핸들러-MCU에 필요한 모든 인터럽트 핸들러를 선언합니다. 이러한 함수는 기본 핸들러의 별칭 일뿐입니다. 즉, 함수가 호출되면 기본 핸들러가 대신 호출됩니다. 또한 주 단위로 선언되므로 코드로 무시할 수 있습니다. 처리기가 필요하면 코드에서 다시 선언하면 코드가 연결됩니다. 그중 하나가 필요하지 않으면 단순히 기본값이 있으며 아무것도 할 필요가 없습니다. 기본 처리기는 응용 프로그램에 처리기가 필요하지만 처리기가 필요하지 않은 경우 코드 디버깅에 도움이되거나 야생에있는 경우 시스템을 복구 할 수 있도록 구성되어야합니다. -링커 스크립트에 __stack 기호가 선언되어 있습니다. 벡터 테이블에 필요합니다. -테이블 자체를 정의합니다. 첫 번째 항목은 스택의 시작에 대한 포인터이고 다른 항목은 핸들러에 대한 포인터입니다. -마지막으로 기본 처리기와 재설정 처리기에 간단한 구현을 제공합니다. 재설정 핸들러는 재설정 후에 호출되고 시작 코드를 호출하는 것입니다.
것을 명심 속성 링커가 올바른 위치 (일반적으로 주소를 0x00000000)에서 테이블을 배치 할 것이다 그래서 같이 벡터 테이블은 ((섹션 ())) 절대적으로 필요하다.
위 파일에서 어떤 수정이 필요합니다.
newlib을 사용하기 때문에 일부 함수의 구현을 제공해야합니다. printf, scanf 등을 구현할 수 있지만 필요하지는 않습니다. 개인적으로 나는 다음을 제공합니다 :
malloc에 필요한 _sbrk. (수정 필요 없음)
#include <sys/types.h>
#include <errno.h>
caddr_t __attribute__((used)) _sbrk(int incr)
{
extern char __heap_start__; // Defined by the linker.
extern char __heap_end__; // Defined by the linker.
static char* current_heap_end;
char* current_block_address;
if (current_heap_end == 0)
{
current_heap_end = &__heap_start__;
}
current_block_address = current_heap_end;
// Need to align heap to word boundary, else will get
// hard faults on Cortex-M0. So we assume that heap starts on
// word boundary, hence make sure we always add a multiple of
// 4 to it.
incr = (incr + 3) & (~3); // align value to 4
if (current_heap_end + incr > &__heap_end__)
{
// Heap has overflowed
errno = ENOMEM;
return (caddr_t) - 1;
}
current_heap_end += incr;
return (caddr_t) current_block_address;
}
_exit는 필요하지 않지만 아이디어가 마음에 듭니다. CMSIS 포함 만 수정하면됩니다.
#include <sys/types.h>
#include <errno.h>
#include "stm32f10x.h"
void __attribute__((noreturn, used)) _exit(int code)
{
(void) code;
NVIC_SystemReset();
while(1);
}
마지막으로 시작 코드!
#include <stdint.h>
#include "stm32f10x.h"
#include "gpio.h"
#include "flash.h"
/** Main program entry point. */
extern int main(void);
/** Exit system call. */
extern void _exit(int code);
/** Initializes the data section. */
static void __attribute__((always_inline)) __initialize_data (unsigned int* from, unsigned int* region_begin, unsigned int* region_end);
/** Initializes the BSS section. */
static void __attribute__((always_inline)) __initialize_bss (unsigned int* region_begin, unsigned int* region_end);
/** Start-up code. */
void __attribute__ ((section(".after_vectors"), noreturn, used)) _start(void);
void _start (void)
{
//Before switching on the main oscillator and the PLL,
//and getting to higher and dangerous frequencies,
//configuration of the flash controller is necessary.
//Enable the flash prefetch buffer. Can be achieved when CCLK
//is lower than 24MHz.
Flash_prefetchBuffer(1);
//Set latency to 2 clock cycles. Necessary for setting the clock
//to the maximum 72MHz.
Flash_setLatency(2);
// Initialize hardware right after configuring flash, to switch
//clock to higher frequency and have the rest of the
//initializations run faster.
SystemInit();
// Copy the DATA segment from Flash to RAM (inlined).
__initialize_data(&__textdata__, &__data_start__, &__data_end__);
// Zero fill the BSS section (inlined).
__initialize_bss(&__bss_start__, &__bss_end__);
//Core is running normally, RAM and FLASH are initialized
//properly, now the system must be fully functional.
//Update the SystemCoreClock variable.
SystemCoreClockUpdate();
// Call the main entry point, and save the exit code.
int code = main();
//Main should never return. If it does, let the system exit gracefully.
_exit (code);
// Should never reach this, _exit() should have already
// performed a reset.
while(1);
}
static inline void __initialize_data (unsigned int* from, unsigned int* region_begin, unsigned int* region_end)
{
// Iterate and copy word by word.
// It is assumed that the pointers are word aligned.
unsigned int *p = region_begin;
while (p < region_end)
*p++ = *from++;
}
static inline void __initialize_bss (unsigned int* region_begin, unsigned int* region_end)
{
// Iterate and clear word by word.
// It is assumed that the pointers are word aligned.
unsigned int *p = region_begin;
while (p < region_end)
*p++ = 0;
}
여기서 일어나고있는 일.
다소간이입니다.
__initialize_data()
하고 __initialize_bss()
그 느린 속도로 실행됩니다에도 불구하고, 당신보다 이전. 그렇지 않으면, 당신은 있는지 확인해야 SystemInit()
하고 Flash_*()
루틴은 전혀 전역을 사용하지 마십시오.
피질 -ms는 전체 크기의 팔과 달리 벡터 테이블을 사용합니다. 또한 모드와 뱅크 레지스터가 없습니다. 그리고 이벤트 / 인터럽트의 경우 ARM 코딩 표준을 준수합니다. 즉, 필요한 최소값을 의미하지만 주소 0의 첫 번째 단어는 스택 포인터의 초기 값이고 두 번째 단어는 재설정시 분기 할 주소입니다. 어셈블리 지시문을 사용하면 매우 쉽습니다.
.globl _start
_start:
.word 0x20001000
.word main
그러나 처음 두 단어가 올바른 값을 갖는 한 원하는대로 무엇이든 할 수 있습니다. 분기를위한 썸 주소에는 lsbit가 설정되어 있습니다. 그것은 실제로 주소의 일부가 아니며 단지 엄지 모드에 있다는 것을 나타냅니다.
이 4 바이트를 무언가로 소비해야하지만 스택 포인터를 설정하는 데 사용하는 다른 코드가있는 경우 벡터 테이블을 사용할 필요가 없으며 거기에 넣은 내용을로드하면 언제든지 변경할 수 있습니다. 전체 크기 / 이전 팔과는 다르지만 스택 포인터는 하나만 있습니다.
스타트 업이라는 단어는 매우 모호하므로 이미 그 지시어로 다룰 수도 있고, 의미에 따라 마이크로 컨트롤러 시작을 마치려면 수천 줄 이상의 C 코드가 필요할 수도 있습니다.
STM32를 사용하는 Esp는 클록을 사용하여 사용하려는 주변 장치를 활성화하고 원하는 주변 장치를 구성해야합니다. 각 공급 업체와 제품군이 서로 다른 논리를 가지고 있고 다른 방식으로 초기화한다는 점을 제외하고는 다른 마이크로 컨트롤러와 크게 다르지 않습니다.
제조업체에서 제공하는 시작 파일은 일반적으로 C 컴파일러 환경을 지원하도록 설계되었습니다. 여기에는 메모리 맵 설정, 메모리 초기화 없음, 변수 초기화 및 시작 설정 (재설정 벡터)과 관련된 많은 것들이 포함됩니다.
일부 시작 파일에는 인터럽트 벡터 및 인터럽트 컨트롤러 설정이 포함되어 있지만 작업 한 일부 환경에는 별도의 어셈블리 언어 파일이 있습니다.
CPU 아키텍처에 따라 다른 모델이 지원되기 때문에 시작 파일에서 복잡성이 나타나는 경우가 있습니다. 모델의 이름은 "compact"및 "large"와 같이 지정할 수 있습니다.
당신이 요구 한 방식은 최소한 당신이 필요로하는 것에 거의 전적으로 달려 있습니다. 따라서 아키텍처, 필요한 환경 및 플랫폼 작동 방식을 완전히 이해해야합니다. 그런 다음 필요에 따라 공급 업체에서 제공 한 파일을 정리하거나 처음부터 직접 작성할 수 있습니다.
그러나 C로 코드를 작성하려면 시작 코드를 그대로두고 프로그래밍 모델을 설정하고 main ()에서 시작하는 것처럼 코드를 집중시키는 것이 가장 좋습니다.