2011년5월12일...인터럽트벡터테이블, 프로그램메모리, 데이터메모리, I/O Resister를 보고 io.h를 대신할 헤더파일 만들기, 타이머/카운터0 관련 인터럽트함수(벡터), 프로젝트(두더지잡기게임)

 

○ 이 때까지 사용한 인터럽트는 모두 2개이다. (타이머/카운터0 오버플로우와 비교매치)



                                 인터럽트 벡터                                

image
image

Datasheet p.60의 표.23을 보면,
리셋의 우선순위가 제일 높아 리셋핀에 Low LEVEL의 신호가 인가되면 ATmega128 MCU는 다른 어떤 신호가 들어와도 상관없이 무조건 리셋이 될 것이다.

 

                                    프로그램메모리                                         

image

 

ATmega128의 메모리
① FLASH 128KB
② SRAM 4KB
③ EEPROM 4KB

0xFFFF = 65535(10)

총 65536개이므로 216이다.
= 26 * 210
= 26 * 1K

프로그램메모리의 워드의 크기는 2Bytes이므로,
프로그램메모리의 크기는,
= 26 * 1K * 2 = 64 * 1K * 2 = 128 * 1K
= 128KBytes이다.
= 64KBytes * 2Bytes

 

 

 

                                 데이터 메모리                                

image

Datasheet p.20의 데이터 메모리맵을 보면,
노멀모드는 ATmega128이 출시되며 새롭게 설정된 것이고, 호환모드는 이전에 출시된 ATmega103과의 프로그램 호환을 위해 Fuse bit를 설정하여 동작시키는 모드이다.

호환모드에는 노멀모드에 있는 160개의 확장 I/O 레지스터가 없다.
그리고 내부SRAM의 크기도 4000Bytes로 노멀모드에 비해 96Bytes가 적다.

 

 

                    ATmega128 레지스터                    

 

image

$00 ($20)번지는 PINF레지스터가 위치한다. 
$00이 호환모드일 때 주소이고, 괄호 안의 $20이 노멀모드일 때 주소이다.


image

$00 ($20) ~ $3F($5F)까지는 64개의 I/O 레지스터이고 ATmega103과 똑같은 레지스터가 있어 호환모드 시에는 이 영역만 사용가능함.

($60) ~ ($FF) - Reserved (추후 버전이 높아지면 증설될 곳의 예약)
160개의 확장 I/O레지스터가 위치한다.

image

오른쪽 Memory Configuration B를 보면 확장레지스터가 없다.
그럼 확장I/O레지스터영역을 보면..

 

image

($60)부터 160개의 확장I/O레지스터의 시작이다.
($60)은 Reserved(예약)이고 이 의미는 ATmega128에선 사용할 수 없는 영역이고,
상위버전이 나와 새로운 기능들이 추가되면서 이런 예약석을 사용하는 것이다.
($61)은 DDRF가 있다. 반갑군.

image

($FF)에서 레지스터영역은 끝이 난다.
($FF)번지 역시 ATmega128에선 사용되지 않는다.
($9C)번지에는 UART1 제어와 상태레지스터가 위치한다.
ATmega103에는 UART1이 없어 시리얼통신포트가 하나 뿐이라는 것을 알 수 있다.

0xFF까지 레지스터영역이므로 총256개가 있군.
① 32개의 범용레지스터
② 64개의 I/O레지스터
③ 160개의 확장I/O레지스터

32 + 64 + 160 = 256

 

 

              I/O Resister를 보고 io.h를 대신할 헤더파일 만들기              


image

io.h파일을 대신할 I/O정의 헤더파일을 만들어야 하니 io.h의 내용을 봐야 한다.
메모장이나 워드패드 또는 텍스트파일을 볼 수 있는 어떤 툴을 이용해서 열어 보자.

image

처음부터 저작권에 대한 이야기가 나온다. 아 머리아파~ 마이아파~
컨트롤+F키를 눌러 필요한 ATmega128에 대한 정보부터 모아보자.


image

#elif defined (__AVR_ATmega128__)
#  include <avr/iom128.h>

