2015-02-15

움직이는 LED 만들기 연습 과제 및 질문 정리

아래 내용은 다음 사이트에서 연습을 하며 질문한 내용들을 정리한 글입니다^^

출처 : http://cafe.naver.com/lazydigital/ 


[과제 1] LED를 맨 오른쪽 1개만 켜고 이를 1초에 한칸씩 왼쪽으로 이동시키고 왼쪽 끝에 도달하면 다시 오른쪽으로 한칸씩 이동시키는 프로그램 작성 (Boundary Detector) (힌트 : 왼쪽으로 이동은 <<, 오른쪽으로 이동은 >> 사용)

//사용 프로그램: Atmel studio 6.1 장비: JKIT-128-1

#define F_CPU 16000000   /* 선언해야 한다고 나와있어서 선언했는데 퓨즈비트를 보니 EXTHIFXTALRES_16KCK_64MS 를 선택한다고 되어 있어서 삭제해도 되는지는 잘모르겠음.*/
#include <avr/io.h>         // Atmel studio 6.1 에서 Atmega128의 i/o PORTx 대한 register에 대해 정의한 헤더파일

void delay_sec (unsigned int sec)        /*delay 함수 딜레이 함수가 없을시 육안으로 현 상을 확인하기 어려움 너무 빠르게 처리됨*/
{
 volatile unsigned int i, j, k;

 for (k=0;k<sec;k++) ;               /*delay 함수 호출시에  unsigned int sec 값을 받기 위해 for문 안에 sec값을 표현 */

  for(i=0;i<1000;i++ )                 //이중 for문
  {
  for(j=0; j<300; j++);                 /*임의로 시간을 단축하여 왕복시간을 단축함 상태확인용 조정하여 1초에 근접시켜 사용*/
 }

}

  int main(void)                 //메인 함수
  {
   DDRA = 0xff;           // PORTA를 출력을 설정하기 위해 방향성을 먼저 선택 "출력포트설정"

   unsigned char i;    //  1byte(8bits) 양의 정수 (0~255)

   while(1)               // 무한루프
   {

    for(i=0x80; i>0x00;i=i>>1)       /* i값은 128값으로 초기화 되어 0x80에서 0x01까지    shift후 0x00에서 조건을 불만족하여 for문을 나오고 PORTA 는 0x01값으로 계속 점등*/
     PORTA = i;                  // i>0x00 은 i>=0x01로 표현해도 값은 동일하게 수행
     delay_sec(1);
    }

    for(i=0x02;i<0x80;i=i<<1)
    {                                            //
     PORTA = i;   // i=2 값으로 초기화 되고 0x02 에서 0x40까지 shift한후에
     delay_sec(1);  // 0x80에서 for문의 0x80<0x80이란 조건으로 불만족되어 for문에서
    }               // 빠져나옴 그후 다시 첫번째 for문에서 i 값은 128값으로 다시 초기화
   }               // i<=0x80 일경우 0x80<<1을 하면 0<0x80이란 조건으로 무한히 만족하여                 // for문을 빠져나오지 못한다. i값은 무한히 0값을 준다. PORTA=0;
  }

[과제 2] LED로 스피커 볼륨처럼 1개 ON a 2개 ON a … a 8개 ON a 7개 ON a … a 2개 ON a 1개 ON a 2개 ON a … 이렇게 반복하여 수행하는 프로그램 작성 (힌트 : 점점 커질 때는 << 실행 후 +1 하고, 점점 작아질 때는 >> 실행)


#define F_CPU 16000000                                      
#include <avr/io.h>    
void delay_sec (unsigned int sec)    
{
 volatile unsigned int i, j, k;

 for (k=0;k<sec;k++) ;
       
 for(i=0;i<1000;i++ )            
  {
  for(j=0; j<300; j++);            
 }

}

int main(void)                 //메인 함수
{
 DDRA = 0xff;           // PORTA를 출력을 설정하기 위해 방향성을 먼저 선택 "출력포트설정"
        PORTA = 0x00;       // PORTA에 기본상태값을 0으로 clear
 unsigned char i;    //  1byte(8bits) 양의 정수 (0~255)

 while(1)               // 무한루프
 {

  for(i=0x80; i>0x00;i=i>>1)      // i=128값으로 초기화, PORTA = 0x00;(0b00000000)
  {                                       // i=i>>1에서 i값은 64로 변화됨 (0b01000000)
   PORTA = PORTA|i;     //  PORTA=0b10000000|0b00000000 로 비트연산하여 PORTA= 0b10000000값 점등
   delay_sec(1);            //   i = 64로 초기화, PORTA = 0x80;
  }                                      //   PORTA =0b10000000|0b01000000 로 비트연산하여PORTA= 0b11000000값 점등
                                                       //   0x80 에서 0x01까지 반복하여 0x00에서 불만족하여 for문에서 빠져나옴
                                                       //   PORTA =0b11111111 값으로 점등된 상태
                                                       //

  for(i=0xfe;i>=0x80;i=i<<1)    //i값은  (0xfe의 양의 정수값) 254로 초기화                                                                                                                              //254>=128을 만족하여 i=i<<1 한 252값으로 shitf됨
  {                                               //PORTA = 0b11111111 & 0b11111110 로 비트연산한 0b11111110 값 점등
    PORTA = PORTA&i;           //i값은 0b11111110값에서 <<1한0b11111100값으로 252로 초기화
    delay_sec(1);                   //PORTA= ob11111110 & 0b11111100 로 비트연산한 0b11111100 값 점등
                             // 0xfe에서 0x80까지 감등하여0x80<<1에서 shitf 후 0에서 조건 불만족으로 for문에서 나옴
  }                                 // 처음부터 다시 무한 반복
 }
}

질문1)))  아래 과제2에서 제가 PORTA에 보내졌던 데이타(?) 값을 다시 사용하여 비트연산 처리하여 사용하는식으로

코드를 작성하였습니다. 동작은 큰 문제는 없어 보이는데 과연 이렇게 꾸미는것이 차후에 좀더 다양한 코드구현에 있어

데이타의 안정성이나 표현의 정확성에 있어서도 큰 무리(?)나 오류가 없는지 궁금합니다 지금이야 단순한 상태 표현에 있어

큰 무리는 없어 보이지만 차후 고급 수준에서 이런식의 표현방법이나 표현식이 코드해석에 있어 도움이 될지 혹은 생각지

못한 치명적 오류에 직면 할수도 있을거 같아서 이제 시작하는 저에게는 알수없는 영역이라 많은 조언을 구합니다

습관을 들일때 좀더 바람직한 방향으로 자리잡고자 하는 욕심이 많이 생깁니다 ^^;

질문2))) #define F_CPU 16000000
  // 선언해야 한다고 나와있어서 선언했는데 퓨즈비트를 보니 EXTHIFXTALRES_16KCK_64MS 를 선택한다고 되어 있어서 삭제해도 되는지는 잘모르겠음.

 <<<<<<요부분인데요 무조건 위 #define F_CPU 16000000 부분을 퓨즈비트가 설정되어 있어도 해줘야 하는건지 궁금합니다.

답변 출처 : http://cafe.naver.com/lazydigital/ 작성자:임베디드홀릭


질문1)
PORTA도 저장이 가능한 레지스터이므로 님이 하신 방법도 많이 사용합니다.

다른 칩들도 메모리 주소를 통해

*(volatile unsigned int *) 0xffxxx <<= 1;

그런데
unsigned char led;
led = 0;
for(i=0x80; i>0x00;i=i>>1) // i=128값으로 초기화, PORTA = 0x00;(0b00000000)
{
led = led | i; // i=i>>1에서 i값은 64로 변화됨 (0b01000000)
PORTA = led;
delay_sec(1); // i = 64로 초기화, PORTA = 0x80;
}

위처럼 임시 변수를 선언하고 이를 이용하는 방식또한 많이 사용합니다.
위 경우 변수를 하나 더 사용하게 되겠지만,
PORTA에 쓰기 하는 led에 어떤 연산을 많이 하게 된다면

일반적으로 PORTA와 같이 IO 레지스터를 접근하는 것보다 로컬 변수(대부분 옵티마이저로
cpu 의 범용 레지스터를 이용) 를 통해 연산후에 해당 레지스터에 쓰기 하는게 더 빠르겠죠.

현재 위 경우에는 직접 레지스터 자체를 이용해서 나빠보이지는 않습니다

질문2)

delay 함수(_delay_ms, _delay_us)는 avr(mcu)칩이 어떤 속도로 도느냐에 따라 코드의 동작이
다릅니다.
즉 동작속도를 F_CPU로 알려주지 않으면 자신이 1MHz로 동작한다고 판단하고 그에 따른 delay를
만들어 냅니다.
F_CPU를 코드(delay.h이전)에 직접 넣는 방법도 있고

컴파일러 호출시
avr-gcc -DF_CPU=xxx
이렇게 옵션으로 넣어서 전달하는 방법이 있습니다.
후자의 경우는 avtstudio의 project 옵션에 들어가서 설정하는 게 있습니다.

그런데 위 코드는 라이버러리의 delay 함수를 사용하지 않았으므로
F_CPU 는 정의 하지 않아도 상관없습니다.

F_CPU는 util/delay.h 에 구현되어 있는 delay 함수가 이용하는 것으로 fuse 비트 설정과 상관없이
delay.h에겐 필요합니다.

for(PORTA=0x80; PORTA>0x00 ; PORTA >>=1) delay_sec(1);

for(PORTA=0x02; PORTA <0x80; PORTA <<= 1) delay_sec(1);

첫번째 과제의 while 문 안은 위처럼 해도 되겠죠.

로컬 변수 i 가 비교, 쉬프트 등에 사용하는게 더 빠르겠죠(범용 레지스터에 i 가 위치할 경우


No comments:

Post a Comment

Total Pageviews