본문 바로가기
Robot/utility

emIDE 프로젝트에 ST Standard Peripheral Library Porting 하기

by lifeseed 2014. 5. 21.

프로젝트 생성후 컴파일이 완료되었으나, 각 Device를 컨트롤하기 위해서는 Driver 코드들을 작성해야 한다.

그러나 다행(?)스럽게도 ST 에서는 Standard Peripheral Library라는 코드를 배포하고 있다.

이를 다운 받아서 emIDE 프로젝트에 추가하여 ARM GCC로 빌드를 하려고 한다.

 

 

Step0] Project 생성 및 Standard Peripheral Library 준비

1) Project 생성 : http://lifeseed.tistory.com/102

2) STM32F10x Standard Peripheral Library (STSW-STM32054)

 

아래 링크를 클릭하면 다운 받을 수 있다.

http://www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/firmware/stsw-stm32054.zip

 

다운 받은 파일의 압축을 풀자. 다음과 같은 디렉토리 구조를 확인할 수 있다.

 

 

각 폴더에는 다음과 같은 파일들이 존재한다.

- Libraries :: CMSIS 및 ST에서 제공하는 각 IP의 Driver 파일

- Project :: 각종 예제 파일

- Utilities :: 각 Evaluation Board별 예제 및 환경 파일

 

 

Step1] Libraries 폴더 Workspace로 복사

 

우선 다운 받은 Standard Peripheral Libraries의 압축을 풀면 Libraries라는 폴더가 있다.

이 폴더를 프로젝트 workspace 폴더로 복사하자.

(Project 폴더로 복사할 수도 있지만 Library 파일들은 수정없이 공통으로 사용가능하므로, Workspace폴더로 복사하도록한다.)

 

 

Libraries폴더를 살펴 보면 CMSIS 와 STM32F10x_StdPeriph_Driver 두개의 폴더로 이루어져 있으며, CMSIS는 다시 CM3의 CoreSupport 와 DeviceSupport로 이루어 져 있다.

 

 

프로젝트에 추가될 파일은 크게 CMSIS의 CM3 CoreSupport 라는 폴더와 STM32F10x_StdPeriph_Driver 라는 폴더를 그대로 추가할 것이며, CMSIS의 CM3 DeviceSupport 에서 Start Up시 필요한 일부 파일을 Project 폴더로 복사해서 사용할 것이다.

Step2] Start.S 파일에 Interrupt Handler 추가
ST에서 제공하는 Library에는 ARM용 gcc 컴파일러를 지원하는 Start up assemble code가 없다.

그래서 Library에 포함된 다른 컴파일러들을 위한 Start up 코드를 참조하여, 프로젝트 생성시 제공되는 startup.S을 수정한다.

사실 추가될 내용은 interrupt handler들에 대한 정의뿐이며, 이와 관련된 코드 수정은 어렵지 않다.

 

.\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm\ 에는 start up파일들이 여럿 존재하는데, 현재 사용하는 CPU인 STM32F103RB가 속하는 Medium-density device용 start up 코드인 startup_stm32f10x_md.s 를 참조한다.

 

왼쪽이 startup_stm32f10x_md.s 파일이며, 오른쪽이 emIDE에서 제공하는 startup.S 파일이다.

 

즉 왼쪽의 External Interrupt Handler 들을 오른쪽 코드의 문법과 동일하게 수정한 후 추가한다.