#elif은 elseif문과 같은 의미이고 많은 AVR시리즈 중에 하나만 선택하도록 하는 조건부 컴파일문이다.
Makefile에서 MCU매크로의 값에 따라 MCU가 선택이 될 것인데..
아직 ‘__’ (언더바 두 개)를 왜 쓰는지 모르겠다.
어쨌든 같은 폴더에 iom128.h가 ATmega128에 해당되는 I/O정의 헤더파일이군…

iom128.h파일도 열어보자.


image

마찬가지로 텍스트에디터로 열어보자.

 

image

마찬가지로 저작권에 관한 이야기부터 시작하고 영어다~ 영어다~ 머리아파~ 마이아파~

 

image

조금 밑으로 스크롤을 하니 I/O레지스터에 관한 정의가 보이고,
_SFR_IO8( )이라는 이상한 매크로를 만나게 된다.
SFR에서 S는 스페셜, F는 펑션, R은 레지스터라는 것을 쉽게 알 수 있다.
IO8은 8비트 I/O라는 뜻이고 아래의 ADC관련은 IO16으로 되어 있으니 16비트 I/O매크로란 뜻이겠군. 흠흠

일단 IO8부터 보기로 할까…

image

sfr_defs.h파일을 열어보자.

image

이 파일도 역시 저작권에 관한 이야기부터 시작한다. 마찬가지로 영어~ 영어~
이 파일도 Marek Michalkiewicz란 분이 만든 것이다.

 

image

조금 밑으로 스크롤 해보니...
#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr) 이 보인다.
_MMIO_BYTE( )를 찾아야 겠군.

 

image

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
uint8_t가 뭔지 모르겠으나 unsigned char가 아닌가 생각된다. 위에 inttypes.h헤더파일을 열어보자…
어랏! 아니네..
stdint.h헤더파일을 열어보자…

image

이번 헤더파일은 다르게 include디렉토리에 있다. 랜덤함수 쓸 때 사용한 stdlib.h파일과 같은 디렉토리에 있다.
마찬가지로 텍스트에디터로 열어보자.


image

typedef unsigned char uint8_t;

typedef문으로 자료형을 새로 만들었다. 즉, uint8_t는 unsigned char타입이라는 것이다.

 

image

다시 돌아와서 _MMIO_BYTE에 대해 자세히 분석해보자.
괄호가 조금 있는데 일단 우측부터,
mem_addr은 엑세스할 메모리주소값이고 함수가 아니더라도 이런 식으로 매크로를 정의해서 인자를 전달해서 사용할 수 있다.
volatile은 컴파일러에게 최적화하지 말 것을 알려주고,
uint8_t는 상기의 typedef문으로 새로 만든 unsigned char이다.

보기 좋게 다시 바꾸어,

( * (volatile unsigned char *) (mem_addr) )

mem_addr은 unsigned char * (포인터)로 캐스팅된 것을 알 수 있다.
우측에 상수 값이 들어갈 것인데 그 상수 값은 8bit MCU니까 당연히 char type이 아닐까 한다.
메모리에 직접엑세스하기 위해 포인터로 접근해야 하는데 const char type은 자료형이 다르다.
그래서 (unsigned char *)(메모리주소) 이렇게 캐스팅하는 것이다.

<symbol table>

type name address
const char mem_addr Code영역
unsigned char * SFR 레지스터영역

심볼테이블을 그려보면 쉽게 이해가 될 것이다. 상수는 SRAM에 들어가는 것이 아니라 FLASH영역에 위치한다.
레지스터영역에 있는 SFR들을 접근하려면 일단 type부터 맞춰야 서로 합의가 이루어져 문제가 되지 않는다.

(volatile unsigned char * ) (mem_addr)
에 대한 이해는 끝났을 것이다.
그럼 마지막 남은 좌측의 * (포인터)는 무엇인가?
주소에 값을 넣으려면 *p = 100;이렇게 하듯이 * (포인터)가 가리키는 곳에 값을 넣는다고 해야 한다.
빼면 당연히 주소를 바꾸라는 명령이 되는데 이러면 에러다.

왜냐하면 mem_addr은 상수이기 때문이다.

