임베디드시스템/AVR
[AVR] DP Blink/Stopwatch,Clock 구분 LED표시/Alarm 시간 표시 기능
KHJ_940803
2021. 11. 3. 10:07
목표 : 시간(초)이 증가 할때 마다 FND의 DP가 BLINK 되며, ALARM 기능을 추가
개발 툴 : atmel studio
개발 보드 : ATMEGA128A
개발 기간 : 2021년 8월 19일
소스코드
button.c
/*
* button.c
*
* Created: 2021-03-30 오후 3:47:49
*
*/
#include "button.h"
void button_Init()
{
BUTTON_DDR &= ~( (1<<BUTTON1) | (1<<BUTTON2) | (1<<BUTTON3) );
DDRE=0xff;
}
uint8_t getButton1State()
{
static uint8_t prevState = 1;
uint8_t curState; // 1:누르는것
curState = BUTTON_PIN & (1<<BUTTON1);
if((prevState == 1)&&(curState == 0)) //쓰레기값
{
_delay_ms(30);
prevState = 0;
return 0;
}
else if((prevState == 0)&&(curState != 0)) //진짜 누름
{
_delay_ms(30);
prevState = 1;
return 1;
}
return 0;
}
uint8_t getButton2State()
{
static uint8_t prevState = 1;
uint8_t curState;
curState = BUTTON_PIN & (1<<BUTTON2);
if ((prevState == 1) && (curState == 0) )
{
_delay_ms(10);
prevState = 0;
return 0;
}
else if ((prevState == 0) && (curState != 0) )
{
_delay_ms(10);
prevState = 1;
return 1;
}
return 0;
}
uint8_t getButton3State()
{
static uint8_t prevState = 1;
uint8_t curState;
curState = BUTTON_PIN & (1<<BUTTON3);
if ((prevState == 1) && (curState == 0) )
{
_delay_ms(10);
prevState = 0;
return 0;
}
else if ((prevState == 0) && (curState != 0) )
{
_delay_ms(10);
prevState = 1;
return 1;
}
return 0;
}
button.h
/*
* button.h
*
* Created: 2021-03-30 오후 3:48:21
*
*/
#ifndef BUTTON_H_
#define BUTTON_H_
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#define BUTTON_DDR DDRD
#define BUTTON_PIN PIND
#define BUTTON1 PD4
#define BUTTON2 PD5
#define BUTTON3 PD6
void button_Init();
uint8_t getButton1State();
uint8_t getButton2State();
uint8_t getButton3State();
#endif /* BUTTON_H_ */
extern.h
/*
* extern.h
*
* Created: 2021-08-09 오후 5:53:23
*
*/
#ifndef EXTERN_H_
#define EXTERN_H_
extern uint8_t FndPositionState;
extern uint32_t sec_data; // 초를 저장 하는 변수
extern uint16_t fnd_data; // fnd 로 출력 할 data
#endif /* EXTERN_H_ */
fnd.c
/*
* fnd.c
*
* Created: 2021-08-09 오후 4:49:28
* Author: khj
*/
#include "fnd.h"
#include "extern.h"
uint16_t fnd_data;
void set_fnd_data(uint16_t data)
{
fnd_data = data;
}
uint16_t get_fnd_data()
{
return fnd_data;
}
void set_sec(uint32_t sec)
{
sec_data = sec; // 초를 받아서 save
}
void fnd_display(void)
{
#if 1 // common 애노우드
uint8_t fnd_font[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xd8,0x80, 0x98}; // common +가 공통애노우드
#else
uint8_t fnd_font[] = {~0xc0, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27,0x7f, 0x67}; // common -가 캐소우드
#endif
uint16_t data = get_fnd_data();
FndPositionState++;
FndPositionState %=4; // FndPositionState = FndPositionState % 4;와 동일
switch(FndPositionState)
{
case 0: // digit1 선택 1000의 자리
FND_DIGIT_PORT = 0b10000000; // D1만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= fnd_font[data/1000%10];
break;
case 1: // digit2 선택 100의 자리
FND_DIGIT_PORT = 0b01000000; // D2만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= fnd_font[data/100%10];
break;
case 2: // digit3 선택 10의 자리
FND_DIGIT_PORT = 0b00100000; // D3만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= fnd_font[data/10%10];
break;
case 3: // digit4 선택 1의 자리
FND_DIGIT_PORT = 0b00010000; // D4만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= fnd_font[data%10]; // 10가 되기 전의 값을 출력 (0~9)
break;
}
}
void fnd_init(void)
{
FND_DATA_DDR=0xff;
FND_DIGIT_DDR=0xff;
FND_DIGIT_PORT=0x00;
}
void fnd_display_clock(void)
{
#if 1 // common 애노우드
uint8_t fnd_font[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xd8,0x80, 0x98}; // common +가 공통애노우드
#else
uint8_t fnd_font[] = {~0xc0, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27,0x7f, 0x67}; // common -가 캐소우드
#endif
uint16_t data = get_fnd_data();
FndPositionState++;
FndPositionState %=4; // FndPositionState = FndPositionState % 4;와 동일
switch(FndPositionState)
{
case 0: // digit1 선택 1000의 자리
FND_DIGIT_PORT = 0b10000000; // D1만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= fnd_font[data/1000%10];
break;
case 1: // digit2 선택 100의 자리
FND_DIGIT_PORT = 0b01000000; // D2만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= fnd_font[data/100%10]-0x80;
break;
case 2: // digit3 선택 10의 자리
FND_DIGIT_PORT = 0b00100000; // D3만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= fnd_font[data/10%10];
break;
case 3: // digit4 선택 1의 자리
FND_DIGIT_PORT = 0b00010000; // D4만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= fnd_font[data%10]; // 10가 되기 전의 값을 출력 (0~9)
break;
}
}
void fnd_display_alrm(void)
{
FndPositionState++;
FndPositionState %=4; // FndPositionState = FndPositionState % 4;와 동일
switch(FndPositionState)
{
case 0: // digit1 선택 1000의 자리
FND_DIGIT_PORT = 0b10000000; // D1만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= 0b10001000;
break;
case 1: // digit2 선택 100의 자리
FND_DIGIT_PORT = 0b01000000; // D2만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= 0b11000111;
break;
case 2: // digit3 선택 10의 자리
FND_DIGIT_PORT = 0b00100000; // D3만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= 0b10001000;
break;
case 3: // digit4 선택 1의 자리
FND_DIGIT_PORT = 0b00010000; // D4만ON 시키고 나머지는 OFF FND_DIGIT_PORT = 0b01111111;
FND_DATA_PORT= 0b10001000; // 10가 되기 전의 값을 출력 (0~9)
break;
}
}
fnd.h
/*
* fnd.h
*
* Created: 2021-08-09 오후 4:50:00
* Author: khj
*/
#include <avr/io.h>
#include "extern.h"
// 변수 type앞에 extern을 쓰는 이유: 이 변수는 내 화일에 있지 않고 다른 화일에 있다고 compiler에 알려 주기 위함
#ifndef FND_H_
#define FND_H_
#define FND_DATA_DDR DDRC
#define FND_DATA_PORT PORTC
#define FND_DIGIT_DDR DDRB
#define FND_DIGIT_PORT PORTB
#define FND_DIGIT_D1 PB4
#define FND_DIGIT_D2 PB5
#define FND_DIGIT_D3 PB6
#define FND_DIGIT_D4 PB7
void fnd_init(void);
void fnd_display(void);
void set_sec(uint32_t sec);
void fnd_display_clock(void);
void fnd_display_alrm(void);
#endif /* FND_H_ */
global.h
/*
* global.h
*
* Created: 2021-08-09 오후 3:39:18
* Author: kcci
*/
#ifndef GLOBAL_H_
#define GLOBAL_H_
uint8_t FndPositionState=0;
uint32_t sec_data; // 초를 저장 하는 변수
uint16_t fnd_data; // fnd 로 출력 할 data
static uint32_t timeTick=0;
#endif /* GLOBAL_H_ */
main.c
/*
* main.c
*
* Created: 2021-08-09 오후 3:22:42
* Author : khj
*/
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "global.h" // 현재 내 디렉토리 밑에 있는 화일을 include할때는 ""
#include "fnd.h"
#include "button.h"
#include "time_clock.h"
//button1 : watch <--> stopwatch
//button2 : stopwatch start <--> stopwatch stop
//button3 : stopwatch reset
//먼저 입출력 정의를 정확하게 하고
// 시나리오 정의를 그 다음으로 한다. (그림 그린것)
//for define watch or stopwatch
#define WATCH 0
#define STOPWATCH 1
unsigned char mode_state = WATCH;// 시작하자마자 시계는 돌아가게 한다.
//for stopwatch
#define STOP 0
#define RUN 1
#define RESET 2
unsigned char stop_watch_state = STOP;// 시작하면 스톱워치는 멈춰있어야 한다.
#define ALRMMIN 30
#define ALRMSEC 20
unsigned char alrm_lock = 0;
void inc_stop_watch_clock();
void stop_watch_stop_state();
void stop_watch_run_state();
void stop_watch_reset_state();
void alrm_ring();
// 0.001sec ==> 1ms마다 이곳으로 들어 온다.
ISR(TIMER0_OVF_vect)
{
static uint16_t sec_count=0;
static uint16_t odd_even=0;
if (++sec_count >= 1000) // 1000ms 는 1초이다.
{
odd_even++;
sec_count=0;
inc_time_sec();
}
if (stop_watch_state == RUN)
{
inc_stop_watch_clock();
}
if((alrm_lock==1)&&(mode_state == WATCH)) // 알람 시간이 되면 alrm_lock 이 1이 되면서 계속 여기서 머문다.
{
if(odd_even%2)
{
fnd_display_alrm();
}
else
{
fnd_display();
}
}
else if((odd_even%2)&&(mode_state == WATCH)) // 홀수 일때 , WATCH 모드일떄
{
fnd_display_clock(); // 점하나를 찍기 위해 4자리수를 부르는 함수를 재탕했다.
}
else if( ( (odd_even+1) %2 ) && (mode_state == WATCH) ) //짝수 일때, WATCH 모드 일때
{
fnd_display();
}
else if( mode_state == STOPWATCH ) // STOPWATCH MODE 일때.
{
fnd_display();
}
timeTick++;
}
uint16_t stop_watch_clock=0; // 전역변수
void inc_stop_watch_clock()
{
static uint8_t count =0;
if (++count >= 100)
{
count =0;
stop_watch_clock++;
}
}
int main(void)
{
TIME myTime;
fnd_init();
button_Init();
// 분주비를 64로 설정 16MHZ / 64 ==> 250,000HZ 이렇게 설정시 TIM0 OV 256/250,000
// 0.001sec ==> 1ms마다 timer INT 가 발생 된다.
TCCR0 |= (1 << CS02) | (0 << CS01) | (0 << CS00);
TIMSK |= (1 << TOIE0); // timer overflow INT설정: TCNT0의 register값이 256이 되는 순간 INT가 발생
sei(); // 모든 인터럽트가 실행 되도록 대문을 연다.
myTime.hour=18;
myTime.min=30;
myTime.sec=10;
set_time_clock(myTime);
// button의 동작 상황을 check하면서 상태 천이 할 수 있도록 한다.
while (1)
{
switch (mode_state){
case WATCH:// 버튼 1을 눌러서 모드를 전환한다.
PORTE =0b00010000; //적색등 점등
get_time_clock(&myTime);
if(alrm_lock==1) {set_fnd_data(ALRMMIN*100+ALRMSEC); } // lock 걸면 세팅한 시간에서 fnd display 하게끔 한다.
else {set_fnd_data(myTime.min*100+myTime.sec);} // 시간을 받아서 fnd data에 넣고 넘겨준다.
alrm_ring(&myTime);
if(getButton1State())
{
mode_state=STOPWATCH;
}
if(getButton3State())
{
alrm_lock=0;
}
break;
case STOPWATCH:// 버튼 1을 눌러서 모드를 전환한다.
PORTE =0b00001000;// 녹색등 점등
switch (stop_watch_state)
{
case STOP:
stop_watch_stop_state();
break;
case RUN:
stop_watch_run_state();
break;
case RESET:
stop_watch_reset_state();
break;
}
if(getButton1State())
{
mode_state=WATCH;
}
}
}
}
// stopwatch가 stop상태에서는 run 버튼이나 reset버튼을 누를수 있다.
void stop_watch_stop_state()
{
set_fnd_data(stop_watch_clock);
if (getButton2State()) // button2을 누르면
{
stop_watch_state=RUN; // RUN상태로 상태 천이
}
if (getButton3State()) // button3를 누르면 RESET버튼
{
stop_watch_state=RESET; // stopwatch를 RESET상태로 상태 천이
}
}
// 현재 stopwatch가 돌고 있는 상태에서 button1을 누르면 stop상태로 천이
void stop_watch_run_state()
{
set_fnd_data(stop_watch_clock);
if (getButton2State()) // button2을 누르면
{
stop_watch_state=STOP; // STOP상태로 상태 천이
}
}
// 현재 stopwatch가 reset상태에서 stop모드로 복귀
void stop_watch_reset_state()
{
stop_watch_clock=0;
set_fnd_data(stop_watch_clock);
stop_watch_state=STOP; // STOP상태로 상태 천이
}
void alrm_ring(TIME *Time)
{
if(Time->min == ALRMMIN)
{
if(Time->sec ==ALRMSEC)
{
alrm_lock =1;
}
}
}
time_clock.c
/*
* time_clock.c
*
* Created: 2021-08-10 오후 2:10:08
* Author: khj
*/
#include "time_clock.h"
TIME timeClock = {0, 0, 0};
void inc_time_sec(void)
{
if (++timeClock.sec >= 60)
{
timeClock.sec =0;
timeClock.min++;
if (timeClock.min >= 60)
{
timeClock.min=0;
timeClock.hour++;
if (timeClock.hour >=24)
{
timeClock.hour=0;
}
}
}
}
void get_time_clock(TIME *Time)
{
*Time = timeClock;
}
void set_time_clock(TIME Time)
{
timeClock = Time;
}
time_clock.h
/*
* time_clock.h
*
* Created: 2021-08-10 오후 2:10:35
* Author: khj
*/
#ifndef TIME_CLOCK_H_
#define TIME_CLOCK_H_
#include <avr/io.h>
// 구조체: 서로다른 자료구조 및 변수의 집합체
typedef struct _time
{
uint8_t hour;
uint8_t min;
uint8_t sec;
} TIME;
void inc_time_sec(void);
void get_time_clock(TIME *Time);
void set_time_clock(TIME Time);
#endif /* TIME_CLOCK_H_ */
작동 동영상