이번에는 스위치와 7세그먼트를 이용하여 1에서 6사이의 숫자를 임의로 표시해 주는 전자주사위를 구현해 보겠다. 처음에는 7세그먼트의 테두리가 빙글빙글 돌고 있다. SW1을 누르면 임의로 1-6사이의 숫자가 빠르게 표시되다가 점점 느려진다. 이 때 숫자가 표시될 때마다 부저가 짧게 울린다. 마지막 숫자가 표시되면 2초 동안 멈추어 있다. 이후 다시 테두리가 돌아간다.


#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include "Am8USBasp.h"
void _delay_ms_var(uchar ucA);
#define NUMREPEAT 20
volatile uchar ucFlag = 0;
ISR(INT1_vect) {
   ucFlag = 1;
}
int main(void) {
   uchar ucaDice[6] = {0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d};
   uchar ucaCirc[6] = {0x03, 0x06, 0x0C, 0x18, 0x30, 0x21};
   uchar ucA=0, ucPrev, ucB, ucC;
   uint uiSeed = 0;
   InitAM8();
   GICR|=0b10000000; // External Interrupt(s) initialization
   MCUCR=0b00001000; // INT1: Falling Edge
   GIFR =0b10000000; // set interrupt flag as '0'
   sei();
   while(1) {
       if (ucFlag == 0) {
           ucA += (ucA==5) -5:1;
           SEG(ucaCirc[ucA]);
           _delay_ms(100);
           uiSeed++;
       } else { // if (ucFlag = 1)
           srand(uiSeed);
           ucPrev = rand()%6;
           for(ucB=1;ucB<=NUMREPEAT;ucB++) {
               do {
                   ucC = rand()%6;
               } while (ucC==ucPrev);
               SEG( ucaDice[ucC] );
               Beep;
               if (ucB == NUMREPEAT)
                   _delay_ms(2000);
               else
                   _delay_ms_var(ucB);
               ucPrev = ucC;
           }
           ucFlag = 0;
       } // else
   } // while(1)
}
void _delay_ms_var(uchar ucA) {
   do {
       _delay_ms(10);
   } while (--ucA>0);
}


이 예제에서 보면 while(1) 반복문이 크게 두 부분으로 나뉘어 있다는 것을 알 수 있는데 ucFlag==0 일 경우와 ucFlag==1 일 경우이다. 전자의 경우는 7세그먼트의 테두리가 돌아가도록 되어 있고 후자의 경우는 숫자들이 반복되어 표시되면서 주사위 숫자(1~6)중 하나를 표시하도록 되어 있다. 전역변수 ucFlag는 SW1이 눌려지면 ISR()함수 내부에서 1값으로 바뀌게 되어있으므로 volatile 키워드를 붙여서 정의하였음에 유의해야한다.


volatile uchar ucFlag = 0;


그 다음으로 설명할 점은 rand()함수의 사용법인데, 이 함수는 stdlib.h 함수에 정의되어 있으며 호출될 때마다 0 에서 RANDOM_MAX 상수 ( 0x7FFFFFFF 값으로 stdlib.h에 정의되어 있음) 사이의 정수를 임의로 발생시키는 함수이다. 그런데 이 함수는 srand()함수로 초기화를 시킨 이후에 사용을 해야지만 진정한 난수를 발생시킬 수 있다. srand()함수를 임의의 시드값으로 초기화 시키지 않으면 시스템이 재시작될 때마다 똑같은 난수가 발생하므로 진정한 난수라고 할 수 없는 것이다. 보통 PC환경에서는 이 시드값으로 시스템의 시간관련 값을 사용하는데 여기서는 그럴 수 없으므로 uiSeed변수를 두어 이 변수값은 계속 증가하게끔 하였다. 그리고 srand()함수를 초기화시킬 때 uiSeed 변수를 두어서 버튼이 눌려질 때 이 값으로 초기화를 시켜주면 그 시점에서의 uiSeed변수 값은 스위치를 누른 시점에 의존하므로 누를 때마다 달라질 것이다. 이렇게 하여 버튼이 눌려질 때마다 다른 시드값으로 초기화되어 진정한 난수가 발생되게 된다.


 그리고 _delay_ms()함수는 입력으로 상수 값만을 받으며 변수 값을 입력으로 줄 수 없다. 따라서 숫자가 표시되는 시간을 점점 늘려가도록 하는데 사용하기 위해서 _delay_ms_var()함수를 별도로 정의하여 (들어온 변수값 × 10ms) 시간 동안 지연할 수 있도록 작성하였다.


 이 프로그램을 PC에서 컴파일하면 958byte (전체 플래시롬의 약 11%)의 실행파일이 생성되었다. 따라서 ATmega8(A)에 꽉 차는 정도의 길이로 프로그램을 하려면 이 예제의 약 10배 정도 (대충 A4용지 10페이지 분량)는 작성해야 함을 알 수 있다.