자! 이제 분석이 끝났으니 나만의 I/O헤더파일을 만들어 보자!

   1:  /******************************************************
   2:          My I/O resister header file
   3:          
   4:          작성자: 김수만
   5:          작성일자: 2011년 5월 15일
   6:          
   7:  ******************************************************/
   8:  //보조 매크로
   9:  #define    _BV(bit) (1 << (bit))    //비트선택용
  10:  /******************************************************
  11:              외부 범용 입/출력 포트
  12:  ******************************************************/
  13:  //포트F
  14:  #define    PINF    *((volatile unsigned char *) 0x20)
  15:  #define    DDRF    *((volatile unsigned char *) 0x61)
  16:  #define    PORTF    *((volatile unsigned char *) 0x62)
  17:  //포트E
  18:  #define    PINE    *((volatile unsigned char *) 0x21)
  19:  #define    DDRE    *((volatile unsigned char *) 0x22)
  20:  #define    PORTE    *((volatile unsigned char *) 0x23)
  21:  //포트D
  22:  #define    PIND    *((volatile unsigned char *) 0x30)
  23:  #define    DDRD    *((volatile unsigned char *) 0x31)
  24:  #define    PORTD    *((volatile unsigned char *) 0x32)
  25:  //포트C
  26:  #define    PINC    *((volatile unsigned char *) 0x33)
  27:  #define    DDRC    *((volatile unsigned char *) 0x34)
  28:  #define    PORTC    *((volatile unsigned char *) 0x35)
  29:  //포트B
  30:  #define    PINB    *((volatile unsigned char *) 0x36)
  31:  #define    DDRB    *((volatile unsigned char *) 0x37)
  32:  #define    PORTB    *((volatile unsigned char *) 0x38)
  33:  //포트A
  34:  #define    PINA    *((volatile unsigned char *) 0x39)
  35:  #define    DDRA    *((volatile unsigned char *) 0x3A)
  36:  #define    PORTA    *((volatile unsigned char *) 0x3B)
  37:   
  38:  /******************************************************
  39:              타이머/카운터관련 레지스터
  40:  ******************************************************/
  41:  #define    TIMSK    *((volatile unsigned char *) 0x57)
  42:  #define    OCIE0    1
  43:   
  44:  /**************************
  45:      타이머/카운터0
  46:  **************************/
  47:  #define    OCR0    *((volatile unsigned char *) 0x51)
  48:  #define    TCNT0    *((volatile unsigned char *) 0x52)
  49:  #define    TCCR0    *((volatile unsigned char *) 0x53)
  50:   
  51:  #define    WGM01    3
  52:  #define    WGM00    6
  53:  #define    CS02    2
  54:  #define    CS01    1
  55:  #define    CS00    0
  56:   
  57:  /******************************************************
  58:                  상태 레지스터
  59:  ******************************************************/
  60:  #define    SREG    *((volatile unsigned char *) 0x5F)
  61:  //전체 인터럽트 활성 
  62:  //SREG레지스터의 최상위 비트는 인터럽트 인에이블
  63:  #define    sei()    SREG = SREG | _BV(7)

 

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr)) 을 쓰면 더 단순해 지겠으나…
하고 싶은 분들만 하세요. ㅎㅎ


                     타이머/카운터0 관련 인터럽트함수(벡터)                    

길어서 링크만 겁니다.

http://sumanaki.tistory.com/48

 

 

                                  프로젝트(두더지잡기게임)                           

두더지잡고 싶은데 점프선이 접촉불량이라 일부 인식이 안되어 항상 70점을 넘을 수 없더라…
현이가 형은 “이미 빵빠레 터트리고 있다”라고 했으나 안되 ㅠㅠ

점프선 만들어 가야겠다…

프로그램이 너무 쉽게 잘 풀어져 있어 길게 설명 안 하고…
ISR부분만 설명하겠다.

   1:  /*******************************************************
   2:      타이머/카운터0 비교매치 인터럽트 서비스 루틴
   3:  *******************************************************/
   4:  void __vector_15(void)
   5:  {
   6:      ++g_elapsed_time;        // 시간변수 1증가    
   7:      key = key | ~PIN_KEY;    //키입력값을 반전하여 저장. 
   8:  }

시간변수 1증가는 다들 아시겠고…
key = key | ~PIN_KEY; 에서,
key는 전역변수로 키 값을 임시 저장하는 버퍼역활을 수행한다.

