8051/PIC > 초보자를 위한 8051 시계키트 강좌 (3) - C소스분석

TODAY1,083 TOTAL680,006
사이트 이용안내
Login▼/회원가입
최신글보기 질문게시판 기술자료 동영상강좌

아두이노 센서 ATMEGA128 PWM LED 초음파 AVR 블루투스 LCD UART 모터 적외선


BASIC4MCU | 8051/PIC | 8051 | 초보자를 위한 8051 시계키트 강좌 (3) - C소스분석

페이지 정보

작성자 키트 작성일2017-09-12 10:10 조회373회 댓글0건

본문

 

Schematic preview
 
   

 

  

FND 점등 부분을 보겠습니다.

FND는 CONNON이 +5V에 연결 되어 있으므로
SEGMENT 핀을 LOW로 떨어뜨려주면 전류가 흘러서 켜집니다.

하드웨어 핀을 일반 변수와 구분 하기 위해서 대문자로 표기 하겠습니다.
//-----------------------------------------------------------
#include                   // 8051 의 SFR 이 정의된 파일
#include                    // 8051 의 인터럽트 번호 정의된 파일 
//-----------------------------------------------------------
// 7 Segment Pattern Data
// bit별 Segment 위치                  GFEDCBA
#define SEG_0        0xC0        // &B11000000    0    ****A***
#define SEG_1        0xF9        // &B11111001    1    *      *
#define SEG_2        0xA4        // &B10100100    2    F      B
#define SEG_3        0xB0        // &B10110000    3    *      *
#define SEG_4        0x99        // &B10011001    4    ****G***
#define SEG_5        0x92        // &B10010010    5    *      *
#define SEG_6        0x82        // &B10000010    6    E      C
#define SEG_7        0xD8        // &B11011000    7    *      *
#define SEG_8        0x80        // &B10000000    8    ****D***
#define SEG_9        0x98        // &B10011000    9
#define SEG_BLK      0xFF        // &B11111111    10 BLANK
//-----------------------------------------------------------
//-----------------------------------------------------------
#define HOUR1        _p0        // 1 시 표시 
#define MIN10        _p2        // 10분 표시 
#define MIN1         _p3        // 1 분 표시 
//-----------------------------------------------------------
_sfrbit HOUR10    =    _p1^0;        // 10시 표시 
_sfrbit LED_AM    =    _p1^1;        // 오전 표시 LED
_sfrbit LED_PM    =    _p1^2;        // 오후 표시 LED
_sfrbit PIEZO     =    _p1^3;        // 피에조 부져
_sfrbit SW_ALARM  =    _p1^4;        // 알람 설정 
_sfrbit SW_SAVE   =    _p1^5;        // 알람 시각 저장 
_sfrbit SW_HOUR   =    _p1^6;        // 시 조정
_sfrbit SW_MIN    =    _p1^7;        // 분 조정 
//-----------------------------------------------------------
_sfrbit SEC_LOW   =    _p0^7;        // 초 표시 LED
_sfrbit SEC_HIGH  =    _p2^7;        // 초 표시 LED
_sfrbit LED_ALARM =    _p3^7;        // 알람 표시 LED
//-----------------------------------------------------------

_sfrbit LED_ALARM =    _p3^7;
위 표기는 컴파일러에서 제공하는 _sfrbit 매크로입니다.

예를들면, 알람 LED를 키고 끄는 데에
    _p3^7=0;
    _p3^7=1;
이렇게 해도 되지만, 
    LED_ALARM=0;
    LED_ALARM=1;
이렇게 하는 것이 더 보기에 편합니다.

#define은 왼쪽의 문장을 오른쪽 문장으로 컴파일러가 치환 해 줍니다.

회로에서 1일때 OFF, 0일 때 ON이므로 #define을 한번 더 이용 해보겠습니다.
#define ON     0
#define OFF    1
    LED_ALARM=ON;
    LED_ALARM=OFF;
ON, OFF가 다른 문장에 영향을 주지 않는지 잘 보셔야 합니다. 


1분 자리의 FND에 3이라는 숫자를 키기 위해서는 아래와 같이 합니다.
    MIN1=fnd_tbl[3];

//fnd_tbl의 4번째 위치에는 숫자 3에 해당하는 FND 상수값이 들어 있습니다.



//-----------------------------------------------------------
// 저는 독수리라서 코딩이 길어지면 시간이 많이 걸립니다.
// 아래처럼 매크로 기능을 이용해서 데이터 형의 명칭을 짧게 바꾸겠습니다. 
//-----------------------------------------------------------
#define U_C     unsigned char
#define U_I     unsigned int
#define CU_C    unsigned char _code
#define CU_I    unsigned int _code
//-----------------------------------------------------------
// 비트변수 -------------------------------------------------
_bit    beep=0;                // 부저
_bit    alarm=0;               // 알람 모드 
// 1바이트 변수 ---------------------------------------------
U_C    clock_10msec=0;         // 1/100 초 단위(0 ~ 99)
U_C    clock_sec=0;            // 초 단위 변수 (0 ~ 59)
U_C    clock_min=0;            // 분 단위 변수 (0 ~ 59)
U_C    clock_hour=0;           // 시 단위 변수 (0 ~ 23)
U_C    alarm_min=0;            // 분 단위 변수 (0 ~ 59)
U_C    alarm_hour=0;           // 시 단위 변수 (0 ~ 23)
// 2바이트 변수 ---------------------------------------------
U_I    icount=0;
// 코드메모리 상수 ------------------------------------------
CU_C fnd_tbl[] = { SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, SEG_BLK };
//-----------------------------------------------------------
//-----------------------------------------------------------
// 함수 -----------------------------------------------------
/********************************************************************
* FND 에 시간을 표시 합니다. (12시간제 표시)                        *
* 비트 변수 a 는 시 분 사이의 LED 의 점멸 상태를 선택합니다.        *
* a = 0: Low, High LED 를 끕니다.                                   *
* a = 1: Low, High LED 를 점멸합니다.                               *
********************************************************************/

void fnd_display(U_C hour, U_C min, bit a){
    U_C t, v;

    t=fnd_tbl[min%10];        // 분을 10으로 나눈 나머지 = 분 1자리
    if(alarm)t&=0x7F;         // 알람이면 비트7을 크리어 시킵니다. (LED_ALARM = _p3^7)
    MIN1=t;                   // 분 1 자리 출력

    t=fnd_tbl[min/10];        // 분을 10으로 나눈 값 = 분 10자리
    if((!a)&&((clock_10msec<50)||(clock_sec>30)))t&=0x7F;

    // High LED Blink(SEC_HIGH = _p2^7)
    MIN10=t;                  // 분 10 자리 출력

    if(hour<12){
        LED_AM=ON; LED_PM=OFF;    // AM Led 점등
        v=hour;
    }
    else
        LED_AM=OFF; LED_PM=ON;    // PM Led 점등
        v=hour-12;           // 오후인 경우 12를 빼줍니다.
    }

    if(v==0)v=12;            // 0 시는 12 시로 교정하여 표시 

     t=fnd_tbl[v%10];        // 시를 10으로 나눈 나머지 = 시 1자리
    if((!a)&&((clock_10msec<50)||(clock_sec<=30)))t&=0x7F;

    // Low LED Blink(SEC_LOW = _p0^7)

    HOUR1=t;                 // 시 1 자리 출력

    if (t>=10)HOUR10=ON;     // 시 10 자리 출력
    else HOUR10=OFF;         // 시 10 자리 출력
}

%는 나머지를 얻기위한 모듈러연산자 입니다.
!는 NOT 연산자입니다.  

아래 문장이 제일 길고 복잡하군요
    if((!a)&&((clock_10msec<50)||(clock_sec>30)))t&=0x7F;
(!a)&& 알람 설정 상태가 아니고
(clock_10msec<50)|| 0.5초 이하이거나
(clock_sec>3030초 보다 크면 
(clock_sec<=3030초 이하이면 
t&=0x7F; 초 표시 LED를 점등




시간 만드는 부분을 보겠습니다.
이래는 메인 함수 시작 부분에 있는 타이머 인터럽트의 설정 입니다.
원 소스에 주석이 잘 달려 있으므로 데이터 시트를 참조해서 공부 하시면 충분 하실 겁니다.
    _TMOD=0x02;        // Timer0 Mode 2 (Auto Reload Mode)
    _TH0=0x06;         // 12MHz 크리스탈에서 250 uSec 인터럽트 주기 
    _TR0=1;            // Timer 0 동작시작 
    _ET0=1;            // 타이머    인터럽트 가능상태 설정    
    _EA=1;             // 글로벌 인터럽트 가능상태 설정

/********************************************************************
* Timer 0 Interrupt                                                 *
* 250uS 주기로 인터럽트가 발생합니다. icount 를 증가시키고          *
* 250uS * 40 = 10mSec 주기                                          *
* beep 비트의 상태에 따라 피에보 부져를 동작시킵니다.               *
********************************************************************/

void _interrupt IVN_TIMER0 time_base(void){
    icount++;
    if(icount>39){
        icount=0;
        clock_10msec++;
    };

    if(beep)PIEZO=~PIEZO;     // BEEP 가 설정되어 있으면 피에조 부져를 ON/OFF 합니다.
}
~는 반전 연산자 입니다. 
데이터 형이 비트이므로 1이면 00이면 1로 바뀝니다.

/********************************************************************
* for 문을 이용한 간단한 딜레이 루틴입니다.                         *
********************************************************************/

void delay(void){
    U_I i;
    for(i=0;i<20000;i++);
}
많이 사용하는 타임 딜레이 루틴입니다.
가장 문제 되는 것이 딜레이 시간입니다.
CPU 클럭이 바뀌면 값을 조정해야 하는데...정확한 시간을 찾는 것이 쉽지 않습니다.

저는 딜레이 루틴을 타이머 인터럽트 내에서 처리 합니다.
_bit DelayFlg=0;
U_I DelayCnt=0;

void _interrupt IVN_TIMER0 time_base(void){
    DelayCnt--;
    if(DelayCnt==0)DelayFlg=1// 최대 655초 범위(65536*10mSec)
}

void delay_10ms(U_I dt){ // 딜레이 시간 = dt * 10mSec
    DelayCnt=dt;
    DelayFlg=0;
    while(!DelayFlg);
}
딜레이를 주는 이유는 키 입력시 채터링이 발생해서

여러번 동작하는 것을 막기 위한 것입니다.

/********************************************************************
* 시간을 카운트 합니다.                                             *
********************************************************************/

void clock_run(void){
    if(clock_10msec>99){ clock_10msec=0; clock_sec++; };
    if(clock_sec>59){    clock_sec=0;    clock_min++; };
    if(clock_min>59){    clock_min=0;    clock_hour++; };
    if(clock_hour>23)    clock_hour=0;
}

제 경우에는 시간도 타이머 인터럽트 안에서 많이 처리 합니다.
void _interrupt IVN_TIMER0 time_base(void){
    icount++;
    if(icount>39){ icount=0;
        clock_10msec++;
        if(clock_10msec>99){ clock_10msec=0;
            clock_sec++; 
            if(clock_sec>59){ clock_sec=0;
                clock_min++; 
                if(clock_min>59){ clock_min=0;
                    clock_hour++;
                    if(clock_hour>23)clock_hour=0;
                };
            };

        };

    };
}

여러 단계라서 복잡하면 원소스 처럼 바꿔도 됩니다.
void _interrupt IVN_TIMER0 time_base(void){
    icount++;
    if(icount>39){       icount=0;       clock_10msec++; };
    if(clock_10msec>99){ clock_10msec=0; clock_sec++; };
    if(clock_sec>59){    clock_sec=0;    clock_min++; };
    if(clock_min>59){    clock_min=0;    clock_hour++; };
    if(clock_hour>23)    clock_hour=0;
}


/********************************************************************
* 알람 시각을 설정합니다.                                           *
* Hour, MIn 스위치를 사용하여 알람 시각을 설정합니다.               *
* SAVE 키를 누르면 알람 시각이 저장 됩니다.                         *
* Alarm 키를 누르면 알람 모드가 해제 됩니다.                        *
********************************************************************/

void alarm_proc(void){
    fnd_display(alarm_hour, alarm_min,1);    // 시간 표시에서 알람시간 표시로 변경
    delay();

    while(1){
        fnd_display(alarm_hour, alarm_min, 1);
        if(!SW_HOUR){  alarm_hour++; if(alarm_hour>23)alarm_hour=0; delay(); };
        if(!SW_MIN){   alarm_min++;  if(alarm_min >59)alarm_min=0;  delay(); };
        if(!SW_SAVE){  alarm=1;    break; };   // Save 키 검사 // 알람 모드 설정
        if(!SW_ALARM){ alarm=0;    break; };   // Alarm 키 검사 // 알람 모드 해제
    }
    delay();
}
    
/********************************************************************
* CPU가 리셋되면 main()함수가 처음으로 실행됩니다.                  *
* Timer 0를 초기화하고 변수 값을 클리어 합니다.                     *
********************************************************************/

void main(void){
    _TMOD=0x02;        // Timer0 Mode 2 (Auto Reload Mode)
    _TH0=0x06;         // 12MHz 크리스탈에서 250 uSec 인터럽트 주기 
    _TR0=1;            // Timer 0 동작시작 
    _ET0=1;            // 타이머    인터럽트 가능상태 설정    
    _EA=1;             // 글로벌 인터럽트 가능상태 설정

    while(1){
        clock_run();    // 시간 계산
        fnd_display(clock_hour, clock_min, 0);

        // 알람 모드, 알람 시간이면 BEEP 비트를 설정. 1분
        if((alarm)&&(alarm_hour==clock_hour)&&(alarm_min==clock_min))beep=1;

        else beep=0;

        if(!SW_MIN){    // 분 조정 스위치
            clock_sec=0; clock_min++; if(clock_min>59)clock_min=0; delay();

        };

        if(!SW_HOUR){    // 시 조정 스위치

            clock_hour++; if(clock_hour>23)clock_hour=0; delay();

        };
        if(!SW_ALARM)    alarm_proc();       // 알람 설정 스위치
    };
}


//---------------------------------------------------------

 

 

 

 

 

 

 

//---------------------------------------------------------
전체적으로 소스를 정리 하면
//-----------------------------------------------------------
#include                   // 8051 의 SFR 이 정의된 파일
#include                    // 8051 의 인터럽트 번호 정의된 파일 
//-----------------------------------------------------------
// 7 Segment Pattern Data
// bit별 Segment 위치                  GFEDCBA
#define SEG_0        0xC0        // &B11000000    0    ****A***
#define SEG_1        0xF9        // &B11111001    1    *      *
#define SEG_2        0xA4        // &B10100100    2    F      B
#define SEG_3        0xB0        // &B10110000    3    *      *
#define SEG_4        0x99        // &B10011001    4    ****G***
#define SEG_5        0x92        // &B10010010    5    *      *
#define SEG_6        0x82        // &B10000010    6    E      C
#define SEG_7        0xD8        // &B11011000    7    *      *
#define SEG_8        0x80        // &B10000000    8    ****D***
#define SEG_9        0x98        // &B10011000    9
#define SEG_BLK      0xFF        // &B11111111    10 BLANK
//-----------------------------------------------------------
#define ON        0
#define OFF       1
//-----------------------------------------------------------
#define HOUR1        _p0        // 1 시 표시 
#define MIN10        _p2        // 10분 표시 
#define MIN1         _p3        // 1 분 표시 
//-----------------------------------------------------------
_sfrbit HOUR10    =    _p1^0;        // 10시 표시 
_sfrbit LED_AM    =    _p1^1;        // 오전 표시 LED
_sfrbit LED_PM    =    _p1^2;        // 오후 표시 LED
_sfrbit PIEZO     =    _p1^3;        // 피에조 부져
_sfrbit SW_ALARM  =    _p1^4;        // 알람 설정 
_sfrbit SW_SAVE   =    _p1^5;        // 알람 시각 저장 
_sfrbit SW_HOUR   =    _p1^6;        // 시 조정
_sfrbit SW_MIN    =    _p1^7;        // 분 조정 
//-----------------------------------------------------------
_sfrbit SEC_LOW   =    _p0^7;        // 초 표시 LED
_sfrbit SEC_HIGH  =    _p2^7;        // 초 표시 LED
_sfrbit LED_ALARM =    _p3^7;        // 알람 표시 LED
//-----------------------------------------------------------
//-----------------------------------------------------------
#define U_C        unsigned char
#define U_I        unsigned int
#define CU_C       unsigned char _code
#define CU_I       unsigned int _code
//-----------------------------------------------------------
// 비트변수 -------------------------------------------------
_bit    beep=0;                // 부저
_bit    alarm=0;               // 알람 모드 
_bit    DelayFlg=0;
// 1바이트 변수 ---------------------------------------------
U_C    clock_10msec=0;         // 1/100 초 단위(0 ~ 99)
U_C    clock_sec=0;            // 초 단위 변수 (0 ~ 59)
U_C    clock_min=0;            // 분 단위 변수 (0 ~ 59)
U_C    clock_hour=0;           // 시 단위 변수 (0 ~ 23)
U_C    alarm_min=0;            // 분 단위 변수 (0 ~ 59)
U_C    alarm_hour=0;           // 시 단위 변수 (0 ~ 23)
// 2바이트 변수 ---------------------------------------------
U_I    icount=0;
U_I    DelayCnt=0;
// 코드메모리 상수 ------------------------------------------
CU_C fnd_tbl[] = {

    SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, SEG_BLK };
//-----------------------------------------------------------
//-----------------------------------------------------------
// 함수 -----------------------------------------------------
void fnd_display(U_C hour, U_C min, bit a){
    U_C t, v;

    t=fnd_tbl[min%10];    if(alarm)t&=0x7F;    MIN1=t;
    t=fnd_tbl[min/10];

    if((!a)&&((clock_10msec<50)||(clock_sec>30)))t&=0x7F;    MIN10=t;

    if(hour<12){    LED_AM=ON; LED_PM=OFF;        v=hour; }
    else{         LED_AM=OFF; LED_PM=ON;        v=hour-12; }
    if(v==0)v=12;

    t=fnd_tbl[v%10];

    if((!a)&&((clock_10msec<50)||(clock_sec<=30)))t&=0x7F;    HOUR1=t;

    if (t>=10)HOUR10=ON;
    else HOUR10=OFF;
}
//-----------------------------------------------------------
void _interrupt IVN_TIMER0 time_base(void){
    icount++;
    if(icount>39){       icount=0;       clock_10msec++; };
    if(clock_10msec>99){ clock_10msec=0; clock_sec++; };
    if(clock_sec>59){    clock_sec=0;    clock_min++; };
    if(clock_min>59){    clock_min=0;    clock_hour++; };
    if(clock_hour>23)    clock_hour=0;

    if(beep)PIEZO=~PIEZO;

    DelayCnt--;    if(DelayCnt==0)DelayFlg=1;
}
//-----------------------------------------------------------
void delay_10ms(U_I dt){ DelayCnt=dt;    DelayFlg=0;    while(!DelayFlg); }
//-----------------------------------------------------------

//-----------------------------------------------------------
void main(void){
    _TMOD=0x02;    _TH0=0x06;    _TR0=1;    _ET0=1;    _EA=1;
    while(1){
        fnd_display(clock_hour, clock_min, 0);

        if((alarm)&&(alarm_hour==clock_hour)&&(alarm_min==clock_min))beep=1;
        else beep=0;

        if(!SW_MIN){

            clock_sec=0; clock_min++; if(clock_min>59)clock_min=0; wait_KeyOff();

        };
        if(!SW_HOUR){

            clock_sec=0; clock_hour++; if(clock_hour>23)clock_hour=0; wait_KeyOff();

        };
        if(!SW_ALARM){

            fnd_display(alarm_hour, alarm_min,1);    wait_KeyOff();
            while(1){
                fnd_display(alarm_hour, alarm_min, 1);
                if(!SW_HOUR){

                    alarm_hour++; if(alarm_hour>23)alarm_hour=0; wait_KeyOff(); };
                if(!SW_MIN){

                    alarm_min++;  if(alarm_min >59)alarm_min=0;  wait_KeyOff(); };
                if(!SW_SAVE){  alarm=1;  wait_KeyOff();   break; };
                if(!SW_ALARM){ alarm=0;  wait_KeyOff();   break; };

            }

        };
    };
}
//---------------------------------------------------------
void wait_KeyOff(void){ // 눌려진 키에서 손을 뗄 때까지 대기

        while(!SW_MIN); while(!SW_HOUR); while(!SW_ALARM); while(!SW_SAVE);

        delay_10ms(10);
}

//---------------------------------------------------------

원소스를 올리게 허락 해 주신 샘플전자 사장님에게 감사드리며
원소스 경우에는 쉽게 알아보도록 배려가 된 소스인데
설명 과정에서 소스를 변경 한 부분도 이해 해주시기 바랍니다. ^^

실력이 허접해서 설명이 부족한 부분이 많습니다.
이해가 안되는 부분 있으면 덧글 달아주세요^^

댓글 0

조회수 373

등록된 댓글이 없습니다.

8051/PICHOME > 8051/PIC > 전체 목록

8051/PIC 목록
제목 작성자 작성일 조회
109 8051 1. 8051 내부구조 이미지 키트 17-09-12 663
108 8051 8051 개요 이미지 키트 17-09-12 565
107 8051 3. 8051의 인터페이스 이미지 키트 17-09-12 580
106 8051 AT89S 시리즈 타겟 ISP 기본 회로도 키트 17-09-12 478
105 8051 keil 8051을 이용한 FND 6자리 시계구현 키트 17-09-12 539
104 8051 8051 6자리 세그먼트 keil 시계 키트 17-09-12 401
103 8051 PWM 0~200kHz 영역의 주파수를 측정 이미지 키트 17-09-12 837
102 8051 8051 4x3 매트릭스 키 스캔 키트 17-09-12 363
101 8051 SE-BATCAR 라인트레이서 조립 KIT - 대만 MEGAWIN 사의 RISC 형 8051 이미지 키트 17-09-12 648
100 8051 코드변경 8051 ==>AVR (출력 포트) 키트 17-09-12 348
99 8051 89S51 실습보드 dip스위치로 led제어 키트 17-09-12 393
98 8051 8051 Architecture - Datasheets - Atmel 키트 17-09-12 318
97 8051 LED 순차점등 키트 17-09-12 519
96 8051 win51 신호등 키트 17-09-12 323
현재글 8051 초보자를 위한 8051 시계키트 강좌 (3) - C소스분석 키트 17-09-12 374
게시물 검색

2019년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
2018년 1월 2월 3월 4월 5월 6월 7월 8월 9월 10월 11월 12월
Privacy Policy
MCU BASIC ⓒ 2017
모바일버전으로보기