분류없음2013.03.13 17:23

   

문제점

  1. Struct에 있는 값을 c#에서 사용하기 위해서는 ref class 또는 ref struct를 만들어야 된다.
    아직 사용법이 미숙하여 해당 변수의 값들을 일일이 복사하여야 한다.
  2. 배열로 선언된 부분을 C#에서 사용하기 위해서는 array<byte>^ 등과 같이 변경하여야 한다.
    1. 굉장히 불편함
  3. c 파일은 dll에 포함이 안되는 거 같다. cpp 파일로 변경하면 오케이
  4. Int32는 예약어로 사용이 불가능 하다. (ATPW 개발 시 사용한 SW_NORMAL도 예약어
  5. win32파일을 변경하면서 개발하기에는 디버깅이 c++ Cli쪽에서 win32쪽에 붙지 않는다.. 아마 해당 내용은 더 확인이 필요하겠지만

       

   

NICanLIB에는 event, delegate 등의 기능도 구현되어 있음.

   

   

   

  1. vs2012에서 WIN32 프로젝트를 이용하여 기존의 c 파일을 컴파일
  2. c++ cli에서 사용할 함수 또는 변수는 WIN32PROJECT1_API를 붙인다.
  3. C++ CLI프로젝트에서 해당 프로젝트를 reference

       

  4. h파일들은 link를 통하여 폴더에 넣고, 해당 프로젝트(win32)의 header 파일의 위치를 추가한다.
  5. 아래와 같이 pragma를 사용하여 해당 header 파일들을 include 한다.

// ClassLibrary1.h

 

#pragma once

 

using namespace System;

using namespace System::Diagnostics;

using namespace System::Runtime::InteropServices;

 

 

#pragma managed(push, off)

 

#include "Win32Project1.h"

#include "define.h"

#include "initStruct.h"

 

#pragma managed(pop)

6. 실제 사용하는 winform 프로그램에서는 C++ CLI의 DLL만 복사하기 때문에 win32의 dll도 복사하여야한다. 쉽게 하는 법은 dll을 winform 프로젝트에서 link로 파일 등록하고, 해당 파일의 속성에서 항상 복사로 하면 된다.

  

  

   

신고
Posted by Frys
Programing/Matlab2010.06.15 17:05
 예전에 작성하였던 코드지만 블로그를 살려보고자 하는 마음에 정리를 해봅니다.

이번 작업을 하게 되면...
1. 매트랩의 External Interface 기능중에 시리얼 기능을 통하여 PC외부의 장치(MCU)와 시리얼 통신(UART)을 하여 데이터를 주고 받을 수 있습니다. 굳!!!
2. 매트랩의 실시간 그래프 기능을 이해할 수 있습니다. 사실 이해는 아니고 그냥 경험정도^^;;
3. 매트랩에서 전달 받은 데이터를 이용해서 자유롭게 매트랩 코드를 이용하여 테스트 할 수 있습니다. (FIR과 FFT등 엄청난 기능을...ㄷㄷㄷ)

테스트 결과는 다음과 같습니다.



매트랩에서 어떻게 MCU에 있는 데이터를 얻어올 수 있을까요..
PC에서 데이터를 가장 많이 사용하는 방법은 시리얼 통신이죠.
그럼 시리얼 통신을 해야되는데요..
시리얼을 혹시 모르시는 분들은 여기를.. http://en.wikipedia.org/wiki/Serial_port

그럼 어떻게 해야되나요..

일단 데이터를 주고 받는 프로토콜?? 이라고 하긴 조금 그렇지만 형식이 필요합니다.
일반적으로 시리얼 통신 패킷은 STX, ETX, CRC 체크 등등이 있지만 우린 필요없죠.. 적어 지금은요 ㅎㅎ

그럼 저만의 프로토콜을 만듭니다.
저는 다음과 같이 시작은 문자열 'a', 끝은 문자열 'b'를 넣고 그 사이에 데이터를 ASCII 값으로 넣습니다. 각 데이터간의 구분은 ','를 이용하구요. 
ASCII 가 궁금하신 분들은 다음을 참고하세요. http://en.wikipedia.org/wiki/Ascii

그럼 예를 볼까요? MCU에서 시리얼 데이터를 다음과 같이 PC로 보내게 됩니다.
a100,200,120b

해석해보면... 가장 처음에 100을 넘기고 그담에 200 그담에 120을 넘기게 되네요!
코드는 다음과 같이 아주 쉽게 작성할 수 있겠죠?

MCU 측의 코드는 다음과 같습니다.

adc에서 값을 읽어온 데이터가 adcbuff[3]라는 공간에 저장되게 된다면..
char adcbuff[4];
char sendbuff[80];

// 어디선가 adc 버퍼를 갱신하겠죠? 

char datasend(){
  sprintf(sendbuff, "a%d,%d,%db", adcbuff[0], adcbuff[1], adcbuff[2]);
  uartsend(sendbuff, strlen(sendbuff));
}



sprintf함수는 printf의 기능을 변수에 만들어 주는 기능을 하구요. stdio.h 를 추가하셔야 됩니다.
uartsend함수는 uart를 통하여 sendbuff에 있는 내용을 전송합니다. 

좋습니다. 이제 준비가 되었어요.


이제는 MATLAB 쪽의 코드를 볼까요?
전체 코드는 다음과 같습니다.
COMPORT = 'COM2';
s = serial(COMPORT, 'BaudRate', 57600, 'Timeout',0.002);
fopen(s)


f = figure(1);

x = 1:100;
y_angle = zeros(1,100);
y_acc = zeros(1,100);
y_gyro = zeros(1,100);

figure(f)

subplot(3,1,1)
grid on
axis([0 100 -100 100])
lh=line(x,y_angle,...
            'marker','.',...
            'markersize',5,...
            'linestyle','-');
subplot(3,1,2)
grid on
axis([0 100 -100 100])
lj=line(x,y_acc,...
            'marker','.',...
            'markersize',5,...
            'linestyle','-');
subplot(3,1,3)
grid on
axis([0 100 -100 100])
lk=line(x,y_gyro,...
            'marker','.',...
            'markersize',5,...
            'linestyle','-');

for i = 1:inf
    data=''; 
    while isempty(strmatch('a',data)) 
    data = fscanf(s); 
    end

    idn = data;
    stringlength = size(idn,2);

    clear value;
    clear cnt;
    
    
    if(idn(1) =='a')
        if(idn(stringlength) =='b')
            packet = idn(2:stringlength-1);
            [value cnt] = string2valuearray(packet,',') ; 


    y_angle = [y_angle(2:100) value(1)]; % <- new data
    y_acc = [y_acc(2:100) value(2)]; % <- new data
    y_gyro = [y_gyro(2:100) value(3)]; % <- new data
    set(lh,...
        'xdata', x,...
        'ydata', y_angle);
    pause(0.001);
    set(lj,...
        'xdata', x,...
        'ydata', y_acc);
    pause(0.001);
        set(lk,...
        'xdata', x,...
        'ydata', y_gyro);
    pause(0.001);
    
        end
    end
end
    
fclose(s)

serialTest.m



코드는 매우 간단합니다.
라인 1에서 PC의 포트의 번호를 입력하시고, serial 함수를 통해서 포트를 열게 됩니다.
자세한 serial함수의 내용은 help 고고싱!
현재는 timeout 기반으로 시리얼 값을 가져오기 때문에 조금 문제가 있지만 serial 함수에서 end 문자열을 지정한다던지 해서 해결 할 수 있습니다.
한마디로 아직 완성된 코드는 아니라는거죠 ㅠㅠ  버그는 많습니다.. ㄷㄷ

라인 13부터 35까지는 실시간 그래프를 그리기 위한 준비 작업입니다. 자세한 내용은 help 고고..

그다음 37에서 부터 74까지 무한 루프를 돌면서 그래프를 그리게 되죠..
실제 그래프를 그리는 부분은 59~70까지고, 50~58까지는 파싱과 데이터 준비 입니다.

이렇게 하면 3개의 데이터를 그래프로 확인할 수 있습니다.
아직 버그는 조금 있지만 그래도 간단히 테스트 용으로는 쓸만했습니다^^
중간에 그래프 보는것을 중단하고 싶으시면 콘솔창을 클릭하시고 Ctrl+C를 클릭하시면 됩니다~


-----------------------------------------------------------------------------------------------------
- 수정 : 2011-08-21, string2valuearray 부분 추가합니다^^;
-----------------------------------------------------------------------------------------------------


function [value, cnt] = string2valuearray(string)

stringlength = size(string,2);

if(string(1) =='a')
    if(string(stringlength) =='b')
        packet = string(2:stringlength-1);
          
        cntComma = size( strfind(packet, ','), 2);
        cnt = cntComma + 1;
        
        
        value = zeros(1, cntComma + 1);
        
        for i = 1 : 1 : cntComma+1
            [a packet] = strtok(packet, ','); %#ok<STTOK>
            value(i) = str2double(a);
        end
        
        
    end
end

string2valuearray.m


시리얼로 받은 내용을 파싱해주는 부분입니다~
이 부분을 빠트렸었네요 ㄷㄷㄷ


신고
Posted by Frys
분류없음2010.02.24 21:07
분류없음2010.02.21 15:21

티스토리에 글을 쓰면 트위터로 간다는데.. 진짜일까요?
지금까지는 잘안되는데.. 어떻게 해야되는걸까요?



유튜브 동영상 삽입 테스트!
제가 좋아하는 크랙 데빗의 Seven Days~
신고
Posted by Frys
Activity/일상2009.12.30 01:20










iPhone 에서 작성된 글입니다.
신고

'Activity > 일상' 카테고리의 다른 글

도가니탕집에서..  (0) 2009.12.30
Posted by Frys
분류없음2009.04.24 11:00

 http://cafe.naver.com/autoset.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=402

   

   

   

버전 관리 프로그램인 SVN (Subversion)을 설치하는 방법을 소개합니다.

   

SVN 에 대해 궁금하신 것이 있다면, 네이버나 기타 검색엔진을 통해 검색해보시면 관련 자료를 찾을 수 있을 것으로

   

생각합니다. (CVS 의 단점을 보완하여 만들어진 것이다 보니 CVS 와 사용법에 있어서 기초적인 것은 같습니다.)

   

   

1. SVN 서버 설치하기
 

   

   

우선 서브버전(앞으로 SVN이라고 합니다) 서버를 설치하기 위해

   

http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91 에 접속합니다.

   

그리고 최신 버전을 다운로드 받습니다.

   

이 글을 작성하는 시점의 최신버전은 1.4.0 입니다.

   

   

다운로드 받은 svn-1.4.0-setup.exe 파일을 더블 클릭해서 실행합니다.

   

   

   

SVN 을 설치하겠냐는 질문이지요? [예] 를 눌러 설치를 계속합니다.

   

   

같은 질문 왜 두번 하는지 모르겠지만, NEXT 를 눌러 설치를 시작해봅니다.

   

   

SVN 에 대한 라이센스 정보를 읽어보시고, 동의함(I accept the agreement) 에 체크하시고,

   

Next 버튼을 클릭합니다.

   

   

간략하게, SVN 정보가 나오고, NEXT 를 누릅니다.

   

   

SVN 서버를 오토셋 설치 폴더 아래 Server 에 설치합니다.

   

여러분이 원하는 폴더에 설치하시면 됩니다.

   

그리고 Next 버튼을 클릭합니다.

   

   

SVN 프로그램 폴더명입니다.

   

그대로 두고, NEXT 를 누릅니다.

   

참고로... 굳이 생성하지 않아도됩니다. (실행할 수 있는 아이콘이 생성되지는 않습니다)

   

   

이 역시, 생성하지 않아됩니다만, 기본 사항에 체크하겠습니다.

   

앞 과정과 현재 나온 과정에서 생성하는 것은 관련 정보를 볼 수 있는 문서(Document)에 대한

   

아이콘 만들기이므로, 필요한 경우에만 체크하시면 됩니다.

   

   

설치되는 장소 확인하시고, Install 버튼을 눌러 본격적(?)으로 설치를 시작합니다.

   

   

   

파일이 복사되고...

   

   

윈도우 95, 98, 밀레니엄 사용자의 경우 Autoexec.bat 파일에 SET .... 부분을 추가하라는 안내가

   

표시되고 있습니다.. 윈도우 2000, XP 사용자는 신경쓰시지 않으셔도 됩니다.

   

NT 계열의 경우, 자동으로 SVN 설치 폴더가 PATH 로 잡혀서 어디서든 svn 명령이 실행되게 됩니다.

   

무슨 말인지 모르면 통과!!

   

   

Finish 를 눌러 설치를 종료합니다.

   

   

   

2. SVN 저장소 만들기 & 서버 시작

   

   

SVN  서버를 통해 버전 관리를 할 프로그램들이 저장되는 폴더를 생성합니다.

   

오토셋 설치폴더 아래 svn_data 라는 폴더로 생성하겠습니다.

   

여러분이 원하시는 장소에, 원하시는 폴더 명으로 생성하시면 됩니다.

   

다만, 생성하신 경로와 폴더명은 반드시 기억하셔야 합니다.

   

   

   

   

   

--- 여기서부터 // 표시가 있는 부분까지는 이후 소개되는 토토이즈 SVN 에서 쉽게하실 수 있는 부분입니다 ---

   

[시작] - [실행] 으로 가신 후, cmd 를 입력합니다.

   

그리고 svn_data 가 있는 폴더로 이동한 뒤,

   

svnadmin create --fs-type fsfs [생성할 저장소명] 을 입력합니다.

   

여기서는 svnadmin create --fs-type fsfs autosetOrga 라고 입력하였습니다.

   

즉, autosetOrga 저장소를 생성하는 것이고 파일시스템 저장소를 사용한다는 의미입니다.

   

생성된걸 확인하기 위해, svn checkout file:///D:/AutoSet/svn_data/autosetOrga 를 실행해봅니다.

   

체크아웃된 리비전 0. 이라고 나오면 정상적으로 체크아웃됨을 알 수 있습니다.

   

// --- 여기까지...

   

svnserve -d -r [저장소경로] 라고 입력함으로써 SVN 서버를 가동합니다.

   

여기서는 svnserve -d -r D:\AutoSet\svn_data 라고 입력하였습니다.

   

참고사항 : svnserve 명령은 어떠한 폴더에서 실행하든 관계없습니다.

   

주의사항 : svnserve 명령 이후, 아무런 상태변화는 없게 됩니다. 이 상태를 유지하고 계셔야 SVN 서버가 작동하게 됩니다.

   

   

위 화면은 토토이즈 SVN 을 사용하지 않고 직접 체크아웃을 하는 방법 중, 네트워크를 통한 체크아웃 방법입니다.

   

이런 방법도 있다는 사실만 알고 넘어가시면 됩니다.

   

   

   

3. SVN 사용자 추가하기 (인증 부분)

   

   

저장소 루트\추가한 저장소 폴더(여기서는 autosetOrga)\conf\passwd 파일을 EditPlus 나 메모장으로 엽니다.

   

파일의 설명에도 써있듯이 매우 간단한 방법으로 인증 정보를 기입하시면 됩니다.

   

아이디 = 비밀번호 형태로 줄 단위로 입력하시면 됩니다.

   

kinor = autoset 이라고 입력하였으므로, 아이디는 kinor 이 되고, 비밀번호는 autoset 이 됩니다.

   

단, 주의 할점은 [users] 섹션 라벨 이후에 입력해주셔야 합니다.

   

일종의 INI 파일 형태로 보시면 됩니다.

   

   

그리고, 인증 정보를 구성하였으니 그 정보를 실제로 써야겠지요?

   

anon-access = read 라고 된 것을 anon-access 를 none 로 변경합니다.

   

이 설정은 익명 사용자의 접근시 읽기를 허용한 것을 허용하지 않는 것으로 설정을 변경하는 것입니다.

   

auth-access = write 라는 것은 인증 받은 사용자의 경우, 쓰기를 허용한다는 설정이 됩니다.

   

password-db 부분은 앞서 사용자를 추가한 패스워드 정보가 있는 파일의 위치를 설정합니다.

   

기본 값으로 놔둡니다.

   

그리고 realm = 에는 이 저장소의 인증시 나오는 타이틀을 입력해줍니다.

   

참고 : 그룹 사용자로 묶고자 한다면 authz-db 의 주석을 해제하고, authz 파일을 수정하면 됩니다.

   

이렇게 사용자 인증 정보 구성까지 마쳤습니다.

   

   

   

4. 거북이 SVN(Tortoise SVN) 설치하기

   

   

http://tortoisesvn.tigris.org/ 에 접속하여, 최신 버전의 토토이즈 SVN 을 다운로드 받습니다.

   

download page 를 클릭해서 다운로드 페이지로 이동합니다.

   

   

아직까지는 대부분의 PC 가 32비트이고 소프트웨어도 32비트 체제에서 만들어지고 있기 때문에...

   

다운로드 받을 파일들은 32 비트에 있는 파일들입니다.

   

토토이즈 SVN 설치파일과 아래 쪽으로 내려가서 한국어 언어팩을 다운로드 받습니다.

   

   

   

다운로드 받은 토토이즈 SVN 설치파일을 더블클릭해서 실행합니다.

   

   

NEXT 버튼을 눌러 설치를 시작합니다.

   

   

라이센스를 읽어보시고, 동의 함에 체크후, Next 버튼을 누릅니다.

   

   

역시.. Next 버튼을 누릅니다.

   

   

Install 버튼을 눌러 설치를 시작합니다.

   

   

파일 복사가 시작되고...

   

   

설치가 완료됩니다.

   

Finish 를 눌러 설치를 종료합니다.

   

   

재 시작을 권유하는 메시지가 나옵니다만,

   

우리가 설치할 프로그램이 더 있으므로 NO 버튼을 과감히 눌러줍니다.

   

   

다음으로, 한국어 언어팩을 설치합니다.

   

다운로드 받은 파일을 더블클릭합니다.

   

   

설치 버튼을 눌러 설치를 시작합니다.

   

   

순식간에 설치가 되는 바람에 파일 복사 장면을 놓쳐버렸네요.

   

마침을 눌러, 패치도 완료합니다.

   

   

자, 이제 아무대서나..  (탐색기나 바탕화면에서..)

   

마우스 오른쪽을 찍! 클릭합니다.

   

그러면 TortoiseSVN 이라는 메뉴가 생긴 것을 확인할 수 있습니다.

   

어라?? 영어로 나오네요?

   

한국어로 보는 것이 편하겠죠?

   

Settings 를 눌러 언어를 변경합니다.

   

   

한국어를 선택하시고, [확인] 버튼을 누릅니다.

   

   

다시 찍! 클릭해보면 이젠 한국어로 잘 나옵니다.

   

   

   

5. 체크아웃 받기 / 파일 추가 / 업데이트 / 커밋하기

   

   

빈 폴더 또는 어떤 폴더에 들어가서 마우스 오른쪽을 누르시고,

   

SVN 체크아웃을 클릭합니다.

   

   

그리고 저장소 URL 에 svn://여러분의 IP주소/저장소명을 입력합니다.

   

svn://127.0.0.1/저장소명을 입력하셔도 됩니다.

   

그리고 최신 리비전에 체크된 걸 확인하시고, [확인] 버튼을 누릅니다.

   

   

   

그러면, SVN 서버 접속을 위해 ID와 암호를 묻게 됩니다.

   

passwd 파일에 추가한 정보를 입력해줍니다.

    

   

인증이 완료되고, 체크아웃이 됩니다.

   

아직 저장소에 저장한것이 없기 때문에 체크아웃을 해도 생성되는 파일은 없습니다.

   

   

   

'오토셋 사용자 설명서.txt' 파일을 생성하고,

   

마우스 오른쪽을 누른 다음 TortoiseSVN - 추가를 눌러 SVN 서버에 파일을 추가합니다.

   

   

   

   

   

   

그리고 SVN 커밋을 눌러 SVN 서버로 전송합니다.

   

   

전송시, 간단한 코멘트를 남길 수 있습니다.

   

확인 버튼을 눌러 커밋합니다.

   

   

   

아래와 같이 내용을 수정하고....
 

   

   

   

혹시 모를 다른 사용자에 의한 수정을 확인하기 위해 업데이트를 한번 해주고...

   

사실.. 혼자 쓰기 때문에 굳이 업데이트 할 필요가 없겠지요...

   

   

수정 된 내용에 대해 간략히 메모하고.. (안쓰셔도 됩니다)

   

확인을 눌러 수정사항을 서버에 적용합니다.

   

   

그러면,리비전 2라는 것을 확인하실 수 있습니다.

   

버전이 한단계 올라간것이지요..

   

   

파일을 선택하고, 수정한 사람 보기를 눌러, 조회할 리비전 범위를 입력 후 확인 버튼을 누르면...

   

   

   

아래아 같이 각 버전별 수정자와 내용을 확인 할 수 있습니다.

   

   

   

이상으로 SVN 설치와 기본 사용법 소개를 마칩니다..

   

시간되면... SVN 서버와 오토셋을 연계하여 사용하는 방법을 올려보겠습니다.. (스샷 찍느라 힘이 빠쪘네요..)

 

   
 

  

 
   

http://www.pyrasis.com/main/SVNSERVEManager

SVNSERVE Manager

 

Windows용 Subversion의 svnserve.exe 매니져입니다. svnserve.exe는 Subversion의 svn:// 프로토콜 서버입니다. svnserve.exe를 편리하게 사용할 수 있도록 도와줍니다.

  • 트레이 아이콘으로 시작/정지 상태표시
  • 시스템 시작시 자동실행
  • 서비스(Service) 설치 및 실행

Windows용 Subversion이 설치되어 있어야 사용가능 합니다.

 

ZIP 파일로 배포되는 Subversion을 사용하고 있는 경우 svnserve.exe를 찾는 창이 뜨게 되는데 svnserve.exe를 맨 처음 한번만 찾아주면 경로가 저장되기 때문에 이후에는 찾는 창이 뜨지 않습니다.

 

서비스 모드는 Subversion 1.4 이상의 버전에서만 지원됩니다.

 

1. Screenshot #



트레이 아이콘

2. Download #

New Version
SVNManager-1.1.1-Setup.msi Version 1.1.1, Setup Package, 2007.8.3 New
SVNManager-1.1.1.zip Version 1.1.1, 2007.8.3 New
프로그램을 잘 사용하셨다면 감사의 말씀정도는 남겨주는 센스!

Old Versions
SVNManager-1.1.0-Setup.msi Version 1.1.0, Setup Package, 2006.12.15
SVNManager-1.1.0.zip Version 1.1.0, 2006.12.15
SVNManager-1.0.5-Setup.msi Version 1.0.5, Setup Package, 2006.12.11
SVNManager-1.0.5.zip Version 1.0.5, 2006.12.11
SVNManager-1.0.4-Setup.msi Version 1.0.4, Setup Package, 2005.7.28
SVNManager-1.0.4.zip Version 1.0.4, 2005.7.28
SVNManager-1.0.3-Setup.msi Version 1.0.3, Setup Package, 2004.10.20
SVNManager-1.0.3.zip Version 1.0.3, 2004.10.20
SVNManager-1.0.2-Setup.msi Version 1.0.2, Setup Package, 2004.10.19
SVNManager-1.0.2.zip Version 1.0.2, 2004.10.5
SVNManager-1.0.1.zip Version 1.0.1, 2004.10.5
SVNManager-1.0.0.zip Version 1.0.0, 2004.10.2

3. ChangeLog #

 

1.1.1, 2007.8.3

  • 윈도우 비스타에서 트레이 아이콘이 트루컬러로 나오도록 수정.

1.1.0, 2006.12.15

  • 서비스 모드 추가

1.0.5, 2006.12.11

  • 포트 번호 입력 박스의 버그 수정.
  • 유니코드 지원으로 한글 저장소 사용가능.

1.0.4, 2005.7.28

  • 시스템 시작시 자동실행 메시지 수정.
  • 포트 번호를 설정할 수 있도록 수정.

1.0.3, 2004.10.20

  • 시스템 시작시 자동실행이 되지 않는 버그 수정.

1.0.2, 2004.10.19

  • 설치 패키지 릴리즈.

1.0.2, 2004.10.5

  • 시스템 시작시 자동실행 설정을 할때 svnserve.exe가 없으면 파일을 찾도록 수정.

1.0.1, 2004.10.5

  • 시스템 시작시 자동실행 할때 창을 자동으로 숨김.

1.0.0, 2004.10.2

  • 1.0.0 릴리즈

4. 사용자 의견 및 버그 보고 #

[출처] SVN 서버와 토토이즈 SVN 설치하기|작성자 정일씨

신고
Posted by Frys
Programing/Java2009.03.13 10:27

Java 설치하기

Java 설치파일 다운하기

자바 프로그래밍을 하기 위해선 Java SE(이하 JDK) 필요합니다. JDK 6 Sun 홈페이지에서 다운받으실 있습니다.

JDK 6 다운로드는 링크를 통해서 다운받으시기 바랍니다. 3/13/2009 기준으로 최신버젼의 파일명은 다음과 같습니다.

jdk-6u12-windows-i586-p.exe 73.1MB

 

※참고로 본 문서와 함께 첨부파일을 이용하시면 더욱 편리합니다.

 

설치하기

위에서 다운받으신 설치파일을 실행합니다.

이하 기본옵션으로 설치하시면 됩니다.

환경변수 등록하기

  1. 컴퓨터 -> 오른쪽 마우스 버튼 클릭-> 속성 -> 고급 -> 환경변수 버튼 클릭 -> 시스템 변수의 새로 만들기를 클릭합니다.

  1. 변수 이름에 JAVA_HOME, 변수값에 JDK6 설치폴더를 입력합니다.

  1. 이미 존재하는 시스템 변수값의 Path 더블 클릭하여 %JAVA_HOME%\bin; 값을 추가합니다.

  1. 재시작 윈도우 버튼 -> 실행 에서 cmd 입력하여 콘솔창을 실행하여 java –version, javac –version 실행하여 버전이 1.6인지 확인해봅니다.

    자바 설치가 완료되었습니다.

 

Eclipse (MyEclipse)

Eclipse 설치하기

  1. Eclipse 설치폴더의 eclipse.ini 파일의 "-Xmx512m" "-Xmx256m" 으로 변경
  2. eclipse.exe 실행 workspace 적당한 위치에 지정 (디폴트로 설정해도 무방)
  3. Window -> Preferences ->Java ->Compiler -> Compiler compliance level -> 1.6 인지 확인
  4. Window -> Preferences ->Java -> Installed JREs 자바 런타임 환경 설정 경로 확인
  5. Window -> Preferences -> General -> Editors -> Text Editors 에서 Show line numbers 체크
  6. Window -> Preferences ->Java ->Code Style -> Formatter -> [New] -> "Eclipse [Kim Ho-jin]" 작성 -> Indentation -> Space only 선택
  7. (Window -> Preferences -> General -> Appearance -> Colors and Fonts -> Java -> Java Editor Text Font 에서 폰트와 크기 설정)

 

"안녕하세요..?" 프로젝트

1. [File -- New -- Project] 선택합니다.

 

2. 프로젝트 이름을 "Hello"라고 입력합니다.

 

3. default output folder "Hello/classes" 지정하고 [Finish]버튼을 클릭합니다.

4. "Hello" 프로젝트를 선택하고 [마우스 오른쪽 버튼 -- Class --Class] 선택합니다.

 

5. "Name" "Hello" 입력하고 "public static void main(String[] args)" 체크하고 [Finsih] 클릭합니다.

6. "Hello.java"클래스를 선택하고 화면과 같이 편집합니다.

 

7. 처음에 자바 어플리케이션을 실행할 때는 [Run As -- Java Application] 이용하고 다음부터는 그냥 "Run" 하면 됩니다.

 

8. 화면 하단에 아래처럼 실행 결과가 보입니다.

 

끝!    

신고

'Programing > Java' 카테고리의 다른 글

Java+Eclipse 환경설정하기  (0) 2009.03.13
Posted by Frys
분류없음2009.03.03 11:34
Programing/VHDL2009.01.22 18:17
테스트 파일
신고

'Programing > VHDL' 카테고리의 다른 글

CycloneI을 이용한 테스트 프로그램  (2) 2009.01.22
Posted by Frys

원본 보러가기

Q

안녕하세요.  영모에요..

맨날 이용하던 지식인 대신 용기내어 Q&A 에 글올립니다..

제가 이번에

RF 모듈에서 나오는 PWM 신호(0~5v)를 MCU로 읽어서

다시 PWM 신호를 만들어서 모터속도를 제어하려고 합니다.

5v를 사용하면 ATtiny2313,
3.3V를 사용하면 ATmega8L 을 사용해서 구현해보려고 하구요.
모터구동 전압은 7.2v입니다.

사실. RF모듈에서 나오는 PWM신호를 바로 모터드라이버로 돌렸는데..

모듈 자체에서 원하는 범위의 듀티비를 가진 신호가 안나오더라구요;

여기서 PWM 신호를 MCU로 읽으려면 소스를 어떻게 짜야하는지 방향만이라도 알려주시면 정말 큰도움이 될것 같습니다..

아니면 PWM 신호에서 듀티비만 증가 시키는 게 회로만으로 구현이 가능하나요?



제가 감기가 걸려서..
선배님들은 감기조심하시길 바래요..

A1

ㅎㅎ 상당히 특이한 발상을 했네~ 시도는 좋았어~ RF 모듈도 어떻게 보면 일정한 주기 내에서 0이나 1이 있는거니까 PWM이라고 봐도 되지~ 근데 조종기의 신호는 내가 위에서 쓴대로 일정한 주개 내에서 0이나 1로 데이터를 보내는게 다야~ 즉 "일정한 주기 내에서 0또는 1로 값을 설정함으로써 데이터를 날린다는거지!" 여기서 키 포인트 "데이터"야~ 즉 PWM처럼 입력해준 정도의 듀티비가 아니라 일정한 주기에 0이나 1을 입력하여서 일정 주기대로 값을 체크하면 무선으로 날아온 데이터가 어떤건지 알수 있다는 원리로 사용하는거야!
즉!!!! RF신호를 이용하여 PWM으로 이용한다는건 결론적으로 불가능하고! RF신호를 다시 데이터 신호로 바꿔주는 모듈이 있어~
보통 송신기 모듈과 수신기모듈이 있는데.. 니가 가지고 있는 RC카로 예를 들면 리모콘이 데이터를 뿌려주는 송신기에 해당하고 자동차가 수신기 모듈을 장착한 예로 볼수있어~ (왜 너도 봤겠지만 조종기에 크리스탈 다는거 봤찌? 그게 위에서 말한 데이터를 날려줄 일정한 주기를 결정하는거야! 따라서 같은 주파수를 사용하는 차가 2대있고 같은 주파수를 사용하는 조종기가 1대있으면 같은 속도 방향으로 이동하는 경우를 볼 수 있지!)

사설이 길었군! 결론은 다음과 같다!
target=_blank>http://devicemart.co.kr/mart7/search.php?query=rf

위의 홈페이지는 디바이스에서 rf 모듈을 검색한거야
보면알겠찌만 송신 수신 같이 있는것과 각개로 있는게 있어.. 따라서 서로간의 정보, 여기서는 PWM 신호를 일방적으로 날리고 싶다면 하나는 송신기 즉 리모콘에 해당하고 하나는 이동할 물체에 수신기를 쌍으로 구성하여 수신기에서 나오는 데이터를 128를 이용하여 수신된 정보를 분석하여 각 모터에 알맞은 PWM을 출력해주면 될거야!
잘 이해가 안될거같아서 블록 다이어그램을 다음 페이지에 추가답변할게~

감기얼른 낫길!!

A2


Denote

Part Name

Link

U1, U4

Atmega 128

바로가기

U2

RF 송신모듈 A2000D

바로가기

U3

RF 수신모듈 A1000E

바로가기

U5

L298, Peak 3A, Motor Driver

바로가기

 

 

 

신고
Posted by Frys
Project/완료2008.08.29 15:13
번역 파일입니다.
사용자 삽입 이미지

신고

'Project > 완료' 카테고리의 다른 글

PIC16_PIC24F 페밀리 번역  (0) 2008.08.29
Posted by Frys
Freeboard/Music2008.08.15 01:29


요즘 최고의 인기!!
;;
신고

'Freeboard > Music' 카테고리의 다른 글

빠삐놈 - Last Remix  (0) 2008.08.15
50Cent - P.I.M.P  (0) 2008.08.15
Snoop Dogg - Drop It Like It's Hot  (0) 2008.08.15
Posted by Frys
Freeboard/Music2008.08.15 01:02


제목은 구리지만.. 노래는 좋군.
50 센트형이 부르고 스눕독 형이 리믹스하고~
굳~
신고

'Freeboard > Music' 카테고리의 다른 글

빠삐놈 - Last Remix  (0) 2008.08.15
50Cent - P.I.M.P  (0) 2008.08.15
Snoop Dogg - Drop It Like It's Hot  (0) 2008.08.15
Posted by Frys
Freeboard/Music2008.08.15 00:38
 


약 2년전 형들과 처음 클럽을 갔을때 인상깊었던 노래.. ㅎㅎ
지금 다시 들어도 좋구리~
신고

'Freeboard > Music' 카테고리의 다른 글

빠삐놈 - Last Remix  (0) 2008.08.15
50Cent - P.I.M.P  (0) 2008.08.15
Snoop Dogg - Drop It Like It's Hot  (0) 2008.08.15
Posted by Frys
TAG 스눕독
Programing/최적화2008.07.21 13:16

1 소개
얼마전에 모바일기기에서 일정수준의 품질을 유지하면서 실행되는 JPEG라이브러리를 만드는 프로젝트를 진행한적이 있었다. 프로젝트를 진행하면서, 여러가지 방법으로 프로그램을 빨리 만들 있다는 사실을 경험적으로 알게 되었다. 문서는 C로된 코드를 속도와 메모리 양측모두에서 최적화하기 위한 경험적인 정보들을 포함하고 있다.

물론 여러분은 C 코드를 최적화 하는 방법에 대한 참고문서를 어렵지 않게 획득할 있을 것이다. 그러나 대부분의 문서가 팁수준에서 문제에 접근할 뿐으로, 컴파일러나 기계수준에서 어떻게 프로그래밍을 해야 하는지에 대한 정보는 담고 있지 않다.

보통 프로그램의 속도를 높이게 되면 코드의 크기가 늘어나게 된다. 코드의 크기가 늘어나면 프로그램이 복잡해지고, 읽고 이해하기 어려워진다. 메모리 자원이 넉넉한 개인PC혹은 서버 컴퓨터라면 문제가 되지 않겠지만 PDA 같은 제한된 메모리 자원을 가진 기기일 경우 심각한 문제가 있다. 1% 속도향상을 위해서 코드의 크기가 10%만큼 늘어난다면 분명 문제가 것이다. 이런 이유로 속도와 코드크기 모두에 대한 최적화를 수행하기로 결정을 했다.

2 선언
내가 진행하는 프로젝트가 ARM 플랫폼에서 진행된 관계로, ARM 최적화와 관련된 팁들이 필요했었다. 나는 인터넷을 통해서 ARM 최적화와 관련된 많은 문서를 검색하고 이중 유용한 것들 중심으로 수집해서 테스트를 했었다. 그러나 대부분의 문서들이 나에게는 도움이 되지 않았음을 고백한다. 이러한 실수를 줄이기 위해서 유용하고 효과적인 몇개의 팁만을 모으기로 결정했다.

3 어디에 필요한가
토론의 주제를 명확히 하고 넘어가자. 컴퓨터 프로그램을 최적화하기 위한 가장 중요한 것은 프로그램을 이루는 각각의 모듈중 어느 부분이 느리게 작동하거나, 메모리를 소비하는지를 찾아내는 것이다. 이들 각각의 부분을 최적화하면 프로그램이 전체적으로 빨라질 것이기 때문이다. 이러한 모듈단위의 최적화는 최적화를 위한 부분을 비교적 쉽게 찾고, 쉽게 해결할 있다는 장점을 가진다.

The optimizations should be done on those parts of the program that are run the most, especially those methods which are called repeatedly by various inner loops that the program can have.

일반적으로 경험이 풍부한 프로그래머들은 아주 쉽게 프로그램이 요구하는 최적화될 필요가 있는 핵심을 쉽게 찾아낼 있을 것이다. 가장 좋은 최적화 방법은 경험많은 프로그래머를 고용하는 것이다. 그러나 경험많은 프로그래머는 매우 비싸며, 경험이 많다고 해도 좋은 결과를 위해서는 최적화를 위한 좋은 툴을 사용할 필요가 있다. Visual C++ 같은 통합 개발환경은 함수단위로 프로그램의 소비시간을 측정할 있는 profiler 제공한다. 리눅스의 경우에는 gprof 같은 profiler 사용할 있다. 혹은 Intel Vtune 같은 프로그램을 사용할 있는데, 이들 프로그램을 사용하면 프로그램의 어느부분이 가장 많은 시간을 소비하는지를 확인할 있다. 개인적인 경험으로 루프 혹은 third party 라이브러리 메서드를 호출하는 영역이 프로그램을 느리게 하는 경우가 많았다.

4 정수
우리가 사용할 값이 음수가 아니라면 int 형대신에 unsigned int형을 사용해야 한다. 어떤 프로세스들은 unsigned integer 연산이 signed 연산보다 매우 빠르다. 또한 나누기/나눗셈 작업의 경우에도 음수가 필요 없다면 unsigned 명시해주는게 좋다.

루프에 사용될 변수라고 한다면, 다음과 같이 깔끔하고 효율적으로 선언할 있을 것이다.

register unsigned int variable_name;

기억해야할 또다른 점은 floating point 연산은 매우 느리다라는 점이다. floating point 데이터 타입은 자바와 함께 하는 컴퓨터과학문 서를 참고하기 바란다. 봐도 floating point 숫자는 다루기가 꽤나 복잡하다는 것을 있을 것이다. 만약 여러분이 소숫점 2자리까지의 정확도를 유지하는 회계프로그램을 만든다면, 모든 값에 x100을해서 int 형으로 바꾼다음 연산을 하도록 한다. 가능하면 외부의 수학라이브러리를 사용하지 않도록 한다. FPUs 같은 라이브러리는 매우 느리다.

5 나눗셈 그리고 나머지
표준적인 프로세서에서의 분모와 분자의 32bit 나눗셈은 20~140 실행 사이클을 가지고 있다. 나눗셈을 이용하면 다음과 같은 시간이 소비된다.

Time (numerator / denominator) = C0 + C1* log2 (numerator / denominator)
= C0 + C1 * (log2 (numerator) - log2 (denominator)).

널리 쓰이는 버젼은 20+4.3N 사이클을 보여준다. ARM 뿐만 아니라 프로세서를 막론하고 이런 연산은 피하는게 바람직하다. 나눗셈연산은 가능하다면 곱셈으로 대체해서 사용하기 바란다.

예를들어 (a/b) > c b * c integer 범위안이라는 것을 안다면 a > (c * b) 다시 쓰일 있다.

6 Combining division and remainder
나눗셈 (x/y) 그리고 나머지(x%y)둘다 종종 필요한 케이스이다
그러한 케이스에 비추어보아 나눗셈펑션을 컴파일러에 결합하는것이좋다 왜냐하면 나눗셈펑션은 항상 나눈값과 나머지를 리턴하기 필요하다 만약둘다 필요하다면 우리는 이와같은 예제를 같이 쓸수있어야한다

int func_div_and_mod (int a, int b) {
        return (a / b) + (a % b);
    }

7 2 배수로 나누기
나누기를 2 배수를 분자로 함으로써, 코드를 효율적으로 만들 있다. 이경우에 컴파일러는 나누기 연산대신에 shift 연산을 있기 때문이다. shift 연산은 가장빠른 연산중의 하나다. 그러므로 가능하면 2 배수로 나눌 있도록 스케일을 조절할 필요가 있다. (예를 들어 66으로 나누어야 한다면 64 나눌 있도록 스케일을 조절하라).

typedef unsigned int uint;

uint div32u (uint a) {
   return a / 32;
}
int div32s (int a){
   return a / 32;
}

이경우에도 signed 값보다는 unsigned 나누어질 있도록 함수를 조절할 필요가 있다. signed 경우에는 더많은 시간이 소비된다. 왜냐하면 오른쪽으로 쉬프트 시킬경우 가장왼쪽의 비트를 0으로 만들어주는 연산이 한번더 들어가기 때문이다.

#include <stdio.h>

int main()
{
  unsigned int a = 1024;
  unsigned b, c;
  b = a/32;    // --- 1
  c = a >> 5;  // --- 2
}

1 2 동일한 결과를 보여주며, 컴파일러내에서도 동일하게 shift 처리된다. 다음은 intel 프로세서에서 gcc 컴파일된 어셈블리어중 1 2 해당되는 부분의 코드다.

movl    $1024, -12(%ebp)
movl    -12(%ebp), %eax
shrl    $5, %eax           # b = a / 32
movl    %eax, -8(%ebp)
movl    -12(%ebp), %eax
shrl    $5, %eax           # c = a >> 5

8 배열을 이용한 index 생성
특정값에 대응되는 문자를 변수에 입력하는 코드를 만든다면 다음과 같이 switch 문을 사용할 것이다.

switch ( queue ) {
  case 0 :   letter = 'W';
     break;
  case 1 :   letter = 'S';
     break;
  case 2 :   letter = 'U';
     break;
}

혹은 if else 문을 사용할 수도 있을 것이다.

if ( queue == 0 )
   letter = 'W';
else if ( queue == 1 )
   letter = 'S';
else
   letter = 'U';

다음과 같이 문자의 배열을 인덱스화 하면 빠른 접근이 가능하다. - 사용하기도 쉽다 -

static char *classes="WSU";
letter = classes[queue];

9 나머지 연산자의 대체
우리는 나눗셈의 나머지를 알기 위해서 나머지 연산자 % 사용한다. 이경우 % 연산대신 판단문을 사용해서 시간을 줄일 있다. 아래의 코드를 비교해 보기 바란다.

uint modulo_func1 (uint count)
{
   return (++count % 60);
}

uint modulo_func2 (uint count)
{
   if (++count >= 60)
  count = 0;
  return (count);
}

if 문은 나머지 연산자보다 빠른코드를 생성한다. 주의 할점은 2번째 함수의 경우 0에서 60사이의 값에 대해서만 제대로 측정이 된다는 점이다.

10 전역 변수
전역 변수는 절대 레지스터에 할당할 없다. 포인터를 사용하여 간접적으로 할당하거나 함수호출을 이용해서 전역변수를 변환할 있다.

따라서 컴파일러는 전역변수의 값을 레지스터에 올려서 캐쉬할 없게 되고 때문에 글로벌 변수를 이용할 때마다 다시 읽어들이는 오버로드가 생기게 된다. 그러므로 가능하면 글로벌 변수를 직접 호출하는 대신에, 로컬변수를 이용해서 필요한 연산을 하고 결과를 글로별 변수에 할당하는 방법을 사용해야 한다.

int f(void);
int g(void);
int h(void);
int errs;

void test1(void)
{
  errs += f();
  errs += g();
  errs += h();
}

void test2(void)
{
  int localerrs = errs;
  localerrs += f();
  localerrs += g();
  localerrs += h();
  errs = localerrs;
}

test1 매번 전역변수를 로드해야 한다. 반면 test2 경우 레지스터에 등록된 localerrs 값을 저장하고 마지막에 한번만 전역변수에 접근함을 있다.

11 Using Aliases
아래의 코드를 보기 바란다.

  void func1( int *data )    {
      int i;

     for(i=0; i<10; i++)
     {
            anyfunc( *data, i);
     }
  }

*data 결코 변하지 않는다고 하더라도, anyfunc 함수를 호출하는 컴파일러는 이걸 수가 없다. 그래서 변수가 사용될 때마다 메모리로 부터 다시 읽어들이게 된다. 문제는 지역변수를 하나더 둠으로써 해결할 있다.

  void func1( int *data )
  {
      int i;
      int localdata;

      localdata = *data;
      for(i=0; i<10; i++)
      {
          anyfunc ( localdata, i);
      }
  }

12 데이터 타입
C
컴파일러는 char, short, int, long, float, double 등의 다양한 원시 데이터 타입을 제공한다. 필요한 영역에 필요한 수준의 데이터 타입을 사용하도록 하자.

13 지역변수
가능하면 지역변수로 char 이나 short 사용하지 않도록 한다. char short 사용될 경우 컴파일러는 값을 저장하기 위해서 8bit 혹은 16bit 할당한 , 남는 크기를 줄이는 작업을 하게 된다. 이는 24bit, 16bit 만큼을 shift 시키는 연산을 하게 됨을 의미한다. 그러므로 입력되는 데이터가 8 혹은 16 비트라고 하더라도, 32bit 연산을 하도록 함수를 만들 필요가 있다.

int wordinc (int a)
{
   return a + 1;
}

short shortinc (short a)
{
    return a + 1;
}

char charinc (char a)
{
    return a + 1;
}

3번째 코드가 가장 빠른결과를 보여줄 것이라고 생각할지도 모르지만, 1번째 코드가 가장 빠르게 작동한다.

14 포인터
구조체를 그대로 넘길경우 구조체의 모든 값이 스택에 올라가기 때문에 느리게 작동한다. 그래서 구조체의 포인터를 넘기는 경우가 많다. 나는 kbyte 구조체를 넘기는 프로그램을 본적이 있다. 이런 경우 포인터를 쓰도록 하자.

포인터를 통해서 구조체를 넘길때, 구조체의 멤버를 수정할일이 없다면 상수로 선언해서 넘기도록 하자.

void print_data_of_a_structure ( const Thestruct  *data_pointer)
{
   ...printf contents of the structure...
}

이렇게 하면 컴파일러는 인자로 넘어온 포인터가 수정할 없는 외부 구조체라는 것을 알게 된다. 이렇게 되면, 값이 사용될 때마다 다시 읽혀질 필요가 없어지게 된다. 또한 이러한 코드는 실수로 구조체 멤버의 변수를 바꾸는 것과 같은 실수를 하지 않도록 해준다.

15 Pointer chains
구조체내의 정보에 접근하려다 보면 포인터의 chain 사용해야 때가 있다. 다음과 같은 경우다.

typedef struct { int x, y, z; } Point3;
typedef struct { Point3 *pos, *direction; } Object;

void InitPos1(Object *p)
{
   p->pos->x = 0;
   p->pos->y = 0;
   p->pos->z = 0;
}

이럴 경우 p->pos 다른 포인터에 할당해서 접근하도록 하자. 이렇게 하면 p->pos 캐쉬되므로 좀더 효율적으로 작동하게 된다.

void InitPos2(Object *p)
{
   Point3 *pos = p->pos;
   pos->x = 0;
   pos->y = 0;
   pos->z = 0;
}

코드가 좀더 보기 좋아진다는 효과도 노릴 있다.

16 Binary Breakdown
여러개의 조건을 검사하다 보면, if else if 여러개 사용하는 경우가 생긴다.

if(a==1) {
} else if(a==2) {
} else if(a==3) {
} else if(a==4) {
} else if(a==5) {
} else if(a==6) {
} else if(a==7) {
} else if(a==8)

{
}

이경우 2개로 나누어서 조건 검사를 하도록 한다.

if(a<=4) {
    if(a==1)     {
    }  else if(a==2)  {
    }  else if(a==3)  {
    }  else if(a==4)   {
 
    }
}
else
{
    if(a==5)  {
    } else if(a==6)   {
    } else if(a==7)  {
    } else if(a==8)  {
    }
}

이렇게 하면 최악의 경우 비교횟수가 절반이 됨을 있다. 필요에 따라서는 아래와 같이 3중루프 코드로 만들 수도 있다. 좀더 빠르게 동작하긴 하겠지만 코드가 보기 어려워진다는 단점이 생긴다.

if(a<=4)
{
    if(a<=2)
    {
        if(a==1)
        {
            /* a is 1 */
        }
        else
        {
            /* a must be 2 */
        }
    }
    else
    {
        if(a==3)
        {
            /* a is 3 */
        }
        else
        {
            /* a must be 4 */
        }
    }
}
else
{
    if(a<=6)
    {
        if(a==5)
        {
            /* a is 5 */
        }
        else
        {
            /* a must be 6 */
        }
    }
    else
    {
        if(a==7)
        {
            /* a is 7 */
        }
        else
        {
            /* a must be 8 */
        }
    }
}

17 Switch 대신 lookup table 사용하라
switch
다음과 같은 경우 사용한다.

* 여러개의 함수중 하나를 호출해야할 필요가 있을
*
다양한 리턴값을 넘겨받고 이를 처리해야 할때
*
여러개의 코드중 하나를 실행시켜야 할때

예를 들어서 조건값을 입력받아서 거기에 맞는 문자열을 리턴하는 아래와 같은 코드가 있다고 가정해보자.

char * Condition_String1(int condition) {
  switch(condition) {
     case 0: return "EQ";
     case 1: return "NE";
     case 2: return "CS";
     case 3: return "CC";
     case 4: return "MI";
     case 5: return "PL";
     case 6: return "VS";
     case 7: return "VC";
     case 8: return "HI";
     case 9: return "LS";
     case 10: return "GE";
     case 11: return "LT";
     case 12: return "GT";
     case 13: return "LE";
     case 14: return "";
     default: return 0;
  }
}

위의 코드는 아래와 같이 효율적인 코드로 만들 있다. 덤으로 보기에도 편하다.

char * Condition_String2(int condition) {
   if ((unsigned) condition >= 15) return 0;
      return
      "EQ\0NE\0CS\0CC\0MI\0PL\0VS\0VC\0HI\0LS\0GE\0LT\0GT\0LE\0\0" +
       3 * condition;
}

첫번째 루틴은 240byte 필요하지만 두번째 루틴은 72바이트만 소모되고 있다.

18 루프
루프는 모든 프로그램에서 사용되는데, 많은 경우 루프에서 과다한 시간을 소비하게 된다. 여러번 실행되는 루프틔 특성상 조그마한 시간의 낭비가 게속 누적되기 때문이다.

18.1 Loop termination
루프를 종료시키기 위한 검사는 항상 count-down-to-zero 방식을 사용하도록 한다. 이것은 좀더 적은 시간을 소비한다. 아래의 두개의 예제는 동일한 일을한다. 다른점이 있다면 첫번째 코드는 루프를 증가시킨다는 점이고 두번째는 루프를 감소시킨다는 점이다.

int fact1_func (int n)
{
    int i, fact = 1;
    for (i = 1; i <= n; i++)
      fact *= i;
    return (fact);
}

int fact2_func(int n)
{
    int i, fact = 1;
    for (i = n; i != 0; i--)
       fact *= i;
    return (fact);
}

18.2 더욱 빠른 for
다음은 0부터 10까지의 숫자를 연산하기 위해서 for 문을 사용한 일반적인 예다.

for (i = 0; i < 10; i++) {...}

i 0,1,2,3,4,5,6,7,8,9 1 증가할 것이다.

가능하면 아래와 같이 숫자를 감소시키는 방향으로 for 문을 사용하라.

for (i = 10; i--;) {...}

첫번재 코드보다 두번째 코드가 빠른 수행능력을 보여준다.

두번째 코드는 i 0 아니면 i 감소시키고 다음 코드를 진행하라의 의미인데, 조건 검사의 경우 0인지 아닌지를 비교하는데 작은 시간이 소비되기 때문이다. 그러므로 두번째 코드는 아래와 같이 재작성할 있다. 두번째 예제코드 보다는 아래의 코드가 보기 쉬우므로, 아래의 코드를 사용하는게 가독성 측면에서 유리할 것이다.

for (i = 10; i ; i--) { }

혹은

for (i = 10; i!=0; i--) { }

이들은 모두 동일한 수행능력을 보여준다.

18.3 Loop jamming

18.4 함수 루프
함수는 호출되기 위한 분명한 오버헤드가 존재한다. 실행해야될 함수가 있는 포인터만 변경하는게 아닌, 값들을 stack push하는 것과 새로운 변수의 할당과 같은 작업이 수행되기 때문이다. 때문에 루프에서 함수를 호출하는 등의 코드는 작성하지 않는게 좋다. 이런류의 코드는 반대로 함수에서 루프를 수행하도록 변경하는걸 추천한다.

for(i=0 ; i<100 ; i++)
{
    func(t,i);
}
-
-
-
void func(int w,d)
{
    lots of stuff.
}

위의 코드는 아래처럼 바꿀 있다. 동일한 일을 좀더 빠르게 수행할 있다.

func(t);
-
-
-
void func(w)
{
    for(i=0 ; i<100 ; i++)
    {
        //lots of stuff.
    }
}

18.5 Population count - 비트 계수하기
아래의 코드는는 주어진 값에 1bit 몇개인지를 검사하는 코드다. 0000 1010 이라면 2 리턴하는 식이다. 이러한 비트필드는 일정한 범위의 값이 참인지 거짓인지를 빠르게 체크하기 위해서 널리 사용될 있다.

다음과 같이 1 오른쪽으로 쉬프트 하면서, & 연산을 한다.

int countbit1(uint n)
{
  int bits = 0;
  while (n != 0)
  {
    if (n & 1) bits++;
    n >>= 1;
   }
  return bits;
}

코드는 다음과 같이 4만큼 쉬프트 하는 식으로 바꿔서, 성능을 높일 있다.

int countbit2(uint n)
{
   int bits = 0;
   while (n != 0)
   {
      if (n & 1) bits++;
      if (n & 2) bits++;
      if (n & 4) bits++;
      if (n & 8) bits++;
      n >>= 4;
   }
   return bits;
}

18.6 Earyl loop breaking
루프를 사용하다보면, 일정 조건이 만족되면 뒤의 프로세스가 더이상 필요 없어지는 경우가 있다. 경우에는 break 이용해서 루프를 벗어나도록 한다.

found = FALSE;
for(i=0;i<10000;i++)
{
    if( list[i] == -99 )
    {
        found = TRUE;
    }
}

if( found ) printf("Yes, there is a -99. Hooray!\n");

위의 코드는 -99 포함되어 있는지 아닌지를 확인하는 프로그램이므로, 일단 발생이 되었다면, 루프를 필요가 없다. 아래와 같이 break 문으로 빠져나가면 쓸데없는 루프의 낭비를 줄일 있다.

    found = FALSE;
    for(i=0; i<10000; i++)
    {
        if( list[i] == -99 )
        {
            found = TRUE;
            break;
        }
    }
    if( found ) printf("Yes, there is a -99. Hooray!\n");

18.7 Loop unrolling

19 함수 디자인
함수를 작고 가볍게 많드는건 좋은 생각이다. 이렇게 함으로써 컴파일러는 register 할당과 같은 영역에서 좀더 쉽게 최적화 할수 있게 된다.

19.1 함수 호출 Overhead
프로세서에서 함수의 호출은 예상과 달리 그리 비용이 들지는 않는다. 함수가 호출되면 register 함수의 인자를 넘기게 된다. 인자들은 char, short, int, float, structure 있다. 이들 인자는 실제 4개만을 전달할 있다는 한계를 가진다. 이상으로 인자가 넘어가게 되면, stack 이용해서 함수의 인자를 넘기게 된다. 당연히 함수를 호출함에 있어서 OverHead 발생하게 된다. 함수호출시 발생하는 인자의 제한에 대해서는 Linux에서의 Assembly문서를 참고하기 바란다.

예제코드

    int f1(int a, int b, int c, int d) {
       return a + b + c + d;
    }

    int g1(void) {
       return f1(1, 2, 3, 4);
    }


    int f2(int a, int b, int c, int d, int e, int f) {
      return a + b + c + d + e + f;
    }

    ing g2(void) {
     return f2(1, 2, 3, 4, 5, 6);
    }

6개의 인자를 사용하는 f2 g2함수는 스택에 저장되어 있는 인자를 꺼내기 위해서 2번의 메모리 접근이 발생하게 된다.

19.2 가능한 인자의 수를 줄여라
그러므로 가능한 적은 수의 인자를 넘겨받도록 함수를 설계할 필요가 있다.

* 4 이하의 인자를 가지도록 함수를 설계하라. 4개가 넘어가면 스택을 통해서 인자를 넘기게 된다.
*
만약 함수가 4 이상의 인자가 사용되면, 스택을 통해서 인자를 넘기게 되고 스택의 크기만큼 메모리 접근이 발생하게 된다.
*
이럴 경우 구조체를 선언하고, 구조체에 대한 포인터를 넘기는 방식을 사용하도록 한다.
*
구조체를 사용하면 인자의 양을 줄일 있으며, 코드 활용성도 높아지게 된다.
*
인자에 사용되는 자료형은 long크기 이상으로 하도록 하자.
* Avoid functions with a variable number of parameters. Those functions effectively pass all their arguments on the stack.

19.3 인라인 함수
__inline
워드를 이용하면 함수를 인라인화 있게 된다. 이것은 일종의 매크로 처럼 작용을 하며, 함수가 호출되는 대신 함수의 본체가 직접 치환이 되어 버린다. 이렇게 함으로써, 함수를 호출하는데 드는 비용을 줄일 있게 된다. 반면 코드가 모두 치환되어 버리므로, 코드의 크기가 커지게 된다.

    __inline int square(int x) {
       return x * x;
    }

    #include <MATH.H>

    double length(int x, int y){
        return sqrt(square(x) + square(y));
    }

comment:
Thanks for very good and intersting postings! (Sorry, can't type Korean). I have some comments:

9) I'm not sure how many cycles are needed to compute % operator; however, IMHO, this would be better than branchness. Including ARM processors, most modern processros are exploiting branch predctions for aggressive dynamic scheduling. So, branch mis-prediction should be considered to minimize negative effects. If branch mis-predctions occur in the second code, I'm pretty sure that this code is slower than the first one. In other words, removing branchness is better.

I think you can replace % operator with & operator for calculating remainder. & should be quite faster than % and branch.

15) Unless we have a really stupid compier, I don't think that this kind of replacement would bring a better performance. A comper will automatically uses *cached* (more precisely, cached into a register) value in computing others unless a variable is declared as volatile.

