임베디드시스템/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_ */

 

작동 동영상

https://youtu.be/M03nVoL0RwA