07_프로그램 구현 3단계 완성!!!
프로젝트/스마트독서실 시스템

07_프로그램 구현 3단계 완성!!!

728x90

겨우 프로그램 완성을 했다...

 

블로그 글을 처음부터 읽어도 좋지만, 프로젝트에 대한 전반적인 내용을 알고싶으면 내가 작성했던 레포트를 참고해 보는것도 좋을 것 같다.

 

 

 

마이크로프로세서2_텀프로젝트_2017142037정민규.hwp
4.69MB

 

 

 

55장 정도 긴 분량이긴 하나.. 들어간 모든 기술들이 집약되어 있다.

 

 

 

 

우선 먼저 동작소스에 대해 간략하게 소개해보자면 아래와 같이 동작한다.

 

1. 초기 상태 2. 좌석을 선택한 경우

3. 번호 입력후 맞는지 확인 3-1. 2번을 누를 시 번호 다시입력
4. Yes입력시 문이 열림(5초동안) 5. 문이 닫힌 후 1번좌석 업데이트

6. 4+*out모드 진입 6-1. 빈 좌석을 선택했을 시




비어있는 좌석만 표시





빈좌석이라 표시 후 초기상태로 돌아감
7. 초기상태에서 사용중인 좌석 선택 시 6-2 1번 선택시




사용중이라 표시 후 초기상태로 돌아감



시작할 때의 번호를 다시 입력하라고 표시
8. 역시 비밀번호 확인이 가능함 8-1 비밀번호 불일치시
8-2 비밀번호 일치시 9.1 2번 좌석에 자리를 등록한 상태




goodbye 출력 후 퇴장처리
10. 10초이상 자리가 비어있을 때 10-2. 자리가 계속 차 있는 경우




> 자리가 비움처리가 되어있음



1번 자리만 사람이 있는 상태






자리가 비움처리되지 않음




2번자리에 사람이 있는 상태
특히 자리가 비웠다가 10초안에 돌아오면 그때부터 카운트가 다시 시작되게 설정해두어져 있음
USART mingyu입력시



그 외 입력어는 작동하지 않음
아래 화면과 같이 각 자리에 저장해두었던 비밀번호가 출력된다.
번은 제 번호, 2,3번은 각각 22222222222,33333333333 로 설정해두었음

 

https://www.youtube.com/watch?v=BqMQQIhrHjc 

 

위 동영상을 보면 조금더 자세한 동작을 알 수 있다.

 

 

저번 코드에서 비밀번호를 입력 후에 퇴장할 수 있는 기능까지 추가하였는데, 이제는 초음파 거리감지 센서를 활용하여 자리를 비워두고 떠난사람이 일정시간 지나면 강제로 비움처리되는 기능을 추가하였다.   추가로 불꽃감지 센서와 USART를 통한 터미널 통신으로 번호 조회가 가능한 기능도 추가적으로 넣어주었다!

 

/*사용 헤더*/
#include <mega128.h >
#include <delay.h >
#include <stdio.h >
#include "lcd.h"
#include "twi.h"
#include "srf02.h"
#include "Keypad.h"
#include "usart.h"

/*STATE 정의*/      // 총 10개의 상태를 사용함
#define NONE 0
#define START 1
#define INPUT_PHONE 2
#define INPUT_PHONE_INIT 3
#define CHECK_PNUM 4
#define CHECK_PNUM_INIT 5
#define EXIT_CHOOSE 6
#define CHECK_PNUM_OUT 7
#define CHECK_PNUM_OUT_INIT 8
#define INPUT_PHONE_OUT 9
#define INPUT_PHONE_OUT_INIT 10
/*usart 정의*/
#define ENTER '\r'
#define MAXLEN 17
unsigned char str[MAXLEN], str2[MAXLEN];
unsigned char master_password[] ="1mingyu";  // 비밀번호 일치시 좌석들 번호출력
unsigned char open_password[] ="1opendo";  //문 제어를 위한 명령어 …[1]

