'My major/avr::robot'에 해당되는 글 17건

  1. 2006/05/24 자동차 완성..^^ (20)
  2. 2006/05/04 avr128의 USART 통신 (9)
  3. 2006/05/02 타이머0번 인터럽트로 1초만들기 (4)
  4. 2006/03/28 인공호흡..-_-;; (24)
  5. 2006/03/09 Interrupt period (3)
2006/05/24 01:08 My major/avr::robot


장장 1주일간 밤을 새며 만든 자동차...
태어나서 완성도 높게 만들어본 첫 작품이다. 그동안 avr만 가지고 놀거나 서보등 모터 회로만 만들어 놀다가 말고, LED 켰다..껐다...등등 타이머나 가지고 놀고...그러다 말았는데....
앞으로 이래저래 많이 쓰이게 될것 같다. 논문쓸때, 졸작할때, 가지고 놀때(?) 등등..
전체 구성은 정말 사람 짱나게 했던 제3회 임베디드대회때 나누어준 라인트레이서와 같은 형식으로 만들었다.
지금 생각해도..-_-^ (빠직)
우선 스탭모터 구동부는 avr128로 구성하고 스탭 드라이버는 SLA7024 두개로 구성하였다.
임베디드 보드와 avr은 RS232로 통신을 하고 주행 컨트롤은 임베디드 보드쪽에서 하게 된다.
임베디드 보드로 사용된 TynuxBox는 현재 회사가 망해서 a/s가 되지 않고 있지만 시리얼,usb,CF,LAN 등 다양한 입출력 포트가 있어 얼마든지 확장이 가능하다.
망가지지 말고 오래오래 가지고 놀수 있었으면 좋겠다...^^

spac:
mcu: avr128
motor: nk223-01at(step motor)
embedded board: TynuxBox(pxa 255)

posted by joyoungtae
2006/05/04 01:49 My major/avr::robot

avr 보드와 또다른 디바이스간 통신방법중 가장 많이 사용되고 쉬운 것이 usart 통신을 이용하는 것이다.
avr128에는 usart통신을 위해 두개의 usart 통신 포트를 제공하고 있다.


위 그림은 avr128의 핀 번호를 나타내는 그림이다. avr128은 USART0와 USART1으로 두개의 usart 통신핀을 제공해 주고 있다. 위 그림에서 PE0핀이 RXD0, PE1 핀이 TXD0 으로 USART0 이고, PD2핀이 RXD1, PD3 핀이 TXD1 으로 USART1 이다. 이 네개의 핀을 이용해서 동시에 두개의 usart통신을 할수 있다. 여기선 USART0하나만 이용하여 usart통신을 해보도록 하자. 다음부터 나오는 모든 USART를 USART0로 통일한다. 보통 데이터 쉬트에서는 USART0와 USART1을 통합해서 USARTn 이라고 호칭한다.

usart통신은 하려면 우선 UDR0 레지스터에 대해 알아야 한다. 이 레지스터는 USART0의 송수신 버퍼 역할을 하는 레지스터이다. 이 레지스터에 값을 적으서 송신을 할수 있고 이 레지스터에서 수신된 값을 읽을 수 있는 것이다. 실질적으로 소스로 구현할때도 다음과 같이 간단하게 구현이 된다.

unsigned char ch;
ch = UDR0;

다음으로 알아볼 레지스터는 보오레이트를 설정하는 레지스터인 UBBR0 이다. 보오레이트란, 데이터 송수신 속도를 말하는 것이다. usart통신은 병렬통신과는 틀리게 하나의 핀으로 전송을 하는 것이기 때문에 전송 속도를 상호간에 약속을 해주어야만 올마른 통신이 가능하다. UBBR0 에는 UBRR0H와 UBRR0L 두개가 있는데 총 12비트로 구성이 된다.
위 그림은 UBBR0 레지스터의 데이터시트 그림이다. 그림에서 보시다 시피 UBBR0H와 UBBR0L 두개가 있는데 H가 상위 비트이고 L이 하위 비트를 가르킨다. 값을 넣을때는 항시 UBRR0H와 UBBR0L 두개모두에 값을 넣어 주어야 한다. 여기서 문제가 되는 것이 있다. 바로 UBRR0H와 UBBR0L에 어떤 값을 넣어야 자신이 원하는 통신속도를 설정할 수 있느냐 이다. 방법은 다음과 같다.