Posted by 살레시오
,

 외부 인터럽트를 실험하기 위해서 INT1핀에 택스위치를 연결한 회로도를 [그림 1]에 도시하였다. 풀업 저항이 AVR내부에 내장되어 있기 때문에 외부에 별도로 풀업 저항을 달아주지 않아서 회로가 굉장히 간단해짐을 알 수 있다. 대신 내부에서 이 두 핀에 풀업 저항을 연결하려면 DDRD3 = '0', PORTD3 = '1'로 설정하여야 한다.


[그림 1] 외부 스위치 회로도


전기적인 접점을 갖는 스위치를 연결하여 사용할 때 꼭 고려해야 될 사항이 있는데 그것은 진동(bouncing) 현상이다. 이것은 전기적인 접점이 닫히거나 열릴 때 그 양단의 전기적인 신호가 깔끔하게 ‘1’에서 ‘0’으로 또는 ‘0’에서 ‘1’로 변하는 것이 아니라 아주 짧은 동안에 접점이 열렸다 닫히기를 반복하는 현상이다. [그림 2]는 접점이 떨어지는 순간의 진동 현상을 보여준다.


[그림 2] 기계적인 접점을 갖는 스위치의 바운싱 현상


이러한 바운싱을 적절히 처리하지 못하면 매우 빠른 속도로 처리되는 uC에서는 스위치가 여러 번 눌려진 것으로 인식하며 이로 인해서 ‘rising edge'나 ’falling edge'에서 인터럽트가 의도하지 않게 여러 번 발생하게 된다. 이러한 기계적인 접점의 진동을 제거하는 것을 디바운싱(debouncing)이라고 하며 크게 하드웨어적인 회로를 이용하는 법과 소프트웨어적인 방법 두 가지로 나뉜다. 많이 사용되는 하드웨어 디바운싱회로를 [그림 3]에 도시하였다.

[그림 3] 디바운싱 회로의 예들 (a) 좌측, (b) 우측


[그림 3]의 (a)회로도를 보면 스위치 양단에 커패시터를 병렬로 달아서 전압 리플을 억제해주는 가장 간단한 회로이다. (b)회로도는 여기에 슈미트 트리거를 추가하여 작은 리플도 제거해주는 회로이다.


 소프트웨어 디바운싱은 하드웨어의 추가 없이 프로그램으로 진동을 제거하는 방식으로 진동이 보통 10ms(길어야 20ms)정도 지속된다는 점에서 착안하여 처음 감지된 이후 일정 시간이 지난 후에도 그 값이 유지가 되는지를 확인하는 알고리즘을 프로그램으로 작성하는 것이다.


 여기에서는 바운싱을 제거하기 위해서 푸시 스위치와 병렬로 1uF의 커패시터를 각각 연결한 가장 간단한 회로를 택하였으며 이 커페시터가 양단의 전압이 급격하게 변하는 것을 어느 정도 억제해 준다.


 내부에 풀업 저항을 연결하였다면 스위치가 안 눌려졌을 때는 INT0/INT1핀으로 ‘1’신호가 입력이 될 것이다. 만약 스위치가 눌려졌다면 이 핀들은 GND에 연결이 되기 때문에 ‘0’신호가 읽려진다. 따라서 스위치를 누르는 순간 1→0으로 변하게 되고 이렇게 변하는 순간을 하강 엣지라고 한다. 반대로 스위치를 누르고 있다가 떼는 순간 0→1로 신호가 바뀌게 되며 이러한 순간을 상승 엣지라고 한다.


스위치 실험 1

 첫 번째 스위치 실험으로 SW1을 누를 때마다 짧게 부저가 울리면서 LED의 상위 니블과 하위니블의 위치가 바뀌는 프로그램을 작성해 보자. 인터럽트는 하강 엣지, 즉 스위치를 누르는 순간에 걸리도록 설정했다.


#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "Am8USBasp.h"
volatile uchar ucLed = 0x0F;
ISR(INT1_vect) {
   BuzzOn;
   _delay_ms(10);
   BuzzOff;
   LED( ucLed=~ucLed );
}
ISR(BADISR_vect) {}
int main(void) {
   InitAM8();
   GICR|=0xC0; // External Interrupt(s) initialization
   MCUCR=0x0A; // INT0: Falling Edge, INT1: Falling Edge
   GIFR=0xC0;
   sei();
   LED(ucLed);
   while(1);
}

 이 예제를 보면 먼저 main()함수 내부의 while(1);에서 무한루프에 빠진다는 것을 알 수 있다. 즉, main()함수 내부의 동작은 여기에서 멈추지만 SW0을 누를 때마다 하드웨어적으로 인터럽트가 걸리게 되고 ISR(INT0_vect)함수가 호출이 된다. 이 함수 내부에서 부저를 짧게 울리고 LED출력(PORTB)를 반전시킨 후 다시 main()함수로 돌아와 무한루프를 계속 돌게 된다. 이것이 인터럽트가 운용되는 기본적인 구조이다. 여기에서는 단순히 무한루프에 빠져 있지만 중요한 점은 main()함수 내에서 스위치의 동작을 검출하려는 어떠한 코드도 없다는 것이다.

