스톱위 만들기 과제를 수행하면서 질문한 것과 답변을 정리한 글입니다.
출처 : cafe.naver.com/laydigital/
[과제]-------------------------------------------------------------------
1. 오후 1시 30분을 “13.30”으로 FND에 디스플레이 하는 디지털시계를 구현해 보세요. “23.59” 다음은 “00.00”입니다. 테스트 용으로는 100배속으로 빠르게 실행되도록, 즉 1분이 아니고 1초마다 숫자가 1씩 증가하도록 처리하면 확인이 쉽습니다.
2. 1번을 과제를 기본으로 하되, SW1을 한번 누르면 ‘시간’의 숫자를 변경하는 모드, 두번 누르면 ‘분’의 숫자를 변경하는 모드, 세번 누르면 일반 시계 모드가 되도록 하고, SW2는 숫자변경 모드일 때 ‘시간’ 또는 ‘분’을 1씩 증가시키는 역할을 하도록 프로그램해 보세요.
------------------------------------------------------------------------
/*program: atmel studio 6.1 ver 8bit */
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#include <util/delay.h>
typedef unsigned char u_8t;
typedef unsigned int u_16t;
#define FND_D PORTC
#define FND_C PORTG
#define IDLE 0
#define NUM_ADD 1
#define hour_MODE 2
#define min_MODE 3
#define NONE 4
volatile u_16t idle_time =0; //초기상태 변수
volatile u_16t cur_time =0; // 증감용도 변수
volatile u_16t stp_time =0; // 인터럽트시 표시값 저장변수
volatile u_16t htp_time =0; // 시간모드사용시 표시값 저장변수
volatile u_16t mtp_time =0; // 분 모드 사용시 표시값 저장변수
volatile u_16t state = IDLE;
volatile u_16t state_mode = NONE;
u_8t fnd_img[]={0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67};
u_16t figure_num[]={1,10,100,1000};
#define figure(x) (0x01<<(x))
#define digit_num(x,y) fnd_img[((x)/figure_num[y])%10]
void init_stop_watch();
void display_watch();
int main(void) // state의 상태값에 따라 카운트되는 값을 fnd에 출력.
{
init_stop_watch(); // 장치사용 준비.
while(1)
{
if(state == IDLE) //아무조작이 없는 상태에서는 계속 시간이 1초마다 증가
{
display_watch(idle_time);
idle_time++;
}
if (state == hour_MODE)
{
display_watch(htp_time);
}
if (state == min_MODE)
{
display_watch(mtp_time);
}
if(idle_time>1440)
idle_time=0;
}
}
ISR(INT4_vect)
{
if(state == IDLE && state_mode== NONE) //sw1:1click --> sw2:시간 변경모드
{
state = hour_MODE;
htp_time = idle_time; // h-mod 표시값
stp_time = idle_time; // 전 idle 상태 값.
cur_time =0; //int5에서 사용(증가변수)
}
if (state == hour_MODE && state_mode == NONE) //sw1: 2click -->sw2:분 변경모드
{
state = min_MODE;
state_mode = NUM_ADD;
//idle_time =0; //idle 값 초기화
mtp_time = htp_time; //m-mod 표시값.
stp_time = htp_time; //전 h-mod 표시값.
cur_time =0; // int5 사용(증가변수)
}
if (state == min_MODE && state_mode == NUM_ADD) //sw1: 3click --> 초기상태 복귀
{
state = IDLE;
state_mode = NONE;
idle_time = mtp_time; // 전 m-mod에서 계산된값을 idle_time에 전달하고 모든변수초기화.
cur_time=0;
htp_time=0;
stp_time=0;
}
_delay_ms(11); //chattering 방지목적
EIFR = 1<<4;
}
SIGNAL(INT5_vect)
{
++cur_time;
if(state == hour_MODE && state_mode== NONE) //sw1:1click -->시간 변경 모드
//sw2:눌러서 1씩증가된 시간값을 출력
{
htp_time = (stp_time + (((cur_time)%24)*60)); //시간 표시부.
}
if(state == min_MODE && state_mode == NUM_ADD) //sw1:2click --> 분 변경 모드
{ //sw2를 클릭하여 1씩 증가된 분값을 출력
mtp_time = (htp_time + (cur_time%60)); //분 표시부.
}
_delay_ms(11); //chattering 방지목적
EIFR = 1<<5;
}
void init_stop_watch() //stopwatch 기능으로 사용할 포트및 스위치 설정.
{
DDRA = 0xff;
DDRC = 0xff;
DDRG = 0x0f;
DDRE = (0<<DDE4)|(0<<DDE5);
sei(); // SREG|= 0x80; global interrupt 를 enable.
EIMSK = (1<<INT4)|(1<<INT5); // 외부인터럽트 mask int4 int5의 alternative function 사용.
EICRB = (1<<ISC41)|(1<<ISC40)|(1<<ISC51)|(1<<ISC50); // 외부인터럽트 컨트롤 레지스터. trigger // 상승edge를 선택.
}
void display_watch(int val) //디스플레이 함수 하나의 문자를 표시할때 걸리는 시간을 기준으로 시간을 조절하여 표현.
{
int i,j;
for(j=0; j<1; j++) //1초 유지. (0~100: 99) 100 * 1/100ms = 1sec.
{ // 확인용 임의의값.
for(i=0; i<4; i++)
{
if(i>=2)
{
FND_D = digit_num(((val/60)%24)*100,i);
}
else if(i<2)
{
FND_D = digit_num(val%60,i);
}
else if(i==2)
{
FND_D |=0x80;
}
FND_C = figure(i);
_delay_us(2500); //4번 표시할때까지 소비된 시간은 1/100 이 됨.
} // _delay_ms: 2.5 * 4 = 10 ms
} // 2.5ms = 2500us
}
욕심은 조금더 간결하게 최적화 시켜보고 싶었는데 더 이상 진전이 없어 과제수행으로 작성하여 올려보았습니다. 살펴보시고 고칠점이나 개선점이 필요한부분이 있다면 작은부분이라도 알려주시길 바랍니다.
if(i>=2)
{
FND_D = digit_num(((val/60)%24)*100,i);
}
else if(i<2)
{
FND_D = digit_num(val%60,i);
}
else if(i==2)
{
FND_D |=0x80;
}
이 부분이 동작하나요?
if
elseif
else if
이렇게 되어 있고
첫번째 조건이
i>=2
이면 이미 i가 2인 조건은 첫번째만 만족합니다.(세번째 else if는 동작안함)
네 확인해 보니 위에 올린코드에서 지적해주신부분은 구문 조건때문에 i==2 일때 작동이 않되네요.
연습한다고 수정해보고 그대로 올렸네여 원래는
if(i>=2)
{
FND_D = digit_num(((val/60)%24)*100,i);
}
if(i<2)
{
FND_D = digit_num(val%60,i);
}
if(i==2)
{
FND_D |=0x80;
}
이렇게 작성하여 연습하고 나중에 구문 연습해본다고 수정한 부분인데 말씀한신데로 else 문과 같이 작성된
조건은 첫번째조건이 만족되서 실행이 않됐습니다. 코멘트 감사합니당.
digit_num(x,y) 매크로가 연산시간을 많이 잡아 먹는 구조입니다.
해당 함수를 호출하는 (val/60)%24) * 100
이 부분도 연산 시간이 많이 필요합니다.
mcu에서는 가능하면 나눗셈(또는 그걸 유도하는 %, /)
곱셈은 피하는게 좋습니다.
초로 관리하고 후에 출력시 시간,분,초 를 추출하면 필연적으로 나눗셈이 포함되므로
코딩이 좀 번거러워도 처음부터
sec, min, hour 로 나누고
ex)
if(++sec >= 60) { sec = 0 ; if(++min >= 60) { min = 0; if(++hour>= 24) hour = 0; } };
No comments:
Post a Comment