November 19, 2024

디바이스마트 미디어:

[66호] 원하는 색상으로 제어가 가능한 아두이노 IoT 스마트 무드등 키트 -

2021-06-25

★2021 ICT 융합 프로젝트 공모전 결과 발표! -

2021-05-12

디바이스마트 국내 온라인 유통사 유일 벨로다인 라이다 공급! -

2021-02-16

★총 상금 500만원 /2021 ICT 융합 프로젝트 공모전★ -

2021-01-18

디바이스마트 온라인 매거진 전자책(PDF)이 무료! -

2020-09-29

[61호]음성으로 제어하는 간접등 만들기 -

2020-08-26

디바이스마트 자체제작 코딩키트 ‘코딩 도담도담’ 출시 -

2020-08-10

GGM AC모터 대량등록! -

2020-07-10

[60호]초소형 레이더 MDR, 어떻게 제어하고 활용하나 -

2020-06-30

[60호]NANO 33 IoT보드를 활용한 블루투스 수평계 만들기 -

2020-06-30

라즈베리파이3가 드디어 출시!!! (Now Raspberry Pi 3 is Coming!!) -

2016-02-29

MoonWalker Actuator 판매개시!! -

2015-08-27

디바이스마트 레이저가공, 밀링, 선반, 라우터 등 커스텀서비스 견적요청 방법 설명동영상 입니다. -

2015-06-09

디바이스마트와 인텔®이 함께하는 IoT 경진대회! -

2015-05-19

드디어 adafruit도 디바이스마트에서 쉽고 저렴하게 !! -

2015-03-25

[29호] Intel Edison Review -

2015-03-10

Pololu 공식 Distributor 디바이스마트, Pololu 상품 판매 개시!! -

2015-03-09

[칩센]블루투스 전 제품 10%가격할인!! -

2015-02-02

[Arduino]Uno(R3) 구입시 37종 센서키트 할인이벤트!! -

2015-02-02

[M.A.I]Ahram_ISP_V1.5 60개 한정수량 할인이벤트!! -

2015-02-02

구글 스케치업 (Google SketchUp)

메인

구글 스케치업은 건축, 인테리어, 미술, 각종 제품 디자인 등 다양한 분야에 활용할 수 있는 그래픽 프로그램 입니다.

3D 모델링을 손으로 스케치한 것처럼 표현력이 우수하고 직관적인 인터페이스로 초보자도 쉽게 제작이 가능합니다.

현재 많은 분들이 사용하고 있지만 아직 경험하지 못한 분들을 위해 간단한 스케치업 사용법을 알아보도록 하겠습니다.

참고로 포토샵 한번 다뤄본 경험이 없는 왕 초보 이니 부족한 점 양해 바랍니다.ㅜㅜ그럼 시작해 볼게요!

[참고] 스케치업은 무료 버전, 유료 버전(스케치업 프로)이 있으며 저는무료 버전을 기준으로 설명합니다.

기본화면

스케치업 기본 화면 입니다. 다운로드 및 설치에 관한 내용은 언급하지 않겠습니다. (언어 한국어)

중앙에 X축(빨간색), Y축(녹색), Z축(파란색) 이 있고, 상단 메뉴바는 프로그램의 기능들을 텍스트로 표시합니다.

[Alt] 키를 누른채 메뉴에 있는 명령어 첫 글자를 누르면 곧바로 해당 메뉴로 이동합니다.

메뉴바 아래로 도구 상자 들이 위치해 있습니다. 선택하기, 그리기, 수정하기, 화면보기 등 기본적인 기능들 입니다.

화면 좌측으로 대표 도구 모음이 있습니다. 3D 모델링 제작을 위한 기본, 편집, 보기 등이 모여 있습니다.

[참고] 대표 도구 모음은 [메뉴바] – [뷰] – [도구 모음] – [큰 도구 세트] 를 체크하면 나타납니다.

 

이제부터 간단한 모형을 직접 그려보면서 스케치업의 특징을 살펴 보도록 하겠습니다.

주택1

3D 도형을 만들기 위해서 우선 2D 도형을 만들어 줍니다. 이를 위해 가장 먼저 면[Face]을 만들어야 합니다.

중요한 점은 선으로만 이루어진 도형으로는 3D 도형을 만들 수 없습니다.

좌측 도구 모음에서 빨간원이 그려진 [직사각형] 툴을 클릭하고 화면의 한 곳을 클릭해서 시작점을 만들어 줍니다.

마우스를 원하는 방향으로 이동하면서 원하는 크기가 되면 다시 클릭하여 사각형을 만들어 줍니다.

시작점을 클릭 후 마우스를 이동하면 우측 하단에 치수가 표시 됩니다. 시작점을 클릭하고 마우스를 원하는 방향으로

이동하고 키보드에 자신이 원하는치수[500,400] 를 입력하고, [Enter] 키를 누르면 입력한 치수의 사각형이 만들어 집니다.

이 방법은 모든 툴의 치수 입력에 동일하게 사용됩니다. 원하는 툴[라인,사각형,원형 등]을 클릭하고시작점을 클릭 해주고

치수를 입력 후 엔터를 누르면 됩니다. 현재 길이는 [mm] 단위 이고, 스케치업을 시작할때 단위를 선택할 수 있습니다.

주택2

이번 툴은 스케치업에서 가장 재미 있고 핵심적인 기능 입니다. 좌측 도구 모음에서 밀기/끌기를 클릭합니다.

처음 만든 사각형에 면을 클릭(원하는 면) 하고 원하는 방향으로 마우스를 이동한 후 다시 클릭해 줍니다.

사진 처럼 2D 모델에서 3D 모델로 순식간에 변경이 됩니다.

푸시풀1

푸시풀(밀기/끌기) 기능은 복잡한 내용이 아니니 연습해 보시면 이해가 빠르게 됩니다.

이처럼 사각형 정면에 원형(면)을 그려주고 푸시풀 기능으로 구멍을 뚫어 주기도 가능 합니다.

푸시풀2

사각형 윗면에 작은 사각형을 그려주고 푸시풀 기능으로 돌출시킬 수 있습니다.

주택3

최초 사각형에서  좌측의 선[Line] 툴을 선택해 줍니다. 라인 툴은 스케치업의 가장 기본적인 툴로 연필 모양 아이콘 입니다.

라인툴을 이용해서 상단 중앙에 선을 그어 줍니다. 스케치업은 자동 스냅 기능이 있어 수직,수평선을 그리기 편리 합니다.

라인선은 X,Y,Z 축의 색상에 맞춰 자동으로 변경 됩니다. 예를 들어 그리는 선이 X축과 평행할 경우 빨간색으로 변하게 됩니다.

라인을 시작할 지점에 아이콘을 이동하면 위치에 따라서 중간점, 가장자리 라는 설명이 나옵니다.

주택4

이동[Move] 툴을 사용하여 중간선을 위로 이동시켜 줍니다. 이동툴은 선이나 면, 혹은 선택한 모형를 이동할때 사용합니다.

모형 전체를 이동할 경우 하나의 그룹으로 만들어 주고 이동 합니다.  마우스를 클릭한채 드래그 하여 모형 전체를 선택하고,

마우스 오른쪽 버튼을 누르고 그룹 만들기를 선택하시면 됩니다.

대략적인 모형이 나오네요.^^ 간단한 주택 모형을 만들어 가고 있습니다. 이제 유리창과 출입문을 만들어 보겠습니다.

주택6

사각툴을 사용하여 정면에 적당한 크기의 사각형(창문과 출입문)을 만들어 줍니다.

거기에 오프셋 툴을 사용하여 최초 사각형과 동일하지만 사이즈가 조금 작은외곽선 을 그려 줍니다.

그리고 푸시풀 툴을 사용하여 돌출시켜 줍니다. 대략적으로 창문과 출입문 느낌이 납니다. 저만 그런가요^^;

오프셋

오프셋 툴은 말로 설명하기 좀 애매하지만, 위 모형처럼 외곽선의 사이즈를 변화시켜 동일한 형태를 그릴 수 있는 툴 입니다.

다시 주택 제작으로 돌아 오겠습니다.

주택8

객체의 선택은 좌측의 선택[Select] 툴을 사용합니다. [Ctrl] 키를 누르고 주택 지붕의 2개 사선을 클릭합니다.

선택된 객체는 색상이 파란색으로 변합니다. 그리고 오프셋툴을 사용하여 위쪽에 평행한 사선을 만들어 줍니다.