위 표는 현재 자신이 사용중인 AVR의 클럭을 이용해서 원하는 보오레이트를 구하는 공식이다. 현재 자신이 사용하는 avr의 클럭이 16Mhz 이고 비동기 일반모드로(Asynchronous Normal Mode) 보오레이트 값이 19200을 원한다면 다음과 같이 계산할 수 있을 것이다. 19200 = 16000000/16(UBRR +1) .
이 것을 계산하면 UBRR = 51.08333333333 값이 나온다. 즉 51 값을 넣어주면 된다는 것이다.
다음 소스는 16Mhz 클럭에 보오레이트 19200으로 통신하기 위해 레지스터를 설정하는 소스이다.

UBRR0H = 0x00;
UBRR0L = 0x33; ->51

자 이제 USART 의 제어및 상태 레지스터에 대해 알아 보자. 이 레지스터 이름은 UCSR0A, UCSR0B, UCSR0C 가 존재한다. 이 내용에 대해선 각자 데이터 쉬트를 참조하도록 하자. 전부 말하려면 몇페이지는 걸릴 것이다. 그리고 다 말할 필요도 없다. 데이터 쉬트에 아주 자세히 나와 있다.

이제 기본적인 USART에 대해 알아 보았으니 가장 중요한 데모 소스를 구현해 보도록 하자.

unsigned char recvData;

/* receive data */
interrupt [USART0_RXC] void usart0_rx_isr(void)
{
  recvData = UDR0;
  if(recvData == 'd')
       PORTB.0 = 0x01;
  if(recvData == 's')
       PORTB.0 = 0x00;
}

void Putchar(unsigned char c)
{
  while((UCSR0A & 0x20) == 0x00);
  UDR0 = c;
  #asm("sei");
}

int main(void)
{
  UBRR0H = 0;
  UBRR0L = 51; //if 16Mhz -> 19200 baud
  UCSR0A = 0X00;
  UCSR0B = 0X98;
  UCSR0C = 0X06;
  SREG = 0x80;
 
  #asm("sei");
  ch = 'a';
  Putchar(ch);
  while (1)
     {
     };
}

이 소스는 처음 시작 될때 usart로 문자 'a'를 송신한다. 그리고 'd'나 's' 문자를 수신하면 B0포트에 값을 넣었다 뺐다 하는 소스이다. 즉 B0포트에 LED가 달려 있다면 LED를 깜박이는 소스가 될 것이다.

위 소스를 차례대로 분석해 보기로 하자.
interrupt [USART0_RXC] void usart0_rx_isr(void)
{
  recvData = UDR0;
  if(recvData == 'd')
       PORTB.0 = 0x01;
  if(recvData == 's')
       PORTB.0 = 0x00;
}

이 소스는 USART0 에서 데이터가 수신되었을때 발생되는 인터럽트 함수 이다. 이 함수는 우선 usart 송수신 버퍼인 UDR0에서 하나의 문자를 받아온다. 그런후 그 문자가 'd'이면 B0포트에 1을 출력하고 's'이면 0을 출력하도록 구성이 되어있다. 만약 B0포트에 LED가 연결되어 있다면 'd'나 's'에 따라 LED가 켜졌다 꺼졌다 할것이다.

void Putchar(unsigned char c)
{
  while((UCSR0A & 0x20) == 0x00);
  UDR0 = c;
  #asm("sei");
}

