키보드를 만듭시다. 어때요~ 참 쉽죠? (15)

나빌레라의 이미지

부트 로더를 분리하기로 마음 먹었기에, 키보드 펌웨어는 두 개가 나와야 한다. 부트 로더와 메인 펌웨어. 그래서 makefile등 빌드에 관계된 파일을 수정해서 바이너리를 분리해야 하고 소스 코드에서도 condition flag로 부트 로더 전용 코드와 메인 펌웨어 전용 코드, 그리고 공통 코드를 식별해야 한다.

컨디션 플래그 설정

부트 로더와 메인 펌웨어 빌드에 둘 다 참여하는 소스 파일 중에서 파일의 일부는 부트 로더용 함수고 나머지는 메인 펌웨어용인 파일이 있다. 대표적으로 main.c 파일이다. 이런 파일에는 컨디션 플래그로 함수를 구분해야 빌드 단계에서 불필요한 바이너리 생성을 막을 수 있다.

int main(void)
{
    HAL_Init();
    SystemClock_Config();
 
    Hal_gpio_init();
    Hal_uart_init();
 
#ifdef LOADER
    if (CheckBootMode())
    {
        debug_printf("BootLoader..\n");
        App_msc_Init();
    }
    else
    {
        debug_printf("Jump to the MainFW..\n");
        Jump(MAIN_FW_BADDR);
    }
#else
        App_hid_Init();
 
        while (USBD_HID_Is_Configured() != true)
        {
            debug_printf("Waiting for USBHID configuration\n");
            USBD_Delay(1000);
        }
 
        Kernel_Init();
        debug_printf("Navilos Start..\n");
        Kernel_start();
#endif
 
    while (1) { }
}

main() 함수를 보면 #ifdef LOADER로 분리한 부분과 그렇지 않은 부분이 있다. #ifdef-#else-#endif로 둘러 쌓인 나머지 코드는 부트 로더와 메인 펌웨어 양쪽에서 모두 쓰는 함수다. 부트 로더도 기본적인 하드웨어 초기화와 GPIO, UART 초기화를 한다. 부트 로더도 UART 출력을 하기 때문이다. 그리고 이제 부트 로더와 메인 펌웨어가 갈린다.

부트 로더일 때는 App_msc_Init() 함수를 호출해서 MSC를 초기화한다. 따라서 호스트 입장에서 키보드는 키보드가 아니라 대용량 저장 장치로 보인다. 대용량 저장 장치 상태에서 부트 로더가 파일을 받아서 키맵을 업데이트하거나 펌웨어를 업데이트하는 것이다.

메인 펌웨어일 때는 App_hid_Init() 함수를 호출해서 HID를 초기화한다. 따라서 호스트는 키보드로 인식해서 원래 키보드라는 디바이스의 목적에 맞는 동작을 한다. 그리고 메인 펌웨어일 때는 Kernel_Init() 함수와 Kernel_start() 함수를 호출해서 나빌로스 RTOS를 사용한다. 그렇기 때문에 메인 펌웨어일 때는 나빌로스 커널 코드가 바이너리에 포함되어야 한다.

Makefile 변경

Makefile에도 C언어 코드의 #ifdef처럼 컨디션 플래그를 쓸 수 있다. 그래서 Makefile을 따로 만들지 않고 이미 있는 Makefile만 수정해서 부트 로더와 메인 펌웨어를 각각 만들 수 있다.

ifdef LOADER
LINKER_SCRIPT = $(PRJ_HOME)/build/$(BOARD)/loader.ld
else
LINKER_SCRIPT = $(PRJ_HOME)/build/$(BOARD)/$(SOC).ld
endif
 
MAP_FILE = $(OUTPUT_DIR)/$(OUTNAME).map
SYM_FILE = $(OUTPUT_DIR)/$(OUTNAME).sym
 