초기값은 0으로 누른 키의 위치 값만 1로 세트하여 저장하고 키를 떼어 ~PIN_KEY가 0이 되어도, (스위치는 풀업이다.)
과거값과의 OR연산을 수행하므로 0으로 리셋이 되지 않는다.

 

 

   1:  /***************************************************************************
   2:      <두더지잡기 게임1-1>
   3:  
   4:      - 무한 루프 진행
   5:      - 랜덤으로 20번 LED 켜짐. 난수를 발생하여 LED On/Off를
   6:          각각 0.5초간 유지
   7:      - LED가 켜진 시점에 해당 스위치를 누르면 점수 5점씩 증가
   8:      - 점수는 FND로 출력(점수 0x00~0x95 표시)
   9:      - 100점인 경우 FND는 0x00 출력, LED는 모두 깜박깜박~
  10:  
  11:      소속: 내장형하드웨어
  12:      작성자: 김수만
  13:      작성일자: 2011년 5월 12일
  14:  
  15:  ***************************************************************************/
  16:  #include "ATmega128.h"
  17:  #include <stdlib.h>
  18:   
  19:  void __vector_15 (void) __attribute__ ((signal, used, externally_visible));     
  20:   
  21:  //포트 정의
  22:  #define DDR_LED            DDRF
  23:  #define PORT_LED        PORTF
  24:  #define DDR_FND            DDRE
  25:  #define PORT_FND        PORTE
  26:  #define    DDR_KEY            DDRC
  27:  #define    PIN_KEY            PINC
  28:  #define    PORT_KEY        PORTC
  29:  //타이머관련
  30:  #define    CPU_CLOCK        16000000        // CPU clock = 16Mhz
  31:  #define TICKS_PER_SEC    1000            // Ticks per sec = 1,000
  32:  #define    PRESCALER        64                // Prescaler = 64
  33:                                          // 클럭의 배수(크기,오버헤드)
  34:  volatile unsigned int g_elapsed_time = 0;    // 시간 변수
  35:  volatile unsigned char key = 0;                // 키입력값 저장 버퍼
  36:   
  37:   
  38:  void initLED(void);                        // LED초기화
  39:  void initKEY(void);                        // KEY초기화
  40:  void initFND(void);                        // FND초기화
  41:  void setTCCR0(void);                    // 타이머0 초기화
  42:  void initOCR0(void);                    // OCR0 초기화
  43:  void setTIMSK(void);                    // TIMSK초기화
  44:  void toggleLED(char * state);            // LED반전
  45:  void sleep(unsigned int elapsed_time);    // 1초 대기
  46:   
  47:   
  48:  //프로그램 시작
  49:  int main(void)
  50:  {
  51:      unsigned char cLED = 0;            //LED
  52:      unsigned char cScore = 0;            //점수
  53:      unsigned char cLoop = 0;
  54:      
  55:      initLED();            // LED초기화
  56:      initFND();            // FND초기화
  57:      initKEY();            // KEY초기화
  58:      setTCCR0();            // TCCR0 설정
  59:      initOCR0();            // OCR0 초기화
  60:      setTIMSK();            // TIMSK 설정
  61:      sei();                // 인터럽트 활성화
  62:      
  63:      /* srand()에 seed값을 시작할 때마다 다르게 넣어주기 위해
  64:         PC에선 내부RTC의 시간을 이용하나 AVR은 RTC가 없기 때문에
  65:         언제 누를지 알 수 없는 KEY와 항상 변화하고 있는 TCNT0의 값을 
  66:         이용한다. */
  67:      while(0xFF == PIN_KEY);            //아무 키나 누를 때까지 대기
  68:      srand(TCNT0);                    //시드값으로 TCNT0전달.
  69:      
  70:      while(0xFF != PIN_KEY);            //키를 땔 때까지 대기
  71:      key = 0;                        //key값 리셋
  72:      sleep(100);                        //100ms대기
  73:      
  74:      while(1)    //무한루프
  75:      {
  76:          for(cLoop = 0; 20 > cLoop ; ++cLoop)
  77:          {
  78:              //1 ~ 7까지 난수를 발생하여 그 수 만큼 좌로 시프트
  79:              cLED = 1 << (rand() % 8);        
  80:              PORT_LED = ~cLED;    //반전하여 출력        
  81:              sleep(1000);        //1000ms 대기
  82:   
  83:              //두더지가 맞았으면 
  84:              if(cLED == key)            //두더지를 맞추었으니
  85:              {
  86:                  cScore = cScore + 5;    //5점증가
  87:              }
  88:              
  89:              key = 0;                 //키값 리셋                
  90:              if(100 != cScore)
  91:              {
  92:                  PORT_FND = (cScore / 10) * 16 + (cScore % 10);        //FND에 점수표시
  93:              }
  94:   
  95:              PORT_LED = 0xFF;    //LED모두 OFF
  96:              sleep(500);            //500ms 대기
  97:          }
  98:   
  99:          if(100 == cScore)            //만약 모두 맞추었다면
 100:          {
 101:              PORT_FND = 0x00;    // "00"표시
 102:   
 103:              for(cLoop = 0; 7 > cLoop ; ++cLoop)    //LED깜빡깜빡
 104:              {
 105:                  PORT_LED = 0x00;    // 모두 ON
 106:                  sleep(500);
 107:                  PORT_LED = 0xFF;    // 모두 OFF
 108:                  sleep(500);
 109:              }
 110:          }
 111:          else                    //100점이 아니면 현재 점수 보여줌.
 112:          {
 113:              PORT_LED = 0xFF;        // LED 모두 OFF
 114:              sleep(5000);            // 5초 딜레이
 115:          }    
 116:   
 117:          cScore = 0;                    // 점수 초기화
 118:          key = 0;                    // 키값 리셋
 119:   
 120:      }
 121:      
 122:      return 1;    //종료
 123:  }
 124:   
 125:  /*******************************************************
 126:      타이머/카운터0 비교매치 인터럽트 서비스 루틴
 127:  *******************************************************/
 128:  void __vector_15(void)
 129:  {
 130:      ++g_elapsed_time;        // 시간변수 1증가    
 131:      key = key | ~PIN_KEY;    //키입력값을 반전하여 저장. 
 132:  }
 133:   
 134:  //LED초기화
 135:  void initLED(void)
 136:  {
 137:      DDR_LED = 0xFF;        //모두 출력
 138:      PORT_LED = 0xFF;    //포트 초기화
 139:      
 140:      return ;
 141:  }
 142:   
 143:  //KEY초기화
 144:  void initKEY(void)
 145:  {
 146:      DDR_KEY = 0x00;        //모두 입력
 147:      PORT_KEY = 0xFF;    //모두 풀업처리
 148:      
 149:      return ;
 150:  }
 151:   
 152:  //FND초기화
 153:  void initFND(void)
 154:  {
 155:      DDR_FND = 0xFF;        //모두 출력
 156:      PORT_FND = 0x00;    // "00"출력
 157:      
 158:      return ;
 159:  }
 160:   
 161:   
 162:  //타이머0 초기화
 163:  void setTCCR0(void)
 164:  {
 165:      TCCR0 = (1 << WGM01) | (1 << CS02);        //CTC Mode, prescanler = 1/64
 166:      //TCCR0 = 0x44;
 167:      return ;
 168:  }
 169:   
 170:  //TCNT0 초기화
 171:  void initOCR0(void)
 172:  {
 173:      //8bit Timer0 = 16,000,000 / 1000 / 64
 174:      //            = 250
 175:      OCR0 = CPU_CLOCK / TICKS_PER_SEC / PRESCALER;
 176:      
 177:      return ;
 178:  }
 179:   
 180:  //TIMSK 초기화
 181:  void setTIMSK(void)
 182:  {
 183:      TIMSK = (1 << OCIE0);        //타이머0 비교 매치 인터럽트 활성
 184:      
 185:      return ;
 186:  }
 187:   
 188:  //1초 대기
 189:  void sleep(unsigned int elapsed_time)
 190:  {
 191:      g_elapsed_time = 0;                            //시간 변수 초기화
 192:      while(elapsed_time > g_elapsed_time);        //참이 될 때까지 대기.
 193:      
 194:      return ;
 195:  }
 196: