AVR Studio + WinAVR + Procyon Library #2
이전에는 아주 간단한 C 소스를 AVR Studio의 통합환경에서 컴파일하고 시뮬레이션까지 하는 예제를 보였다. 이제는 조금은 세밀한 부분을 살펴 보자. 이 내용들은 WinAVR의 Makefile 의 내용들을 어떻게 수정하는가 하는 부분과 아주 밀접한 관계가 있다.
예제 1> 링커에서 필요한 라이브러리 추가 해주기..
Windows용, DOS용의 컴파일러들은 내가 수학함수, (Square Root, sqrt())같은 함수를 쓸 때, math.h 파일만 포함을 시키더라도 자동적으로 math library를 찾아서 필요한 모듈들을 링크 시켜 준다. 다른 함수들의 경우도 라이브리들도 마찬가지이다. 하지만, WinAVR(gcc)은 이런 작업을 자동으로 해 주지 않는다.
이 과정을 살펴 보자.

위와 같은 프로젝트를 새로 만들고,
[Next] 버튼을 누르고, Debug Platform 을 AVR Simulator, Device를 ATMega128을 선택한다.
아래와 같은 프로그램을 작성하고
#include <stdio.h>
int main(void)
{
int ii;
float aa, bb;
aa = 4.4;
bb= sqrt(aa);
return 0;
}
이것을 컴파일 하면?? 몇몇 경고(warning)와 한 개의 에러가 발생한다.
“undefined reference to ‘sqrt’
이 에러는 math.h 헤더 파일을 포함시키지 않았기 때문에 발생한다. 그러면 #include<math.h> 를 추가하고 컴파일을 해 보자. 역시나 같은 에러가 발생한다..
“undefined reference to ‘sqrt’
그렇다면? 헤더를 포함시켰는데 도? 같은 에러가 발생을 하다니.. 라고 생각을 할 수 있다. 하지만, 앞서 math.h를 포함하지 않은 경우의 경고를 자세히 살펴 보면..