당연히 .long Default_Handler 는 제거하여 준다.

    /* External interrupts */
    .long    WWDG_IRQHandler        /*  0:  Watchdog Timer            */
    .long    PVD_IRQHandler        /*  1:  PVD through EXTI Line detection     */
    .long    TAMPER_IRQHandler  /* 2: tamper                   */
    .long    RTC_IRQHandler        /*  3:  Real Time Clock           */
    .long    FLASH_IRQHandler  /* 4: flash global                   */
    .long    RCC_IRQHandler  /* 5: RCC global                   */
    .long    EXTI0_IRQHandler  /* 6: EXTI Line0                   */
    .long    EXTI1_IRQHandler  /* 7: EXTI Line1                   */
    .long    EXTI2_IRQHandler  /* 8: EXTI Line2                   */
    .long    EXTI3_IRQHandler  /* 9: EXTI Line3                   */
    .long    EXTI4_IRQHandler  /* 10: EXTI Line4                   */
    .long    DMA1_Channel1_IRQHandler /* 11: DMA1 Channel1 global   */
    .long    DMA1_Channel2_IRQHandler /* 12: DMA1 Channel2 global   */
    .long    DMA1_Channel3_IRQHandler /* 13: DMA1 Channel3 global   */
    .long    DMA1_Channel4_IRQHandler /* 14: DMA1 Channel4 global   */
    .long    DMA1_Channel5_IRQHandler /* 15: DMA1 Channel5 global   */
    .long    DMA1_Channel6_IRQHandler /* 16: DMA1 Channel6 global   */
    .long    DMA1_Channel7_IRQHandler /* 17: DMA1 Channel7 global   */
    .long    ADC1_2_IRQHandler  /* 18: ADC1 and ADC2 global   */
    .long    USB_HP_CAN_TX_IRQHandler /* 19: CAN1 TX   */
    .long    USB_LP_CAN_RX0_IRQHandler /* 20: CAN1 RX0   */
    .long    CAN_RX1_IRQHandler  /* 21: CAN1 RX1   */
    .long    CAN_SCE_IRQHandler  /* 22: CAN1 SCE   */
    .long    EXTI9_5_IRQHandler  /* 23: EXTI Line5-9                   */
    .long    TIM1_BRK_IRQHandler /* 24: TIM1 Break    */
    .long    TIM1_UP_IRQHandler  /* 25: TIM1 Update   */
    .long    TIM1_TRG_COM_IRQHandler /* 26: TIM1 Trigger and Commutation */
    .long    TIM1_CC_IRQHandler  /* 27: TIM1 Capture Compare */
    .long    TIM2_IRQHandler  /*  28:  Timer2 global   */
    .long    TIM3_IRQHandler  /*  29:  Timer3 global   */
    .long    TIM4_IRQHandler  /*  30:  Timer4 global   */
    .long    I2C1_EV_IRQHandler  /* 31: I2C1 event  */
    .long    I2C1_ER_IRQHandler  /* 32: I2C1 error  */
    .long    I2C2_EV_IRQHandler  /* 33: I2C2 event  */
    .long    I2C2_ER_IRQHandler  /* 34: I2C2 error  */
    .long    SPI1_IRQHandler  /* 35: SPI1 global  */
    .long    SPI2_IRQHandler  /* 36: SPI2 global  */
    .long    USART1_IRQHandler  /* 37: USART1 global */
    .long    USART2_IRQHandler  /* 38: USART2 global */
    .long    USART3_IRQHandler  /* 39: USART3 global */
    .long    EXTI15_10_IRQHandler /* 40: EXTI Line10-15  */
    .long    RTCAlarm_IRQHandler /* 41: RTC alarm through EXTI line */
    .long    USBWakeUp_IRQHandler /* 42: USB OTG FS Wakeup through EXTI line */
    .size    __isr_vector, . - __isr_vector

 

