BASIC4MCU | 질문게시판 | avr Atmega 소스코드 잘못된부분 수정바랍니다.
페이지 정보
작성자 전자초짜 작성일2018-06-01 17:11 조회8,875회 댓글1건본문
#include <stdio.h> // sprintf()를 위해 #include
#include <avr/io.h>
#include <avr/interrupt.h>
void Delay_us(unsigned int time_us)
{
register unsigned int i;
for(i=0; i<time_us; i++) // 4 cycle +
{
asm volatile(" PUSH R0 "); // 2 cycle +
asm volatile(" POP R0 "); // 2 cycle +
asm volatile(" PUSH R0 "); // 2 cycle +
asm volatile(" POP R0 "); // 2 cycle = 12 cycle = 0.9216 MHz
} // for 11.0592 MHz
// =약 1.085 us
}
void Delay_ms(unsigned int time_ms)
{
register unsigned int j;
for(j=0; j<time_ms; j++)
{
Delay_us(1000);
}
} //약 1.085 ms
// ADC
void adc_init(void) // AD 컨버터 초기화
{
ADMUX = 0x40;
//AVCC단자전압을 기준전압으로 이용
ADCSRA = 0xA5;
//ADC enable, free running mode, 프리스케일러 32분주
ADCSRA |= 0x40;
// A/D변환 시작
}
// AD 컨버팅을 한다.
// 오차를 줄이기 위해 16번 ADC를 하여 평균한다.
uint16_t get_adc(uint8_t channel) // 1024분주로 AD 컨버팅을 한다.
{
uint16_t result = 0;
uint8_t i = 0;
ADMUX = 0x40 | channel;
//MUX를 조작하여 ADC 채널을 선택한다.
Delay_us(150); //MUX 변환 시간 동안 대기
ADCSRA |= (1 << ADSC); // 변환 시작
for(i = 0; i < 16; i++)
{
ADCSRA |= (1 << ADIF); //ADC재시작
while( !((ADCSRA >> ADIF) & 1) );
//ADC 완료될 때 까지 대기
result += ADC; //1024분주 값으로 데이터를 받아온다.
}
ADCSRA &= (~(1 << ADSC)); //변환을 멈춤
return (result >> 4);//쉬프트
}
//LCD
#define LCD_DCTRL (DDRG)
#define LCD_DDATA (DDRA)
#define LCD_CTRL (PORTG)
#define LCD_DATA (PORTA)
#define LCD_RS (PG0)
#define LCD_RW (PG1)
#define LCD_E (PG2)
void lcd_control(uint8_t data)
{
LCD_CTRL = LCD_CTRL & ~_BV(LCD_RW);
// #define _BV(bit) (1 << (bit)) // RW=0
LCD_CTRL = LCD_CTRL & ~_BV(LCD_RS);
// RS=0 IR(인스트럭션 레지스터) 쓰기 동작을 실행한다.
LCD_DATA = data;
LCD_CTRL = LCD_CTRL | _BV(LCD_E);// EN = 1
Delay_ms(1);
LCD_CTRL = LCD_CTRL & ~_BV(LCD_E);//EN = 0
}
void lcd_init(void)
{
LCD_DDATA = 0xFF; // A포트를 출력으로 사용
LCD_DCTRL = 0x07;
// G포트의 0,1,2번 핀인 LCD_RS,LCD_RW,LCD_EN 핀을 출력으로 사용
LCD_DATA = 0; // A포트의 데이터 출력 값을 0으로 한다.
LCD_CTRL = 0; // RS,RW,EN 의 데이터 출력 값을 0으로 한다.
lcd_control(0x38); // Function Set (8bit, 2line, 5 x 7 dot)
lcd_control(0x0C); // Display ON, Cursor OFF
lcd_control(0x06); // Entry Mode Set (어드레스 +1 커서를 우로 이동, not shift)
lcd_control(0x01); // Clear Display
}
void lcd_write_char(uint8_t data)
{
LCD_CTRL = LCD_CTRL & ~_BV(LCD_RW); //RW=0
LCD_CTRL = LCD_CTRL | _BV(LCD_RS); //RS=1
LCD_DATA = data;
//DR(데이터 레지스터)쓰기 동작,D.D.RAM 에 데이터값을 쓴다.
LCD_CTRL = LCD_CTRL | _BV(LCD_E);
Delay_ms(1);
LCD_CTRL = LCD_CTRL & ~_BV(LCD_E);
}
void lcd_position(uint8_t x, uint8_t y)
{
uint8_t location=0;
if(y>0x01)
y=0x01;
if(x>0x0f)
x=0x0f;
if(y == 0)
location = x + 0x80; //1행에 표시
else
location = x + 0xC0; //2행에 표시
lcd_control(location);
}
// D.D.RAM adress를 x 로 설정한다. 1행의 주소는 0x00~0x27까지 2행의 주소는 0x40 ~ 0x67 까지
void lcd_display_string(uint8_t x, uint8_t y, char *string)
{
lcd_position(x,y); // 글자를 출력할 위치를 정해준다.
while(*string != '\0')
//포인터의 마지막인 null 문자가 될 때까지 포인터 주소 값을 증가시키며 DR어드레스에 쓴다.
{
lcd_write_char(*string);
string++;
}
}
//시간 컨트롤
char sec='0',sec10='0',min='0',min10='0',hour='0',hour10='0',mid='A';
//시간을 표현하기 위한 변수들을 char 타입으로 숫자와 문자를 저장한다.
char time[17]; //LCD에 시간을 표시하기 위한 배열을 설정한다.
void Time_Control()
{
if(hour10=='0' && hour=='0' && min10=='0' && min=='0' && sec10=='0' && sec=='0')
{
if(mid == 'A')
{mid='P';}
else
{mid='A';}
}
//00:00:00 이 되는 순간 A는 P로 P는 A로 바꾼다.
else if(hour10 == '1' && hour > '1' )
{
hour10='0';
hour='0';
}
//시간이 12으로 증가하면 00시로 변경시킨다.
else if(hour > '9')
{
hour='0';
hour10++;
}
// hour가 10 이면 hour = '0' 이되고 hour10 은 1증가시킨다.
else if(min10 > '5')
{
min10='0';
hour++;
}
//min10이 6 이되면 min10 은 0 이 되고 hour를 1증가시킨다.
else if(min > '9')
{
min='0';
min10++;
}
//10분이 되면 min= '0' 이 되고 min10 에 1을 증가시킨다.
else if(sec10 > '5')
{
sec10='0';
sec='0';
min++;
}
//sec10이 6이되면 sec10을 0으로 바꾸고 min을 1증가시킨다.
else if(sec > '9')
{
sec='0';
sec10++;
}
//sec가 10이 되면 sec은 0 으로 바꾸고 sec10은 1증가시킨다.
}
double humi=0;
ISR(TIMER0_OVF_vect) //TIMER0 오버플로우 인터럽트 발생 시 실행
{
TCNT0 = 156;
humi++;
if (humi==104) // 약 0.9956초를 카운트
{
lcd_display_string(0, 0,time);
humi = 0;
sec++;
}
}
//1초를 카운트 하기 위해 오버플로우가 될때까지의 값을 100으로 설정하고 104번의 루프를 돌려 약 1초를 만들었다.
//1초마다 LCD에 시간을 표시하도록 하였다.
//LCD를 표시하는 딜레이시간을 고려하여 104번의 루프를 돌리면 오차가 최소화 된다.
// 외부 인터럽트
ISR(INT4_vect) //INT4 interrupt service routine
{
Delay_ms(10);
if(TCCR0 &= 0x07)
{TCCR0 &= 0x00;}
else
{TCCR0 |= 0x07;}
}
//TCCR0 레지스터의 마지막 3비트를 비교하여 프리스케일러가 1024분주이면 인터럽트 발생 시
//TCCR0의 마지막 3비트가 0이 되어 카운터를 멈춘다.
//다시 한번 인터럽트를 걸면 1024분주로 카운터가 시작된다.
ISR(INT5_vect)//INT5 interrupt service routine
{
Delay_ms(25);
if(mid == 'A')
{
mid='P';
lcd_display_string(0, 0,time);
}
else if(mid == 'P')
{
mid='A';
lcd_display_string(0, 0,time);
}
}
// 인터럽트 발생 시 문자 A는 P로 바꾸고 P는 A로 바꾸어 오전 오후를 표시한다.
//메인
#define FORWARD 0XFF // 모터 정방향 인에이블(2번핀)
#define DISABLE 0X00 // 모터 정지
int main(void)
{
int i=0;
char string[16]; // LCD에 나타낼 온도 부분의 문자열을 저장할 임시 변수
uint16_t result; // A/D 컨버팅 결과를 받아올 임시 변수
unsigned char Setup_hour,Setup_min,Setup_sec,Hold_Humi;
//스위치 사용을 위하여 변수 설정
char *Introduce1=" 2008 TermProject";
char *Introduce2=" 200412459";
char *Introduce3=" Hwang se jun";
char *Introduce4=" 200511405";
char *Introduce5=" Seo dong hwan";
char *Introduce6=" 200711490";
char *Introduce7=" Kim se mi";
// 처음에 켜면 나오는 학번 및 이름을 저장한 포인터 변수 설정
Delay_ms(20);
// LCD와 MCU 등은 전원이 들어온 후 약간의 시간이 지나야 정상 동작하므로 초기 지연시간을 준다.
DDRE = 0x00;
//INT4와 INT5를 사용하기위해 E포트를 입력으로 사용함을 선언.
DDRC = 0x00;
//스위치를 사용하기 위해 C포트를 입력으로 사용함을 선언.
DDRF = 0x00; // ADC를 입력으로 한다.
DDRD = 0xfC; //LED를 모두 출력으로 사용함을 선언.
PORTE = 0x0C;
//E포트의 INT4,INT5 의 데이터 값에 1을 주어 인터럽트를 사용하도록 한다.
DDRB= 0xff; //포트B를 모터 출력으로 사용 지정
int rx;
rx=getchar(); // 문자에 대한 표준입력함수
EICRA = 0x00;
//INT3~0은 low level일 때 trigger로 설정. 위에서 포트의 입력을 모두 1로 설정하므로 off와 동일.
EICRB = 0x0A; //INT4,INT5는 falling edge일 때trigger
EIMSK = 0x30; //INT4,INT5 를 on
EIFR = 0x30; // INT4,INT5가 트리거 되었음을 표시
sei(); //Global interrupt enable
adc_init(); // AC 컨버터 초기화
lcd_init() ; // LCD 초기화
Delay_ms(10);
for(i=0;i<17;i++)
{
lcd_display_string(0, 0,Introduce1++);
Delay_ms(25);
}
Delay_ms(100);
lcd_control(0x01);
for(i=0;i<10;i++)
{
lcd_display_string(0, 0,Introduce2++);
Delay_ms(25);
}
for(i=0;i<13;i++)
{
lcd_display_string(0, 1,Introduce3++);
Delay_ms(25);
}
Delay_ms(100);
lcd_control(0x01);
for(i=0;i<10;i++)
{
lcd_display_string(0, 0,Introduce4++);
Delay_ms(25);
}
for(i=0;i<14;i++)
{
lcd_display_string(0, 1,Introduce5++);
Delay_ms(25);
}
Delay_ms(100);
lcd_control(0x01);
for(i=0;i<10;i++)
{
lcd_display_string(0, 0,Introduce6++);
Delay_ms(25);
}
for(i=0;i<10;i++)
{
lcd_display_string(0, 1,Introduce7++);
Delay_ms(25);
}
Delay_ms(50);
lcd_control(0x01);
Delay_ms(50);
// 학번 이름 등 소개하는 글을 LCD에 차례대로 띄운다.
TCCR0 = _BV(CS02)|_BV(CS01)|_BV(CS00); // Prescaling : clk/1024
TIMSK = 0x01; //Enable overflow
UBRR0L = 71; // USART BAUD RATE REG.송수신속도 조절 (11059200/16/9600)-1=71
UCSR0B = 0x18; // USART CONTROL&STATUS REG. 송수신부 enable
while(1) // 무한 루프 - 프로그램은 종료되지 않는다.
{
time[0]=mid;
time[1]='m';
time[2]=' ';
time[3]=hour10;
time[4]=hour;
time[5]=':';
time[6]= min10;
time[7]=min;
time[8]=':';
time[9]=sec10;
time[10]=sec;
time[11]=' ';
time[12]='*';
time[13]='^';
time[14]='^';
time[15]='*';
//time[17]배열의 요소를 한개씩 지정해 준다.
Hold_Humi=(PINC & 0x08);
Setup_hour = (PINC & 0x01);
Setup_min =(PINC & 0x02);
Setup_sec = (PINC & 0x04);
Time_Control();
// 스위치5번이 연결되어있는 PC0 핀의 값을 Setup_hour에 입력
// 스위치6번이 연결되어있는 PC1 핀의 값을 Setup_min에 입력
//스위치7번이 연결되어있는 PC2 핀의 값을 Setup_sec에 입력
//스위치를 누르면 PINC에 각각 걸리는 값이 0이 된다.
if(Setup_hour == 0x00)
{
Delay_ms(50);
hour++;
}
//SW5를 누르면 시간이 1씩 증가한다.
if(Setup_min == 0x00)
{
Delay_ms(50);
min++;
}
//SW6을 누르면 분이 1씩 증가한다.
if(Setup_sec == 0x00)
{
Delay_ms(50);
sec++;
}
//SW7을 누르면 초가 1씩 증가한다.
lcd_display_string(0, 0,time); //시간을 LCD의 첫줄에 표시한다.
if(Hold_Humi == 0x00 )
{
sprintf(string, "Humidity:%2d.%d RH", result/10, result%10);
// 결과를 포맷에 맞게 문자열로 생성한다.
lcd_display_string(0, 1, string);
}
//SW8을 누르면 현재 온도가 그대로 LCD에 고정되어 표시된다.
else
{
result = (uint16_t) get_adc(2);
// 1024 분해능으로 ADC2 결과를 받아온다.
result=((result*5/10.24)-80)*10/3.1;
}
//SW8을 누르고 있지 안은 경우에는 계속해서 ADC의 결과를 받아서 result값에 저장한다.
if(result < 250)
{
PORTD=0x04;
}
else if(result < 300 && result > 250)
{
PORTD=0x0C;
}
else if(result < 350 && result > 300)
{
PORTD=0x1C;
}
else if(result < 400 && result > 350)
{
PORTD=0x3C;
}
else if(result < 450 && result > 400)
{
PORTD=0x7C;
}
else if( result > 450)
{
PORTD=0xFC;
}
//습도가 45% 이하일때는 LED불빛이 한개만 켜지고 5% 증가할 때마다 LED의 불이 하나씩 더 켜진다.
if(result > 450)
{
PORTD=0xFC;
PORTB = FORWARD;
lcd_control(0x01);
sprintf(time, "Sooo Humid *-_-*");//string
lcd_display_string(0, 0, time);
sprintf(string, "Humidity:%2d.%d RH", result/10, result%10);
lcd_display_string(0, 1,string);
Delay_ms(1);
while(1)
{ Hold_Humi=(PINC & 0x08);
Setup_hour = (PINC & 0x01);
Setup_min =(PINC & 0x02);
Setup_sec = (PINC & 0x04);
Time_Control(0x01);
while((UCSR0A & 0x80) == 0x00);// CPU가 수신 문자를 읽어서
{ // 수신 버퍼가 비어있는 상태라면 (RXC=0)
rx=UDR0; // 받은 데이터를 임시 변수에 저장
while((UCSR0A & 0x20) == 0x00); // 송신이 가능한 동안 (UDRE0=0)
{
UDR0=rx;// 데이터를 보낸다.(임시변수에 저장된
// 데이터를 UDR0에 저장한다.)
}
}
{ Delay_ms(1); //딜레이}
if ((rx=='S')||(rx=='s')) //s값을 받으면 정지시킨다.
{ PORTB=DISABLE;
PORTD=0XFF;
Delay_ms(1);
}
else if((rx=='e')||(rx=='E'))
{
break;
}
else
{
PORTB=FORWARD; //s값이 아니면 led가 켜진다.
PORTD=0XFF;
Delay_ms(1);
}
}
PORTD = 0x00;
}
//현재의 습도가 45%가 넘어가면 글씨가 LCD에 표시가 되고 부저가 울리며 LED의 모든 불이 켜진다.
//45% 이하로 내려오면 정상작동을 한다.
}
else
{
PORTB=0x00;
sprintf(string, "Humidity:%2d.%d RH", result/10, result%10);
lcd_display_string(0, 1, string);
}
Delay_ms(1); //LCD가 동작하기 위한 시간지연
}
return 0;
}
for(i=0;i<17;i++)
{
lcd_display_string(0, 0,Introduce1++);
Delay_ms(25);
}
Delay_ms(100);
lcd_control(0x01);
Hold_Humi=(PINC & 0x08);
Setup_hour = (PINC & 0x01);
Setup_min =(PINC & 0x02);
Setup_sec = (PINC & 0x04);
Time_Control();
// 스위치5번이 연결되어있는 PC0 핀의 값을 Setup_hour에 입력
// 스위치6번이 연결되어있는 PC1 핀의 값을 Setup_min에 입력
//스위치7번이 연결되어있는 PC2 핀의 값을 Setup_sec에 입력
//스위치를 누르면 PINC에 각각 걸리는 값이 0이 된다.
if(result < 250)
{
PORTD=0x04;
}
else if(result < 300 && result > 250)
{
PORTD=0x0C;
}
else if(result < 350 && result > 300)
{
PORTD=0x1C;
}
else if(result < 400 && result > 350)
{
PORTD=0x3C;
}
else if(result < 450 && result > 400)
{
PORTD=0x7C;
}
else if( result > 450)
{
PORTD=0xFC;
}
//습도가 45% 이하일때는 LED불빛이 한개만 켜지고 5% 증가할 때마다 LED의 불이 하나씩 더 켜진다.
if(result > 450)
{
PORTD=0xFC;
PORTB = FORWARD;
lcd_control(0x01);
sprintf(time, "Sooo Humid *-_-*");//string
lcd_display_string(0, 0, time);
sprintf(string, "Humidity:%2d.%d RH", result/10, result%10);
lcd_display_string(0, 1,string);
Delay_ms(1);
while(1)
{ Hold_Humi=(PINC & 0x08);
Setup_hour = (PINC & 0x01);
Setup_min =(PINC & 0x02);
Setup_sec = (PINC & 0x04);
Time_Control(0x01);
while((UCSR0A & 0x80) == 0x00);// CPU가 수신 문자를 읽어서
{
// 수신 버퍼가 비어있는 상태라면 (RXC=0)
rx=UDR0; // 받은 데이터를 임시 변수에 저장
while((UCSR0A & 0x20) == 0x00); // 송신이 가능한 동안 (UDRE0=0)
{
UDR0=rx;// 데이터를 보낸다.(임시변수에 저장된
// 데이터를 UDR0에 저장한다.)
}
}
{ Delay_ms(1); //딜레이}
if ((rx=='S')||(rx=='s')) //s값을 받으면 정지시킨다.
{ PORTB=DISABLE;
PORTD=0XFF;
Delay_ms(1);
}
else if((rx=='e')||(rx=='E'))
{
break;
}
else
{
PORTB=FORWARD; //s값이 아니면 led가 켜진다.
PORTD=0XFF;
Delay_ms(1);
}
}
PORTD = 0x00;
}
//현재의 습도가 45%가 넘어가면 글씨가 LCD에 표시가 되고 부저가 울리며 LED의 모든 불이 켜진다.
//45% 이하로 내려오면 정상작동을 한다.
}
else
{
PORTB=0x00;
sprintf(string, "Humidity:%2d.%d RH", result/10, result%10);
lcd_display_string(0, 1, string);
}
댓글 1
조회수 8,875master님의 댓글
master 작성일
뭐가 안되는지 상세히 작성해주세요