17) Really good!! I've never seen that.

18) Absolutely agree with you. I'm always using * for (i = n - 1; i >= 0; --i) * insted of * for (i = 0; i < n ; ++i) *

신고

'Programing > 최적화' 카테고리의 다른 글

C 코드 최적화  (2) 2008.07.21
C코드 최적화하기  (0) 2008.07.21
Posted by Frys
Programing/최적화2008.07.21 13:10

소개

얼마전에 모바일기기에서 일정수준의 품질을 유지하면서 실행되는 JPEG라이브러리를 만드는 프로젝트를 진행한적이 있었다. 이 프로젝트를 진행하면서, 여러가지 방법으로 프로그램을 더 빨리 만들 수 있다는 사실을 경험적으로 알게 되었다. 이 문서는 C로된 코드를 속도와 메모리 양측모두에서 최적화하기 위한 경험적인 정보들을 포함하고 있다.

물론 여러분은 C 코드를 최적화 하는 방법에 대한 참고문서를 어렵지 않게 획득할 수 있을 것이다. 그러나 대부분의 문서가 팁수준에서 문제에 접근할 뿐으로, 컴파일러나 기계수준에서 어떻게 프로그래밍을 해야 하는지에 대한 정보는 담고 있지 않다.

보통 프로그램의 속도를 높이게 되면 코드의 크기가 늘어나게 된다. 코드의 크기가 늘어나면 프로그램이 복잡해지고, 읽고 이해하기 어려워진다. 메모리 자원이 넉넉한 개인PC혹은 서버 컴퓨터라면 문제가 되지 않겠지만 PDA와 같은 제한된 메모리 자원을 가진 기기일 경우 심각한 문제가 될 수 있다. 1%의 속도향상을 위해서 코드의 크기가 10%만큼 늘어난다면 분명 문제가 될 것이다. 이런 이유로 속도와 코드크기 모두에 대한 최적화를 수행하기로 결정을 했다.