그리고 Compile Error를 제거하기위해, 각 Interrupt Handler들을 Weak속성으로 Define해둔다.

    def_default_handler    NMI_Handler
    def_default_handler    HardFault_Handler
    def_default_handler    MemManage_Handler
    def_default_handler    BusFault_Handler
    def_default_handler    UsageFault_Handler
    def_default_handler    SVC_Handler
    def_default_handler    DebugMon_Handler
    def_default_handler    PendSV_Handler
    def_default_handler    SysTick_Handler
    def_default_handler    Default_Handler

 

    def_default_handler    WWDG_IRQHandler        /*  0:  Watchdog Timer            */
    def_default_handler    PVD_IRQHandler        /*  1:  PVD through EXTI Line detection     */
    def_default_handler    TAMPER_IRQHandler  /* 2: tamper                   */
    def_default_handler    RTC_IRQHandler        /*  3:  Real Time Clock           */
    def_default_handler    FLASH_IRQHandler  /* 4: flash global                   */
    def_default_handler    RCC_IRQHandler  /* 5: RCC global                   */
    def_default_handler    EXTI0_IRQHandler  /* 6: EXTI Line0                   */
    def_default_handler    EXTI1_IRQHandler  /* 7: EXTI Line1                   */
    def_default_handler    EXTI2_IRQHandler  /* 8: EXTI Line2                   */
    def_default_handler    EXTI3_IRQHandler  /* 9: EXTI Line3                   */
    def_default_handler    EXTI4_IRQHandler  /* 10: EXTI Line4                   */
    def_default_handler    DMA1_Channel1_IRQHandler /* 11: DMA1 Channel1 global   */
    def_default_handler    DMA1_Channel2_IRQHandler /* 12: DMA1 Channel2 global   */
    def_default_handler    DMA1_Channel3_IRQHandler /* 13: DMA1 Channel3 global   */
    def_default_handler    DMA1_Channel4_IRQHandler /* 14: DMA1 Channel4 global   */
    def_default_handler    DMA1_Channel5_IRQHandler /* 15: DMA1 Channel5 global   */
    def_default_handler    DMA1_Channel6_IRQHandler /* 16: DMA1 Channel6 global   */
    def_default_handler    DMA1_Channel7_IRQHandler /* 17: DMA1 Channel7 global   */
    def_default_handler    ADC1_2_IRQHandler  /* 18: ADC1 and ADC2 global   */
    def_default_handler    USB_HP_CAN_TX_IRQHandler /* 19: CAN1 TX   */
    def_default_handler    USB_LP_CAN_RX0_IRQHandler /* 20: CAN1 RX0   */
    def_default_handler    CAN_RX1_IRQHandler  /* 21: CAN1 RX1   */
    def_default_handler    CAN_SCE_IRQHandler  /* 22: CAN1 SCE   */
    def_default_handler    EXTI9_5_IRQHandler  /* 23: EXTI Line5-9                   */
    def_default_handler    TIM1_BRK_IRQHandler /* 24: TIM1 Break    */
    def_default_handler    TIM1_UP_IRQHandler  /* 25: TIM1 Update   */
    def_default_handler    TIM1_TRG_COM_IRQHandler /* 26: TIM1 Trigger and Commutation */
    def_default_handler    TIM1_CC_IRQHandler  /* 27: TIM1 Capture Compare */
    def_default_handler    TIM2_IRQHandler  /*  28:  Timer2 global   */
    def_default_handler    TIM3_IRQHandler  /*  29:  Timer3 global   */
    def_default_handler    TIM4_IRQHandler  /*  30:  Timer4 global   */
    def_default_handler    I2C1_EV_IRQHandler  /* 31: I2C1 event  */
    def_default_handler    I2C1_ER_IRQHandler  /* 32: I2C1 error  */
    def_default_handler    I2C2_EV_IRQHandler  /* 33: I2C2 event  */
    def_default_handler    I2C2_ER_IRQHandler  /* 34: I2C2 error  */
    def_default_handler    SPI1_IRQHandler  /* 35: SPI1 global  */
    def_default_handler    SPI2_IRQHandler  /* 36: SPI2 global  */
    def_default_handler    USART1_IRQHandler  /* 37: USART1 global */
    def_default_handler    USART2_IRQHandler  /* 38: USART2 global */
    def_default_handler    USART3_IRQHandler  /* 39: USART3 global */
    def_default_handler    EXTI15_10_IRQHandler /* 40: EXTI Line10-15  */
    def_default_handler    RTCAlarm_IRQHandler /* 41: RTC alarm through EXTI line */
    def_default_handler    USBWakeUp_IRQHandler /* 42: USB OTG FS Wakeup through EXTI line */
    .end

 

아직은 Libraries 폴더를 프로젝트에 추가하지 않은 상태이므로, Build를 수행하면 프로젝트 생성시 추가되는 코드들만 compile된다. 에러가 없이 Compile되어야 한다.

 

 

Step3] 프로젝트에 Libraries 추가

우선 Libraries에 있는 두개의 폴더를 추가하도록 하자.

 

프로젝트창에서 프로젝트 명을 우클릭하여 Add files recursively... 를 선택하자.

 

Libraries 의 STM32F10x_StdPeriph_Driver를 선택하고 OK를 눌러 코드들을 추가한다.

 


 