스위치 실험 2

 두 번째 스위치 실험으로 SW1을 누르면 LED가 위로 한 칸씩 움직이고 끝까지 가면 다시 처음 위치로 되돌아가는 프로그램을 작성해 보자. 이전 실험과 마찬가지로 스위치를 누를 때마다 부저가 짧게 울리게끔 해보자.


#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "Am8USBasp.h"
volatile uchar ucLed = 0x01;
ISR(INT1_vect) {
   ucLed = (ucLed == 0x80) 0x01:(ucLed<<1);
   LED(ucLed);
   Beep;
}
ISR(BADISR_vect) {}
int main(void) {
   InitAM8();
   GICR|=0xC0; // External Interrupt(s) initialization
   MCUCR=0x0A; // INT1: Falling Edge
   GIFR=0xC0;
   sei();
   LED(ucLed);
   while(1);
}



Posted by 살레시오
,

 ATmega8A의 외부 인터럽트를 받아들이는 핀은 [그림 1]에 표시된 바와 같이 두 개가 있다. ‘외부 인터럽트를 받아들인다’는 것은 쉽게 설명하면 이 핀에 걸리는 전압의 변화를 감지하여 그 시점에서 특정한 일을 수행토록 할 수 있다는 의미이다. 예를 들어 INT0핀(4번핀)의 전압이 5V에서 0V로 떨어지는 순간 ISR함수를 호출한다든가 혹은 0V에서 5V로 올라가는 순간 ISR 함수를 실행토록 할 수 있다는 말이다. 이러한 외부 인터럽트 핀을 이용하여 버튼이 눌려지는 것을 감지한다든가 혹은 센서의 측정값이 변하는 것을 감지한다든가 하는 응용을 할 수 있다.


[그림 1] ATmega8(A)의 외부 인터럽트핀


일반적으로 PD2:3핀을 외부인터럽트로 사용하려면 외부 소자에서 신호를 읽어들여야 하기 때문에 방향을 입력(DDRD2='0', DDRD3='0')으로 설정한다. 하지만 방향을 출력으로 설정해 놓아도 인터럽트를 발생시킬 수 있는데 이 경우는 PORTD2 비트나 PORTD3 비트를 조작하여 (즉, 외부 요인이 아니라 내부 프로그램에 의해서) 그 신호 변화에 따라서 내부적으로 인터럽트를 발생시킬 수 있다. 이러한 방식을 ‘소프트웨어 인터럽트’라고 한다.


 관련된 레지스터들로서 일단 MCUCR(MCU Control Register)이 있다.

[그림 2] MCUCR

[그림 2]에서 외부 인터럽트와 관련된 비트는 0번부터 3번까지 4비트이다. ISC01:0 두 비트로 INT0의 동작을 설정하며 ISC11:0 두 비트로 INT1의 동작을 설정한다.


[표 1] 인터럽트 0/1의 발생 제어

ISCn1

ISCn0

동작

0

0

INTn의 low level에서 인터럽트 발생

0

1

INTn의 신호가 변하면 인터럽트 발생

1

0

INTn의 ‘falling edge’에서 인터럽트 발생

1

1

INTn의 ‘rising edge’에서 인터럽트 발생


ISCn1:0='00'이면 INTn핀이 '0'값이면 인터럽트가 무조건 발생한다. 즉, INTn핀이 ‘0’이 되면 ISR(INTn_vect)함수를 호출하고 이 함수가 끝나고 나서도 또 ‘0’이라면 다시 인터럽트가 발생해서 ISR(INTn_vect)함수를 다시 호출하게 된다. ISCn1:0='10'이면 하강 엣지(edge) 에서 인터럽트가 발생하게 되는데 ’1‘에서 ’0‘으로 변하는 순간을 하강 엣지라고 한다. 반대로 ISCn1:0='11'이면 상승 엣지에서 인터럽트가 발생하게 되는데 ’0‘에서 ’1‘로 변하는 순간을 상승 엣지라고 한다. ISCn1:0='01'로 설정하면 하강 엣지와 상승 엣지 두 경우 모두에서 인터럽트가 발생하게 된다. 다음 <그림 6.3.2>에 이들 각각의 경우를 도시하였다. 이 그림에서 하단의 위쪽으로 향하는 화살표는 인터럽트 요구를 표시한다.


[그림 3] 외부 인터럽트 동작 모드


GICR 레지스터의 구조는 다음과 같다.


[그림 4] GICR 레지스터