이 소스는 데이터 송신할때 사용되는 함수이다. 위에서 본 인터럽트처럼 정해저 있는 것이 아니라 그냥 사용자 임의로 만들면 될 것이다...^^;; 안에 내용을 보면 whlie()함수내에서 UCSR0A 레지스터의 5번째 비트값인 UDRE0 값을 비교한다. UDRE0는 UDR0가 비어 있는지 비어있지 않는지를 나타 내는 레지스터로 UDR0가 비어 있다면 1로 셋되고 아니면 0으로 셋된다. 즉 while()문은 버퍼가 빌때까지 기다리는 루프가 될것이다.
그리고 루프에서 벗어 났다는 것은 버퍼가 비었다는 의미이므로 UDR0버퍼에 문자를 넣어 데이터를 송신할 수있게 된다. 그리고 다음에 #asm("sei"); 구문이 나오는데 이것은 global 인터럽트를 인에이블 시키는 것으로 인터럽트가 발생할수 있도록 설정하는 것이다.

  UBRR0H = 0;
  UBRR0L = 51; //if 16Mhz -> 19200 baud
  UCSR0A = 0X00;
  UCSR0B = 0X98;
  UCSR0C = 0X06;
  SREG = 0x80;

자 이제 가장 중요한 것이 나오게 된다. 우선 첫째줄과 두번째줄의 UBRR0H와 UBRR0L이 나온다 이것은 보오레이트를 설정하는 것이라고 위에서 말했다. 16Mhz에서 192000 보오레이트를 원한다면 51을 넣으면 된다. 계산하는 방법은 위에서 이미 언급하였다. 다음으로 UCSR0A, UCSR0B, UCSR0C 가 나온다. UCSR0A는 모두 0으로 셋팅 하였기에 언급하지 않고 UCSR0B와 UCSR0C만 보도록 하자. UCSR0B에 0X98을 넣는 다는 것은 비트로 계산해 보면 10011000 을 넣는 것이다.


다음은 UCSR0B의 레지스터 구조를 나타낸 그림이다. 여기에 0X98을 넣었다는 것은 RXCIE0, RXEN0, TXEN0를 1로 셋팅 하였다는 것이다. RXCIE0는 수신완료 인터럽트를 개별적으로 허용하는 비트이다. 이 것이 1로 셋팅된다면 데이터가 수신 되었을때 수신완료 인터럽트가 발생하게 된다. RXEN0는 USART0포트의 수신부가 동작하도록 허용하는 비트이다. 반드시 1로 셋팅해야 할 것이다. TXEN0는 USART0포트의 송신부가 동작하도록 허용하는 비트이다. 이것도 1로 셋팅해 줘야 할 것이다.
다음 UCSR0C에 0X06을 넣는데 데이터 시트 찾아 보면 알게 될 것이다. 단순히 비동기 일반 모드로 USART 통신을 하고 싶다면 위에 써 놓은것으로 하면 잘 될 것이다. 손이 아파서 더이상 적지 못하겠다...GG~
ㅡㅡ;;
posted by joyoungtae
2006/05/02 18:23 My major/avr::robot

avr의 타이머0번 인터럽트를 이용하여 1초만들기를 해보겠다.
우선 간단히 소스를 보자.

short time = 0;

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TCNT0 = 0x83;
   if(time == 1000)
   {
       time = 0;
       if(PORTA.0 == 0x01){ PORTA.0 = 0x00; }
       else if(PORT.A == 0x00){ PORT.A = 0x01; }
  }else
       time++;
}

void main(void)
{
  PORTA = 0x00;
  DDRA = 0xFF;
  TCCR0 = 0x05;
  TCNT0 = 0x83;
  TIMSK = 0x01;
  #asm("sei");

  while(1){}
}

우선 타이머0번 인터럽트가 어떻게 동작하는지 다시한번 살펴보기로 한다.(여기선 AVR128 로 설명한다)
타이머0번 인터럽트는 8비트 인터럽트로 동작을 한다.
이 타이머를 동작시키기 위해서는 타이머에 사용할 클럭을 먼저 설정해 주어야한다.
여기서 설정하는 클럭은 몇 클럭마다 TCNT0 레지스터 값을 1씩 증가시킬것인지를 결정하는 것이다.
왜 TCNT0 레지스터를 1증가시키는 클럭을 알아보느냐 하면 타이머0번 인터럽트가 발생하는 시점에 관련이 있기 때문이다. 타이머0번 인터럽트가 언제 발생하느냐 하면 TCNT0값이 증가되다가 255값이 넘어가게 되면 타이머0번 인터럽트가 발생하게 되는 것이다. (왜 255냐 하면 8비트 레지스터 이기 때문이다.)
클럭설정을 하기위한 레지스터는 TCCR0 레지스터이다.
TCCR0 레지스터의 데이터 시트를 살펴보면 다음과 같이되어 있다.