그리고 다시 Add files recusively를 눌러 Libraries > CMSIS > CM3 의 CoreSupport 폴더를 선택하고 OK를 눌러 파일들을 추가한다.

 


Step 4] Include path 추가

메뉴의 Project > Build Option을 실행한다.

Search directories 의 Complier 탭에서 Add 버튼을 눌러 추가된 Libraries의 Header 파일이 있는 폴더의 PATH를 추가한다.

( emIDE는 Default로 프로젝트 폴더의 Inc파일을 Include하도록 되어 있다.)

 

..\Libraries\CMSIS\CM3\CoreSupport

..\Libraries\STM32F10x_StdPeriph_Driver\inc

 

..\Libraries\CMSIS\CM3\CoreSupport

 

..\Libraries\STM32F10x_StdPeriph_Driver\inc

 

 

Step5] Device Support 파일 추가

1) Device Support file 추가

.\workspace\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\ 폴더로 이동하면

stm32f10x.h

system_stm32f10x.c

system_stm32f10x.h

세개의 파일과 startup 폴더가 존재한다.

 

여기서 위 세개의 파일을 프로젝트 폴더로 복사한다.

.h파일은 Inc 폴더로 복사하고 .c 파일은 Src 폴더로 복사하자.

 

그리고 Add files 명령을 이용하여 프로젝트에 복사된 3개의 파일을 추가한다.

 

2) example file 추가

.\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\GPIO\IOToggle\ 폴더에서 Interrupt를 처리하기 위한 stm32f10x_it.c/h 파일과 Library들의 header를 include하는 stm32f10x_conf.h 파일을 프로젝트 폴더로 복사하자.

물론 .h파일은 Inc 폴더로 복사하고 .c 파일은 Src 폴더로 복사한다.

 

 

Step6] STM32 Build Define 추가
stm32f10x.h 파일을 include 하면 library에서 제공하는 api들을 사용할 수 있다.

단 이를 위해 CPU Type의 Define 및 library들의 header를 include하는 stm32f10x_conf.h를 stm32f10x.h에 포함시키기 위한 define이 추가되어야 한다.

 

메뉴의 Project > Build Option을 실행한다.

Compiler Settins 탭 내부의 #define 탭을 클릭한 후 Edit Box 창에 STM32F103RB 칩에 해당되는 STM32F10X_MD 및 stm32f10x_conf.h을 include  USE_STDPERIPH_DRIVER를 define 탭에 추가한다.

 

 

이제 준비가 되었다 Build 버튼을 눌러 빌드를 수행하보자.

 

어라... 추가한 파일 system_stm32f10x.c 에서 에러가 발생한다.



기존에 프로젝트 생성시 만들어진 main.c 에 있는 SystemInit()라는 함수가 중복으로 사용되었기 때문에 발생하는 에러이다.

 

우리는 당연히 STM32F10x의 SystemInit() 함수를 사용할 것이므로 main.c에 있는 SystemInit 함수를 삭제한다.

 

다시 빌드하면... 두둥... 빌드가 완성되었다.

 


이 것으로 ST에서 제공하는 Standard Peripheral Library를 사용할 준비가 다 되었다.

 

 

주저리 주저리 길게 설명하였지만 요약하면 다음과 같다.

 

1. Startup코드에 External Interrupt Handler들을 추가한다. (library에 포한된 startup_md.s를 참조)

2. CMSIS CoreSupport 파일 2개와 STM32F10x_StdPeriph_Driver를 프로젝트에 등록시키고 빌드 속성에 Include path를 추가한다.

3. CMSIS 의 DeviceSupport에서 stm32f10x.h 를 포함한 3개의 파일과, Example파일에서 stm32f10x_conf.h를 포함한 3개의 파일을 프로젝트 폴더로 복사후 등록한다.

4. 빌드속성에서 STM32F10X_MD 및 USE_STDPERIPH_DRIVER 를 추가한다.

5. main.c에서 SystemInit() 함수를 제거한다.

 

지금의 과정을 반복하기가 상당히 까다롭다고 생각되면 생성된 코드를 Skeleton코드로 복사해두고 필요시마다 사용하면 된다.

 

@ github에서 코드를 다운 받을 수 있다.

 

git clone https://github.com/sooya/nucleof103-ex.git