선언

내가 진행하는 프로젝트가 ARM 플랫폼에서 진행된 관계로, ARM 최적화와 관련된 팁들이 필요했었다. 나는 인터넷을 통해서 ARM 최적화와 관련된 많은 문서를 검색하고 이중 유용한 것들 중심으로 수집해서 테스트를 했었다. 그러나 대부분의 문서들이 나에게는 도움이 되지 않았음을 고백한다. 이러한 실수를 줄이기 위해서 유용하고 효과적인 몇개의 팁만을 모으기로 결정했다.

어디에 필요한가

토론의 주제를 명확히 하고 넘어가자. 컴퓨터 프로그램을 최적화하기 위한 가장 중요한 것은 프로그램을 이루는 각각의 모듈중 어느 부분이 느리게 작동하거나, 큰 메모리를 소비하는지를 찾아내는 것이다. 이들 각각의 부분을 최적화하면 프로그램이 전체적으로 빨라질 것이기 때문이다. 이러한 모듈단위의 최적화는 최적화를 위한 부분을 비교적 쉽게 찾고, 쉽게 해결할 수 있다는 장점을 가진다.

The optimizations should be done on those parts of the program that are run the most, especially those methods which are called repeatedly by various inner loops that the program can have.

일반적으로 경험이 풍부한 프로그래머들은 아주 쉽게 프로그램이 요구하는 최적화될 필요가 있는 핵심을 쉽게 찾아낼 수 있을 것이다. 가장 좋은 최적화 방법은 경험많은 프로그래머를 고용하는 것이다. 그러나 경험많은 프로그래머는 매우 비싸며, 경험이 많다고 해도 더 좋은 결과를 위해서는 최적화를 위한 좋은 툴을 사용할 필요가 있다. Visual C++ 과 같은 통합 개발환경은 함수단위로 프로그램의 소비시간을 측정할 수 있는 profiler를 제공한다. 리눅스의 경우에는 gprof와 같은 profiler를 사용할 수 있다. 혹은 Intel Vtune와 같은 프로그램을 사용할 수 있는데, 이들 프로그램을 사용하면 프로그램의 어느부분이 가장 많은 시간을 소비하는지를 확인할 수 있다. 개인적인 경험으로 루프 혹은 third party 라이브러리 메서드를 호출하는 영역이 프로그램을 느리게 하는 경우가 많았다.