INT0비트가 세트(‘1’)되고 SREG의 I비트가 세트(‘1’)되었다면 외부 인터럽트 0번은 활성화된다. 마찬가지로 INT1비트가 세트(‘1’)되고 SREG의 I비트가 세트(‘1’)되었다면 외부 인터럽트 1번은 활성화된다.


 외부 인터럽트를 활성화시키기 위한 SFR의 설정을 다음과 같이 요약할 수 있다.

       ( 필요하다면 PD2(PD3)핀에 내부 풀업 저항을 연결한다.)

       ① 인터럽트 0번(1번)의 동작을 MCUCR을 설정하여 정한다.

       ② GICR레지스터의 INT0(INT1)비트를 세트시킨다.

       ③ SREG레지스터의 I비트를 세트시킨다.

이렇게 설정이 완료된 시점 이후부터는 INT0(INT1)핀에서 전기적인 신호가 변하면 인터럽트가 발생하게 된다.



Posted by 살레시오
,

 AVR툴체인으로 인터럽트를 처리하기 위해서 정해진 형식을 가지는 함수를 작성해야 한다. 어떤 인터럽트가 발생했을 때 이를 받아서 처리하는 함수를 ISR함수라고 하며 이 형식은 <interrupt.h>헤더파일에 정의되어 있다. 따라서 인터럽트를 사용하기 위해서는 이 헤더파일을 반드시 인클루드 시켜줘야 하며 ISR 정의 형식은 다음과 같다.


#include <avr/interrupt.h>
ISR(<vector>) {
   // 함수의 본체
}


여기서 <vector>는 발생한 인터럽트가 어느 것이냐에 따라 미리 정해진 식별자이며 ATmega8A의 경우 다음 표와 같이 정의된다.


[표 1[ ATmega8(A)의 인터럽트 벡터

번호

<vector>

인터럽트 종류

비고

1

INT0_vect

외부 인터럽트 0번

외부

발생

2

INT1_vect

외부 인터럽트 1번

3

TIMER2_COMP_vect

타이머/카운터2 비교매치

내부

발생

4

TIMER2_OVF_vect

타이머/카운터2 오버플로

5

TIMER1_CAPT_vect

타이머/카운터1 캡춰

6

TIMER1_COMPA_vect

타이머/카운터1 비교매치A

7

TIMER1_COMPB_vect

타이머/카운터1 비교매치B

8

TIMER1_OVF_vect

타이머/카운터1 오버플로

9

TIMER0_OVF_vect

타이머/카운터0 오버플로

10

SPI_STC_vect

직렬 전송 완료

11

USART_RXC_vect

USART 읽기(Rx) 완료

12

USART_UDRE_vect

USART 데이터레지스터 비워짐

13

USAER_TXC_vect

USART 쓰기(Tx) 완료

14

ADC_vect

ADC 완료

15

EE_RDY_vect

EEPROM 준비완료

16

ANA_COMP_vect

아날로그 비교

17

TWI_vect

TWI 인터페이스

18

SPM_RDY_vect

프로그램메모리 쓰기 준비완료


AVR-Toolchain에서는 ISR을 작성하지 않은 인터럽트가 발생하면(즉, 인터럽트 발생은 허용시켰는데 해당하는 ISR함수가 없다면) 리셋 벡터로 점프하도록 내부에서 처리하고

있다. 이것을 회피하려면 다음과 같이 ISR함수를 정의하면 ISR함수가 없는 모든 인터럽트를 처리하도록 할 수 있다.


#include <avr/interrupt.h>
ISR(BADISR_vect) {
   // 함수의 본체
}


또한 인터럽트와 관련된 매크로함수로서 sei(), cli() 함수가 있다. sei()함수는 인터럽트 발생을 전역적으로 허용하는 것이고, cli()함수는 반대로 인터럽트의 발생을 전역적으로 허용치 않도록 설정하는 함수이다.


 어떤 경우에는 ISR()함수와 다른 함수 (예를 들어서 main()함수) 들과 변수를 공유할 수도 있다. 이 경우에는 그 변수를 반드시 전역변수로 선언해야 하며, 한 가지 주의할 점은 만약 ISR()내부에서 이 변수가 갱신된다면 반드시 volatile 키워드를 붙여서 선언해야 한다는 점이다. 다음의 예를 보자.


#include <avr/interrupt.h>
int myValue; //<-
ISR(INT0_vect) {
   myValue++;
}
int main(void) {
   ⋮
   while (myValue == 0); // wait for interrupt
       TurnLEDOn;
   ⋮
}


위의 예에서 main()함수의 while()문 안에서는 myValue 변수 값이 변하지 않으므로 무한루프에 빠지게 된다. 이것의 내부적인 동작 방식을 보면 myValue==0 조건을 검사하기 위해서 맨 처음에는 myValue값을 읽어서 레지스터에 저장하고 그 다음 반복부터는 그 레지스터에 저장된 값(맨 처음 읽어들인 값)을 0과 비교하게 되므로 값이 ISR( )함수에서 갱신된 값이 반영이 안 되는 것이다. 따라서 무한루프에 빠지게 되고 여기서 프로그램이 멈추게 된다. 하지만 전역변수 myValue를 다음과 같이 volatile형으로 정의하면 매 반복마다 myValue값을 다시 읽어 들이게 되므로 ISR( )함수 내부에서 갱신된 값이 반영이 된다.


volatile int myValue;


 참고로 AVR은 하드웨어적으로 인터럽트 처리가 시작되면 SREG의 글로벌 인터럽트 프랙 I가 자동적으로 0이 되어 인터럽트처리가 끝날 때까지 추가적인 인터럽트는 발생하지 못하는 상태가 된다.



Posted by 살레시오
,

 마이크로컨트롤러(uC)가 어떤 작업을 수행하고 있는 도중에 특수한 이벤트가 발생하면 수행 중인 작업을 중단하고 발생한 이벤트를 처리한 후 이전에 수행하던 작업으로 되돌아가 나머지 작업을 계속 처리하게 된다. 이러한 ‘즉시 처리해야 하는 특수한 이벤트의 발생’을 인터럽트(interrupt)라고 한다. 인터럽트가 발생하면 uC는 그것을 처리하는 작업들이 수록된 함수로 명령 수행 과정을 옮기게 되는데 이 함수를 인터럽트 서비스 루틴 (interrupt service routine, 이하 ISR)이라고 한다

.

 인터럽트는 uC시스템에서 굉장히 중요한 비중을 차지한다. 비유를 들어서 설명하자면 어떤 전화기가 있는데 전화가 걸려왔는데도 벨소리도 안 나고 진동도 안 된다고 가정해보자. 이 전화기로 꼭 받아야만 하는 중요한 전화가 올 예정이라면 그 사람은 다른 일은 못하고 몇 초에 한 번씩 전화가 왔나 안 왔나 들어서 확인해 봐야 할 것이다. 하지만 전화가 걸려왔을 때 벨소리가 나거나 진동하는 전화라면 편하게 다른 일을 하고 있다가 전화가 오면 벨소리를 듣고(인터럽트 요구) 전화를 받아 용무를 처리한 후 하던 일을 계속할 것이다.


 이전 포스트에서 사용된 버튼을 가지고 더 설명하면 ‘버튼이 눌려졌다.’라는 특수한 이벤트를 감지하려면 uC가 main()함수 내에서 항상 주기적으로 이 버튼의 상태를 검사했어야 했다. 하지만 이것을 인터럽트로 처리하면 uC입장에서는 이 버튼의 상태를 항상 검사할 필요가 없이 다른 일을 할 수가 있는 것이다. 다른 작업을 하고 있다가 ‘버튼이 눌려지면’ 그 즉시 인터럽트를 발생하여 하던 일을 멈추고 이를 처리할 ISR을 호출한 뒤 ISR이 끝나면 하던 일을 다시 하면 되는 것이다.


 이와 같이 (외장 혹은 내장) 주변기기와 uC가 통신을 수행하는데 있어서 uC가 주변기기의 상태를 메인루틴에서 상시 검사하지 않고 인터럽트로 처리하면 전체적인 시스템의 운용 효율이 매우 높아지게 된다.


[그림 1] 인터럽트의 개념도


 AVR에서 처리하는 인터럽트는 크게 외부 인터럽트와 내부 인터럽트로 나눌 수 있다. 외부 인터럽트는 용어 그대로 외부에서 발생하는 신호이고 내부 인터럽트는 내장된 주변 장치(타이머/카운터, ADC 등등)에서 발생하는 것이다.



Posted by 살레시오
,

 학부생들에게는 (공대생이라 할 지라도) 수학 계산에 쓰이는 프로그램이 낯설 것이다. 여기서 '수학 계산에 쓰이는 프로그램'이라는 것은 윈도우를 깔면 기본으로 제공되는 계산기 프로그램을 말하는 것이 아니라 수학 문제를 해결하는데 사용되는 전문적인 프로그램을 말하는 것이다.


 사실 공대에 들어와서 <일반 수학>이나 <공학 수학>같은 과목들을 배울 때에는 손으로 풀거나 공학용 계산기 같은 것으로 함수 입력해서 그래프를 그리고 하는 방식이 아직까지는 익숙한 풍경이다. 학부생들이 전문적인 컴퓨터 프로그램을 이용한다는 것은 그리 일반적이지는 않은 것 같다. '곱셈이나 나눗셈' 하면 '계산기'가 보통은 떠오르지만 '방정식'을 푸는데 컴퓨터로 그 식들을 입력하고 엔터키를 치면 그 해를 간단히 구할 수 있다는 것을 아는 학부생들이 얼마나 될까.


 이러한 수학 과목에서 매스메티카(mathematica)나 매트랩(matlab)을 실습에 도입한 교재들이 간간히 있기는 하다. 하지만 이들 패키지는 고가의 상용 제품들이므로 학부생들이 자유롭게 실습에 이용하기에는 제약이 따른다. 하지만 훌륭한 대안이 있는데 바로 Maxima와 Sage라는 프리웨어들이다.

[그림 1] Maxima의 외형

 이 그림의 첫번째 명령에서 보면 입력방정식이 x2+ax+b=0 으로 a와 b라는 기호를 어떤 수치값을 갖는 변수가 아니라 문자상수로 취급하여 그 해를 보여줌을 알 수 있다. 이렇게 문자 상수를 숫자처럼 다루는 것을 기호식 해석(symbolic analysis)라 하고 mathematica, maple, maxima, sage와 같은 프로그램이 사용된다. 반면, 변수 이외의 모든 기호는 어떤 수치값을 지정하여 문제를 푸는 것을 수치 해석(numerical analysis)라고 하고 일반 계산기 (프로그램), MATLAB, SciLab, Octave, FreeMat 등의 프로그램이 사용된다.


[그림 2] Sage의 외형


 개인적인 관심은 <공학 수학>의 교과 과정에 Maxima나 Sage 같은 프로그램의 사용법을 접목시킬 수 있는가이다. 공과 대학의 수학 과목들이 학생들이 주어진 공학적 문제를 수학적으로 모델링하고 그것을 해결하는 능력을 배양하는 데에 목적이 있지만 이제는 컴퓨터를 이용한 문제 해결 능력도 같은 비중으로 다루어져야 한다고 생각하기 때문이다. 즉, 기존에는 수학에서 컴퓨터 이용 능력이 보조적인 역할로 다루어졌지만 이제는 동등하거나 혹은 더 비중있게 다루어져야 하지 않을까 하는 생각이 든다.



Posted by 살레시오
,

 PC로 문서 작업하다보면 눈의 피로 때문에 색상을 반전하고 싶은 경우가 있다. 어떤 에디터들은 dark테마 같은 기능이 있어서 전체 색상을 어두운 색조로 바꿀 수 있는데 HWP 나 pdf 문서를 볼 때는 이런 기능이 아쉽다.


 윈도우즈7에 기본으로 포함되는 돋보기(magnifier)라는 유틸에 이 화면을 반전하는 기능이 있다. 먼저 [Windows키]+[U] 를 누르면 접근성센터가 열리는데 여기서 {돋보기 시작(G)} 버튼을 누르거나 [Alt]+[G] 를 누르면 돋보기를 실행시킬 수 있다. 또는


  • [윈도키]+[+] 혹은 [윈도키]+[-]


를 누르면 돋보기가 실행되면서 화면이 한 단계 확대/축소된다.



여기서 기어모양의 아이콘(설정)을 누르면 다음과 같은 화면이 뜨는데 여기서 {색 반전 사용(I)} 을 선택 [v] 하거나 [I] 키를 누르면 화면이 반전된다. 또는 돋보기가 실행된 상태에서 단축키


  • [ctrl]+[alt]+[ I ]


를 눌러도 화면이 반전된다. (반드시 돋보기가 실행된 상태여야 한다.)



돋보기를 종료시키려면


  • [윈도키]+[ESC]


를 누르면 된다.


컴퓨터 작업을 많이해야 되는데 눈이 시원찮은 사람들에게는 무척이나 유용한 기능이다.




Posted by 살레시오
,

 무료로 사용할 수 있는 캡쳐 프로그램 중에 모노스냅(monosnap)이라는 프로그램이 있는데 블로깅이나 자료를 만드는데 무척이나 유용하다. 화면 영역을 캡쳐하는 가장 기본적인 단축키는 다음과 같다.

  • [Ctrl]+[Alt]+[5] : 화면 영역 선택 캡쳐



특히 캡쳐된 화면에 각종 도형이나 문구를 깔끔하게 넣을 수 있는데 하단에 보면 화살표, 사각형, 선, 원, 펜, 텍스트, 화살표+텍스트, 등이 있다.



그리고 캡처된 이미지의 특정 영역을 흐리게(블러링) 만들어 주는 도구도 있다.



그림 객체의 색과 두께도 선택할 수 있으며 이미지를 잘라내거나 사이즈를 조절할 수도 있다. 그림 객체가 캡쳐 화면을 벗어나면 이미지를 자동으로 확장시켜 준다.



설정창에서의 단축키는 다음과 같다.


위 그림을 보면 초기에는 frozen area 키가 설정되어 있지 않은데 이것은 특정 윈도의 영역만 선택해서 캡쳐하는 기능이므로 단축키를 지정하여 사용하면 편하다. 예를 들어서 [ctrl]+[alt]+[3]으로 지정해 놓으면 이 단축키를 이용하여 프로그램창을 깔끔하게 캡쳐할 수 있다. 자주 사용하는 단축키는 두 개 정도이다.


  • [Ctrl]+[Alt]+[5] : 화면 영역 선택 캡쳐

  • [Ctrl]+[Alt]+[0] : 편집화면 열기 (가장 최근 캡쳐 이미지)


많이 사용되지는 않지만 화면 동영상이나 웹캠 영상도 이것으로 녹화할 수 있다. 이것은 단축키가 지정되어 있지 않고 다음과 같이 메뉴를 찾아들어가야 된다.


하지만 화면을 녹화할 경우에는 이 프로그램보다는 오캠(Ocam)같은 전용 유틸을 사용하는 것이 좋다.


 네이버캡쳐와 비교하면 그림 객체가 더 깔끔하게 들어간 다는 점과 블러링(blurring) 기능이 있다는 장점이 있지만 선택 영역 캡쳐와 윈도 캡쳐 단축키가 나눠져 있다는 점이 좀 불편하다. 네이버캡쳐는 [print screen]키만 누르면 자동으로 선택영역이나 윈도를 인식하여 캡쳐해 준다.



Posted by 살레시오
,

 7세그먼트는 숫자나 시간을 표시하는데 사용되며 우리 주변에서 흔히 볼 수 있는 부품이다. 외관은 아래와 같다.


[그림 1] 7세그먼트의 외관

구동원리는 LED 어레이와 동일하고 도트까지 포함해서 총 8개의 LED가 숫자를 표시하기 위한 레이아웃으로 배치되어 있다. 각각의 LED의 위치 및 명칭과 구조는 다음 [그림 2]와 같다.

[그림 2] 7세그먼트의 구조와 각 LED의 명칭


7세그먼트는 애노드 공통형(anode common type)과 캐소드 공통형(cathod common type)으로 나뉘는데 이는 LED들을 어디에서 결선했는지에 따라서 달라지며 각각에 대한 내부 회로도는 다음 [그림 3]과 같다.


[그림 3] 7세그먼트의 내부 회로도


ATmega8(A)을 이용한 7세그먼트 실험을 위한 회로도는 [그림 4]와 같다. 이것을 구동하기 위해서는 DDRD6='0', PORTD6='1'로 설정해서 GND에 연결된 트랜지스터를 ON시켜야 하여 매크로 Turn7SegOn에 이 동작을 수행하도록 정의하였다. 포트핀의 방향 설정은 초기화 함수 InitAM8()에서 수행한다.

[그림 4] 실험 키트의 7세그먼트 회로도


예를 들어서 숫자 1을 표시 하는 경우는 아래와 같이 LED B와 C를 켜면 된다.

[그림 5] 숫자 1을 표시할 경우


표시 숫자에 따른 전체 데이터 값들은 다음 그림과 같다.


[표 1] 7세그먼트의 숫자 표시 데이터


숫자 0부터 9까지 순차적으로 표시하기

 가장 간단한 실험인 숫자 0부터 9까지 순차적으로 표시하기를 수행한다. <표 5.4.1>에 적힌 데이터를 포트B에 순서대로 내보내기만 하면 된다.


#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include "Am8USBasp.h"
uchar nums[10] =
{0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f };
int main(void) {
   uchar byNumId;
   InitAM8();
   while(1) {
       for (byNumId=0; byNumId<10; byNumId++) {
           SEG(nums[byNumId]);
           _delay_ms(500);
       }
   }
}


숫자 데이터를 저장하기 위해서 배열을 사용했다. main()함수에서 하는 일은 이 배열의 데이터를 시차를 두고 내보는 일을 무한히 반복하는 것이다.



Posted by 살레시오
,

 여기에서는 이전 포스트에 이어서 포트를 이용한 LED 실습을 진행한다.

LED실험 2

 두 번째 LED실험도 앞의 실험과 비슷하다. 이번에는 다음과 같은 순서로 1→2→3→4→1→2→3→4→1→… 계속 시차를 두고 반복하는 실험이다.


[표 1] 두 번째 LED 실험

순서

LED상태

이진수

16진수

1

●●●○○●●●

0b00011000

0x18

2

●●○●●○●●

0b00100100

0x24

3

●○●●●●○●

0b01000010

0x42

4

○●●●●●●○

0b10000001

0x81


프로그램 예는 다음과 같다.


#define F_CPU 16000000

#include <avr/io.h>

#include <util/delay.h>

#include "Am8USBasp.h"

int main(void) {

   uchar ucA, ucaRelay[4] = {0x18, 0x24, 0x42, 0x81};

   InitAM8();

   while(1) {

       for (ucA=0; ucA<4; ucA++) {

           LED(ucaRelay[ucA]);

           _delay_ms(200);

       }

   }

}


이 예를 보면 typedef 명령어로 unsigned char형은 byte형으로 재정의하였으며 앞으로도 이것은 계속 사용할 것이다. 그리고 LED로 순차적으로 내보낼 데이터는 배열로 처리하였음을 눈여겨 보기 바란다.


LED실험 3

 다음 [표 2]에 설명한 바와 같이 1→2→ … →13→14→1→2→ … 의 순서로 14가지 패턴을 반복하면 마치 LED한개가 좌우로 왕복하는 듯이 보이는 예제이다. 이 세 번째 LED실험은 앞의 것들과 달리 조금 복잡해 보인다.


[표 2] 세 번째 LED 실험

순서

LED상태

이진수

16진수

1

●●●●●●●○

0b00000001

0x01

2

●●●●●●○●

0b00000010

0x02

3

●●●●●○●●

0b00000100

0x04

4

●●●●○●●●

0b00001000

0x08

5

●●●○●●●●

0b00010000

0x10

6

●●○●●●●●

0b00100000

0x20

7

●○●●●●●●

0b01000000

0x40

8

○●●●●●●●

0b10000000

0x80

9

●○●●●●●●

0b01000000

0x40

10

●●○●●●●●

0b00100000

0x20

11

●●●○●●●●

0b00010000

0x10

12

●●●●○●●●

0b00001000

0x08

13

●●●●●○●●

0b00000100

0x04

14

●●●●●●○●

0b00000010

0x02


 이번 프로그램을 이전 실험과 같이 14개의 데이터를 포트에 순차적으로 내보내는 식으로 프로그램을 작성할 수도 있으나 그렇게 하면 너무 비효율적인 프로그램이 된다. 그래서 비트이동 연산자와 반복문을 이용하면 좀 더 간단하게 프로그램이 가능하다. 프로그램 예는 다음과 같다.


#define F_CPU 16000000

#include <avr/io.h>

#include <util/delay.h>

#include "Am8USBasp.h"

typedef enum DIR {UP, DOWN} EDIR;

int main(void) {

   uchar ucLed = 1;

   EDIR eDir = UP;

   InitAM8();

   while(1) {

       if (eDir == UP) {

           ucLed <<= 1;

           if (ucLed == 0x80)

               eDir = DOWN;

       } else { // if (eDir == DOWN)

           ucLed >>= 1;

           if (ucLed == 0x01)

           eDir = UP;

       }

       LED(ucLed);

       _delay_ms(200);

   }

}


LED실험 4 : LED탑 쌓기

 마지막으로 아래와 같은 패턴을 보여주는 프로그램을 작성해보자. 벽돌로 하나씩 탑을 쌓는 것처럼 보여서 ‘LED 탑 쌓기’실험이라고 이름을 붙여 보았다.


[표 3] 네 번째 LED 실험

순서

LED상태

이진수

16진수

1

○●●●●●●●

0b01111111

0x7F

2

●○●●●●●●

0b10111111

0xBF

3

●●○●●●●●

0b11011111

0xDF

4

●●●○●●●●

0b11101111

0xEF

5

●●●●○●●●

0b11110111

0xF7

6

●●●●●○●●

0b11111101

0xFB

7

●●●●●●○●

0b11111110

0xFD

8

●●●●●●●○

0b11111110

0xFE

9

○●●●●●●○

0b01111111

10

●○●●●●●○

0b10111111

11

●●○●●●●○

0b11011111

12

●●●○●●●○

0b11101111

13

●●●●○●●○

0b11110111

14

●●●●●○●○

0b11111101

15

●●●●●●○○

0b11111110

16

○●●●●●○○

0b01111111

17

●○●●●●○○

0b10111111

18

●●○●●●○○

0b11011111

19

●●●○●●○○

0b11101111

20

●●●●○●○○

0b11110111

21

●●●●●○○○

0b11111101

22

○●●●●○○○

0b11111110


이 실험은 이제 포트에 데이터를 순차적으로 내보내는 식으로는 프로그램이 거의 불가능하다. 이번에도 역시 비트이동연산자와 반복문을 조합하여 프로그램을 작성할 수 있다.


#define F_CPU 16000000

#include <avr/io.h>

#include <util/delay.h>

#include "Am8USBasp.h"

int main(void) {

  uchar ucaLEDTower[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };

   uchar ucled, uck, ucl;

   InitAM8();

   while (1) {

       for (uck=0;uck<8;uck++) {

           ucled = 0x80;

           for (ucl=0;ucl<8-uck;ucl++) {

               LED( ucled | ucaLEDTower[uck] );

               _delay_ms(200);

               ucled >>= 1;

           }

       }

   }

}


위 프로그램은 길이는 짧지만 한 줄씩 읽어가면서 분석해 볼 여지가 있다. 다중 반복문을 사용하고 있는데 내부 반복문에서는 LED가 위에서 아래로 한 칸씩 떨어지는 것을 비트쉬프트 연산자로 처리하고 있으며 이 때 PORTB로 내보내는 데이터를 배열 byaLEDTower[]과 OR연산을 하여 마치 벽돌이 쌓여 있는 듯한 효과를 내는 것이다. 배열 byaLEDTower[]은 벽돌이 쌓인 모양의 데이터를 가지고 있다. 바깥 반복문에서는 떨어지는 루틴을 8번 반복하게끔 하는데 떨어지는 곳의 높이를 점차로 높인다.


 이번 포스트에서는 LED를 이용하여 간단한 예제들을 살펴보았다. 독자가 C언어의 문법을 잘 숙지하고 있다면 어떻게 동작이 되는지 쉽게 분석해 볼 수 있을 것이다.




Posted by 살레시오
,