char num[3][11];  //유저 폰 번호 저장공간 …[2]
unsigned char ti_Cnt_1ms;  //초음파 센서구동을위한 cnt 
unsigned char LCD_DelCnt_1ms;
unsigned char Distance_cnt_1ms; // 시간지나면 자리비움처리를 위한 cnt
unsigned char ch =0;
interrupt [USART1_RXC] void usart1_receive(void)
{   //마스터 동작 기능 수행
    unsigned char i;
    
    str[ch] = UDR1;          //인터럽트 발생 시 수신된 문자를 str[ch]에 저장
   
     if(str[ch] == ENTER){   //enter가 눌릴 때 비밀번호 일치를 확인
        char access_cnt =0;
        for(i =0;i <MAXLEN;i ++) str2[i] ='a';
        for(i =0;i <MAXLEN;i ++) str2[i] = str[i];
        
        str[ch-1] =0x00;   
        ch =0;             //문자열 초기화 
         
        for (i =1;i <7;i ++) {  //비밀번호 일치 확인
            if (master_password[i] == str2[i]) access_cnt ++; 
        }
        if (access_cnt ==6) {  //각 좌석의 전화번호 출력
            puts_USART1("\n first seat : \n");
            for(i =0;i <11;i ++)  putch_USART1(num[0][i]);
            puts_USART1("\n Second seat : \n");
            for(i =0;i <11;i ++)  putch_USART1(num[1][i]);
            puts_USART1("\n Third seat : \n");
            for(i =0;i <11;i ++)  putch_USART1(num[2][i]); 
        }
        
        access_cnt =0;
         for (i =1;i <7;i ++) { //문 제어
            if (master_password[i] == str2[i]) access_cnt ++; 
        }
        if (access_cnt ==6) {
            OCR1A =3000;
            LCD_Clear();
            LCD_Pos(0,0);
            LCD_Str("Door Open") ;    
            delay_ms(5000);
            OCR1A =4710;
            STATE = START;
            fnd[0]=0; 
        }
        
        
               
        for(i =0;i <MAXLEN;i ++) str2[i] =0;
     }
     else ch ++;
     delay_ms(10);
        
       
    
    if ( ch >= MAXLEN) ch =0;    //최대 글자 이상일때 에러모드
}
void Timer0_Init()  //초음파 센서구동 타이머 인터럽트 …[3]
{
    TCCR0 = (1 <<WGM01)|(1 <<CS00)|(1 <<CS01)|(1 <<CS02);
    TCNT0 =0x00;
    OCR0 =100;
    TIMSK = (1 <<OCIE0);
}
interrupt[TIM0_COMP] void timer0_comp(void)    //실제 카운트 증가
{
    ti_Cnt_1ms++;
    LCD_DelCnt_1ms++;
    
}
   
    
    
int SRF_Run(char Sonar_Addr){    //SRF02 주소로 값을 받아오는 함수
    unsigned char res;    
    unsigned int Sonar_range;  
    
    res = getRange(Sonar_Addr, &Sonar_range);
        if(res)
        {         
            return 0;
        }
        else if(LCD_DelCnt_1ms >100)
        {              
            LCD_DelCnt_1ms =0;            
            return Sonar_range;
        }        
}