ASM_SRCS = $(wildcard $(PRJ_HOME)/boot/$(BOARD)/*.S)
ASM_OBJS = $(patsubst $(PRJ_HOME)/boot/$(BOARD)/%.S, $(OUTPUT_DIR)/%.os, $(ASM_SRCS))
 
VPATH = boot/$(BOARD)               \
        hal/$(BOARD)                \
        hal/$(BOARD)/drivers        \
        lib                         \
        lib/$(ARM_ARCH)             \
        app/$(BOARD)                \
        app/$(BOARD)/USB            \
        app/$(BOARD)/HID            \
        app/$(BOARD)/MSC            \
        app/                        \
        kernel
 
C_SRCS  = $(notdir $(wildcard $(PRJ_HOME)/boot/$(BOARD)/*.c))
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/hal/$(BOARD)/*.c))
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/hal/$(BOARD)/drivers/*.c))
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/lib/*.c))
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/lib/$(ARM_ARCH)/*.c))
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/app/$(BOARD)/*.c))
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/app/$(BOARD)/USB/*.c))
ifdef LOADER
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/app/$(BOARD)/MSC/*.c))
else
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/app/$(BOARD)/HID/*.c))
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/app/*.c))
C_SRCS += $(notdir $(wildcard $(PRJ_HOME)/kernel/*.c))
endif

Makefile 전체 코드는 길기 때문에 일부만 가져왔다. 위 코드를 보면 ifdef LOADER로 컨디션 플래그를 사용했다. 그래서 부트 로더로 빌드 할 때는 링커 스크립트로 loader.ld를 쓰고 메인 펌웨어로 빌드 할 때는 원래 쓰던 링커 스크립트를 쓴다.

아래에 보면 부트 로더일 때만 app/$(BOARD)/MSC/.c를 소스 파일 목록에 넣고 메인 펌웨어일 때는 app/$(BOARD)/HID/.c로 HID 관련 소스 코드를 포함한다. 그리고 메인 펌웨어일 때만 kernel/*.c로 RTOS 커널 소스 코드를 포함한다.

링커 스크립트 분리

링커 스크립트에서도 컨디션 플래그를 쓸 수 있을 것 같은데 방법을 찾지 못했다. 그래서 그냥 링커 스크립트는 두 개를 만들기로 했다. 분명 방법이 있을텐데.. 나중에라도 찾으면 다시 하나로 합칠 생각이다. 일단 지금은 두 개로 간다.

링커 스크립트를 두 개로 나눈 이유는 딱 하나다. 펌웨어 바이너리의 리로케이션 시작 주소가 다르기 때문이다. 부트 로더와 메인 펌웨어가 저장되는 플래시 메모리의 페이지 오프셋 주소가 다르기 때문이다.

그래서 링커 스크립트 다른 부분은 다 똑같고, 아래 코드만 다르다.

먼저 부트 로더의 링커 스크립트

/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 64K
}

사실 원래 있던 링커 스크립트와 같다. 부트롬 입장에서는 부트 로더가 정상적인 펌웨어로 보여야 하니까.

MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx)      : ORIGIN = 0x8005000, LENGTH = 43K
}

위가 메인 펌웨어용 링커 스크립트 파일에서 다른 부분이다. 정확히는 FLASH 메모리 영역 지정 값이 다르다. 시작 주소값이 0x8005000이다. 왜 이렇냐면 내가 부트 로더 크기로 20KB를 할당했기 때문이다. 20KB을 십육진수로 하면 0x5000이다. 그리고 길이도 64KB에서 20KB만큼 빼고 키맵 영역으로 1KB를 또 빼서 43KB로 했다. 이러면 빌드할 때 메인 펌웨어 바이너리 크기가 43KB를 넘어가면 빌드 과정에서 에러나 나므로 디버그하기 조금 더 편하다.

HAL 코드에 벡터 테이블 오프셋 수정

이부분은 STM32F103 HAL 코드에 종속된 수정 사항이다. 만약 HAL을 쓰더라도 STM32의 제조사가 배포하는 HAL을 쓰지 않는다면 작업해야 하는 내용이 다를 것이다.

제조사 제공 HAL 코드 중에 system_stm32f1xx.c 파일이 있다. 이 파일 내용을 쭉 훑어 보다보면 아래 코드가 보인다.

#define VECT_TAB_OFFSET  0x00000000U /*!< Vector Table base offset field. This value must be a multiple of 0x200. */

이 코드를 아래처럼 수정한다.

#ifdef LOADER
#define VECT_TAB_OFFSET  0x00000000U /*!< Vector Table base offset field. 
                                  This value must be a multiple of 0x200. */
#else
#define VECT_TAB_OFFSET  MAIN_FW_ENTRY_OFFSET
#endif

MAIN_FW_ENTRY_OFFSET 대신 값을 직접 써도 된다. 다만 저렇게 해 놓는 것이 나중에 숫자를 바꿀일이 있을 때 소스 코드 전체에 반영되므로 좋은 습관이다. MAIN_FW_ENTRY_OFFSET은 MemoryMap.h 파일에 내가 만든 값이다.

#define EFLASH_PAGESIZE     0x400
#define MAIN_FW_PAGENUM      20
#define MAIN_FW_ENTRY_OFFSET (MAIN_FW_PAGENUM * EFLASH_PAGESIZE)

결과적으로 링커 스크립트에서 적었던 0x5000이다. 다 같은 값을 필요한 곳에 수정하고 추가하는 것이다.

빌드

컨디션 플래그로 LOADER를 지정했으므로 빌드할 때,

make LOADER=1

이렇게 빌드 옵션으로 컨디션 플래그를 지정하고 빌드하면 로더가 빌드된다. 그리고 원래 빌드하던 것처럼 아무 옵션 없이 빌드하면 메인 펌웨어가 빌드된다. 부트 로더는 처음에 MSC 기능 추가하기 전에 펌웨어 다운로드 했던 것처럼 UART를 통해서 다운로드해야 한다. 부트 로더가 동작하고 나면 이제부터는 UART 장비 도움없이 펌웨어를 업그레이드 할 수 있다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.