평행한 사선 좌측과 우측에 라인툴을 이용해서 하나의 면[Face]으로 만들어 줍니다.

주택9

하나의 면[Face]이 만들어지면 3D 도형을 만들 수 있습니다. 푸시풀 툴을 사용하여 앞으로 돌출 시켜 주택 처마를 만듭니다.

주택10

궤도[Orbit] 툴을 사용하면 화면을 360도 자유롭게 회전이 가능합니다. 마우스를 클릭한채 원하는 방향으로 움직이면 됩니다.

간단한 방법으로 마우스 가운데 버튼을 누른채로 마우스를 움직이면 동일한 기능이 됩니다.

라인툴

선을 그리는 라인툴을 사용할때 스케치업은 자동으로 수직/수평을 맞추기 위한 가이드 라인이 보여 집니다.

위 사진처럼 Z축(파란색) 과 평행인 지점이 파란색으로 변하게 됩니다. 간혹 이런 가이드 라인을 잡기 위해

마우스를 이리저리 옮기는 경우가 많은데요. 키보드의 화살표키를 이용해서 각 축과 평행한 선을 만들 수 있습니다.

시작점을 클릭하고 [] 키를 누른채 드래그 하면 X축(빨간선)과 평행한 선을 그릴 수 있습니다.

다른 축도 마찬가지로 화살표 Z축(파란색) [], Y축(초록색) [] 키를 이용하시면 됩니다.

 

이번엔 전자부품 레귤레이터 소자를 스케치업을 사용해서 그려 보도록 하겠습니다.

소자1

사각형 툴을 사용해서 사각형을 그려 줍니다. 치수는 7805(TO-220) 도면을 참고 하였습니다.

소자2

푸시풀 툴을 사용해서 2D 모형을 3D 모형으로 만들어 줍니다.

소자3

줄자도구 툴을 사용해서 안내점을 만들어 주고, 라인 툴을 사용해서 가로선을 그려 줍니다. 하나의 면[Face] 이 만들어 집니다.

소자4

푸시풀 툴을 사용해서 3D 모형으로 만들어 주고, 줄자도구 를 사용해서 원의 중앙이 위치할 안내선을 그려주고 원을 그립니다.

상단 라인의 중앙은 마우스 포인트가 위치하면 현재 지점이 어느 위치 인지 자동으로 알려주고 있습니다.

소자6

푸시풀 툴을 사용해서 원을 밀어서(?) 공간을 비워 줍니다. 그리고 소자의 다리를 만들기 위해 우측에 사각형을 그려 줍니다.

소자7

다리의 상단에 라인 툴을 사용해서 작은 사각형을 그려주고, 푸시풀 툴을 사용해서 3D 모형으로 만들어 줍니다.

소자8

이렇게 하나의 작은 구성 요소가 완성되면, 전체 선택을 하고 마우스 오른쪽 버튼을 누르고 하나의 그룹으로 만들어 줍니다.

마우스를 드래그 하여 전체를 선택하거나, 마우스 왼쪽 버튼을 세번 클릭하면 연결된 모든 객체가 선택되어 집니다.

소자9

그룹으로 만든 모형은 전체가 동일하게 적용됩니다. 회전 툴을 사용하여 다리를 180도 회전 시켜 줍니다.

소자10

줄자도구 툴을 사용해서 다리가 위치할 안내선을 그려 줍니다. 이동 툴을 사용해 안내선 교차점으로 다리를 이동시켜 줍니다.

이동 툴을 누르고 [Ctrl] 키를 누르고 이동하면 똑같은 모양으로 복사되어 이동이 됩니다. 이동하면서 복사가 됩니다.

소자11

페인트통 툴을 사용해서 소자의 색상을 입혀 줍니다.  소자의 세세한 부분은 표현하지 않았습니다.

메인

일반적인 소자를 몇개 그려 보았습니다. 여기까지 스케치업의 간단한 기능을 살펴 보았습니다.

 

스케치업은 프로그램 다운로드 및 동영상 자습서를 통해서 기본 항목에서 고급 항목까지 학습을 도와 주고 있습니다.

스케치업 메인1

이번 글은 스케치업의 소개와 초보자도 쉽게 접할 수 있도록 간단한 내용으로 구성하였습니다.

개인적으로 DIY 작업에 사용할 도면을 스케치업으로 만들고 본 작업에 들어가니 매우 편리 합니다.

제 수준(?)이 좀 더 업그레이드 된 후 다시 알찬 내용으로 찾아 뵙도록 하겠습니다. ^^;

어려워 하지 마시고 한번 시작해 보시기 바랍니다. 부족한 글을 여기까지 읽어 주셔서 정말 감사드립니다.

[18호]파워서플라이 LP-3010, LP-303DU, LP-305DU 출시

18product001

18product001

(주)지이에스티에서 고기능의 직류 전원 공급기 3종을 출시했다. 순수 국내 기술로 만든 리니어 타입의 직류 전원 공급기로서, 정밀 실험에 적합하도록 설계했다. 출력을 스위치로 제어할 수 있도록 함으로써 사용자의 실수를 감소시킬 수 있으며, 자동 트래킹 기능으로 시리얼이나 패러럴로 사용시 볼륨 스위치로 간편하게 전압 및 전류 조절이 가능하다. 정전압과 정전류 모드를 지원하며, 과전압에 대한 보호회로도 장착되어 있다. 보다 자세한 사항은 디바이스마트 쇼핑몰을 통해서 확인할 수 있다.

■ LP-3010, LP-303DU, LP-305DU 특징 ■

·  가변 전류 : 0~10A, 0~3A 2CH, 0~5A 2CH 제공
·  가변 전압 : 0~30V, 0~30V 2CH 제공
·  심플한 디자인 및 사이즈
·  오토 트래킹 기능 내장
·  안정적인 전압, 전류 조정
·  자동 직렬 or 병렬연결 사용
·  사이즈 : 235 x 370 x 135
·  작은 리플 및 노이즈
·  보호회로 내장

제품구입하러가기

TEL. 032-765-7795
FAX. 032-765-7794
www.gestek.co.kr

[18호]한국전자제조 산업전을 다녀와서…

18hotep006
18hotep006 한국전자제조 산업전을 

다녀와서…

 

글 | 김형준 episode@ntrex.co.kr

SMT, PCB, Display, 인쇄전자 및 필름, 광학기기 산업 등 전자제조산업 전반에 걸친 기술들을 한눈에 볼 수 있는 ELECTROINCS MANUFACTURING KOREA 2013(이하EMK2013)이 지난 4월 3일(수)부터 5일(금)까지 3일 동안 코엑스에서 개최가 되었다.

지난 13년간 진행된 SMT/PCB & NEPCON KOREA 2013(국제표면실장 및 인쇄회로기판 생산기자재전)외에도 LED Packing EXPO(LED 장비재료산업전), Photonics Seoul(포토닉스서울), Film Technology Show(국제 기능성 필름산업전), Printed Electronics & Electronic Materials Show (국제 인쇄전자 및 전자재료 산업전)등 5개의 세부 전시회로 개최된 이번 EMK2013은 K.fairs, Reed Exhibitions, 한국광학기기협회(KOIA), J.EXPO가 공동 주최하며 지식경제부 후원, 삼성테크윈 협찬으로 국내외 약 25개국 260여 업체, 750개 부스의 큰 규모로 진행이 되었다.

18hotep003 18hotep004
18hotep005 18hotep007

이번 EMK2013은 삼성테크윈의 정밀전자 조립장비인 고속칩마운터 EXCEN을 비롯하여 YAMAHA사의 한국 공식 대리점인 ㈜NYS의 YSM400 등 정밀전자 조립장비 외에도 국내 인두기 및 공구 생산업체인 EXSO와 PACE 한국 총판 대리점인 테크노 통상 등 다양한 분야의 업체들이 참가해 이번 전시회를 빛냈다.

또한, EMK2013 전시 기간 중 IPC한국센터와 사단법인 한국전자기술협회가 주최하고 산업통상자원부의 후원으로 “제 1회 대한민국 국가대표 납땜왕 선발대회”가 열렸다. 학생부와 일반부로 나뉘어 진행이 된 이번 대회는 140여명의 대회 참가자 중 일반부 1등의 영광은 ㈜LIG넥스원에서 근무하고 계시는 여성참가자 이진영씨가, 학생부에서는 고려대학교 전자공학부 문환진씨가 1등을 차지하며 많은 이들의 축하를 받았다.

18hotep001 18hotep002