정수

우리가 사용할 값이 음수가 아니라면 int 형대신에 unsigned int형을 사용해야 한다. 어떤 프로세스들은 unsigned integer의 연산이 signed 연산보다 매우 빠르다. 음수를 사용할 경우 2의보수 연산을 추가로 해줘야 하기 때문이다. 2의보수 연산에 대한 내용은 C 프로그래밍 문서를 참고하기 바란다. 문서를 보면 왜 signed 연산에 더 많은 시간이 소비되는지를 알 수 있을 것이다.

루프에 사용될 변수라고 한다면, 다음과 같이 깔끔하고 효율적으로 선언할 수 있을 것이다.
register unsigned int variable_name; 
 

기억해야할 또다른 점은 floating point 연산은 매우 느리다라는 점이다. floating point 데이터 타입은 자바와 함께 하는 컴퓨터과학문서를 참고하기 바란다. 척 봐도 floating point 숫자는 다루기가 꽤나 복잡하다는 것을 알 수 있을 것이다. 만약 여러분이 소숫점 2자리까지의 정확도를 유지하는 회계프로그램을 만든다면, 모든 값에 x100을해서 int 형으로 바꾼다음 연산을 하도록 한다. 가능하면 외부의 수학라이브러리를 사용하지 않도록 한다. FPUs와 같은 라이브러리는 매우 느리다.

