BASIC4MCU | 질문게시판 | atmega128 도트매트릭스 질문입니다
페이지 정보
작성자 ahn1 작성일2021-07-16 14:27 조회3,556회 댓글1건본문
지렁이 게임 작성 중입니다 ㅜㅜ
인터럽트 = PORT D
LATCH,CLOCK,RED,GREEN = PORT F
ENABLE = PORT A0
A0~A3 = PORT B0~B4
매트릭스에 점 세개만 표시되고 지렁이 움직임을 맡고 있는 인터럽트가 반응이 없습니다.
7-세그먼트 부분은 상관없이 인터럽트만 해결하고 싶습니다..
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/delay.h>
#define F_CPU 16000000UL
#ifndef BV
#define BV(bit) (1<<(bit))
#endif
#ifndef cbi
#define cbi(reg,bit) reg &= ~(BV(bit))
#endif
#ifndef sbi
#define sbi(reg,bit) reg |= (BV(bit))
#endif // reg의 bit번째 비트 셋
#define LINE_ADDR PORTB
#define red_on sbi(PORTF,4) //빨간색 ON
#define red_off cbi(PORTF,4) //빨간색 off
#define green_on sbi(PORTF,5) //초록색 ON
#define green_off cbi(PORTF,5) //초록색 off
#define latch_on sbi(PORTF,6)
#define latch_off cbi(PORTF,6)
#define clock_on sbi(PORTF,7)
#define clock_off cbi(PORTF,7)
#define enable_on sbi(PORTA,0)
#define enable_off cbi(PORTA,0)
#define UP 1 // 지렁이 움직이는 방향 위
#define DOWN 2 // 지렁이 움직이는 방향 아래
#define LEFT 3 // 지렁이 움직이는 방향 왼쪽
#define RIGHT 4 // 지렁이 움직이는 방향 오른쪽
// 사용 변수
volatile unsigned int dot_i = 0; // 타이머 오버플로우(1ms) 인터럽트 함수에서
// 다음에 표시할 라인 번호
volatile unsigned int h_x, h_y; // 머리 x, y 좌표
volatile unsigned int o_x, o_y; // 먹이 x, y 좌표
volatile unsigned int dir; // 지렁이 이동 방향
volatile unsigned int tcnt = 0;
volatile unsigned int length = 0; // 지렁이 마디 수
volatile unsigned int kk; // 꼬리 배열 번호(새로운 머리 좌표 저장할 배열 번호)
volatile unsigned int lent_time, st = 0;
volatile unsigned int dsp_no = 0; // 표시 세그먼트 번호
volatile unsigned int dot_red[16][16]; // 도트매트릭스 제어신호에서 사용할 R성분
volatile unsigned int dot_green[16][16]; // 도트매트릭스 제어신호에서 사용할 G성분
volatile unsigned int w_x[256], w_y[256]; // 마디 좌표 저장 배열 변수
volatile unsigned int buf_x[256], buf_y[256]; // 마디 좌표 일시 저장 작업 배열 변수
// 7-세그먼트 숫자 출력 패턴
unsigned int seg_pat[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
// 7-세그먼트 ON 값
unsigned int seg_on[4] = {0x08, 0x04, 0x02, 0x01};
unsigned int w_init[3][2] = {{0, 0}, {1, 0}, {2, 0}}; // 지렁이 시작 마디 좌표
unsigned long sijak[16][16] ={ // "웜게임" 문자 패턴
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1},
{0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1},
{0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1},
{0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1},
{0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1},
{0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1},
{0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1},
{0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1},
{0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1},
{0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1},
{0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1},
{0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1}};
unsigned long end[16][16] ={ // "END" 문자 패턴
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0},
{1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0},
{1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0},
{1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
void worm_init(void); // 게임 시작을 위한 초기설정
void sw_input(void); // 스위치 입력 처리
void disp_init(void); // "웜게임" 출력(오렌지색)
void disp_end(void); // "END" 출력
volatile unsigned char flag=0;
ISR(INT0_vect)
{
flag = 1;
}
ISR(INT1_vect)
{
flag = 2;
}
ISR(INT2_vect)
{
flag = 3;
}
ISR(INT3_vect)
{
flag = 4;
}
int main(void)
{
DDRA = 0xFF; PORTA = 0x00;
DDRF = 0xFF; PORTF = 0x00;
DDRB = 0xFF; DDRD =0x0F;
// 도트매트릭스 표시용 타/카0 설정
TIMSK = 0x01; // TOIE0 = 1, 타이머/카운터0 인터럽트 인에이블
ASSR = 0x00; // 타이머/카운터0 타이머 모드
TCCR0 = 0x04; // 일반모드, 프리스케일 = CK/64
TCNT0 = 0x00; // 1/16us*64분주*(256-0)=1.024ms
// 7-세그먼트 표시용 타/카2 설정
TIMSK = TIMSK | 0b01000000; // TOIE2=1(오버플로우)
TCCR2 = 0b00000100; // 256분주
TCNT2 = 0; // 1/16us*256분주*(256-0)=4.9ms
SREG = 0x80; // 전역 인터럽트 인에이블 비트 I 셋
EIMSK = 0x0F ;
EICRA = 0xFF ;
sei();
while(1){
disp_init(); // 시작을 위한 초기설정
dir = RIGHT; // 지렁이 이동방향 오른쪽 설정
worm_init(); // 지렁이 게임 시작 상태 설정
st = 1; // 게임 진행
srand(TCNT0); // TCNT0값 랜덤 seed값으로 이용
while(st) sw_input(); // 스위치 입력 체크
disp_end(); // "END" 표시
_delay_ms(2000);
}
}
// 도트매트릭스 "웜게임" 출력 표시
void disp_init(void)
{
int i, j;
// "웜게임" 표시
for(j = 0;j < 16;j++){
for(i = 0;i < 16;i++){
dot_green[j][i] = sijak[j][i];
dot_red[j][i] = sijak[j][i];
}
}
}
// 도트매트릭스 "END" 출력 표시
void disp_end(void)
{
int i, j;
// "END" 표시
for(j = 0;j < 16;j++){
for(i = 0;i < 16;i++){
dot_red[j][i] = end[j][i];
}
}
}
// 게임 초기 상태 설정
void worm_init(void)
{
int i, j, x, y;
// 도트매트릭스 클리어
for(j = 0;j < 16;j++){
for(i = 0;i < 16;i++){
dot_green[j][i] = 0;
dot_red[j][i] = 0;
}
}
// 지렁이 초기 상태의 마디 좌표 설정(길이 3)
for(i = 0;i < 3;i++){
x = w_init[i][0];
y = w_init[i][1];
w_x[i] = x; // 마디 x좌표
w_y[i] = y; // 마디 y좌표
dot_green[y][x] = 1; // 녹색 LED on(지렁이 몸체)
}
h_x = w_init[2][0]; // 머리 x 좌표
h_y = w_init[2][1]; // 머리 y 좌표
length = 3; // 길이 3
kk = 0; // 꼬리 배열 번호
dot_i = 0; // 타이머 인터럽트 함수에서 표시할 라인 초기 번호
o_x = 12; // 초기 먹이 위치
o_y = 12;
dot_red[o_y][o_x] = 1; // 먹이 적색 표시
lent_time = 500 - length * 5; // 게임 진행 속도 딜레이 계산
}
// 스위치 입력 처리
void sw_input()
{
if(flag == 1)dir = UP;
else if(flag == 2) dir = DOWN;
else if(flag == 3) dir = LEFT;
else if(flag == 4) dir = RIGHT;
}
// 타이머/카운터0 (256 - 0) * 64 / 16 us = 1.024ms
ISR(TIMER0_OVF_vect)
{
int bx, by, i, di;
tcnt++; // 게임 진행속도 조절 카운터
enable_off;
for(di = 0;di < 16;di++)
{ // 한 라인 데이터 출력
if(dot_red[dot_i][di] == 1) red_on; // 적색 먹이
else red_off;
if(dot_green[dot_i][di] == 1) green_on; // 녹색 지렁이 마디
else green_off;
clock_on; // 클럭 ON
_delay_us(1);
clock_off; // 클럭 OFF
_delay_us(1);
}
_delay_us(2);
enable_on; // Enable 비트 셋
latch_off; // Latch 비트 리셋
_delay_us(2);
latch_on; // Latch 비트 셋
PORTB = (PORTB & 0b00001111) | (dot_i << 4); // 표시 라인 설정
_delay_us(2);
enable_off; // 인에이블 비트 리셋
dot_i = (dot_i + 1) % 16; // 표시라인 1증가
// 게임 진행속도에 맞춰 지렁이 이동방행으로 1칸 이동 처리
if(st == 0 || tcnt < lent_time) return;
tcnt = 0;
if(dir == UP) { // 이동방향 = 위 ?
if(h_y > 0) h_y--; // 머리 Y좌표 1감소
else st = 0; // 윗쪽 벽 충돌 게임종료
}
else if(dir == DOWN){ // 이동방향 = 아래 ?
if(h_y < 15) h_y++; // 머리 Y좌표 1증가
else st = 0; // 아래쪽 벽 충돌 게임종료
}
else if(dir == LEFT){ // 이동방향 = 왼쪽 ?
if(h_x > 0) h_x--; // 머리 X좌표 1감소
else st = 0; // 왼쪽 벽 충돌 게임종료
}
else { // 이동방향 = 오른쪽 ?
if(h_x < 15) h_x++; // 머리 X좌표 1감소
else st = 0; // 오른쪽 벽 충돌 게임종료
}
if(st == 0) return; // 게임종료 ?
if(o_x == h_x && o_y == h_y){ // 먹이좌표 = 새로운 머리좌표
// 지렁이 마디 좌표 재정렬 저장
for(i = 0;i < length;i++){
buf_x[i] = w_x[kk];
buf_y[i] = w_y[kk];
kk = (kk + 1) % length;
}
for(i = 0;i < length;i++){
w_x[i] = buf_x[i];
w_y[i] = buf_y[i];
}
w_x[length] = h_x; // 머리부분 좌표
w_y[length] = h_y;
kk = 0; // 꼬리부분 배열 번호
length = length + 1; // 길이 1증가
if(length < 50) lent_time = 500 - length * 5; // 길이에 따라 점점 빠르게
dot_red[o_y][o_x] = 0; // 현재 먹이 제거
dot_green[h_y][h_x] = 1; // 먹이 부분 추가
// 새 먹이 발생
while(1){
o_x = rand() % 16;
o_y = rand() % 16;
if(dot_green[o_y][o_x] != 0) continue; // 지렁이 없는 위치에
dot_red[o_y][o_x] = 1;
break;
}
}
else { // 지렁이 단순 이동(먹이 먹지 않은 이동)
bx = w_x[kk]; // 꼬리 좌표 백업
by = w_y[kk];
w_x[kk] = h_x; // 현재의 머리좌표 kk번째 배열에 저장
w_y[kk] = h_y;
dot_green[by][bx] = 0; // 현재 꼬리부분 클리어
kk= (kk + 1) % length; // 새로운 꼬리부분 배열 번호 갱신
if(dot_green[h_y][h_x] == 1) st = 0; // 몸부분과 충돌 체크
else dot_green[h_y][h_x] = 1; // 새 머리 부분 추가
}
}
// 지렁이 마디 수 7-세그먼트 표시(4.9ms마다 한 개씩)
// 1/16us*256분주*(256-0) = 4.9ms
ISR(TIMER2_OVF_vect)
{
unsigned int val;
PORTG = seg_on[dsp_no]; // dsp_no 세그먼트 ON
// 표시값 구하기
if(dsp_no == 0) val = length % 10; // length 1자리
else if(dsp_no == 1) val = (length / 10) % 10; // length 10자리
else val = length / 100; // lenght 100자리
// 표시값 표시하기
PORTG = (( seg_pat[val] & 0x0F) << 4) | (PORTG & 0x0F) ; // A~D 표시
PORTG = (seg_pat[val] & 0x70 ) | (PORTG & 0x0F); // E~G 표시
dsp_no = (dsp_no + 1) % 3; // 표시 세그먼트 번호 갱신(0~2)
}
댓글 1
조회수 3,556master님의 댓글
master 작성일
while(1){
o_x = rand() % 16;
o_y = rand() % 16;
if(dot_green[o_y][o_x] != 0) continue; // 지렁이 없는 위치에
dot_red[o_y][o_x] = 1;
break;
}
인터럽트 안에서 무한루프를 사용하는 경우
조건을 통해서 빠져나오기가 불가능 할 수도 있습니다.
인터럽트 안에서는 메인으로 나오지 못하며, 다른 인터럽트가 발생하더라도 다른 인터럽트 실행이 안됩니다.
그냥 그 자리에서 무한루프에 빠져버리게 되는 것이죠
if(dot_green[o_y][o_x] != 0) continue; // 지렁이 없는 위치에
현재 루프안에서 계속 머무르면 o_y, o_x 값이 변경될 수가 없고
if문 조건을 만족한다고 하면 break가 실행 될 수가 없는 것이죠