다양한 분야에 많은 업체들이 이번 전시회에 참여를 하였지만, 하나하나 일일이 돌아보지 못한 아쉬움을 뒤로 하고, 내년에는 올해보다 발전된 전시회를 기대해보며 이번 기사를 마친다.

 

 

[18호]JK전자와 함께하는 ARM 완전정복(4)-2

18jk
jk전자 JK전자와 함/께/하/는 ARM 완전 정복

Ⅱ.ARM Applications – 2부

글 | JK전자

(7) Stack Pointer 초기화

각 프로세서 모드별(7개 동작모드)로 Processor Mode를 전환하면서 Stack을 초기화해야 합니다. 이전 강좌인 ARM Architecture의 ARM Register 부분을 다시 보시면 R13(SP)는 각 동작 모드별로 뱅크 되어 있는 레지스터임을 확인 할 수 있습니다. 여기서 주의해야 할 점은 User Mode Stack은 제일 마지막에 초기화해야 합니다. 왜 그럴까요? User Mode는 비특권 모드이므로 한번 User Mode로 진입을 하여 SWI 명령등을 사용하지 않으면 다시 특권 모드로 진입을 할 수 없어서 다른 Processor Mode의 Stack Pointer를 초기화할 수가 없습니다. 이런 이유로 해서 User Mode의 Stack Pointer를 제일 마지막에 초기화하도록 해야 합니다.

;function initializing stacks
InitStacks
;Don’t use DRAM,such as stmfd,ldmfd……
;SVCstack is initialized before
;Under toolkit ver 2.5, ‘msr cpsr,r1′ can be used instead of ‘msr cpsr_cxsf,r1′
mrs r0,cpsr
bic r0,r0,#MODEMASK
orr r1,r0,#UNDEFMODE|NOINT
msr cpsr_cxsf,r1 ;UndefMode
ldr sp,=UndefStackorr r1,r0,#ABORTMODE|NOINT
msr cpsr_cxsf,r1 ;AbortMode
ldr sp,=AbortStackorr r1,r0,#IRQMODE|NOINT
msr cpsr_cxsf,r1 ;IRQMode
ldr sp,=IRQStackorr r 1,r0,#FIQMODE|NOINT
msr cpsr_cxsf,r1 ;FIQMode
ldr sp,=FIQStack ;// 아래(System Mode Stack) 부분 추가 함.bic r0,r0,#MODEMASK|NOINT
orr r1,r0,#SYSMODE
msr cpsr_cxsf,r1 ;SYSMode
ldr sp,=SYSStackbic r0,r0,#MODEMASK|NOINT
orr r1,r0,#SVCMODE
msr cpsr_cxsf,r1 ;SVCMode
ldr sp,=SVCStack;USER mode has not be initialized. We use always SVC mode.mov pc,lr ;The LR register won’t be valid if the current mode is not SVC mode.

(8) Segment Initialization

ROM Binary가 만들어 지고 나서 프로그램이 실행되기 위해서는 반드시 RAM이 있어야 합니다. Segment Initialization은 RAM에서 프로그램이 올바른 데이터를 가지고 실행될 수 있도록 RAM 영역에 전역변수의 초기값들을 저장하는 일을 합니다. 좀 더 자세한 사항은 이전 강좌인 ARM Architecture 4.4 Linker 부분을 참조하시기 바랍니다.

18feajkarm042

18feajkarm043

위의 그림에서 왼쪽이 ROM Binary 이고 오른쪽이 RAM 영역입니다.
- .data : C언어 등에서 선언한 전역변수들의 초기값이 저장되어 있는 영역입니다.
- .bss : 0으로 초기화 되는 영역입니다. 초기값을 지정하지 않은 전역변수등이 여기에 해당합니다.
- .textrw : 컴파일러에 의해 생성된 RAM에서 실행되는 함수들 입니다.

#pragma segment = “.bss”
#pragma segment = “.data”
#pragma segment = “.data_init”
#pragma segment = “.rodata”
#pragma segment = “.textrw”
#pragma segment = “.textrw_init”
#pragma segment = “CSTACK”
void InitSegment()
{
unsigned int k, n;
unsigned char *pdst, *psrc;//
// initialize zero-initialized segment
//
n = (unsigned int)__section_end(“.bss”) – (unsigned int)__section_begin(“.bss”);
pdst = (unsigned char *)__section_begin(“.bss”);for(k=0; k<n; k++)
{
*(pdst + k) = 0;
}
//
// initialize non-zero-initialized segment
//
n = (unsigned int)__section_end(“.data_init”) – (unsigned int)__section_begin(“.data_init”);
pdst = (unsigned char *)__section_begin(“.data”);
psrc = (unsigned char *)__section_begin(“.data_init”);
for(k=0; k<n; k++)
{
*(pdst + k) = *(psrc + k);
}//
// initialize segment for ram-function
//
n = (unsigned int)__section_end(“.textrw_init”) – (unsignedint)__section_begin(“.textrw_init”);
pdst = (unsigned char *)__section_begin(“.textrw”);
psrc = (unsigned char *)__section_begin(“.textrw_init”);
for(k=0; k<n; k++)
{
*(pdst + k) = *(psrc + k);
}return;
}

(9) main 함수로 이동

EXTERN main
ldr pc, =main

드디어 모든 Stack초기화와 Segment 초기화 과정을 끝내고 C 함수를 호출할 수 있게 되었습니다. 이제부터는 C 코드를 이용해서 각 디바이스들을 테스트할 수 있게 되었네요.

 

5.2 GPIO Output(LED On/Off)

LED를 컨트롤 하기 위해서는 포트를 Output으로 설정한 후에 GPIO(General Purpose Input Output) 포트에 Low or High를 출력하면 됩니다. 아래 회로도는 우리가 실습에 사용하고 있는 Mini2440의 LED 회로도입니다. 4개의 LED가 있는데 각 LED는 GPB5 ~ 8 에 연결이 되어 있습니다. 그렇다면 LED1을 켜기 위해서 GPB5 포트에 Low(0)로 해야 할까요? 아니면 High(1)로 해야 할까요? 정답은 LED의 한쪽 끝이 VDD33V에 연결이 되어 있기 때문에 Low로 설정해야 전류의 흐름이 발생하여 LED가 켜지게 됩니다.

18feajkarm045
실험예제
LED1, LED2는 On을 하고, LED3, LED4는 Off 시켜 봅니다.
(1) GPBCON 레지스터에 GPB5 ~ GPB8을 Output으로 설정합니다.

18feajkarm014

// GPB5 Output
(*(volatile unsigned *)0×56000010) = (*(volatile unsigned *)0×56000010) & ~(0×3 << 10);
(*(volatile unsigned *)0×56000010) = (*(volatile unsigned *)0×56000010) | (0×1 << 10);// GPB6 Output
rGPBCON = rGPBCON & ~(0×3 << 12);
rGPBCON = rGPBCON | (0×1 << 12);// GPB7 Output
rGPBCON = rGPBCON & ~(0×3 << 14);
rGPBCON = rGPBCON | (0×1 << 14);// GPB8 Output
rGPBCON = rGPBCON & ~(0×3 << 16);
rGPBCON = rGPBCON | (0×1 << 16);

(2) GPBDAT 레지스터의 GPB5, GPB6을 Low로, GPB7, GPB8을 High로 세팅합니다.

// LED1 On
rGPBDAT = rGPBDAT & ~(0×1 << 5);
// LED2 On
rGPBDAT = rGPBDAT & ~(0×1 << 6);
//rGPBDAT = rGPBDAT | (0×1 << 6);
// LED3 Off
rGPBDAT = rGPBDAT | (0×1 << 7);
// LED4 Off
rGPBDAT = rGPBDAT | (0×1 << 8);

diag.c – led_test()

// GPB5 Output
(*(volatile unsigned *)0×56000010) = (*(volatile unsigned *)0×56000010) & ~(0×3 << 10);
(*(volatile unsigned *)0×56000010) = (*(volatile unsigned *)0×56000010) | (0×1 << 10);// GPB6 Output
rGPBCON = rGPBCON & ~(0×3 << 12);
rGPBCON = rGPBCON | (0×1 << 12);// GPB7 Output
rGPBCON = rGPBCON & ~(0×3 << 14);
rGPBCON = rGPBCON | (0×1 << 14);// GPB8 Output
rGPBCON = rGPBCON & ~(0×3 << 16);
rGPBCON = rGPBCON | (0×1 << 16);// LED1 On
rGPBDAT = rGPBDAT & ~(0×1 << 5);
// LED2 On
rGPBDAT = rGPBDAT & ~(0×1 << 6);// LED3 Off
rGPBDAT = rGPBDAT | (0×1 << 7);
// LED4 Off
rGPBDAT = rGPBDAT | (0×1 << 8);

소스코드 분석

(*(volatile unsigned *)0×56000010) = (*(volatile unsigned *)0×56000010) & ~(0×3 << 10);
GPBCON 레지스터의 주소가 0×50000010입니다. 0×3(b11)을 왼쪽으로 10번 쉬프트를 하면 b110000000000이고 이것을 “~” (Bit clear) 시키면 GPB5[11:10] 부분이 “00″ 으로 Clear 됩니다.

(*(volatile unsigned *)0×56000010) = (*(volatile unsigned *)0×56000010) | (0×1 << 10);
0×1(b01)을 왼쪽으로 10번 쉬프트를 하면 b010000000000 이고 이것을 “|” (OR) 시키면 GPB5[11:10] 부분이 “01″ 으로 Output 설정됩니다.

소스 코드중에 volatile 이라는 것을 사용하고 있습니다. 이것은 컴파일러 최적화에서 제외되는 효과가 있습니다.

컴파일러 최적화 전 컴파일러 최적화 전
int a = 0; // 전역 변수
int b = 0; // 전역 변수
int i; // 로컬 변수for(i=0;i<100;i++)
b = b + a * 100;
int a = 0; // 전역 변수
int b = 0; // 전역 변수
int i; // 로컬 변수for(i=0;i<100;i++)
b = b + 0; // a * 100;

위의 코드에서 오른쪽 코드처럼 개발자가 의도하지 않게 컴파일러에 의해 최적화가 되어 “a*100″ 부분을 최적화하여 b 변수에 항상 0으로 저장이 되도록 할 수도 있습니다. 물론 일반적인 상황에서는 아무 문제가 되지 않지만 for 루프 수행중에 외부 인터럽트가 발생하여 인터럽트 서비스 루틴에서 a의 값을 0이 아닌 다른 값으로 변하게 한 후 다시 for 루프를 수행하면 개발자는 b에 0이 아닌 다른값이 저장되기를 기대하고 있겠지만 최적화된 코드에서는 b에 항상 0 이 저장이 되어 있을 것입니다. 이것은 개발자가 의도한 결과가 아닙니다. 이런 현상을 방지하기 위해서는 변수 a 를 volatile로 선언을 하면 됩니다. 특히나 SFR 레지스터 등에 값을 세팅하는 작업을 한다면 항상 volatile 로 선언을 해서 사용하는 것이 좋습니다.

5.3 GPIO Input( KEY Input) – Polling

LED를 켰을 때와 반대로 이번에는 GPIO포트를 이용해서 입력을 받아 보도록 하겠습니다. 포트에서 입력을 받기 위해서는 포트를 Input으로 설정한 후에 GPIO(General Purpose Input Output) 포트를 읽으면 됩니다. 6개의 KEY가 있는데 우리는 INT11, INT13에 연결되어 있는 K2, K3에 대해서 폴링 방식으로 입력을 감지해 보도록 하겠습니다. 참고로 폴링 방식이란 코드에서 무한 루프를 돌면서 특정 행위를 하는 것을 말합니다. 이번 경우에는 Key가 눌러졌는지를 감시하는 것이겠지요. 폴링과 다른 방식으로는 인터럽트 방식이 있습니다. 다음 절에서 공부하게 될 내용입니다.

18feajkarm044

실험예제
K2, K3 를 읽어서 KEY가 눌러져 있으면 LED2, LED3 를 On으로 하고, 눌러져 있지 않으면 Off로 설정해봅시다.

(1) GPGCON 레지스터에 GPG3, GPG5를 Input으로 설정합니다.

18feajkarm015

// KEY3, GPG5 Input
rGPGCON = rGPGCON & ~(0×3 << 10);
// KEY2, GPG3 Input
rGPGCON = rGPGCON & ~(0×3 << 6);

 

(2) GPGDAT 레지스터의 GPG3, GPG5를 읽어서 “0″ 이면 KEY가 눌린 상태이고, “1″ 이면 KEY가 눌러지지 않은 상태입니다.

diag.c – key_test()

// KEY3, GPG5 Input
rGPGCON = rGPGCON & ~(0×3 << 10);
// KEY2, GPG3 Input
rGPGCON = rGPGCON & ~(0×3 << 6);while(1)
{
if( (rGPGDAT & (0×1 << 3)) == 0 ) // KEY2 pressed
// LED2 On
rGPBDAT = rGPBDAT & ~(0×1 << 6);
else
// LED2 Off
rGPBDAT = rGPBDAT | (0×1 << 6);
if( (rGPGDAT & (0×1 << 5)) == 0 ) // KEY3 pressed
// LED3 On
rGPBDAT = rGPBDAT & ~(0×1 << 7);
else
// LED3 Off
rGPBDAT = rGPBDAT | (0×1 << 7);Delay(80);
};

 

5.4 GPIO Input( KEY Input) – Interrupt

이번에는 인터럽트 방식으로 K2, K3 입력을 감지해보도록 하겠습니다. 인터럽트 방식은 폴링방식보다 효율적이기는 하지만 좀 더 복잡합니다. 여기서 효율적이라고 하는 것은 CPU사용을 효과적으로 한다는 것이지 폴링방식보다 빠르다는 말은 아닙니다. 인터럽트 방식은 인터럽트 요청이 왔을때 인터럽트 서비스 루틴으로 분기할 때까지 지연시간이 있어 폴링 방식보다 느릴 수 있습니다. 하지만 폴링방식처럼 계속 루프를 돌면서 대기하지 않아도 되기 때문에 CPU활용면에서는 효율적입니다.

실험예제
K2, K3를 읽어서 KEY가 눌러져 있으면 LED2, LED3를 On으로 하고 눌러져 있지 않으면 Off로 설정해봅시다.

(1) GPGCON 레지스터에 GPG3, GPG5를 EINT(0×2)으로 설정합니다.

18feajkarm017

// KEY3, GPG5 EINT13
rGPGCON = rGPGCON & ~(0×3 << 10);
rGPGCON = rGPGCON | (0×2 << 10);// KEY2, GPG3 EINT11
rGPGCON = rGPGCON & ~(0×3 << 6);
rGPGCON = rGPGCON | (0×2 << 6);

(2) EXTINT1 레지스터에 EINT11, EINT13을 Falling Edge로 설정합니다.

Extintn(External Interrupt Control Register n)

The 8 external interrupts can be requested by various signaling methods. The EXTINT register configures configures the signaling method between the level trigger and edge trigger for the extemal interrupt request, and also configuress the signal polarity.
To recognize the level interrupt, the valid logic level on EXTINTn pin must be retained for 40ns at least because of the noise filter.

18feajkarm018

// set eint13 falling edge trigger
rEXTINT1 &= ~(0×7 << 20);
rEXTINT1 |= (0×2 << 20);// set eint11 falling edge trigger
rEXTINT1 &= ~(0×7 << 12);
rEXTINT1 |= (0×2 << 12);

 

(3) EINTPEND 레지스터의 Pending bit를 Clear 합니다.

18feajkarm019

// clear eint 11,13
rEINTPEND |= (1 << 11)|(1 << 13);

“It is cleard by writing 1″ 이라는 문구가 보이는데요. 무슨 의미 일까요? 일반적으로 우리가 SFR 레지스터를 설정할 때에 아래와 같이 먼저 Bit를 Clear 시키고 Set 하는 2단계 과정을 거치게 됩니다.

rEXTINT1 &= ~(0×7 << 20);
rEXTINT1 |= (0×2 << 20);

하지만 인터럽트 서비스 루틴 안에서는 이것 조차도 비효율적일수 있고 Atmoic Operation이 아니기 때문에 Pending Clear를 하는 과정에서 새로운 인터럽트가 발생할 경우에 새로운 인터럽트가 지연될 수도 있습니다.

rEINTPEND |= (1 << 11)|(1 << 13);

이러한 이유로 위와 같이 “1″ 을 써넣어서 바로 Pending을 Clear 할 수 있도록 한 것입니다.

 

(4) EINTMASK 레지스터에서 Interrupt를 Enable 합니다.

18feajkarm020

// enable eint 11,13
rEINTMASK &= ~((1 << 11)|(1 << 13));

 

(5) SRCPND 레지스터의 Pending bit를 Clear 합니다.
18feajkarm021

rSRCPND = (0×1<<5); // Clear pending bit

 

(6) INTPND 레지스터의 Pending bit를 Clear 합니다.
18feajkarm022

rINTPND = (0×1<<5); // Clear pending bit

 

(7) EINT 인터럽트 서비스 루틴의 함수 포인터를 설정합니다.

pISR_EINT8_23 = (U32)isr_eint_8_23;

 

(8) INTMSK에서 EINT8_23 의 인터럽트를 Enable 합니다.
INTERRUPT MASK(INTMSK) REGISTER
This register also has 32 bits each of which is related to an interrupt source. If a specific bit is set to 1, the CPU does not service the interrupt request from the corresponding interrupt source(note that even in such a case, the corresponding bit of SRCPND register is set to 1). If the mask bit is 0, the interrupt request can be serviced.
18feajkarm023

rINTMSK &= ~((0×1<<5));

 

(9) Startup코드의 IRQ Exception에서 인터럽트 번호를 확인하고 인터럽트 서비스 루틴으로 분기합니다.

IsrIRQ
sub sp,sp,#4 ;reserved for PC –> 1
stmfd sp!,{r8-r9} ; –> 2
ldr r9,=INTOFFSET ; –> 3
ldr r9,[r9] ; –> 4
ldr r8,=HandleEINT0 ; –> 5
add r8,r8,r9,lsl #2 ; –> 6
ldr r8,[r8] ; –> 7
str r8,[sp,#8] ; –> 8
ldmfd sp!,{r8-r9,pc} ; –> 9

▶ 1 : 스택포인터를 1 감소하여 빈 공간을 만듭니다. 인터럽트 핸들러 함수의 주소값이 저장될 공간입니다.
▶ 2 : r8, r9를 사용하고 복원해야 하므로 stack에 저장합니다. stmfd는 먼저 어드레스를 감소하고 저장하므로 1에서 만들어진 빈공간은 그대로 남아 있습니다.
▶ 3, 4 : INTOFFSET을 r9에 로드합니다.
▶ 5 : HandleEINT0를 r8에 로드합니다. HandleEINT0는 Startup 코드의 아래 부분에 정의되어 있습니다.
▶ 6,7 : HandleEINT0 + INTOFFSET*4의 수식이 됩니다. INTOFFSET 레지스터에는 발생한 인터럽트 소스의 Offset 값이 들어있는 레지스터입니다.

우리는 EINT8_23을 이용할 것이기 때문에 INTOFFSET 레지스터에는 5가 저장되어 있을 것입니다. 그러므로 HandleEINT0 + 5*4 즉 HandleEINT8_23의 주소가 됩니다.
소스코드에서 key_test_eint() 함수에서 pISR_EINT8_23 = (U32)isr_eint_8_23; 의 코드를 삽입하면 EINT8_23 인터럽트가 발생하면 isr_eint_8_23 함수로 분기를 하게 됩니다.

18feajkarm024
▶ 8 : str r8,[sp,#8] 에 의해서 맨 처음에 Stack의 빈공간을 만들어 두었던 곳으로 인터럽트 핸들러 함수의 포인터 주소값을 저장합니다.
▶ 9 : r8, r9 를 Stack에서 복원하고 pc 에 인터럽트 핸들러 함수의 주소가 저장이 되어 실제로 인터럽트 핸들러 함수가 실행이 됩니다.

 

(10) 인터럽트 서비스 루틴에서 GPGDAT 레지스터의 GPG3, GPG5를 읽어서 “0″ 이면 KEY가 눌린 상태이고, “1″ 이면 KEY 눌러지지 않은 상태입니다.

지금까지 설명한 인터럽트 관련 설정들을 S3C2440 인터럽트 컨트롤러 블럭도와 비교해서 보시기 바랍니다.

18feajkarm046
Interrupt MODE도 IRQ/FIQ 설정을 해야 하지만 S3C2440의 기본 인터럽트 설정 값이 IRQ 이기 때문에 생략하였습니다.
외부 인터럽트 1개를 서비스 받기 위해서 정말 많은 과정이 필요 하네요. ARM Applications 이후에 하게될 Cortex-M3 Architecture 부분에서도 인터럽트 서비스에 대해서 설명을 할텐데, 그때 Cortex-M3가 얼마나 간결하고 효율적인지 비교해 보시기 바랍니다.

diag.c – key_test_eint()

__irq __arm void isr_eint_8_23(void)
{
if(rINTPND==BIT_EINT8_23)
{
ClearPending(BIT_EINT8_23);
if(rEINTPEND&(1<<11))
{
rEINTPEND |= 1 << 11;
rGPBDAT = rGPBDAT ^ (0×1 << 6); // toggle
}
if(rEINTPEND&(1<<13))
{
rEINTPEND |= 1 << 13;
rGPBDAT = rGPBDAT ^ (0×1 << 7); // toggle
}
}
}
void key_test_eint(void)
{// KEY3, GPG5 EINT13
rGPGCON = rGPGCON & ~(0×3 << 10);
rGPGCON = rGPGCON | (0×2 << 10);
// KEY2, GPG3 EINT11
rGPGCON = rGPGCON & ~(0×3 << 6);
rGPGCON = rGPGCON | (0×2 << 6);// set eint13 falling edge trigger
rEXTINT1 &= ~(0×7 << 20);
rEXTINT1 |= (0×2 << 20);// set eint11 falling edge trigger
rEXTINT1 &= ~(0×7 << 12);
rEXTINT1 |= (0×2 << 12);// clear eint 11,13 –> clear by writing “1″ test
rEINTPEND |= (1 << 11)|(1 << 13);// enable eint 11,13
rEINTMASK &= ~((1 << 11)|(1 << 13));
rSRCPND = (0×1<<5); // Clear pending bit –> clear by writing “1″ test
rINTPND = (0×1<<5); // Clear pending bit –> clear by writing “1″ testpISR_EINT8_23 = (U32)isr_eint_8_23;
// Enable EINT8_23 Interrupt
rINTMSK &= ~((0×1<<5));}

 

5.5 Timer

임베디드 시스템에서 Timer는 가장 중요하고 필수적인 요소중의 하나로 OS에서 Task 스케줄링을 위해서 사용되기도 합니다. 전자액자, 차량용 블랙박스 등의 응용 어플리케이션에서도 특정 시간 이후에 인터럽트를 발생시켜 정해진 일을 수행하는 경우등 응용분야는 무수히 많습니다.
시간 이야기가 나와서 이야기 하는데 CPU의 클럭 속도가 1Hz 라는 것은 무엇을 의미하는 것일까요? 이것은 1초에 1번의 Tick이 발생한다는 것입니다. 아주 정확한 것은 아니지만 캐시가 있는 시스템에서 캐시 Hit(캐시에서 데이터 혹은 명령어를 가지고 올 경우)일 경우에 1초에 1개의 명령어를 수행한다는 것과 같습니다. S3C2440이 400MHz 로 동작한다면 1초에 4억번의 Tick이 발생하는 것입니다. 정말 어마어마 하지요. 상상해 보세요. 요즘 최신 스마트폰들은 2GHz CPU이면서 그것도 듀얼 코어입니다. 아래는 시간과 주파수 사이의 관계입니다. Timer 설정하는데 이정도는 알고 있어야 합니다. 잘 기억하시기 바랍니다.

- 1sec = 1,000ms = 1,000,000us = 1,000,000,000ns
- 1Hz = 1KHz = 1MHz = 1GHz

실험예제
S3C2440 CPU의 내장된 Timer4를 이용해서 1초에 한번씩 Timer 인터럽트를 발생시켜 LDE2를 Blink(On/Off) 하는 실험을 해보도록 하겠습니다.

(1) TCFG0 레지스터의 Timer4 Prescaler를 15로 설정합니다.

Timer CONFIGURATION REGISTER0 (TCFG0)
Timer input clock Frequency = PCLK/{prescaler value+1}/{divider value}
{prescaler value} = 0~255
{divider value} = 2,4,8,16

18feajkarm025

// Timer4 Prescaler = 15
rTCFG0 = 0×0;
rTCFG0 = (15 << 8);

 

(2) TCFG1 레지스터의 Timer4 Divider를 1/2 로 설정합니다.

18feajkarm026

// Timer4 Divider = 2
rTCFG1 = 0×0;
rTCFG1 = (0×0 << 16);

위와 같이 설정하면 Datasheet 에 있는 아래 공식에 의해서

Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
Timer input clock Frequency = 50000000/(15+1)/2 = 1562500

위의 계산식의 의미는 Timer4의 클럭이 1초에 1562500번 발생 한다는 의미입니다. 그러므로 Timer4 buffer count(TCNTB4)를 15625 로 설정해 주면 10ms마다 Auto Reload(Timer Overflow)가 되어 인터럽트가 발생하게 됩니다. 물론 간단하게 TCNTB4를 1562500로 설정해 주면 1초에 한번 발생하는 인터럽트를 만들 수 있겠지만, 안타깝게도 S3C2440의 타이머는 16비트 타이머이므로 최대 값으로 0xFFFF(65535) 이상을 사용할 수가 없습니다.

 

(3) TCNTB4 레지스터에 Timer4 buffer count를 15625로 설정합니다.

18feajkarm027

// buffer count
rTCNTB4 = 15625; // interrupt resolution 10msec

(4) TCON 레지스터에 Update TCNTB4와 Auto Reload를 설정합니다.

18feajkarm028

 

(5) TCON 레지스터를 설정하여 Timer를 Start 시킵니다.

// Start Timer 4
rTCON |= (1<<20);

 

(6) TCON에서 Manual Update를 해제하고 INTMSK에서 인터럽트 마스크를 Clear 시킵니다.

// clean manual update
rTCON &= ~(1<<21);// Enable interrupt
rINTMSK &= ~(0×1<<14);

이렇게 하면 Timer4 인터럽트가 10msec마다 한번씩 발생하게 됩니다.
18feajkarm040
diag.c – timer4_test()

__irq __arm void isr_timer4(void)
{
rSRCPND = BIT_TIMER4; //Clear pending bit
rINTPND = BIT_TIMER4;// Timer가 10msec 마다 발생하므로 1초에 한번씩 toggle 시키기 위해서
if( ++one_seconds_var == 100 )
{
rGPBDAT = rGPBDAT ^ (0×1 << 6); // toggle LED2
one_seconds_var = 0;
}void timer4_test(void)
{/*
Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
{prescaler value} = 0~255
{divider value} = 2, 4, 8, 16
period = (prescaler value + 1) * (divider value) * buffer count / PCLK = 10 ms
e.g.; PCLK = 50 Mhz
10 ms = (15 + 1) * 2 * 15625 / (50000 * 1000)
15626 = 10 ms * (50000 * 1000) / 2 / (15 + 1)
*/// Timer4 Prescaler = 15
rTCFG0 = 0×0;
rTCFG0 = (15 << 8);// Timer4 Divider = 2
rTCFG1 = 0×0;
rTCFG1 = (0×0 << 16); // Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value} // 50000000/(15+1)/2 = 1562500 –> 1초에 발생하는 Timer Tick// buffer count
rTCNTB4 = 15625; // interrupt resolution 10msecrTCON = rTCON & ~(0xffffff) | 0×1< rTCON = rTCON | (1 << 22); // Auto reloadpISR_TIMER4 = (int)isr_timer4;// Start Timer 4
rTCON |= (1<<20);// clean manual update
rTCON &= ~(1<<21);// Enable interrupt
rINTMSK &= ~(0×1<<14);

}

여기서 잠깐! 일반함수와 “__irq __arm” 가 있는 함수의 차이점은 무엇일까요?

일반 함수의 Return 부분

18feajkarm039

ISR 함수의 Return 부분

18feajkarm048
ISR 함수는 ARM Architecture에서 배운대로 Return을 SUBS PC,LR,#4로 하고 있습니다. 잘 기억이 나지 않으시면 ARM Architecture 자료에서 Pipeline과 Interrupt 부분을 다시 보시기 바랍니다.

5.6 PWM Buzzer

Buzzer, LED 등의 밝기 조정 등에 응용할 수 있습니다.

실험예제
Buzzer을 울리도록 하고 외부 인터럽트 방식으로 Key K2를 누르면 주파수를 10Hz 올리고 K3를 누르면 10Hz 주파수가 내려가도록 해봅시다.

18feajkarm049

 

(1) GPBCON 레지스터에서 GPB0를 TOUT0(Timer0 Output)으로 설정합니다.

18feajkarm029

rGPBCON &= ~0×3; // set GPB0 as tout0, pwm output
rGPBCON |= 0×2;

 

(2) TCFG0 레지스터의 Timer0 Prescaler를 15로 설정합니다.

PWM TIMER CONTROL REGISTERS
TIMER CONFIGURATION REGISTER0 (TCFG0)
Timer input clock Frequency = PCLK /{prescaler value+1} / {divider value}
{prescaler value} = 0~255
{divider value} = 2, 4, 8, 16

18feajkarm030

rTCFG0 &= ~0xff;
rTCFG0 |= 15; // prescaler = 15+1

 

(3) Timer0의 Divider 를 1/8 로 설정합니다.

18feajkarm031

rTCFG1 &= ~0xf;
rTCFG1 |= 2; // mux = 1/8

 

(4) Timer0의 Count buffer register, compare buffer register를 설정합니다.

18feajkarm032

rTCNTB0 = (Pclk>>7)/buzzer_freq;
rTCMPB0 = rTCNTB0>>1; // rTCNTB0/2

여기까지 설정을 하면 아래와 같이 1KHz의 주파수가 만들어집니다.

Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
Timer input clock Frequency = 50000000/(15+1)/8 = 390625 → 1초에 발생하는 Timer Tick

50000000Hz(PCLK) >> 7 = 390625 → 결국은 1초에 1번 1Hz값, buzzer_freq(1000)으로 나누어 주면 TCNTBn은 1KHz가 됩니다.

추가로 TCMPBn은 TCNTBn/2로 하면 최종적으로 1KHz인 Timer0(TOUT0)으로 1KHz인 PWM 파형이 출력됩니다.

 

18feajkarm050

 

(5) Timer0를 Start 시킵니다.

18feajkarm033

rTCON &= ~0x1f;
rTCON |= 0xb; //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
rTCON &= ~2; //clear manual update bit

diag.c – pwm_buzzer_test()

__irq __arm void isr_eint_8_23(void)
{if(rINTPND==BIT_EINT8_23)
{
ClearPending(BIT_EINT8_23);if(rEINTPEND&(1<<11)) // KEY2
{
rEINTPEND |= 1 << 11;
rGPBDAT = rGPBDAT ^ (0×1 << 6); // LED2 togglebuzzer_freq += 100;
pwm_buzzer_test();
}
if(rEINTPEND&(1<<13)) // KEY3
{
rEINTPEND |= 1 << 13;
rGPBDAT = rGPBDAT ^ (0×1 << 7); // LED3 togglebuzzer_freq -= 100;
pwm_buzzer_test();
}
}
}void pwm_buzzer_test(void)
{
rGPBCON &= ~0×3; // set GPB0 as tout0, pwm output
rGPBCON |= 0×2;rTCFG0 &= ~0xff;
rTCFG0 |= 15; // prescaler = 15+1rTCFG1 &= ~0xf;
rTCFG1 |= 2; // mux = 1/8// Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
// 50000000/(15+1)/8 = 390625 –> 1초에 발생하는 Timer Tick// PCLK = 50000000Hz >> 7 = 390625 –> 결국은 1초에 1번 1Hz 값이다.
// buzzer_freq(1000) 으로 나누어 주면 1KHz가 된다.
rTCNTB0 = (PCLK>>7)/buzzer_freq;rTCMPB0 = rTCNTB0>>1; // rTCNTB0/2

rTCON &= ~0x1f;
rTCON |= 0xb; //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
rTCON &= ~2; //clear manual update bit
}

 

5.7 UART

임베디드 프로그램에서 Timer와 함께 UART도 필수적인 인터페이스입니다. 개발초기에 디버그 모니터용으로 활용하기도 하고 개발이 완료된 이후에는 제품의 Setting, Firmware 업그레이드 등에도 활용합니다.

18feajkarm051

18feajkarm052

실험예제
UART0를 PC와 115200bps Baudrate로 통신(RX, TX)을 하는 Echo server로 만들어 봅시다.

(1) GPH2를 TXD로 GPH3을 RXD로 설정하고, GPH의 Pullup을 Disable 합니다.

18feajkarm034

rGPHCON &= ~( (0×3 << 6) | (0×3 << 4) );
rGPHCON |= ( (0×2 << 6) | (0×2 << 4) );
rGPHUP = 0x7ff; // The pull up function is disabled GPH[10:0]

 

(2) UART0의 FIFO Buffer를 Disable 합니다.

18feajkarm035

rUFCON0 = 0×0; //UART channel 0 FIFO control register, FIFO disable

 

(3) UART0의 Auto Flow Control을 Disable 합니다.

18feajkarm036

rUMCON0 = 0×0; //UART chaneel 0 MODEM control register, AFC disable

 

(4) UART Line Control Regier 설정 (Normal Mode, No Parity, One Stop Bit, Word Length = 8)

18feajkarm037

rULCON0 = 0×3; //Line control register : Normal,No parity,1 stop,8 bits

 

(5) UART Control Regier 설정

18feajkarm038

rUCON0 = (0×0 << 10) | (0×1 << 6) | (0×0 << 5) | (0×0 << 4) | (0×1 << 2) | (0×1 << 0);

 

(6) UART Baudrate Divisor Register 설정

18feajkarm047

rUBRDIV0 = ( (int)(Pclk/16./baud+0.5) -1 ); //Baud rate divisior register 0 value = 26

UBRDIV = (int)( UART clock / ( buad rate x 16) ) -1
( UART clock : PCLK, FCLK/n or UEXTCLK )

우리는 UART Clock으로 PCLK 50MHz(50000000)을 사용하고 있으므로 계산식은 다음과 같습니다.

UBRDIV = ( PCLK / (115200*16) ) – 1 = 26

diag.c – uart0_test()

void uart0_send_byte(int data)
{
while(!(rUTRSTAT0 & 0×2)); //Wait until THR is empty.
Delay(10);
WrUTXH0(data);}void uart0_test(unsigned int baud)
{
rGPHCON &= ~( (0×3 << 6) | (0×3 << 4) );
rGPHCON |= ( (0×2 << 6) | (0×2 << 4) );rGPHUP = 0x7ff; // The pull up function is disabled GPH[10:0]rUFCON0 = 0×0; //UART channel 0 FIFO control register, FIFO disable
rUMCON0 = 0×0; //UART chaneel 0 MODEM control register, AFC disable//UART0
rULCON0 = 0×3; //Line control register : Normal,No parity,1 stop,8 bitsrUCON0 = (0×0 << 10) | (0×1 << 6) | (0×0 << 5) | (0×0 << 4) | (0×1 << 2) | (0×1 << 0);rUBRDIV0 = ( (int)(Pclk/16./baud+0.5) -1 ); //Baud rate divisior register 0 value = 26while(!(rUTRSTAT0 & 0×4)); //Wait until tx shifter is empty.uart0_send_byte(‘E’);
uart0_send_byte(‘c’);
uart0_send_byte(‘h’);
uart0_send_byte(‘o’);
uart0_send_byte(‘\r’);
uart0_send_byte(‘\n’);while(1)
{
Delay(10);
while(!(rUTRSTAT0 & 0×1)); //Receive data ready
uart0_send_byte((*(volatile unsigned char *)0×50000024));
}

}

위의 코드는 터미널창에 “Echo” 를 Display 하고 터미널에서 입력한 문자를 바로 Echo 하여 다시 터미널 창에 표시하게 됩니다.

 

이것으로 ARM Applications 4부를 마치며,
다음호에서는 III. Cortex-M3 Architecture에 대하여 살펴보도록 하겠습니다. 

[18호]JK전자와 함께하는 ARM 완전정복(4)-1

18jk
jk전자 JK전자와 함/께/하/는 ARM 완전 정복

Ⅱ.ARM Applications – 2부

글 | JK전자

자료는 ARM을 처음 접하는 입문자로서 S/W 엔지니어 혹은 H/W 엔지니어를 대상으로 하였습니다. 처음에는 Cortex-M3 구조를 목표로 하였으나 전통적인 ARM(주로 ARM7, ARM9)의 구조에 대해서 먼저 이야기한 다음 Cortex-M3 구조에 대해서 하기로 마음을 고쳐 먹었습니다. Cortex-M3도 ARM 이기 때문에 전통적인 ARM의 구조에 대해서 잘 이해하고 Cortex-M3 구조와 비교해 보면서 공부한다면 큰 도움이 될 것입니다.

ARM Architecture 강의에서는 특정 CPU(S3C2440, STM32Fxx) 에 대한 내용 보다는 ARM Core의 이론적인 구조 자체에 대해서 많은 설명을 하였습니다. 이번 강의에서는 Samsung의 ARM9 CPU S3C2440 개발보드를 이용해서 실제 실습을 통해서 ARM에 대해서 공부해 보도록 하겠습니다.
I. ARM Architecture ~ IV. Cortex-M3 Applications 까지 분명 쉽지 않은 지루하고도 먼 여행이 되겠지만 ARM을 공부하는 임베디드 관련 엔지니어라면 언젠가는 한 번은 넘어야 하는 산이라 생각됩니다. 부디 이 자료가 ARM을 정복하는데 조금이라도 도움이 되시길 바랍니다.

 

강의 전체 로드맵

I. ARM Architecture | 임베디드 시스템 개론에 대한 설명과 ARM7, ARM9 의 구조에 대해서 설명합니다.
II. ARM Applications | 삼성의 S3C2440(ARM9) 개발보드(S3C2440 Mini 개발보드)를 이용해서 어셈블리어와 UART, GPIO 등을 실습합니다.
III. Cortex-M3 Architecture | Cortex-M3의 특징과 구조에 대해서 설명합니다.
IV. Cortex-M3 Applications | STM32F103VCT6 Dragon 개발보드를 이용해서 GPIO, LCD, SPI, UART, MP3, SDIO, I2C 등을 실습합니다.

이 강의 자료에 대한 모든 질의사항은 http://cafe.naver.com/avrstudio의 ARM Architecture Q&A게시판에 글을 남겨 주시거나 jk@deviceshop.net로 메일을 보내주시기 바랍니다. 가급적이면 여러 사람이 질문에 대한 답변을 공유할 수 있도록 네이버 카페 게시판을 이용해주셨으면 합니다. 감사합니다.

Ⅱ. ARM Applications 2부 목차

5. S3C2440 개발보드 실습
5.1 S3C2440 Startup 코드 분석
5.2 GPIO Output(LED On/Off)
5.3 GPIO Input(KEY Input) – Polling
5.4 GPIO Input(KEY Input) – Interrupt
5.5 TIMER
5.6 PWM Buzzer
5.7 UART

5장에서는 우리가 실습에 사용할 Mini2440 개발보드를 가지고 지금까지 이론으로만 공부했던 어셈블리어와 C언어를 이용해서 주변 장치들을 제어하는 실습을 해보도록 하겠습니다. 실제 타켓보드에 내가 작성한 프로그램을 다운로드하여 동작을 확인하는 일은 항상 가슴이 설레입니다.

5. S3C2440 개발보드 실습

5.1 S3C2440 Startup 코드 분석
ARM 개발보드의 부트코드(Startup)에는 지금까지 이론으로 배웠던 내용들이 거의 모두 포함이 되어 있습니다. 부트 코드만 잘 분석해도 CPU의 50% 이상은 알고 있다고 해도 과언이 아닙니다. 부트코드 기능을 간략하게 요약해 보면 아래와 같습니다.

- Clock & Power Initialization
- Setup each exception handler
- Memory (SDRAM) Initialization
- Peripheral Initialization
- Stack Initialization for each Processor Mode
- Interrupt Handler Setup
- Segment Initialization
- Jump to User Application

이제부터 부팅이 되는 순서대로 실제 코드를 분석해 보도록 하겠습니다.

(1) Exception Vector Table

__program_start
b ResetHandler
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt

CPU에 전원이 인가되면 처음으로 시작되는 Vector Table (0×0000 0000)입니다.

(2) Watchdog Disable
Watchdog가 무엇일까요? 직역을 하면 “지키는 개” 이런 뜻이네요. Watchdog는 보통 S/W적으로 설정한 시간동안 Kick(집지키는 개를 한번씩 차주어야 잠을 자지 않겠죠.)을 해주지 않으면 CPU를 Reset 시키는 기능으로 주로 사용합니다. 왜 이런 기능이 필요한 걸까요? 우리가 자주 사용하는 스마트폰을 예로 들어 보도록 하겠습니다. 스마트폰 사용중에 어떤 App을 실행시켰는데 그 이후로 스마트폰이 그 App때문에 터치도 되지않고, 전원 버튼도 입력이 되지 않게 먹통이 되었다고 가정을 하면 배터리를 분리시킨 후 다시 연결하는 방법 이외에는 방법이 없습니다. 이때 만약 Watchdog가 활성화 되어 있다면 스마트폰이 먹통이 되는 순간 S/W적으로 설정한 시간동안 Kick이 없으면 스마트폰이 Reset(재부팅)이 되어 다시 사용할 수 있는 상태가 될 것입니다. 배터리를 분리하는 것보다는 낫겠죠.
그리고 부트코드에서 Watchdog Disable 하는 것은 부팅이 완료되기도 전에 Watchdog에 의해서 CPU가 Reset이 되는 것을 방지하기 위해서 하는 것입니다.

18feajkarm001

0×5300 0000 번지의 SFR 레지스터를 제어하면 WTCON을 설정할 수 있습니다. 실제로는 5번 비트만 “0 = Disable” 해도 됩니다.

ResetHandler
ldr r0,=WTCON ;watch dog disable
ldr r1,=0×0
str r1,[r0]

(3) Interrupt Disable

부팅 중에 예측하지 못하는 인터럽트가 발생하지 않도록 Disable 하는 것이 안전합니다.

CPSR.I(1), CPSR.F(1) : 부팅 시에 “1″ 로 마스킹 되어 있습니다.

- S3C2440 CPU Level의 Interrupt Controller를 Disable 합니다.
Interrupt Mask Register
Interrupt SubMask Register

아래 그림은 S3C2440 CPU의 인터럽트 컨트롤러 블럭도입니다. “S3C2440 CPU Level 의 Interrupt Controller를 Disable” 한다는 것은 아래 블럭도에서 바로 “SUBMASK”, “MASK”를 Disable(“1” 로 Mask) 한다는 것입니다.

18feajkarm041

18feajkarm002

ldr r0,=INTMSK
ldr r1,=0xffffffff ;all interrupt disablestr r1,[r0] ldr r0,=INTSUBMSK
ldr r1,=0x3ff ;all sub interrupt disable
str r1,[r0]

 

(4) PLL 설정
우리가 사용하는 S3C2440 Mini 개발보드는 외부 Crystal로 12MHz를 사용합니다.
Input Frequency가 12MHz 일때, FCLK:HCLK:PCLK = 400MHz : 100MHz : 40MHz, 즉 1:4:8 비율로 분주가 되도록 설정합니다.

(4.1) Clock Divider Control Register(CLKDIVIN) 설정

18feajkarm003

 

18feajkarm004

CLKDIV_VAL 값이 “b00101”으로 세팅이 되어 있어서 FCLK:HCLK:PCLK = 1:4:8 비율로 분주가 되도록 설정이 됩니다.
최종적으로는 12MHz의 입력주파수를 받아서 동작 주파수가 400MHz가 되도록 설정합니다.

(4.2) UPLL Control Register(USB CLK) 설정

18feajkarm005
Upll = (m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV

Fin = FCLK 입력으로 들어오는 Crystal 주파수 12MHz
UPLL = ((56+8)*12) / ((2+2)*2*2) = 48MHz

18feajkarm006
UPLL은 USB컨트롤러에서 사용할 CLK으로 결국은 12MHz Crystal 입력을 받아서 48MHz를 만들어서 사용하고 있습니다.

(4.3) MPLL Control Register(Main CLK) 설정

Mpll = (2 * m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
MPLL = (2*(92+8)*12) / ((1+2)*2*1) = 400MHz

MPLL은 결국은 12MHz Crystal 입력을 받아서 FCLK = 400MHz, HCLK = 100MHz, PCLK = 50MHz를 만들어서 사용하고 있습니다. HCLK과 PCLK의 계산은 CLKDIV 레지스터 설정을 FCLK:HCLK:PCLK = 1:4:8 비율로 분주비를 설정했기 때문에 자동으로 계산이 됩니다.
- 참조로 PLL 계산 방식은 S3C2440 Datasheet를 참조하시기 바랍니다.

ldr r0,=CLKDIVN ; 0x4C000014
ldr r1,=CLKDIV_VAL ; CLKDIV_VAL=5 —> 1:4:8
str r1,[r0];Configure UPLL
ldr r0,=UPLLCON ; 0x4C000008
ldr r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV) ; U_MDIV=56, U_PDIV=2, U_SDIV=2
str r1,[r0];Configure MPLL
ldr r0,=MPLLCON
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)
str r1,[r0]

 

(5) Internal Bus Mode

     bl MMU_SetAsyncBusMode

- Synchronous : Core Clock System Clock(HCLK)에 동기화 되어 사용

- Asynchronous : System Clock(HCLK)과 관계없이 Free Running Clock(FCLK)을 이용

우리는 FCLK을 이용할 것이기 때문에 MMU_SetAsyncBusMode 함수를 호출하였습니다.

(6) Memory System 초기화
시스템에 연결된 FLASH, SDRAM, I/O Device 등과 같은 장치들을 제어하기 위해서 Memory Controller를 초기화해야 합니다.

- Access Timing
- Data Bus Width
- Wait Cycle
- Refresh Rate
- Bank Memory Size

소스 코드들이 꽤 알아먹기 힘든 코드들이네요. 복잡해 보이지만 하는 일은 BWSCON 주소의 SFR에 SMRDATA의 4Byte(32-bit) 데이터들을 루프를 돌면서 Write 하는 것입니다.

- SMRDATA : 메모리 컨트롤러 SFR에 기록할 내용들을 4Byte 길이로 해서 테이블 형태로 데이터를 순차적으로 가지고 있습니다.
- BWSCON : S3C2440 메모리 컨트롤러의 SFR 주소입니다.

소스 파일의 memcfg.inc에 다음과 같이 정의되어 있습니다.
;BWSCON
DW8 EQU (0×0)
DW16 EQU (0×1)
DW32 EQU (0×2)

WAIT EQU (0×1<<2)
UBLB EQU (0×1<<3)

B1_BWSCON EQU (DW32)
B2_BWSCON EQU (DW16)
B3_BWSCON EQU (DW16+WAIT+UBLB)
B4_BWSCON EQU (DW16) ; N.C.
B5_BWSCON EQU (DW16) ; N.C.
B6_BWSCON EQU (DW32) ; MINI2440 SDRAM (K4S281632C)-2M*16bit*4Bank*2, SDRAM(K4S561632C) 32MBx2, 32-bit
B7_BWSCON EQU (DW32) ; N.C.

SMRDATA의 맨 첫번째 데이터들이 무엇을 의미하는 것일까요?

(0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) –> (0+(0×2<<4)+(0×1<<8)+(0xD<<12)+(0×1<<16)+(0×1<<20)+(0×2<<24)+(0×2<<28))

상위 비트부터 살펴보도록 하겠습니다.

- (0×2<<28) = DW7[29:28] = Bank7에는 어떤 Memory 디바이스도 연결되어 있지 않습니다. 사실은 무의미한 코드입니다.

18feajkarm007
- (0×2<<24) = DW6[25:24] = Bank6은 SDRAM이 연결되어 있는 메모리 뱅크입니다. 데이터 라인은 32bit(2b10) 입니다.

18feajkarm008

- (0×1<<20) = DW5[21:20] = N.C(Not connected)

18feajkarm009
- (0×1<<16) = DW4[17:16] = N.C(Not connected)

18feajkarm010
- (0xD<<12) = ST3[15], WS3[14], DW3[13:12] = 16-bit DM9000 Ehternet 컨트롤러 설정입니다.

18feajkarm011

- (0×1<<8) = DW2[9:8] = N.C(Not connected)

18feajkarm012
- (0×2<<4) = DW1[5:4] = N.C(Not connected)

18feajkarm013
- (0×0) = DW0[2:1] = Read only 영역으로 OM[1:0] 핀에 의해서 결정됩니다. 우리가 사용하는 개발보드는 16bit Data width의 NOR Flash 입니다.

18feajkarm013
나머지 데이터 설정값들도 Datasheet를 참조해서 분석해 보시기 바랍니다.

;Set memory control registers
ldr r0,=SMRDATA
ldr r1,=BWSCON ; 0×48000000
add r2, r0, #52 ;End address of SMRDATA
L5
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne L5.
.
.
LTORG
SMRDATA DATA
DCD (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ;GCS0
DCD ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) ;GCS1
DCD ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) ;GCS2
DCD ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) ;GCS3
DCD ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) ;GCS4
DCD ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) ;GCS5
DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) ;GCS6
DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) ;GCS7
DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
DCD 0×32 ;SCLK power saving mode, BANKSIZE 128M/128M DCD 0×30 ;MRSR6 CL=3clk
DCD 0×30 ;MRSR7 CL=3clk
DATA

JK전자와 함께하는 ARM 완전정복(4)-2 에서 계속 됩니다.