void main(void)
{   /* 초기 설정 */
    unsigned char res;
    char Sonar_Addr =0xE0; //검사할 SRF02 주소를 저장
    unsigned int Sonar_range[3]={0,};  //각 좌석의 초음파센서값 저장공간 
    char Message[40];  //초음파 탐지 test를 위해 사용
     
    int t =0; //키패드로 받은 숫자 
    int count =0; //Keypad로 값을 받을 때 계속받지 않도록 사용 
    int finalnum =0; //FND에 출력으로 넣어줄 변수 
    int fnd[12]={0,}; //FND에 쌓아 올려지면서 저장되는 공간
    signed int angle =0; // 서브모터 각도로 넣을 변수 
    char STATE = START;  // LCD상태제어를 위한 변수              
    char user_state[3] = {'X','X','X'}; //현재 좌석의 상태 저장
    int i =0;  //for문 동작을 위한 i
    char user_pnumber[3][11]; //유저 비밀번호 받을 때 임시 저장 
    char check_pnumber[11]; //비밀번호 일치확인을위한 공간
    char user_name; //1~3번중 어느좌석 유저를 가르키는지 저장
    
    char empty_cnt[3]={0,}; //각 좌석이 10초이상 비어있는지 확인하기위한 cnt
    
    DDRD |=0x03; 
      
    LCD_Init();  //LCD초기설정
    Timer0_Init(); //초음파 구동을 위한 타이머 
    FND_PORT_Init(); // 포트들 입출력 초기 설정 (Keypad.h 안에 있음.)
    Init_TWI(); 
    Init_Timer1(); //서브모터 구동을 위한 PWM타이머 
    delay_ms(1000);
    SREG |=0x80;  //인터럽트 개방
    
    OCR1A =4710; //개방 : 3000 , 닫힘 : 4710
    startRanging(Sonar_Addr);
    ti_Cnt_1ms =0; 
    LCD_DelCnt_1ms =0; 
     
    Init_USART1_IntCon(0,RX_Int);  //USART 개방
    DDRD.7 =1;  //불꽃감지 센서 입력 설정
    
    
    
    while(1)  
    {   /* 초음파 센서 */  
        if(ti_Cnt_1ms >100)  //센서를 탐지하여 비어있는지 감지 …[4]
        {               
            if (Sonar_Addr ==0xE0){
                Sonar_Addr =0xEC;
                startRanging(Sonar_Addr);
                Sonar_range[0] = SRF_Run(Sonar_Addr);
                
                
            } 
            else if (Sonar_Addr ==0xEC) {
                Sonar_Addr =0xE2;
                startRanging(Sonar_Addr);
                Sonar_range[1] = SRF_Run(Sonar_Addr);
                
            }
            else{
                Sonar_Addr =0xE0;
                startRanging(Sonar_Addr);
                Sonar_range[2] = SRF_Run(Sonar_Addr);
                
            }
            
             
            /*   초음파 확인용 테스트 코드  *//*
            LCD_Clear();
            sprintf(Message, "%03dcm", Sonar_range[0]);
            LCD_Pos(0,0);
            LCD_Str(Message);
                
            sprintf(Message, "%03dcm", Sonar_range[1]);
            LCD_Pos(1,0);
            LCD_Str(Message); 
            
            sprintf(Message, "%03dcm", Sonar_range[2]);
            LCD_Pos(1,5);
            LCD_Str(Message); */
            
            /* 자리비움 처리 */
            for (i =0;i <3;i ++){  //좌석이 사용중이고, 30cm이상일 때 비워져있다 판단
                if ( (Sonar_range[i] >30)&&(user_state[i] =='O') ){
                   empty_cnt[i] ++;
                    /*sprintf(Message, "%03dcm", empty_cnt[i]); 
                    LCD_Clear();
                    LCD_Pos(0,0);
                    LCD_Str(Message);  *///cnt확인용 
                }
                else empty_cnt[i] =0;
            
                if (empty_cnt[i] >15 ) {
                 user_state[i] ='E';
                 if (STATE == NONE ) STATE = START;
                }
            }
            
            
                
            LCD_DelCnt_1ms =0;             
            ti_Cnt_1ms =0;
        }
        
        
        /*   /*불꽃감지 센서동작*/ //불꽃감지시 시스템 리셋이아닌 경우 경고만 출력되게 설정 
        if(PIND.7 == 1){ 
           LCD_Clear();
              LCD_Pos(0,0); // 문자열 위치 0행 1열 지정
              LCD_Str("Warning....."); // 문자열 str을 LCD 첫번째 라인에 출력
              delay_us(10);
              LCD_Pos(1,0); // 문자열 위치 1행 1열 지정
               LCD_Str("Fire!!!    "); // 문자열 str을 LCD 두번째 라인에 출력 
             LCD_Pos(1,0);
              delay_us(10); 
                
            while(1)
            {
                    
                 for(i=0;i<20;i++){    //사이렌 소리
                  Buzzer_play(Sol/2);
                  delay_ms(10);
                }  
                delay_ms(10);
                for(i=0;i<20;i++){
                    Buzzer_play(Re/2); 
                    delay_ms(10);
                }  
            } 
            
        }
            else{
               
        }*/
        
        
        /* 키패드 동작 */
         
        t= Changenum(KeyScan());  
        if(t <11 & t >0 ) //숫자가 눌리면 새로운 값을 저장하도록 count값 설정 
            {
                count++; 
                delay_ms(50);
            }
        else if(t ==0 & zero_flag) //zero_flag가 실행된 경우에만 0으로 입력  …[5]
            {
                count++;
                zero_flag =0; //계속 0으로 입력된 상태가 안되게 zero_flag를 다시 0으로
                delay_ms(50);
            }    
        else if(t ==13) // FND 출력숫자 리셋버튼 기능 
            { 
                fnd[0]=0,fnd[1]=0,fnd[2]=0,fnd[3]=0;               
            }
        else if (t ==14)  // LCD를 초기 화면으로
            {
               STATE = START;
            } 
            
        if((count%2) ==0){ //count가 짝수일때 들어온 t값을 저장하고
                            //다시 count를 홀수로 만듬 
            for(i =11;i >0;i --) {       
                fnd[i] = fnd[i -1];
                delay_us(10);
            }
            fnd[0] = t;
            count++;
            delay_ms(50);
        }
        finalnum =1000 *fnd[3] +100 *fnd[2] +10 *fnd[1] + fnd[0];
        OUTFND(finalnum); //FND 출력 …[6]
        buzzer_play_function(t); //숫자에 맞는 음 출력
        
        
        
       
        /* LCD 제어 */
        
        switch (STATE) {  //LCD처리를 STATE로 사용
        
            case NONE: //기본 상태
                if (fnd[1] <=3 && fnd[1] >0 && fnd[0] ==10) { //1~3번 좌석선택
                    user_name = fnd[1]-1;
                    STATE=INPUT_PHONE_INIT ;
                }
                if (fnd[1]==4 && fnd[0] ==10) { //4번을 눌렀을 경우
                    LCD_Clear();
                    LCD_Pos(0,0);
                    LCD_Str("Choose Seat");
                    LCD_Pos(1,0);
                    if (user_state[0] =='O') LCD_Str("1  ");
                    if (user_state[1] =='O') LCD_Str("2  ");
                    if (user_state[2] =='O') LCD_Str("3  ");
                    STATE=EXIT_CHOOSE ;
                }
                break;
            case START: //상태 업로드
                LCD_Pos(0,0);
                LCD_Str("StudyRoom  4:OUT");
                sprintf(Message, "1:%c 2:%c 3:%c", user_state[0],user_state[1],user_state[2]);
                LCD_Pos(1,0);
                LCD_Str(Message); 
                STATE = NONE;
                break;
                
            case INPUT_PHONE: //입장 : 폰번호 입력
                
                if (fnd[0] ==10){ 
                    sprintf(user_pnumber[user_name], "%d%d%d%d%d%d%d%d%d%d%d", fnd[11],fnd[10],fnd[9],fnd[8],fnd[7],fnd[6],fnd[5],fnd[4],fnd[3],fnd[2],fnd[1]);
                    user_state[user_name] ='X';
                    for(i =0;i <11;i ++){
                        num[user_name][i] = user_pnumber[user_name][i];
                    }
                    STATE = CHECK_PNUM_INIT;                     
                } 
                delay_ms(10);                 
                break;         
                
            case INPUT_PHONE_INIT: //폰번호 입력 전에 출력 …[7]
                fnd[0]=0;
                LCD_Clear();
                LCD_Pos(0,0);
                LCD_Str("Input PhoneNum") ;
                STATE = INPUT_PHONE;
                
                if (user_state[user_name] =='O'){ //누군가 사용중일 경우
                    LCD_Clear();
                    LCD_Pos(0,0);
                    LCD_Str("Someone Used");
                    delay_ms(1000);
                    STATE = START;
                }
                break; 
            case CHECK_PNUM:  //사용중으로 변경 및 문 개방
                
                if (fnd[1] ==1 && fnd[0] ==10){
                    user_state[user_name] ='O';
                    OCR1A =3000;
                    LCD_Clear();
                    LCD_Pos(0,0);
                    LCD_Str("Door Open") ;    
                    delay_ms(5000);
                    OCR1A =4710;
                    STATE = START;
                    fnd[0]=0;     
                    user_name =4;
                }
                else if (fnd[1] ==2 && fnd[0] ==10) STATE = INPUT_PHONE_INIT;
            break;
            case CHECK_PNUM_INIT: //사용중으로 변경 및 문 개방상태 진입전 설정
                fnd[0]=0;
                LCD_Clear();
                LCD_Pos(0,0);
                LCD_Str(user_pnumber[user_name]); 
                LCD_Pos(1,0);
                LCD_Str("1:Yes  2:No");
                fnd[1] =0;
                STATE = CHECK_PNUM;
            break;
            
            case EXIT_CHOOSE:  //퇴장 좌석 선택 
            
                user_name = fnd[1]-1;
                if (fnd[1] <=3 && fnd[1] >0 && fnd[0] ==10){
                    
                    STATE = INPUT_PHONE_OUT_INIT; 
                    
                    if (user_state[user_name] !='O'){ //이미 비어있는 좌석인 경우
                        LCD_Clear();
                        LCD_Pos(0,0);
                        LCD_Str("Empty Seat");
                        delay_ms(1000);
                        fnd[0]=0;
                        STATE = START;
                    }
                }
                
                
            break;
             case CHECK_PNUM_OUT_INIT: //비밀번호 일치시 탈출 전 설정 
                 fnd[0]=0;
                 fnd[1] =0;
                 LCD_Clear();
                 LCD_Pos(0,0);
                 LCD_Str(check_pnumber); 
                 LCD_Pos(1,0);
                 
                 LCD_Str("1:Yes  2:No");
                 STATE = CHECK_PNUM_OUT;
               
                
            break;
            case CHECK_PNUM_OUT:   //비밀번호 일치시 탈출
            
                 if (fnd[1] ==1 && fnd[0] ==10){
                   int cnt =0;
                   for(i =0;i <11;i ++){
                      if(num[user_name][i] == check_pnumber[i] ) cnt ++;  } 
                   
                    if (cnt ==11 ){  //비밀번호 일치
                       LCD_Clear();
                       LCD_Pos(0,0);
                       LCD_Str("User Check") ; 
                       LCD_Pos(1,0);
                       LCD_Str("Good Bye") ;
                       OCR1A =3000;
                        delay_ms(5000);
                        OCR1A =4710;    
                      user_state[user_name] ='X';
                      STATE = START;
                      fnd[0] =0;
                    }
                    else{ //비밀번호 불일치
                      LCD_Clear();
                      LCD_Pos(0,0);
                      LCD_Str("Wrong Password") ; 
                      delay_ms(2000);
                      fnd[0] =0;
                      STATE = START;
                    } 
                }
                else if (fnd[1] ==2 && fnd[0] ==10) STATE = INPUT_PHONE_OUT_INIT;
                
                
            break;
            case INPUT_PHONE_OUT: //퇴장 : 폰번호 입력
                
                if (fnd[0] ==10){
                    sprintf(check_pnumber, "%d%d%d%d%d%d%d%d%d%d%d", fnd[11],fnd[10],fnd[9],fnd[8],fnd[7],fnd[6],fnd[5],fnd[4],fnd[3],fnd[2],fnd[1]); 
                    
                    STATE = CHECK_PNUM_OUT_INIT;                     
                } 
                delay_ms(10);                 
                break;  
             
             case INPUT_PHONE_OUT_INIT: //퇴장 : 폰번호 입력 전 설정
                
                
                fnd[0]=0;
                LCD_Clear();
                LCD_Pos(0,0);
                LCD_Str("Input PhoneNum") ;
                STATE = INPUT_PHONE_OUT;            
                break;         
                
           
        }
     
    } 
}

 