AVR128은 많은 분주를 제공해 주고 있다. (분주란, 실클럭을 얼마로 나누어 쓸것이지를 말한다.) 위 소스에선 TCCR0 에 0x05를 넣어주었으므로, 128분주를 선택한것이다. 여기서...0x05가 왜 128분주이냐하면 0x05를 비트로 변환하면 00000101이기 때문이다. 이렇게 128분주를 선택하게 되면 avr은 실질 클럭에서 128만큼 나눈 클럭으로 TCNT0 레지스터를 증가하게 된다. 예를 들자면 현재 AVR에 16Mhz의 클럭을 사용하고 있다면 16000000/128 = 125000 hz 이므로 125khz로 TCNT0 값을 증가하게 되는 것이다. 그러면 1/125000 = 0.000008초 가 나오게 된다. 이것은 TCNT0값이 1증가하는데 8마이크로 초가 걸린다는 것이다.

자 이제 TCNT0값이 8마이크로 초마다 1씩 증가하는 것으로 설정했으면 간단하게 1밀리초 마다 인터럽트가 걸리게 해준다면 1초 만들기는 쉬울것이다. 왜냐하면 인터럽트가 1000번 걸리면 1초이기 때문이다.
(위 소스도 지금 설명한 예시와 똑같이 동작하고 있다.)
자 그럼 8마이크로 초로 얼마나 지나야 1밀리초가 될까...나눠보면 될것이다.
0.001/0.000008 = 125 이다. 즉 TCNT0 값이 125번 증가되면 되는 것이다. 자..근데 TCNT0 값은 255가 되야 인터럽트가 발생하게 된다. 그럼 125번 증가후 인터럽트가 걸리게 하려면 어떻게 할까? 결론은 간단하다. TCNT0 값을 0부터 시작하는 게아니라 중간에 125번후 255가 되게 값을 미리 넣어두는 것이다. 얼마의 값을 넣어 두면 255가 되는데 125번 더하면 될까? 255에서 125번을 빼보면 될것이다. 계산해 보면 255 - 124 = 131 이다. 여기서 125를 빼지 않고 124를 뺀 이유는 0번부터 시작하는 것으로 생각해야 되기 때문이다. 0부터 시작해서 125번이면 124이기 때문이다.
이렇게 되면 결론적으로 TCNT0 값을 131로 초기화 시키고 TCNT0값을 증가시키면 125번 증가후 255값을 넘어서 인터럽트가 걸리게 된다. 1번에 8마이크로 초니 125 * 8마이크로초 = 1밀리초가 된다.
자 이제 1밀리초마다 인터럽트가 걸리게 해두었으니 간단하게 인터럽트 1000번 걸리도록 하면 1초이다. 변수하나 선언해서 그 변수를 인터럽트마다 증가시켜 1000이 될때까지 기다리면 정확히 1초가 만들어지게 되는 것이다.

다음은 TIMSK 레지스터를 보자.
이레지스터는 타이머 인터럽트를 사용하기 전에 반드시 비트값을 적절하게 셋팅해 줘야 한다.

타이머0번 인터럽트를 사용하기 위해선 TIMSK 레지스터의 TOIEO 값을 1로 셋팅해 줘야 한다.
TIMSK레지스터의 TOIEO 값이 1일때 타이머카운터 인터럽트가 인에이블 되고 0일데 디스에이블 되기 때문에 타이머 인터럽트를 사용할땐 반드시 1로 셋팅해 줘야 한다.

자 이젠 소스를 분석해 보자...물론 위에서 설명한 것과 같은 내용일 것이다.

short time = 0;
- 1000번을 카운트 하기 위한 변수이다.

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
- 타이머0번 인터럽트 호출 함수 선언부이다.

TCNT0 = 0x83;
- TCNT0에 0x83을 넣었다. 0x83은 131과 같은 수이다.

if(time == 1000)
- time 변수가 1000이 되면 1초가 된것이기 때문에 비교를 하여 참이면 그에 따른 동작을 해주면 된다.