나눗셈 그리고 나머지

표준적인 프로세서에서의 분모와 분자의 32bit 나눗셈은 20~140의 실행 사이클을 가지고 있다. 나눗셈을 이용하면 다음과 같은 시간이 소비된다.
Time (numerator / denominator) = C0 + C1* log2 (numerator / denominator) 
= C0 + C1 * (log2 (numerator) - log2 (denominator)). 
 
널리 쓰이는 버젼은 약 20+4.3N의 사이클을 보여준다. ARM 뿐만 아니라 프로세서를 막론하고 이런 연산은 피하는게 바람직하다. 나눗셈연산은 가능하다면 곱셈으로 대체해서 사용하기 바란다.

예를들어 (a/b) > c 는 b * c가 integer 범위안이라는 것을 안다면 a > (c * b)로 다시 쓰일 수 있다.

Combining division and remainder

나눗셈 (x/y) 그리고 나머지(x%y)둘다 종종 필요한 케이스이다
그러한 케이스에 비추어보아 나눗셈펑션을 컴파일러에 결합하는것이좋다 왜냐하면 나눗셈펑션은 항상 나눈값과 나머지를 리턴하기 필요하다 만약둘다 필요하다면 우리는 이와같은 예제를 같이 쓸수있어야한다
int func_div_and_mod (int a, int b) { 
        return (a / b) + (a % b); 
    } 
 

Division and remainder by powers of two

2의 배수로 나누기

나누기를 할 때 2의 배수를 분자로 함으로써, 코드를 더 효율적으로 만들 수 있다. 이경우에 컴파일러는 나누기 연산대신에 shift 연산을 할 수 있기 때문이다. shift 연산은 가장빠른 연산중의 하나다. 그러므로 가능하면 2의 배수로 나눌 수 있도록 스케일을 조절할 필요가 있다. (예를 들어 66으로 나누어야 한다면 64로 나눌 수 있도록 스케일을 조절하라).
typedef unsigned int uint; 
 
    uint div32u (uint a) { 
     return a / 32; 
    } 
    int div32s (int a){ 
     return a / 32; 
    } 
 
이경우에도 signed 값보다는 unsigned 로 나누어질 수 있도록 함수를 조절할 필요가 있다. signed의 경우에는 더많은 시간이 소비된다. 왜냐하면 오른쪽으로 쉬프트 시킬경우 가장왼쪽의 비트를 0으로 만들어주는 연산이 한번더 들어가기 때문이다.

#include <stdio.h> 
 
int main() 
{ 
  unsigned int a = 1024; 
  unsigned b, c; 
  b = a/32;    // --- 1 
  c = a >> 5;  // --- 2 
} 
 
1과 2는 동일한 결과를 보여주며, 컴파일러내에서도 동일하게 shift 처리된다. 다음은 intel 프로세서에서 gcc로 컴파일된 어셈블리어중 1과 2에 해당되는 부분의 코드다.
movl    $1024, -12(%ebp) 
movl    -12(%ebp), %eax 
shrl    $5, %eax           # b = a / 32 
movl    %eax, -8(%ebp) 
movl    -12(%ebp), %eax 
shrl    $5, %eax           # c = a >> 5 
 

배열을 이용한 index 생성

특정값에 대응되는 문자를 변수에 입력하는 코드를 만든다면 다음과 같이 switch 문을 사용할 것이다.
switch ( queue ) { 
  case 0 :   letter = 'W'; 
     break; 
  case 1 :   letter = 'S'; 
     break; 
  case 2 :   letter = 'U'; 
     break; 
} 
 
혹은 if else 문을 사용할 수도 있을 것이다.
 if ( queue == 0 ) 
   letter = 'W'; 
 else if ( queue == 1 ) 
   letter = 'S'; 
 else 
   letter = 'U'; 
 

다음과 같이 문자의 배열을 인덱스화 하면 더 빠른 접근이 가능하다. - 사용하기도 쉽다 -
static char *classes="WSU"; 
letter = classes[queue]; 
 

나머지 연산자의 대체

우리는 나눗셈의 나머지를 알기 위해서 나머지 연산자 %를 사용한다. 이경우 % 연산대신 판단문을 사용해서 시간을 줄일 수 있다. 아래의 두 코드를 비교해 보기 바란다.
uint modulo_func1 (uint count) 
{ 
   return (++count % 60); 
} 
 
uint modulo_func2 (uint count) 
{ 
   if (++count >= 60) 
  count = 0; 
  return (count); 
} 
 
if 문은 나머지 연산자보다 빠른코드를 생성한다. 주의 할점은 2번째 함수의 경우 0에서 60사이의 값에 대해서만 제대로 측정이 된다는 점이다.

전역 변수

전역 변수는 절대 레지스터에 할당할 수 없다. 포인터를 사용하여 간접적으로 할당하거나 함수호출을 이용해서 전역변수를 변환할 수 있다.

따라서 컴파일러는 전역변수의 값을 레지스터에 올려서 캐쉬할 수 없게 되고 때문에 글로벌 변수를 이용할 때마다 다시 읽어들이는 오버로드가 생기게 된다. 그러므로 가능하면 글로벌 변수를 직접 호출하는 대신에, 로컬변수를 이용해서 필요한 연산을 하고 그 결과를 글로별 변수에 할당하는 방법을 사용해야 한다.
int f(void); 
int g(void); 
int h(void); 
int errs; 
void test1(void) 
{ 
  errs += f(); 
  errs += g(); 
  errs += h(); 
} 
void test2(void) 
{ 
  int localerrs = errs; 
  localerrs += f(); 
  localerrs += g(); 
  localerrs += h(); 
  errs = localerrs; 
} 
 
test1은 매번 전역변수를 로드해야 한다. 반면 test2의 경우 레지스터에 등록된 localerrs에 값을 저장하고 마지막에 한번만 전역변수에 접근함을 알 수 있다.

Using Aliases

Using Aliases
Consider the following example -

void func1( int *data ) {
int i;

for(i=0; i<10; i++)
{
anyfunc( *data, i);
}
}
Even though *data may never change, the compiler does not know that anyfunc () did not alter it, and so the program must read it from memory each time it is used - it may be an alias for some other variable that is altered elsewhere. If we know it won't be altered, we could code it like this instead:

void func1( int *data )
{
int i;
int localdata;

localdata = *data;
for(i=0; i<10; i++)
{
anyfunc ( localdata, i);
}
}

This gives the compiler better opportunity for optimization.

Live variables and spilling
As any processor has a fixed set of registers, there is a limit to the number of variables that can be kept in registers at any one point in the program.

Some compilers support live-range splitting, where a variable can be allocated to different registers as well as to memory in different parts of the function. The live-range of a variable is defined as all statements between the last assignment to the variable, and the last usage of the variable before the next assignment. In this range, the value of the variable is valid, thus it is alive. In between live ranges, the value of a variable is not needed: it is dead, so its register can be used for other variables, allowing the compiler to allocate more variables to registers.

The number of registers needed for register-allocatable variables is at least the number of overlapping live-ranges at each point in a function. If this exceeds the number of registers available, some variables must be stored to memory temporarily. This process is called spilling.

The compiler spills the least frequently used variables first, so as to minimize the cost of spilling. Spilling of variables can be avoided by:

Limiting the maximum number of live variables: this is typically achieved by keeping expressions simple and small, and not using too many variables in a function. Subdividing large functions into smaller, simpler ones might also help.
Using register for frequently-used variables: this tells the compiler that the register variable is going to be frequently used, so it should be allocated to a register with a very high priority. However, such a variable may still be spilled in some circumstances.


머릿말
가벼운 모바일 계획을 하는데 그래픽 손상없이 충분히 실행이 가능한 JPEG 라이버리 프로젝트를 진행하는동안 나는 여러가지 방법으로 컴퓨터 프로그램을 더 빠르게 만들수있다는것을 보았다

난 가능한 속도 심지어 메모리까지 c 코드를 최적화하기 위한 이 방법을 모든 경험 그리고 정보를 모았다

비록 다수의 c 최적화코드에 대한 가이드(참고서)를 이용할수있다.
하지만 거기엔 철저한 컴파일러에 대한 지식 그리고 너가 하고있는 프로그래밍을 거기에 대신 가질순 없다

가끔 프로그램의 속도를 낸다는것은 코드의 증가의 원인이 된다

이 코드의 증가는 프로그램을 보충하고 읽는데 역효과를 가지고있다

이것은 너의 모바일과 같은 프로그래밍, PDA 같은 엄격한 메모리의 제한이 필요한 프로그램에서는 받아드릴수 없을것이다

그래서 최적화를 공부 하는동안 우리는 코드안에서 메모리와 스피드 둘다 최적이 되는 방법을 할것이다



