[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;
}
'코딩 > AVR' 카테고리의 다른 글
[AVR]출입 통제 시스템 (3) | 2021.11.09 |
---|---|
[AVR] 계산기 (0) | 2021.11.09 |
[AVR]전자레인지 동작 동영상 (0) | 2021.11.09 |
[AVR] 모터 속도제어 (선풍기) (0) | 2021.11.09 |
[AVR] DP Blink/Stopwatch,Clock 구분 LED표시 시간 표시 기능 (0) | 2021.11.09 |