코드를 단순히 설명하자면 아래 멘트로 설명할 수 있겠다.

 

 

동작 구현을 위해서 크게 세 파트로 나누어서 구현을 진행하였습니다. 초음파 센서를 일정 시간마다 쏘아서 검출하면서 자리가 사용 되어 있는 자리이면서 비어있는 시간이 일정 시간(데모모델 약 10) 이후까지 비어있음을 감지하기 위한 부분, LCD상에서 사용자가 직접 번호를 누르고 입장하거나 번호를 누르고 일치하는 경우에만 퇴장하는 등의 기능을 구현한 부분, USART를 사용하여 관리자가 번호조회 및 문 제어가 가능한 부분으로 이루어져 있고 그 외에 부저,FND,키패드 등의 사용을 위한 부분이 들어 있습니다.

 

특히, LCD제어를 위해서 STATE변수를 사용하여 제어하였습니다. STATE를 사용하여 LCD를 제어할 경우 상태로 동작하기에 코드 이해가 수월하며 언제든 원하는 동작으로 문제없이 돌아올 수 있는 장점이 있어 위 방식을 선택하였습니다.

 

헤더파일로는 위에서 소개했던 5개의 파일이 들어가며 위 구동함수에서 설명하였던 함수들을 모아서 기능을을 구현하였습니다. 더 자세한 설명은 아래코드를 진행하며 주석으로 더 자세히 서술하였습니다.

 

기본적으로 키패드 숫자로 동작하며, O는 사용중, X는 비어있음, E는 짐이 있을 수 있으나 치우고 사용가능한 좌석을 의미한다. 특히 사용자의 입력버튼은 *버튼이라 가정하였고 m1버튼으로는 FND초기화, m2버튼으로는 사용자 초기화면으로 돌아갈 수 있다.

 

 

 

특히 코드중간중간 주석에 ... 처리한 부분이 있는데 그부분의 설명은 아래와 같다.

 

[ 1 ] : LCD에서 enter키도 눌리면 저장되어서 첫 번째 단어가 \n이 들어가는 문제가 발생하여 \n을 삭제해주는 방법 대신 비밀번호 앞에 의미없는 단어를 넣어서 동작하도록 수행하였습니다.

 

[ 2 ] : sprintf()를 사용하여 문자열을 바꿀 때, 그 전에 사용한 sprintf()를 이용하여 바꾼 문자열에 쓰레기값으로 바뀌는 현상이 일어나서 sprintf()를 사용함과 동시에 깊은복사를 통해 값을 저장해줌으로 문제를 해결하였습니다.

 

[ 3 ] : pwm신호를 위한 타이머 1번은 keypad.h 헤더안에 들어있습니다.

 

[ 4 ] : 초음파센서를 매 while문마다 탐지하는 것이 아닌, 일정 시간마다 주소를 바꿔가면서 1 > 2 > 3 > 1 > 2 > .. 순서로 바꿔가면서 탐지하는 방식으로 설정하였습니다.

 

[ 5 ] : zero_flag가 없다면 keypad가 항상 값을 0으로 받아오게 설정하였기 때문에 해당 flag를 이용하여 0이 눌렸을 경우에만 0으로 값을 인정하게 받아오도록 설정하였습니다. 이는 keypad.h의 함수에서 아무것도 안 눌릴 경우 0으로 반환하게 설정했기 때문입니다.

 

[ 6 ] : OUTFND()를 통하여 출력을 구현할 때 인수로 int형의 숫자를 출력하게끔 설정하였기 때문에 finalnum변수를 통하여 변환 과정이 필요했습니다.

 

[ 7 ] : STATE를 위한 LCD제어에서 어떠한 키 입력을 대기하는 상태와 LCD를 출력하는 상태를 분리하여 코드를 볼 때 덜 헷갈리게 하였고 또한 LCD가 계속해서 출력하는 상태를 방지하여 주었습니다.

 

 

 

 

 

728x90