[AVR] UltraSonic 활용

2021. 11. 9. 22:17코딩/AVR

선생님과 함께한 수업 내용입니다.

모든 코드를 담지는 않았고 main,ultrasonic,I2c,I2c_lcd,UART,menu 정도만 담았습니다.

MAIN.c

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdio.h>  
#include <avr/interrupt.h>   // interrupt 관련 lib가 들어 있다. 
#include "uart0.h"
#include "ds1302.h"
#include "global.h"
#include "fnd.h"
#include "System.h"
#include "timeClock.h"
#include "StopWatch.h"
#include "ultrasonic.h"
#include "buzzer.h"

extern volatile uint8_t buzzer_start_flag;  // button을 눌렀을때 1로 set, 부저time out이 될때 0로 set
extern volatile int buzzer_timer; //부저 동작시간 200ms


enum _mode{WATCH,STOPWATCH,ULTRA};
uint8_t mode;
uint8_t modeState;

#define  PRESCALER 1024
volatile int ultrasonic_distance=0;
volatile int ultrasonic_trigger_timer=0;
char sbuff[40];

// printf를 동작 시키위한 mapping작업 
FILE OUTPUT = FDEV_SETUP_STREAM(UART0_transmit, NULL, _FDEV_SETUP_WRITE);

ISR(TIMER0_OVF_vect) //인터럽트 서비스 루틴 1ms 주기로 인터럽트 발생
{
	TCNT0 = 0;
	static uint16_t secCount = 0;
	
	ultrasonic_trigger_timer++;	//for 초음파 센서 측정 주기 계산하는 카운터
	
	if (buzzer_start_flag)
		buzzer_timer++;
	if (++secCount >= 1000)
	{
		secCount = 0;
		incTimeSec();
	}
	
	if(getStopWatchState() == RUN)
	{
		incStopWatchClock();
	}
	
	fndDisplay();   //1ms마다 디스플레이
	incTimeTick();  //1ms마다 틱이 발생
}
//PE4 : INT4 초음파 센서로부터 INT펄스가 들어 오면 이곳으로 진입
//초음파가 반사된 시간을 거리로 환산하는 작업
//초음파 속도 1초에 340M
//1cm이동하는데 29us소요 : 왕복 58us소요
//160000000hz / 1024 ==> 15,625Hz
//T=1/f 1/15625 ==> 0.0000064sec x 1000 = 0.064s

ISR(INT4_vect)//I인터럽스S서비스R루틴
{
	if (ECHO_PIN & (1 << ECHO))//라이지 에지
	{
		TCNT1 = 0;
	}
	else //폴링에지
	{
		ultrasonic_distance=1000000.0*TCNT1*PRESCALER/F_CPU;
		//에코핀의 펄스 폭을 마이트로초로 환산
// 		sprintf(sbuff,"distance:%3dcm  ", ultrasonic_distance/58); //cm거리 단위로 환산
// 		I2C_LCD_write_string_XY(0, 0,sbuff);
	}
	
}


int main(void)
{	PORTA = 0x00;
	DDRA = 0xff;
	TIME myTime = {0, 0, 0};

	TCCR0 |= ((1<<CS02) | (0<<CS01) | (0<<CS00));	//111 => 분주비를 1024로 설정 //100 => 분주비를 64로 설정
	TIMSK |= (1<<TOIE0);	//타이머 오버플로 인터럽트 설정
	
	ultrasonic_init();
	DS1302_Init();			//DS1302 RTC chip을 초기화 한다.
	I2C_LCD_init();			//lcd 모듈 초기화
	stdout = &OUTPUT;		//FILE : 1(stdout) 0: stdin 2:stderr
	UART0_init();			//UART0를 초기화 한다.
	fndInit();
	button_Init();
	buzzer_Init();
	
	
	setTimeClock(myTime);
	setStopWatchState(STOP);	
	modeState = WATCH;
	display_command_menu();
	
	sei();

	while (1)
	{
		switch(modeState)
		{
			case WATCH:
			getTimeClock(&myTime);
			setFndData(myTime.min*100 + myTime.sec);
			display_clock_lcd();
			buzzer_off_check();
			if(getButton1State())
			{
				modeState = STOPWATCH;
			}
			break;
			
			case STOPWATCH:
			display_clock_lcd();
			buzzer_off_check();
			switch (getStopWatchState())
			{
				case STOP:
				stopWatchStopState();
				break;
				
				case RUN:
				stopWatchRunState();
				break;
				
				case RESET:
				stopWatchResetState();
				break;
			}
			if(getButton1State())
			{
				modeState = ULTRA;
			}
			break;
			
			case ULTRA:
			
			measure_distance();
			buzzer_off_check();
			if(getButton1State())
			{
				modeState = WATCH;
			}
			break;
		}
		menu_exec();
		
		
	}
}

