가장 먼저 할일은 AtmelStudio에서 megaOS 프로젝트를 생성하는 것으로부터 시작하겠다. 프로젝트를 만들면 AtmelStudio 7의 경우 자동으로 main.c 파일이 생성되는데 이 파일을 삭제하여 완전히 텅 빈 상태로 만들어 둔다. 이제 링커 스크립트 파일을 만드는것으로부터 코딩을 시작하도록 하겠다. megaOS.lds라는 이름의 파일을 새로 만든 후 다음과 같이 입력한다. OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr") OUTPUT_ARCH(avr:5) MEMORY { flash (rx) : org = 0, len = 32K sram (rw!x) : org = 0x800100, len = 2K } SECTIONS { .text : { KEEP(*(...
지금부터 ATmega328에서 동작 되는 RTOS를 만들어 보겠다. 편의상 이 OS의 이름을 "megaOS"라고 하겠다. ATmega328만을 목표로 OS를 만들겠지만 ATmega128과 같은 다른 ATmega 계열의 MCU는 조금만 수정하면 쉽게 해당 MCU에서 동작시켜 볼 수 있을것이다. 여력이 된다면 실전 과정에서 ATmega뿐만 아니라 다른 종류의 MCU에도 쉽게 포팅할 수 있는 구조를 만들어 볼 생각이다. megaOS의 기본 개념은 "eCos"라는 RTOS에서 가지고 왔다. 그럴리는 없겠지만 혹시라도 megaOS를 상용제품에 적용할 생각이 있다면 eCos 라이센스를 잘 확인하여 문제가 발생하지 않도록 주의하기 바란다. http://ecos.sourceware.org/ 에 들어가면 eCos의 소스..
한때에는 임베디드 시스템 프로그램을 RTOS를 기반으로 개발하는 것이 일반적이었던 때가 있었다. RTOS의 종류로는 VxWorks, pSOS와 같은 고가의 상용 제품과 더불어 uC/OS, FreeRTOS, RTEMS 등 소스가 공개되어 무료 또는 저가의 라이센스 비용만 지불하면 사용할 수 있는것들을 포함하여 그 수가 헤아릴수 없을 정도이다. 그러나, 최근에는 UNIX 계열 OS인 리눅스를 임베디드 시스템의 OS로 적용하는 것이 대세이기 때문에 점차 상용 RTOS는 사양길로 접어들고 있다. 서버 컴퓨터용 OS인 리눅스가 어떻게 임베디드 시스템 OS 시장을 장악하게 되었는지 그 이유를 따져본다면 다음과 같은 이유때문이 아닐까 싶다. 아마, 가장 큰 이유는 리눅스가 무료이기 때문일것이다. 게다가 소스코드까지 ..
입문 과정중에 인터럽트의 개념과 간단한 사용법, 그리고 인터럽트가 발생하는 시점부터 어떤 절차를 거쳐 인터럽트가 종료되는지 설명하였다. 이번 글에서는 Atmel Studio에서 제공하는 인터럽트 코드를 사용하지 않고, 프로그래머가 직접 인터럽트 서비스 코드를 만드는 방법에 대해서 설명하도록 하겠다. 이번 글을 위한 프로젝트는 앞에서 설명한 startup 프로젝트를 그대로 사용하도록 하겠다. 즉, startup.S 코드는 다음과 같다. #include .section .vectors .global _start _start: clr r1 out AVR_STATUS_ADDR, r1 ldi r28,lo8(__stack) ldi r29,hi8(__stack) out AVR_STACK_POINTER_HI_ADDR,..
이번 글에서는 앞에서 설명한 링커 스크립트 파일과 crt0를 응용하여 startup 코드를 직접 만들어 보는 방법에 대해서 설명하도록 하겠다. 먼저 링커 스크립트 파일부터 만들어 보도록 하겠다. OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr") OUTPUT_ARCH(avr:5) ENTRY(_start) MEMORY { flash (rx) : org = 0, len = 32K sram (rw!x) : org = 0x800100, len = 2K } SECTIONS { .text : { *(.vectors) *(.text) . = ALIGN(2); *(.text.*) . = ALIGN(2); } > flash .data : { __data_start = .; *(.d..
C 프로그램에서는 반드시 main() 함수가 하나 있어야 한다. 그 이유는 C 프로그램은 main() 함수로부터 시작되기 때문이다. 즉, 프로그램의 진입점으로 규정했기 때문에 반드시 main() 함수가 있어야 한다. 만약 main() 함수가 없으면 프로그램이 어디서부터 시작되어야할지 모르기때문에 컴파일러는 main() 함수가 없다고 에러를 발생하게 된다. 그러나 이미 앞에서 링커 스크립트를 설명할 때 main() 함수가 실행되기 이전에 처리해 두어야 하는 내용들이 있다고 하였다. 즉, ROM에 있는 .data 섹션을 RAM으로 복사해주고, .bss 섹션을 0으로 초기화 해주어야 한다. 이러한 일련의 초기화 코드를 crt0라고 통칭한다. AVR 프로그램에 들어있는 crt0 코드는 어떻게 생겼는지 분석해 보..
일반적으로 컴퓨터에서 실행되는 어플리케이션 프로그램을 C나 C++같은 언어로 개발할 때에는, 만들어진 실행파일이 물리적으로 어디에 저장되어 있는지, 실행될 때 메모리의 어느 주소에 어떤 구조로 로드되는지 몰라도 프로그램을 실행하는데 전혀 문제될 것이 없다. 하지만, 임베디드 시스템 프로그램을 개발할 때에는 얘기가 조금 달라진다. 프로그래머는 CPU에 따른 메모리 맵이 어떻게 생겼는지 알아야 하고, 이러한 메모리 맵에 맞도록 실행파일을 만들어야 한다. 이번 글에서 설명하고자 하는 링커 스크립트라는 것이 바로 CPU의 메모리 맵에 따라 실행파일을 어떻게 생성할 것인지를 알려주게 된다. 이 블로그의 입문과정 첫번째 글인 "hello, world"에서 컴파일 과정을 전체적으로 간단히 설명하였는데, 마지막 단계가..
Flash 메모리 프로그램의 첫번째 단계는 'Erase'라고 bootloader 프로젝트에서 이미 언급하였다.Bootloader 프로그램은 Self-programming 방법이므로 page 단위로 erase를 하였으나, ISP에서는 page 단위의 erase 명령어가 없고, chip erase만 제공한다.따라서 ISP 프로젝트에서는 항상 chip erase만 사용하게 된다. #define CHIP_ERASE0 0xAC #define CHIP_ERASE1 0x80 static void erase_chip(void) { cmd_data[0] = CHIP_ERASE0; cmd_data[1] = CHIP_ERASE1; cmd_data[2] = 0; cmd_data[3] = 0; isp_cmd(); _delay_..
이번 글에서는 target CPU의 flash 메모리에 쓰여져 있는 데이터를 읽어 오는 기능에 대해서 설명하도록 하겠다. Bootloader 프로젝트에서는 bootloader 프로그램과 application 프로그램이 동일한 CPU에 존재하기 때문에, 프로젝트가 시작되는 시점에는 application 영역에 의미있는 데이터가 쓰여져 있지 않다. Bootloader program 영역 이외에는 모든 데이터가 0xFF로 읽히게 된다. 따라서 bootloader 프로젝트에서는 flash 메모리 write 기능부터 구현하게 되었다. 그리고, write 된 데이터가 제대로 쓰여졌는지 확인하기 위하여 ISP 장비를 이용하면 되니까 크게 문제 될것이 없었다. 하지만, ISP 프로젝트의 경우에는 write 기능부터 구..
ISP 프로젝트를 위한 회로 구성을 완료한 다음 회로가 제대로 연결되었는지 확인하는 단계를 반드시 거쳐야 한다.먼저 확인할 내용은 PC와 UART가 제대로 연결되었는지, ISP 프로그래머와 target CPU가 SPI로 제대로 연결되었는지 확인을 하여야 한다.입문 과정의 SPI 프로젝트를 이용하여 UART와 SPI 통신 기능을 검증해 보도록 하겠다. static void read_device_info(void) { portb->port &= ~SPI_SS; enter_program_mode(); get_signature(); portb->port |= SPI_SS; } static void port_init(void) { /* SPI port init */ portb->ddr = (SPI_SS | SPI..