선언
사실 나는 프로젝트를 진행하는동안 최적화된 ARM을 하기위해 (왜냐하면 나의 프로젝트는 ATM lpatfor때문이다) 이 팁을(http://www.arm.com/pdfs/DAI0034A_efficient_c.pdf) 사용하고있었다
그러나 나는 인터넷으로부터 많은 문서를 이용하였지만 모든팁이 나의 일에 도움이 되지 않았다
그래서 나는 매우 유용하고 효과적인 팁만을 모았다
또한 나는 조금수정된 ATM의 일부환경에 그들이 적용하고있는 그들의 방법을 가지고있다
내가 앞에서 언급한 것들은 PDF파일이며 단지 여러싸이트에서 단지 만들어진 자료를 수집했다
나는 이 자료를 혼자만의 발견이 아니다
이 정보의 references는 이글의 끝에 기제하겠다



어디서 필요한가?
이 포인트(문제제기)없이 토론은 시작될수없을것이다
최적화된 컴퓨터 프로그램에 대게 중요한 부분은 완벽하게 그 부분 또는 그 프로그램에서 천천히 실행되는 또는 메모리를 거대하게 잡아먹는 프로그램의 모듈을 찾아내는 것이다
만약 각각부분을 분리한다면 최적화된 그 다음 전체적으로 프로그램은 자동적으로 더 빨라질것이다

최적화는 프로그램이 가지고잇는 여러가지 내부루푸에서 되풀이 하여 불리워지는 이런 부분들이 끝난 다음에 할것이다

경험이 있는 프로그래머들을 위해 아주 쉽게 프로그램이 요구하는 대게 최적화된 어텐션(외부로부터 처리요구) 포인트를 찾아내는대 유용할것이다

그러나 그것들은 프로그램의 부분을 탐지하는대 많이 이용된다
나는 Bisual C+ IDE's를 이용해 프로그램이 낭비하는 부분을 찾는 비법을 이용할것이다

다른툴로써는 프로그램이 느린 부분을 찾아내기에 매우 탐지하기 좋은 intel Bten을 이용할것이다

it will usually be a particular inner or nested loop, or a call to some third party library methods, which is the main culprit for running the program slow.



정수(Integers)
우리는 우리가 쓰고있는 벨류가 음수가 아니라면 int형 대신 unsigend int형을 사용해야 한다
어떤 프로세서들은 unsinged integer의 연산을 signed연산(혼자 코드를 기록하기엔 좋은습관이다)보다 매우 빨리한다

그래서 int 벨류를 루프에 이용할경우 깔끔한 좋은선언 :

register ungigned int variable_name;

비록 이것이 어떤 컴파일러라도 레지스터에 알려 unsigned 는 아마 만들어질것이다
그러나 이것은 모든 컴파일러에 적합한 것은 아니다

기억하라, integer연산은 floating-point연산보다 빠르다
이것은 유용할것이다. 프로세스에 직접적으로 외부의 FPUs 또는 floating point 수학 라이버리에 의지하는것보다 오히려 더 빠를것이다

우리는 소수 둘째자리까지 정확할 필요가 있고 (예를들어 간단한 회계 거래), 모든것을 100배한 다음 가능한 늦게 floating point로 변환할 필요가 있다



나눗셈 그리고 나머지
표준 프로세서에서 분모와 분자의 32bit 나눗셈은 20~140의 실행싸이클을 가지고있다
나눗셈 함수는 각각의 bit를 분할할 일정한 시간이 더걸린다

Time (numerator / denominator) = C0 + C1* log2 (numerator / denominator)
= C0 + C1 * (log2 (numerator) - log2 (denominator)).

널리 쓰이는 버전은 약 20 + 4.3N 의 싸이클를 가지고있다
ARM프로세서가 이 좋지않은 연산은 피하는것이 바람직한다
가능하면 때때로 그런 표현은 곱셈에 의하여 나눗셈을 대체하여 다시 쓸수있다

예를들어 (a / b) > c 는 만약 b를 실제적이고[?] b*c가 integer범위안이라는것을 안다면 a > (c * b) 로 다시 쓰일수있다
이것은 unsigned 나눗셈은 하나의 오퍼랜드가 unsigned라는 책임을 진다면 이것은 signed 나눗셈보다 훨신더 빠를것이다



결합나눗셈 그리고 나머지
나눗셈 (x/y) 그리고 나머지(x%y)둘다 종종 필요한 케이스이다
그러한 케이스에 비추어보아 나눗셈펑션을 컴파일러에 결합하는것이좋다 왜냐하면 나눗셈펑션은 항상 나눈값과 나머지를 리턴하기 필요하다
만약둘다 필요하다면 우리는 이와같은 예제를 같이 쓸수있어야한다

int func_div_and_mod (int a, int b) {
return (a / b) + (a % b);
}



거듭제곱에 의한 나눗셈과 나머지
We can make a division more optimized if the divisor in a division operation is a power of two. The compiler uses a shift to perform the division. Therefore, we should always arrange, where possible, for scaling factors to be powers of two (for example, 64 rather than 66). And if it is unsigned, then it will be more faster than the signed division.



typedef unsigned int uint;

uint div32u (uint a) {
return a / 32;
}
int div32s (int a){
return a / 32;
}



나눗셈은 나눗셈 펑션이 불리워지는것을 피할것이다 그리고 unsigned 나눗셈은 signed 나눗셈보다 더 조금 불리워진다
signed 나눗셈은 더 많은 실행시간을 가질것이다 왜냐하면 이것은 슈프트 순환은 -무한대를 가르키는동안 0쪽으로 돌것이다



나머지연산을 위한 대안
우리는 나눗셈을 나머지연산 이용한다
그러나 이것은 때때로 문장을 체크할때 코드를 다시쓰게된다



이 두개의 예제를 잘 생각해보자



uint modulo_func1 (uint count)
{
return (++count % 60);
}



uint modulo_func2 (uint count)
{
if (++count >= 60)
count = 0;
return (count);
}

나눗셈보다는 if문이이 더 빠른 연산을 수행하기때문에 더 선호된된다. 또한 새로운 버전은 입력의 범위가 0-59일때만 작동한다는 것을 기억하라



배열의 인댁스 활용
만약 어떤 밸류에 의하여 특별한 변수를 선언하기를 희망한다면 이와같이 해라 :

switch ( queue ) {
case 0 : letter = 'W';
break;
case 1 : letter = 'S';
break;
case 2 : letter = 'U';
break;
}

if ( queue == 0 )
letter = 'W';
else if ( queue == 1 )
letter = 'S';
else
letter = 'U';


아래의 방법(더빠른)은 charater 배열의 인덱스를이용해 간단하게 이용할수있다 예 :
static char *classes="WSU"; letter = classesqueue;



전역 변수
전역변수는 절때 레지스터에 할당되지 않는다.
포인터를 사용하여 간접적으로 전역변수를 할당하거나 펑션 콜(함수호출)을 이용해서 전역변수를 변환할 수 있다

따라서, 컴파일러는 레지스터에 있는 전역 변수값을 캐시에 입력하지 못해, 그 결과 전역 변수를 이용하면 여분의 것(대개 불필요함)이 로드되고 저장된다.

우리는 그런까닭에 전역변수를 안쪽 루프에 써선안된다

어떠한 함수에서 전역 변수를 많이 이용한다면, 그러한 전역 변수를 지역 변수로 복사하는 것이 이득이므로 레지스터에 할당할 수 있다

이것은 유일하게 함수가 호출되었을때 전역변수는 사용되지 않는것이 가능하다

int f(void);
int g(void);
int errs;
void test1(void)
{
errs += f();
errs += g();
}
void test2(void)
{
int localerrs = errs;
localerrs += f();
localerrs += g();
errs = localerrs;
}


test1는 로드해야만한고 전역 errs벨류는 함수가 호출될때마다 증가된값이 저장되는것에 반해 test2의 localerrs는 레지스터에 저장하고 오직 하나의 명령만 저장된다는것에 주목하라


(별칭/가명)의 이용

Consider the following example -

void func1( int *data ) {
int i;
for(i=0; i<10; i++)
{
anyfunc( *data, i);
}
}

*data는 결코 바뀌지 않는다 해도 컴파일러는 anyfunc()가 그것을 변경하지 않았다는것을 알지 못하기때문에 그 프로그램은 이것이 사용될때마다 매번 메모리로부터 읽어들여야만 한다. -이것은 아마도 다른 어떤곳에서 변경되어지는 변수의 가칭일수도 있다. 만약 우리가 그것이 변경되어질수 없다는것을 안다면, 대신 우리는 그것을 이렇게 코딩할수 있을것이다

void func1( int *data )
{
int i;
int localdata;

localdata = *data;
for(i=0; i<10; i++)
{
anyfunc ( localdata, i);
}
}

이것은 컴파일러에게 보다 최적화될수있는 기회를 준다



Live variables 그리고 유출

어떤 프로세서는 일정한 레지스터들을 가지고있다, 거기에는 프로그램안에 레지스터들안에 다른 하나의 포인터를 가질수있다
어떤 컴파일러들은 빠른 live-range 지원해준다, 언제? 다른 레지스터에 변수뿐만아니라 메모리안에 다른함수의 부분에도 할당한다

변수의 live-range는 모든 문장 사이에 지정한 변수의 마지막 할당과 같이 정의한다 그리고 마지막 변수의 용법 전에 다음할당한다

이 정렬에서, 확실한 변수의 값은 확실하다, 따라서 이것은 사용이가능하다

live ranges사이에, 변수의 값는 필요하지 않다 : 이것은 죽엇다, 그래서 이 레지스터는 다른변수를 이용할수있고 컴파일러가 레지스터들의 보다많은 변수를 할당할수잇도록 허락해준다
몇몇의 레지스터들은 레지스터 할당변수는 가장작은 몇몇의 overlapping live-ranges at each point in a function 필요로한다
만약 이것을 넘게되면 몇몇의 레지스터들은 이용할수있게된다, 약간의 변수들은 임시메모리를 파괴해야만한다. 이것은 프로세서가 유출을 불러온다.

이 컴파일러의 유출은 아주조금 종종 변수의 처음에 익숙하다, 그래서 최소로 유출을 줄이는것이다. 변수의 유출은 다음을 피해야한다 :

*최대로 제한하는 몇몇의 살아잇는 변수 : 이 전형적으로 이루어지는 곁에 관리를 간단히 표시한다, 그리고 사용해서는 안되는 많은 함수안에 변수들.
나누어 큰 함수들은 더 작게, 간단한 것 또한 도움이 될지모른다.

*자주이용되는 변수들의 레지스터를 이용 : 이것은 레지스터 변수가 종종 이용되는 컴파일러 말한다, 그래서 이것은 레지스터를 할당하고 매우높은 우선순위 를 할수잇다. 그러나 그러한 변수들은 아마 조용한,얌전한 어떤환경에서 유출될것이다

번역한마디
억지도많고 이해가되지 않아서 억어지로 짜맞춘게 많아 ..
미안해 ㅤㅎㅛㅇ들 .. 좋은글 막 날로 먹으려고하고있어 ..
오래걸렸어 .. 적어도 일주일에 하나씩은 하려고 노력중이야 ..
앞으로는 좀더 그럴싸하게 할께 .. 미안해 ..
신고

'Programing > 최적화' 카테고리의 다른 글

C 코드 최적화  (2) 2008.07.21
C코드 최적화하기  (0) 2008.07.21
Posted by Frys

2008년 6월 9일 월요일

오후 6:58

원래 OpenCV에서 처리한 데이타는 Intel Image processing Library (IPL) 맞춰서 나옵니다. 그런데 이걸 일반적으로 윈도우에서 띄워 봐야 겠지요? OpenCV Yahoo group 보니까 이렇게 나와 있네요.

void  FillBitmapInfo1( BITMAPINFO* bmi, int width, int height, int bpp ) 
{
    BITMAPINFOHEADER* bmih = &(bmi->bmiHeader); 
    memset( bmih, 0, sizeof(*bmih)); 
    bmih->biSize   = sizeof(BITMAPINFOHEADER); 
    bmih->biWidth  = width; 
    bmih->biHeight = -abs(height); 
    bmih->biPlanes = 1; 
    bmih->biBitCount = bpp; 
    bmih->biCompression = BI_RGB; 
    if( bpp == 8 ) 
    { 
        RGBQUAD* palette = bmi->bmiColors; 
        int i; 
        for( i = 0; i < 256; i++ ) 
        { 
            palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i; 
            palette[i].rgbReserved = 0; 
        } 
    } 
} 
   
   
void ShowImage1( IplImage * src, HDC dc, int x, int y, int w, int h, 
                 int from_x, int from_y ) 
{ 
    if( src->width > 0 ) 
    { 
        uchar buffer[sizeof(BITMAPINFOHEADER) + 1024]; 
        BITMAPINFO* bmi = (BITMAPINFO*)buffer; 
        int bmp_w = src->width; 
        int bmp_h = src->height; 
   
        FillBitmapInfo1( bmi, bmp_w, bmp_h, src->nChannels*8 ); 
   
        int sw = MAX( MIN( bmp_w - from_x, w ), 0 ); 
        int sh = MAX( MIN( bmp_h - from_y, h ), 0 ); 
   
        int res = SetDIBitsToDevice( 
              dc,                        // handle to DC 
              x,                         // x-coord of destination upper-left corner 
              y,                         // y-coord of destination upper-left corner 
              sw,                        // source rectangle width 
              sh,                        // source rectangle height 
              from_x,                    // x-coord of source lower-left corner 
              from_y,                    // y-coord of source lower-left corner 
              from_y,                    // first scan line in array 
              sh,                        // number of scan lines 
              src->imageData + from_y*src->widthStep // array of DIB bits 
              (BITMAPINFO*)bmi,          // bitmap information 
              DIB_RGB_COLORS );          // RGB or palette indexes 
    } 
} 


     

이렇게 쓰면 되겠지요?

     
HDC  myDC= GetDC(0);  // THIS IS YOUR DESKTOP-WINDOW
                      //ADJUST IT TO YOUR WINDOW

ShowImage1(your_img ,myDC,0,0,100,100,0,0);
                      //ADJUST TO YOUR NEEDS


GetDC()
자기가 원하는 윈도우의 Handle DeviceContext 돌려주는 것이니 원하는 윈도우의 Handle
인자로 넣어 주시면 됩니다. 윈도우 API설명이 궁금하시면 Petzold Programming Windows 참고하세요.
번역본 역시 있습니다.

Programming Windows, 5/E : The Definitive Guide to the Win32 API - Petzold, Charles

실제 사용 법

HDC myDC= ::GetDC(m_hWnd);  // THIS IS YOUR DESKTOP-WINDOW
                            //ADJUST IT TO YOUR WINDOW
    
ShowImage1(ShowOrg ,myDC,10,10,320,240,0,0);        //ADJUST TO YOUR NEEDS
ShowImage1(ShowMark ,myDC,350,10,160,160,0,0);        //ADJUST TO YOUR NEEDS

::ReleaseDC(m_hWnd,myDC);

신고
Posted by Frys

2008년 6월 11일 수요일

오전 2:10

CString::GetAt // 인덱스 넘버에 의해 하나의 문자를 반환한다

CString s("abcdef");

ASSERT(s.GetAt(2) =='');

   

CString::SetAt // 인덱스 넘버에 의해 지정받은 한문자를 덮어쓴다

   

CString::Compare // CString 객체와 다른 문자열을 Windows CE IstrCmp 함수를 사용해서 비교한다

CString s1("abc");

CString s2("abd");

ASSERT(s1.Compare(s2) == -1);

   

CString::CompareNocase // CString 객체와 다른 문자열을 Windows CE lstrcmpi 함수를 사용해서 비교한다.  ^^ 대문자 소문자 상관없이 비교

CString s1("abc");

CString s2("ABC");

ASSERT(s1.CompareNoCase(s2) == -1);

   

CString::Mid // 메소드는 CString 객체의 0 위치부터 nCount 부분 문자열을 추출한다

CString s("abcdef");

ASSERT (s.MId(2,3) == _T("cde"));

   

CString::Left // 왼쪽부터 nCount 믄자열을 추출

CString s("abcdef");

ASSERT(s.Left(2) ==("ab"));

   

CString::Right // 오른족부터 nCount문자열을 추출

CString s("abcdef");

ASSERT(s.Right(2) ==("ef"));

   

신고
Posted by Frys



   

신고

'Capstone_졸작 > 작업페이지' 카테고리의 다른 글

IplImage를 윈도우에서 띄우기 (MFC, Windows API)  (0) 2008.06.12
문자열 처리 (CString)  (0) 2008.06.12
영상에서 마커 인식하기  (0) 2008.06.12
Linux 타이머 사용하기  (1) 2008.06.12
임베디드 opencv 처리 에러  (0) 2008.06.12
라인소스분석  (0) 2008.06.12
Posted by Frys

2008년 6월 3일 화요일

오전 12:20

목록열기

전체목록 (2200)

리눅스에서의 타이머 - Signal로 이용하기

EmbeddedLinux 강좌

2006/11/29 11:05

http://blog.naver.com/kingseft/140031659639

물론 디바이스 드라이버를 이용해서 CPU의 내장 타이머들을 이용해서 직접적으로 제어를 해도 되지만... 대략 1초 이상의 초단위 타이머를 사용하고자 한다면 심플하게 signal 함수를 이용해서 초단위의 타이머를 구현할 수 있다.

   

signal 중에서 SIGALRM 이 있는데 이 signal은 alarm 함수가 보내는 signal 이다. 이 alarm을 주기적으로 발생하고 signal을 처리 하면 리눅스에서도 타이머를 사용할 수 있다.

   

   

#include <stdio.h>

#include <signal.h>

#include <unistd.h>

   

#define INTERVAL        1

   

void mytimer(int signo)

{

        printf("mytimer called........\n");

        alarm(INTERVAL);       //  이렇게 해주면 알람처리후에 다시 알람이 등록되어 이후에도

                                        // 계속 구동되게 된다.

}

   

main()

{

        struct sigaction act;

   

        act.sa_handler = mytimer;

        sigemptyset(&act.sa_mask);

        act.sa_flags = 0;

   

        sigaction(SIGALRM, &act, 0);

   

        alarm(INTERVAL);

   

        while(1)

        {

                sleep(100);         

        }

}

   

   

신고

'Capstone_졸작 > 작업페이지' 카테고리의 다른 글

문자열 처리 (CString)  (0) 2008.06.12
영상에서 마커 인식하기  (0) 2008.06.12
Linux 타이머 사용하기  (1) 2008.06.12
임베디드 opencv 처리 에러  (0) 2008.06.12
라인소스분석  (0) 2008.06.12
라인트레이싱  (0) 2008.06.12
Posted by Frys

2008년 5월 23일 금요일

오전 10:09

에러 1 : 어떤 연산을 반복 하였을 때 메모리 부족 에러.(메모리 반환이 제대로 이루어 지지 않았음)

oom-killer: gfp_mask=0xd2

DMA per-cpu:

cpu 0 hot: low 14, high 42, batch 7

cpu 0 cold: low 0, high 14, batch 7

Normal per-cpu: empty

HighMem per-cpu: empty

   

Free pages: 1340kB (0kB HighMem)

Active:10941 inactive:3242 dirty:0 writeback:0 unstable:0 free:335 slab:477 mapped:10087 pagetables:46

DMA free:1340kB min:1024kB low:1280kB high:1536kB active:43764kB inactive:12968kB present:65536kB pages_scanned:7199 all_unreclaimable? no

lowmem_reserve[]: 0 0 0

Normal free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no

lowmem_reserve[]: 0 0 0

HighMem free:0kB min:128kB low:160kB high:192kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no

lowmem_reserve[]: 0 0 0

DMA: 37*4kB 19*8kB 3*16kB 1*32kB 1*64kB 1*128kB 1*256kB 1*512kB 0*1024kB 0*2048kB 0*4096kB = 1340kB

Normal: empty

HighMem: empty

Swap cache: add 0, delete 0, find 0/0, race 0+0

Free swap = 0kB

Total swap = 0kB

Out of Memory: Killed process 980 (video-capture).

Trying to free nonexistent resource <00000000-000fffff>

Killed

[root@KROBO nfs]$

   

   

파일 정보가 제대로 입력되었는지 확인…

fbvar.xres = 640, yres =480, bpp = 16

nSize =112, ID=0, nChannels = 3, alphaChannel = 0, depth = 8, channelSeq[0]=66, channelSeq[1]=71, channelSeq[2]=82, channelSeq[3]=0,

dataOrder =0, origin = 0, align = 4, width = 160, height=120, imageSize = 57600, widthStep = 480

   

typedef struct _IplImage

{

int nSize; /* sizeof(IplImage) */

int ID; /* version (=0)*/

int nChannels; /* Most of OpenCV functions support 1,2,3 or 4 channels */

int alphaChannel; /* ignored by OpenCV */

int depth; /* pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,

IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported */

char colorModel[4]; /* ignored by OpenCV */

char channelSeq[4]; /* ditto */

int dataOrder; /* 0 - interleaved color channels, 1 - separate color channels.

cvCreateImage can only create interleaved images */

int origin; /* 0 - top-left origin,

1 - bottom-left origin (Windows bitmaps style) */

int align; /* Alignment of image rows (4 or 8).

OpenCV ignores it and uses widthStep instead */

int width; /* image width in pixels */

int height; /* image height in pixels */

struct _IplROI *roi;/* image ROI. if NULL, the whole image is selected */

struct _IplImage *maskROI; /* must be NULL */

void *imageId; /* ditto */

struct _IplTileInfo *tileInfo; /* ditto */

int imageSize; /* image data size in bytes

(==image->height*image->widthStep

in case of interleaved data)*/

char *imageData; /* pointer to aligned image data */

int widthStep; /* size of aligned image row in bytes */

int BorderMode[4]; /* ignored by OpenCV */

int BorderConst[4]; /* ditto */

char *imageDataOrigin; /* pointer to very origin of image data

(not necessarily aligned) -

needed for correct deallocation */

}

IplImage;

   

소스

   

printf("nSize =%d, ID=%d, nChannels = %d, alphaChannel = %d, depth = %d, channelSeq[0]=%d, channelSeq[1]=%d, channelSeq[2]=%d, channelSeq[3]=%d, dataOrder =%d, origin = %d, align = %d, width = %d, height=%d, imageSize = %d, widthStep = %d\n", \

src_image->nSize,src_image->ID,src_image->nChannels,src_image->alphaChannel,src_image->depth,src_image->channelSeq[0],src_image->channelSeq[1],src_image->channelSeq[2],src_image->channelSeq[3],\

src_image->dataOrder,src_image->origin,src_image->align,src_image->width,src_image->height,src_image->imageSize,src_image->widthStep);

   

파일 정보 자체는 제대로 입력되어 있음.

   

   

윈도우 어플리케이션에서 실행

무리없이 잘 작동함. 똑같은 코드로 배열을 IplImage로 대입.

   

사용자 삽입 이미지

Threshold

결과값이 나오지 않음

   

EqualizeHist

화면이 지글거리며 노이즈 처럼 나옴.

   

   

윈도우 버전으로 했을때는 다 잘됨.

구현해놓은 RGB배열 to IPL도 문제없이 작동됨.

   

구글 검색을 통해 알게 된 정보에 의해 ./configure옵션에 -enable--static옵션을 추가함으로 정적 라이브러리를 사용하게 하였음. 그래도 안됨.

   

플롯팅 연산 처리 과정에서 문제가 발생함.

   

따라서.. 간단한 mat, cvCvtColor등을 제외하고는 함수가 제대로 작동하지 않음.

   

   

   

윈도 어플로 사각형 판별 완료

원 영상

cvCanny알고리즘 적용

Hough - trans알고리즘 적용

   

Square Sample 프로그램 적용

   

   

신고

'Capstone_졸작 > 작업페이지' 카테고리의 다른 글

영상에서 마커 인식하기  (0) 2008.06.12
Linux 타이머 사용하기  (1) 2008.06.12
임베디드 opencv 처리 에러  (0) 2008.06.12
라인소스분석  (0) 2008.06.12
라인트레이싱  (0) 2008.06.12
Line Trace  (0) 2008.06.12
Posted by Frys

   

  

   

  

2008년 4월 29일 화요일

오전 3:19

  

  

 

<file://D:\STUDY\Project\LineTracer>

  

  

  

     

  

   

(33294+27184 )/2=30239

  

 

  

   

  

    

  

 

현재 샘플링 하는 함수에 들어갔을때 데이터 갱신이 안됨.

  

    

   

   

  

   

  

  

  

 

VCC 가 연결이 안됐다!!!

푸른게 수광

없는게 발광

   

  

  

  
신고

'Capstone_졸작 > 작업페이지' 카테고리의 다른 글

Linux 타이머 사용하기  (1) 2008.06.12
임베디드 opencv 처리 에러  (0) 2008.06.12
라인소스분석  (0) 2008.06.12
라인트레이싱  (0) 2008.06.12
Line Trace  (0) 2008.06.12
라인센싱방법  (0) 2008.06.12
Posted by Frys

 

   

 

 

신고

'Capstone_졸작 > 작업페이지' 카테고리의 다른 글

임베디드 opencv 처리 에러  (0) 2008.06.12
라인소스분석  (0) 2008.06.12
라인트레이싱  (0) 2008.06.12
Line Trace  (0) 2008.06.12
라인센싱방법  (0) 2008.06.12
Servo_Motor  (0) 2008.06.12
Posted by Frys

2008년 2월 9일 토요일

오후 9:16

신고

'Capstone_졸작 > 작업페이지' 카테고리의 다른 글

임베디드 opencv 처리 에러  (0) 2008.06.12
라인소스분석  (0) 2008.06.12
라인트레이싱  (0) 2008.06.12
Line Trace  (0) 2008.06.12
라인센싱방법  (0) 2008.06.12
Servo_Motor  (0) 2008.06.12
Posted by Frys

   

   

   

주기를 10~20ms로 만들자 그리고 나서 생각하자.

   

   

   

   

신고

'Capstone_졸작 > 작업페이지' 카테고리의 다른 글

임베디드 opencv 처리 에러  (0) 2008.06.12
라인소스분석  (0) 2008.06.12
라인트레이싱  (0) 2008.06.12
Line Trace  (0) 2008.06.12
라인센싱방법  (0) 2008.06.12
Servo_Motor  (0) 2008.06.12
Posted by Frys
Capstone_졸작/구조2008.06.12 21:16

   

   

   

  

   

  



  

  

 

  

  

   

신고

'Capstone_졸작 > 구조' 카테고리의 다른 글

구조 규격  (0) 2008.06.12
보드 규격  (0) 2008.06.12
DSP 핀정의 2  (0) 2008.06.12
DSP2812 핀정의  (0) 2008.06.12
상판 규격 정의  (0) 2008.06.12
외부 구조 치수 계획  (0) 2008.06.12
Posted by Frys
Capstone_졸작/구조2008.06.12 21:15




   

신고

'Capstone_졸작 > 구조' 카테고리의 다른 글

구조 규격  (0) 2008.06.12
보드 규격  (0) 2008.06.12
DSP 핀정의 2  (0) 2008.06.12
DSP2812 핀정의  (0) 2008.06.12
상판 규격 정의  (0) 2008.06.12
외부 구조 치수 계획  (0) 2008.06.12
Posted by Frys

티스토리 툴바