void measure_distance(void)
{
	if(ultrasonic_trigger_timer >= 1000) //1초 reached
	{
		
		
		sprintf(sbuff,"distance:%3dcm  ", ultrasonic_distance/58); //cm거리 단위로 환산
		I2C_LCD_write_string_XY(0, 0,sbuff);
		I2C_LCD_write_string_XY(1, 0,"                              ");
		ultrasonic_trigger();
		ultrasonic_trigger_timer=0;
		
		if((ultrasonic_distance/58) <=5)
		PORTA=0xff;
		
		else
		PORTA=0x00;
	}
	void buzzer_off_check()
	{
		if (buzzer_timer>200);
	}
}

ULTRASONIC.c

#include "ultrasonic.h"
//초음파센서 초기화
void ultrasonic_init(void)
{
	TRIG_DDR |=(1 << TRIG); //출력(1)으로 설정
	ECHO_DDR &= ~(1<< ECHO); //입력(0) 으로 설정
	
	//외부 인터럽트 트리거 방식을 설정 (LOW/상승/하강에지를 선택하는 레지스터)
	EICRB |= (0 << ISC41) | (1 << ISC40); // 상승/하강에지
	//EICRA : INT 0~3
	//EICRB : INT 4~7
	// 0 0 : INT의 LOW 신호시 일반적인 INT요청
	// 0 1 : 어떠한 신호의 변화든 INT 요청
	// 1 0 : 하강에지
	// 1 1 : 상승에지
	EIMSK |= (1 << INT4);	//외부 인터럽트 4번을 요청
	TCCR1B |= (1 << CS12) | (1 << CS10);
	// 16비트 1번 타이머/카운터를 1024로 분주 16000000/1024 ==> 15625HZ
}

void ultrasonic_trigger(void)
{
	TRIG_PORT &= ~(1 << TRIG);	//LOW출력
	_delay_us(1);
	TRIG_PORT |= (1 << TRIG);
	_delay_us(15);
	TRIG_PORT &= ~(1 << TRIG);
}

UART.c

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>   // interrupt 관련 lib가 들어 있다. 
#include "uart0.h"
#include "extern.h"


// UART0로 부터 1byte가 들어 오면 이것을 자동 실행
// RX interrupt 서비스 루틴
ISR(USART0_RX_vect)
{
	volatile uint8_t data;
	
	data = UDR0;    // UART 하드웨어 버퍼 UDR0를 읽어서 data에 저장 
	
	if (data == '\n' || data == '\r')   // led1on\n
	{                                   // 
		rxString[rxindex] = '\0';
		rxindex=0;
		rxReadyFlag=1;      // 완전한 msg를 받았다는 신호를 알려준다. 
	}
	else
	{
		rxString[rxindex++] = data;   //  rxString[rxindex] = data;   rxindex++;
	}
}

uint8_t isRxString()
{
	return rxReadyFlag;
}