time = 0;
- 변수가 1000이 되었기에 다시 0으로 초기화 시켜 준다.

if(PORTA.0 == 0x01){ PORTA.0 = 0x00; }
else if(PORT.A == 0x00){ PORT.A = 0x01; }
현재 A포트가 0이면 1로 1이면 0으로 출력.

}else
time++;
- 값이 1000이 아닐때는 time 변수를 증가
}

void main(void)
{
PORTA = 0x00;
- a포트를 0으로 초기화

DDRA = 0xFF;
- 전 a포트를 출력으로 설정

TCCR0 = 0x05;
- 128 분주로 설정

TCNT0 = 0x83;
- 131값으로 초기화

TIMSK = 0x01;
- 타이머/카운터 인터럽트 인에이블
#asm("sei");
- 인터럽트 인에이블

while(1){}
- 메인함수가 끝나지 않도록 루프를 넣음
}

TCNT0 나 TCCR0 두개의 값을 적절히 이용하면 자신이 원하는 시간을 얼마든지 만들수 있다는 것을 알수 있다. 하지만 계산하는데 머리가 좀 아플것이다..^^
posted by joyoungtae
2006/03/28 21:35 My major/avr::robot
오늘 산학을 통해 구입한 avr128을 테스트 하기 위해 프로그램을 하나 만들어 올려보았다..
하지만 올라갈 프로그램을 안 올라가고 나오는 메세지는 "-24 Unkowned Device Message Error"....
이게 뭐지..하면서 이래저래 뒤져 보았다.
이런 메세지가 뜨는 이유는 세가지 란다.
첫째, 연결을 잘못했을 경우.
둘째, 전원 인가를 잘못해서 칩이 죽은 경우.
셋째, 클럭 설정이 잘못 설정이 되어 외부 클럭으로 살려줘야 할때.

첫째, 둘째 의 문제는 아니었다. 몇번이고 확인했다.
그래서 아무래도 내부 클럭설정에 문제가 있는것 같았다. 웹을 뒤저본 결과 이경우에는 외부 클럭을 이용해서 소위 인공호흡을 해줘야 한다는 것이다.
방법은 이러하다 외부 오실로를 이용해서 avr의 XTAL1 핀에 클럭을 넣어주어는 것이다.
그러면 다시 살아 난다는 것이다..-_-;;
뭐 어쨌든 8Mhz 오실로로 XTAL1에 클럭을 인가해 주었다. 하지만..나오는것은...
"-24 Unkowned Device Message Error".... 메세지
이런..정말 난감했다. 친구 한테 전화했더니..아무래도 고장 같단다...AS보내라고 한다.
이런..완전 난감...
이때 총명형의 isp 보드가  생각이 났다. 그래서 총명형의 isp보드를 이용해서 프로그램을 올려보았다......결론은,
아주~~잘올라 간다 이다....ㅠㅠ
문제는 ISP 보드 아니면 시리얼 케이블 이었다.
완존 헛물켜버렸다..
아...아무래도 내가 가지고 있는 시리얼 케이블은 9핀이 모두 연결이 되어 있지 않은것 같다.
혹시나..시리얼ISP 가지고 계신분들은 시리얼케이블 9핀 다연결된건지 꼭 확인하시길..ㅠㅠ
posted by joyoungtae
2006/03/09 01:58 My major/avr::robot

timer0 interrupt

interrupt period = 1/(Hz) * clk분차 * (256 - x ) = sec

예를 들면,
8Mz 크리스탈을 사용하고 clk분차를 1024로 한다면 식은 다음과같다
1/(8000000) * 1024 * (256 - x) = sec 로 나온다..
x값은 자신이 알아서 잘 구해봐야할것이다..
여기서 나오는 sec는 뭐알겠지만 interrupt가 걸리는 시간 주기가 나오게 된다..
외워두면 좋고 아니여도 좋고...안외워도 그냥 생각해서 구하면 되니깐..
근데 공식 놔두고 그냥 대입하니깐 편함...ㅡㅡ;;

ps. 여기서 x값은 TCNT0 레지에 들어갈 값이다...

posted by joyoungtae