../sqrttest.c:9: warning: implicit declaration of function `sqrt’
라는 부분이 있다. 이 부분에서 차이가 있는 것이다. 앞서 math.h 파일을 포함하지 않았을 때에는 함수의 형태를 선언(declaration)을 하지 않았고, 이번에는 math.h 파일 안에서 함수의 형태가 저장이 되어 있으니, 이 경고는 사라진 것이고, 마지막에 링크를 할려니 sqrt() 함
수와 관련된 부분이 없다는 것이다.
이 경우, 함수가 소스 형태가 아닌 라이브러리 형태로 되어 있는데, 이것은 c:\WinAVR\avr\lib\libm.a 파일이다. 확장명이 a 인 것은 linux 쪽에서 사용하는 방식이 그러려니 하고 넘어가자. 음.. 아니다! WinAVR에서는 라이브러리의 확장명이 a 로 되어 있다고 기억해 두자.. 나중에 써먹을 일이 있을지도 모른다.
배우 송광호씨도, 반칙왕에서 배운 두발차기를 나중에 살인의 추억(맞나??)에서 잘 써먹었다고 하니, 아직은 젊고 머리가 좋을 때, 아니 늙었더라도 한번은 기억을 해 보자..
자.. 그러면 링크 과정에서 어떻게 이 라이브러리를 추가 하는가? 예전이라면 직접 Makefile을 수정을 하겠지만, 이제는 AVR Studio가 있으니 여기서 수정하는 방법을 알려 주겠다.
Makefile을 아주 잘 고칠 수 있다는 사람들은 이 글을 읽는 것을 여기서 중지 해도 된다. 이 첫번째 예제의 목표가 이것인데, 아는 사람은 이 글을 읽는 시간도 아끼는 것이 좋지 않을까?? 그리고 실수(float)를 문자열로 바꾸는 예제가 예제 2번에 있으니 그걸 보고 싶다면 아래로.. 죽~~ 넘어가기 바란다.
프로젝트 윈도우( 용어는 알 수 없다.. 정확히 배우질 못해서..알게 되면 알려 주리라..)에서 프로젝트 이름의 옆에서 마우스 우측버튼(음.. 본인은 예전 과다한 마우스 작업으로 오른쪽 어깨랑 목을 7주일 동안 거의 꼼짝을 못한 적이 있어서 현재 왼손으로 마우스를 쓰고 있기는 한데, 오른 손 잡이를 기준으로 설명하겠다.)을 눌러서 팝업 메뉴가 뜨면,

거기서 [Edit Configuration Options…]를 선택하고,

라이브러리 설정 창에서 libm.a 를 선택한 후, [—->] 버튼을 눌러서 링크할 라이브러리에 추가를 해 준다. [확인]을 누르고 다시 컴파일을 하기 전에 이 옵션이 과연 Makefile을 어떻게 수정했는가를 잠시 살펴 보자.
현재 작업은 C:\WinAVR\my\4answer\sqrttest\ 디렉토리에서 하고 있으며, 소스파일과 hex파일도 이 디렉토리에 생겨난다. 그러나 Makfile은 C:\WinAVR\my\4answer\sqrttest\default 디렉토리에 있다. 현재 있는 Makefile 을 Makefile.default 라는 이름으로 카피를 해 놓고, 툴바에서 Build Active Configuration 버튼 을 누르자.. 이 버튼의 그림은 이 설명서 시리즈 1번에 표시가 되어 있다. F7 을 눌러도 된다. libm.a 라이브러리를 추가하고 난 후라서 성공적으로 hex파일이 생겨난 것을 볼 수 있을 것이다. Build 과정에서 생기는 프로그램의 실행과정을 살펴 보면,
avr-gcc -mmcu=atmega128 sqrttest.o -lm -o sqrttest.elf
라는 부분이 있다 여기서 –l 옵션은 링크할 때 필요한 라이브러리를 지정해 주는 옵션이고, m 은 libm.a 에서 m 을 의미한다. 이것은gcc 에서 사용하는 방법이니, 이렇게 한다 라고 기억을 해 두자..
그리고 나서, 이 디렉토리에 있는 Makefile과 Makefile.default 를 비교해 보자.
왼쪽이 라이브러리 추가 하기 전이고, 오른쪽이 math 라이브러리를 추가한 후 이다 36~37번의 2개의 내용이 추가 되어 있음을 알 수 있다.
자.. 하나의 예제를 통해서 라이브러리를 어떻게 추가하는 지 알아 보았다. 하지만 이 예제를 작성하면서 한가지 사실을 더 알게 되었다. #include<math.h> 를 제거 하더라도, 라이브러리 옵션만 잘 잡아 주면 문제가 없다는 것이다. gcc가 혼자서 libm.a 를 뒤져서 sqrt()함수를 찾는 다는 것이다. 물론 함수의 이름이 implicit하게 선언되었다는 경고(warning)은 발생한다. 이렇게 사용하는 것이 가능은 하지만, 이렇게 하지 않도록 한다. 반드시 기존의 함수를 이용할 때에는 해당하는 헤더파일을 포함하도록 한다.
잘못 든 습관 나중에 고생!!
이말을 기억하면서 말이다.
이제 조금은 더 골치 아픈 예를 하나 살펴 보자.
예제 2>: 실수를 문자열로 바꾸는 예제를 이용해서 디버거도 써보자..
이 문제는 예전에 본인이 실수(floating number)를 문자열로 바꾸어서 LCD에 표시하는 작업을 하다가 해결을 못해서 www.avrfreaks.net 에서 그 답을 찾은 것인데, 일단 프로그램을 다음과 같이 수정해 보자.
주의> 실수(by mistake)로 sqrt() 의 매개변수와 return 값을 double이 아닌 float로 했는데, 그림을 다시 다 캡쳐하는 것이 번거로워서 그냥 쓰기로 했다. 여러분들이 사용할 때 반드시 전부 double로 하기 바란다.
#include <stdio.h>
#include <math.h>
int main(void)
{
int ii;
float aa, bb;
unsigned char ucBuffer[32];
aa = 4.4;
bb= sqrt(aa);
sprintf(ucBuffer,”SQRT(4.4) =[%5.2f]”,bb);
return 0;
}
Build 를 해 보면 잘 된다. 그렇다면 ucBuffer안을 어떻게 확인 할 것인가?? 이제 디버거를 사용해 보자!!
이 글을 읽는 사람들이 전부 같은 study board를 가지고 있다면 소스를 거기에 맞게 작성하겠지만, 사용자 마다 하드웨어의 구성이 틀리고, 시리얼 포트를 쓸 것인지, LCD를 쓸 것인지 모르니.. 이렇게 그냥 버퍼 szBuffer에 문자열을 구성한 후 그 문자열을 확인 해 보는 것이다. 이렇게 하면 디버거 사용법도 익힐 수 있으니… 일거 양득이라고 생각을 하고, 예제를 만들었다.
프로그램 소스 중에서 return 0; 의 줄에 커서를 위치 시키고, 툴바에서 Build and Run 버튼을 누른다.
그러면 노란색 화살표가 main() 함수의 첫번째 줄에 가 있게 된다. 음 그리고 return 0; 앞에 있는 짙은 갈색의 동그라미는 무시하기 바란다.. Break Point 인데 내가 설정을 해 놓기는 했지만, 설명을 하지 않을 것이기 때문이다.

디버거는 프로그램의 모든 과정을 따라 갈 수 있는데 지금의 문제는 szBuffer의 내용을 보는 것이므로, 좀전에 커서를 일부러 return 0; 에 위치시켜 놓았으므로, Run To Cursor (Ctrl+F10) 버튼을 누른다. 그러면 노란색 화살표가 return 0; 으로 옮겨 가게 된다. 이 말은 이 화살표가 있는 곳 까지 프로그램이 수행 되었다는 것이다.
툴 바에서 Toggle Watch Window 를 누른다. 변수들의 값을 보는(watch) 툴을 사용하겠다는 것이다.
그러면 하나의 독립된 창이 뜨게 되는데,
Name 란에 aa, bb, ucBuffer 를 입력한다. aa와 bb 는 초기의 값과 sqrt() 계산 결과이다.
음. 그리고 aa 가 4.400001 로 되어 있는데, 이런 증세에 대해서는 machine epsilon이나 실수를 저장하는 포멧 IEEE-??? 과 관계가 있는 것으로 알고 있는데 혹시 학문적으로 정확히 알고 있는 사람은 알려 주시기를 바란다.
우리가 지금 보고 싶은 것은 ucBuffer[32]의 값(내용)이다. 이 Watch 윈도에서 배열은 앞에 [+]로 표시를 해 놓았으니, 이 심볼을 클릭하면 내용을 보여 준다.

자 내용이 프로그램에서 지정한 출력이 포멧 처럼,
SQRT(4.4) =[… 까지는 맞는데 계산 결과인 bb값을 %5.2f 포멧에 맞춰서 제대로 표현 못하고 있다. 다섯칸을 차지하면서 마지막에 ?만 나온다. 만일 출력되는 실수(float)의 길이를 포멧에서 정하지 않으면, 자동적으로 ? 만 나오게 된다.
이 문제는 라이브러리 옵션과 함께 링크 옵션을 정해 줘야만 해결할 수 있다.
앞서의 1번의 예제에서와 같이, library를 추가해 주면 되는데, 필요한 라이브러리는 libprintf_flt.a 이다.

또한
Custom Option에서

[Linker Options] 에 위와 같은 옵션을 넣어 준다. 이후 다시 시뮬레이션을 해 보면,

원하는 문자열을 얻을 수 있다. 사실 이 글을 적으면서도 저 링커 옵션에 대해서는 확실히 알아 낼 수가 없었다, 내가 가지고 있는 “ATMega128 마스터” 제 1판에서도 이렇게 실수를 sprintf()함수를 이용해서 변환시키는 예제는 없는 것 같으며(모든 예제를 면밀히 살피지는 않았지만 …) 링커의 옵션에 대한 부분에서도 다루고 있지 않다.
그래서 그냥 쓰기로 했다. 사실 이렇게 그냥 쓰는 것에 대해선 나중에 이 부분과 관련되어 문제가 발생할 경우 해결을 못하는 경우가생긴다. 하지만 어쩔 수 없이 그냥 쓰자. 나중에 알게 되면 정리를 하기로 하고 말이다..
그리고 , 이 시뮬레이션을 통해서 디버그를 돌리면서 이 2가지의 예제에 대해서 작업을 하다 보니, 종종 에러가 발생을 한다.
하지만 이것은 이 예제에서 다루고자 하는 내용이 아니므로 이 문제의 해결은 하지 않고서 넘어가겠다.
이 두가지의 예제를 통해서,
1> AVR Studio에서 Makefile 을 쉽게 수정하는 방법을 배웠고!
2> 디버거를 사용하는 것을 배웠고!
3> 실수(float number)를 문자열로 변환할 때 필요한 라이브러리와 옵션들을 알아 보았다.!
To be continued????? Still I don’t now …..