uint8_t *getRxString()
{
	rxReadyFlag=0;
	return rxString;
}
// uart0를 초기화 하는 함수
#define BAUD 9600
#define BAUD_9600  ( (F_CPU / (BAUD * 8)) -1)   // 207
void UART0_init(void)
{
	UBRR0H = 0x00;
	UBRR0L = 207;   // 9600으로 설정
	
	UCSR0A |= ( 1 << U2X0);   // 2배속 sampling속도를 8로 
	UCSR0C |= 0x06;   // 비동기, data길이: 8bit/nonpairty/1stopbit
	// UART0를 송신.수신이 가능 하도록 enable
	// RXEN0 : 수신이 가능 하도록 enable 
	// TXEN0  : 송신이 가능 하도록 
	// RXCIE0  : UART0로 부터 1byte가 들어 오면 interrupt가 뜨도록 설정 
	UCSR0B = ( (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0));
}
// UART0로 1byte를 전송하는 함수
void UART0_transmit(char data)
{
	while ( !(UCSR0A & (1 << UDRE0)))   // UDRE0의 bit가 1이 되는순간 exit
		;
	UDR0 = data;   // UDR(Uart Data Register)에 값을 넣는다. 
}
// UART0로 부터 1byte를 읽어 오는 함수
unsigned char UART0_receive(void)
{
	// 76543210
	// 1   ===> RXC0 bit (RX complet)
	while ( !(UCSR0A & (1 << RXC0)))   // RXC0의 bit가 1이 되는순간 exit
		;      // nop : no operation
	
	return UDR0;   // 수신된 1byte를 return 	
}
/*
*  UART0로 string(문자열: 끝은 \0)을 출력 하는 함수 
*/
void UART0_print_string(char *str)
{
	for (int i=0; str[i] != '\0'; i++)
	{
		UART0_transmit(str[i]);
	}
}

menu.c

/*
 * menu.c
 *
 * Created: 2021-08-17 오후 3:06:51
 *  Author: kcci
 */ 
#include "menu.h"

void display_command_menu(void)
{
	printf("\n-- help --\n");
	printf("gettime\n");
	printf("settimeyymmddhhmmss\n");
}

void show_menu(void)
{
	printf("----- MENU ------\n");
	printf("1. show Time\n");   // 시 분 초
	printf("2. show Date\n");   // 년 월 일
	printf("3. update Date_time\n");
	printf("Select : ");
}

void menu_exec()
{
	char *rxData;
	char yy[4], mm[4], dd[4], hh[4], min[4], ss[4];
	struct _ds1302 updateData;
	
	//gettime
	//settimeyymmddhhmmss
	//led0on
	//led0off
	//ledallon
	//ledalloff
	if (isRxString())
	{
		rxData = getRxString();   // Message를 읽어 온다.
		
		if (strncmp(rxData, "help", sizeof("help")) == 0)
		{
			printf("\n-- help --\n");
			printf("gettime\n");
			printf("settimeyymmddhhmmss\n");
		} 
		else if (strncmp(rxData, "gettime", sizeof("gettime")) == 0)
		{
			DS1302_GetTime(&stTime);
			DS1302_GetDate(&stTime);
			
			printf("date %04d:%02d:%02d\n", 2000+stTime.year, stTime.month, stTime.date);
			printf("time %02d:%02d:%02d\n", stTime.hour, stTime.minutes, stTime.seconds);
		}
		// 0123456789012345678
		// settimeyymmddhhmmss
		else if (strncmp(rxData, "settime", 7) == 0)
		{
			printf("%s\n", rxData);

			if (digit_check(rxData+7, 2) == NULL)
			{
				strncpy(yy,rxData+7,2); 
			}
			else
			{
				printf("\nIncorrect Year !!!\n");
				return;
			}
			 
			if (digit_check(rxData+9, 2) == NULL)
			{
				strncpy(mm,rxData+9,2); 
			}
			else
			{
				printf("\nIncorrect Month !!!\n");
				return;
			}
			
			if (digit_check(rxData+11, 2) == NULL)
			{
				strncpy(dd,rxData+11,2); 
			}
			else
			{
				printf("\nIncorrect Date !!!\n");
				return;
			}

			if (digit_check(rxData+13, 2) == NULL)
			{
				strncpy(hh,rxData+13,2);
			}
			else
			{
				printf("\nIncorrect hour !!!\n");
				return;
			}

			if (digit_check(rxData+15, 2) == NULL)
			{
				strncpy(min,rxData+15,2);
			}
			else
			{
				printf("\nIncorrect minutes !!!\n");
				return;
			}

			if (digit_check(rxData+17, 2) == NULL)
			{
				strncpy(ss,rxData+17,2);
			}
			else
			{
				printf("\nIncorrect second !!!\n");
				return;
			}

			updateData.year = atoi(yy);
			updateData.month = atoi(mm);
			updateData.date = atoi(dd);
			updateData.hour = atoi(hh);
			updateData.minutes = atoi(min);
			updateData.seconds = atoi(ss);
			
			DS1302_SetTimeDates(updateData);
			
			printf("\n\ntime update success!!!!\n");
			DS1302_GetTime(&stTime);
			DS1302_GetDate(&stTime);			
			printf("date %04d:%02d:%02d\n", 2000+stTime.year, stTime.month, stTime.date);
			printf("time %02d:%02d:%02d\n", stTime.hour, stTime.minutes, stTime.seconds);
 
		} 
		else
		{
			printf("\n\nInvalid Command !!!\n");			
		} 		
	}
}

void display_clock_lcd(void)
{
	static uint8_t prevSec=0;    // 1초 1번씩 시간을 출력 하기 위한 변수 
	char buff[40];
	
	DS1302_GetTime(&stTime);
	DS1302_GetDate(&stTime);
	
	if (prevSec != stTime.seconds)   // 초가 변경 되었으면
	{
		prevSec = stTime.seconds;
		sprintf(buff, "date %04d-%02d-%02d", 2000+stTime.year, stTime.month, stTime.date);
		I2C_LCD_write_string_XY(0, 0, buff);
		
		sprintf(buff, "time %02d:%02d:%02d", stTime.hour, stTime.minutes, stTime.seconds);
		I2C_LCD_write_string_XY(1, 0, buff);
	}
}

int digit_check(char *buff, int len)
{
	for (int i=0; i < len; i++)
	{
		if (buff[i] >= '0' && buff[i] <= '9')
			;
		else return 1;
	}
	
	return 0;
}
void updateDateTime()
{
	uint8_t rxdata=0;
	struct _ds1302 updateData;
	
	// year:
	// month:
	// date:
	// hour:
	// minute:
	// sec: 
	for (int i=0; i < 6; i++)
	{
		switch (i)
		{
			case 0:  // 년
				printf("year(00~99): ");
				rxdata = get_number();
				updateData.year = rxdata;
				printf("set year: %d\n", updateData.year);
				break;
			case 1:  // 월
				printf("month: ");
				rxdata = get_number();
				updateData.month = rxdata;
				printf("set month: %d\n", updateData.month);
				break;
			case 2:   // 일
				printf("date: ");
				rxdata = get_number();
				updateData.date = rxdata;
				printf("set date: %d\n", updateData.date);
				break;
			case 3:   // 시 
				printf("hour: ");
				rxdata = get_number();
				updateData.hour = rxdata;
				printf("set hour: %d\n", updateData.hour);
				break;
			case 4:   // 분 
				printf("minutes: ");
				rxdata = get_number();
				updateData.minutes = rxdata;
				printf("set minutes: %d\n", updateData.minutes);
				break;
			case 5:   // 초
				printf("sec: ");
				rxdata = get_number();
				updateData.seconds = rxdata;
				printf("set sec: %d\n", updateData.seconds);
				break;
			break;			
		}	
	}
	DS1302_SetTimeDates(updateData);
	return 0;
}

uint8_t get_number()
{
	uint8_t rxdata=0, *rxstring;
	
	while(isRxString()==0)
		;
	rxstring = getRxString();
	rxdata = atoi(rxstring);
	
	return rxdata;
}

I2C_LCD.c

#define F_CPU	16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include "I2C.h"
#include "I2C_LCD.h"


void I2C_LCD_write_data(uint8_t data)
{
	char data_u, data_l;
	uint8_t data_t[4] = {0,};
		
	data_u = (data&0xf0);      // 상위 4bit 데이터
	data_l = ((data<<4)&0xf0); // 하위 4bit 데이터
	data_t[0] = data_u|0x0D;   //en=1, rs=1           |D7|D6|D5|D4|X|E|RW|RS|
	data_t[1] = data_u|0x09;   //en=0, rs=1
	data_t[2] = data_l|0x0D;   //en=1, rs=1
	data_t[3] = data_l|0x09;   //en=0, rs=1

	for(char i=0;i<4;i++){
		I2C_write_byte(SLA_W, data_t[i]);
	}
}

void I2C_LCD_write_command(uint8_t command)
{
	char data_u, data_l;
	uint8_t data_t[4];
	data_u = (command&0xf0);      // command의 상위 4bit 저장
	data_l = ((command<<4)&0xf0); // command의 하위 4bit 저장
	data_t[0] = data_u|0x0C;  //en=1, rs=0           |D7|D6|D5|D4|X|E|RW|RS|
	data_t[1] = data_u|0x08;  //en=0, rs=0
	data_t[2] = data_l|0x0C;  //en=1, rs=0
	data_t[3] = data_l|0x08;  //en=0, rs=0
	
	for(char i=0;i<4;i++){
		I2C_write_byte(SLA_W, data_t[i]);
	}
}

void I2C_LCD_clear(void)
{
	I2C_LCD_write_command(COMMAND_CLEAR_DISPLAY);
	_delay_ms(2);
}

void I2C_LCD_init(void)
{
	I2C_init(10000);
	_delay_ms(50);
	//Initialization of HD44780-based LCD (4-bit HW)
	I2C_LCD_write_command(0x33);
	I2C_LCD_write_command(0x32);
	I2C_LCD_write_command(0x28);   //Function Set 4-bit mode
	I2C_LCD_write_command(0x0c);   //Display On/Off Control
	I2C_LCD_write_command(0x06);   //Entry mode set
	I2C_LCD_write_command(0x01);   //Clear Display
	//Minimum delay to wait before driving LCD module
	_delay_ms(10);
}

void I2C_LCD_write_string(char *string)
{
	uint8_t i;
	for(i=0; string[i]; i++)
	I2C_LCD_write_data(string[i]);
}

void I2C_LCD_goto_XY(uint8_t row, uint8_t col)
{
	col %= 16;
	row %= 2;
	
	uint8_t address = (0x40 * row) + col;
	uint8_t command = 0x80 + address;
	
	I2C_LCD_write_command(command);
}

void I2C_LCD_write_string_XY(uint8_t row, uint8_t col, char *string)
{
	I2C_LCD_goto_XY(row, col);
	I2C_LCD_write_string(string);
}

I2C.c

#include <avr/io.h>
#include "I2C.h"

void I2C_init(unsigned int baud){
	TWBR = baud;
}

void I2C_start(void)
{
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	while (!(TWCR & (1<<TWINT)));  // 시작 완료 대기
}

void I2C_transmit(uint8_t data)
{
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);
	while (!(TWCR & (1<<TWINT)));
}

void I2C_write_byte(uint8_t address, uint8_t data)
{
	I2C_start();
	I2C_transmit(address);
	I2C_transmit(data);
	I2C_stop();
}

void I2C_stop(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN)| (1<<TWSTO);
}

uint8_t I2C_receive_ACK(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN) |(1<<TWEA);
	
	while( !(TWCR & (1<<TWINT)));             // 수신 완료 대기
	
	return TWDR;
}

uint8_t I2C_receive_NACK(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN);
	
	while( !(TWCR & (1<<TWINT)));             // 수신 완료 대기
	
	return TWDR;
}