이번 글에는 메인 모듈 회로 구성 회로와 마이컴 모듈인 ATMega128용 소스를 소개 드립니다.

제가 한방에 납땜하고, 설계하고, 코딩하지는 않았습니다.
나름 여러 재료와 함께 브레드보드(일명 빵판)도 생에 처음으로 구입하여 미리 구성하고 확인하여 회로를 완성 했습니다.
전원 연결, 포트 3개를 이용하여 하나는 기어센서로 부터 입력 7개, 디버깅용 - 즉 센서 감지용 LED 7개(실은 미리 8개 연결), 그리고 최종 기어단수 표시용 7-segment용 출력 8단자 입니다.

[수정: 2010-1-8 ]
풀업 저항 10k옴* 8개는 삭제 합니다. 소스코드도 수정 되었습니다.
제가 내부 풀업 셋팅을 몰라서 사용 했는데, 코드 한줄 추가로 설정 했습니다(초자라 이해를...)
파일도 수정된 내용으로 바꾸었습니다.
아래 회로 그림에서 10k부분은 완전히 제거 하시면 됩니다(더 간단하죠)


글은 모두 4개로 구분하여 올렸습니다.
1. 소개글, 개별 단수 인식 방법과 필요한 부품
2. 센서 모듈 만들기와 장착
3. 회로 구성을 위한 회로도와 마이컴 소스코드 [지금 보고있는 글입니다]
4. 완성품 설치와 동작 모습 소개


우선 브레드 보드로 마이컴 모듈을 장착하고, 기어 상태 입력을 위한 부분, 현재 입력 상태 확인용 LED 부분, 최종 기어 단수 표시를 위한 7-Segment 출력 부분으로 구성 했습니다.

입력은 Port A 단자와 연결
입력값 확인용 출력단은 Port C 단자와 연결
7-Segment 출력은 Port  F 단자와 연결 했습니다.

포트 선택의 기준은 한쪽에 몰려 있어서 그냥 사용 했습니다.
AVR은 처음 만져 보는 것이라, 인터럽터, 타이머쪽 고려는 전혀 되지 않았습니다.
- 회로, 코드 보시고 고수분들의 한수 지도 의견 환영 합니다.

상세 연결은 브레드 보드와 연결된 실제 회로 구성 사진으로 대신 하고자 합니다.

위 사진 보고는 느낌밖에 오지 않습니다 ^.^ 밑에 회로도를 보시기 바랍니다.

중요한 핀 번호와 개별 연결 단자와의 구성은 아래와 같이 정리 할 수 있습니다.


네... 회로도가 잘 되었는지 모르겠네요 ^.^


AVR 동작을 위한 code는 아래와 같습니다. 실제 사용하는 code입니다.
제가 AVR 프로그래밍은 처음이라 인터럽트, 타이머는 사용하지 않는 단순한 폴링 방식의 구현임을 감안 하셨으면 합니다. 고수분들의 한수 지도 환영 합니다.
코드에 코멘트를 보시면 이해에 도움이 될 듯 합니다.

다운로드:

/*******************************************************************************
 (C) 2009 Seung-Won Lee   http://whoisit.tistory.com    SoftWareYi@gmail.com


  수동기어 단수표시 장치

  @File  GearPositionIndicator.c
*******************************************************************************/


#include <avr/io.h>

#define    PORT_SEG            PORTF    // Segment 출력용 port
#define    SEG_COMMON_ANODE    1        // Anode 공통 == + 극 공통으로 출력이 0으로 나가야 동작 하는 경우

#ifdef    SEG_COMMON_ANODE
    #define    SEGOUT(value)    (~value)    // Coommon Anode이므로 bit 상태를 뒤집는다
#else
    #define    SEGOUT(value)    (value)
#endif


const unsigned char    SEGMENT_MINUS    = 0b10000000;    // -
const unsigned char    SEGMENT_0        = 0b01110111;    // 0
const unsigned char    SEGMENT_1        = 0b00010100;    // 1
const unsigned char    SEGMENT_2        = 0b10110011;    // 2
const unsigned char    SEGMENT_3        = 0b10110110;    // 3
const unsigned char    SEGMENT_4        = 0b11010100;    // 4
const unsigned char    SEGMENT_5        = 0b11100110;    // 5
const unsigned char    SEGMENT_6        = 0b11100111;    // 6
const unsigned char    SEGMENT_7        = 0b01110100;    // 7
const unsigned char    SEGMENT_8        = 0b11110111;    // 8
const unsigned char    SEGMENT_9        = 0b11110110;    // 9
const unsigned char    SEGMENT_BACK    = 0b11111001;    // P. for Back
const unsigned char    SEGMENT_ERROR    = 0b11101011;    // E. for Error
const unsigned char    SEGMENT_H3        = 0b10100010;    // 한자 3
const unsigned char    SEGMENT_ALL        = 0b11111111;    // All for test


//******************************************************************************
//******************************************************************************
void init( void )
{
    // Enable pull up
    int    SpecialFunctionIO = SFIOR;
    SpecialFunctionIO = ~4 & SpecialFunctionIO;
    SFIOR = SpecialFunctionIO;
}



//******************************************************************************
// 7Segement test 출력
//******************************************************************************
void Display7SegmentOnPortF( void )
{
    const unsigned char    segmentData[] = {
                SEGMENT_0,        // 0
                SEGMENT_1,        // 1
                SEGMENT_2,        // 2
                SEGMENT_3,        // 3
                SEGMENT_4,        // 4
                SEGMENT_5,        // 5
                SEGMENT_6,        // 6
                SEGMENT_7,        // 7
                SEGMENT_8,        // 8
                SEGMENT_9,        // 9
                SEGMENT_H3,        // =
                SEGMENT_BACK,    // R
                SEGMENT_ALL,    // All for test
    };
    static int    index = 0;
    static long int delay = 0;

    if ( ++delay > 10000 )
    {
        delay = 0;
        PORT_SEG = SEGOUT(segmentData[ index++ ]);    // Invert for common Anode LED
        if ( index >= sizeof(segmentData)/sizeof(segmentData[0]) )
            index = 0;
    }
    else
    {
        //PORT_SEG = SEGOUT(0);    // Turn off
    }
}

//******************************************************************************
// Port로 부터 입력된 기어 위치값으로 부터 현재 기어 위치를 char 문자로 돌려준다
//    중립='N'
//    각 단수='1'~'6'
//    후진:'B'
//    기어조작중:'-'
//    에러상태:'E'
//******************************************************************************
typedef struct
{
    char    state;
    char    position;
} stateToPosition;

char getTransmissionValue( char state )
{
    const stateToPosition    stateData[] = {
        { 0b00100010, 'N' },    // Natural
        { 0b00010100, '1' },    // 1
        { 0b01000100, '2' },    // 2
        { 0b00010010, '3' },    // 3
        { 0b01000010, '4' },    // 4
        { 0b00010001, '5' },    // 5
        { 0b01000001, '6' },    // 6
        { 0b00011000, 'B' },    // Back
    };
    char    result = '-';
    int        sensingCount = 0;
    int        i;

    state = state & 0b01111111;    // 상위 1bit 사용 안함 처리
   
    // CheckError - 최소한 하나의 bit는 1이어야 정상이다. (실은 2개)
    if ( !(0b01111111 & state) )
    {
        return 'E';    // 센서로부터 아무런 입력값이 없을 경우 Error처리
    }

    // 변속 중인 상태 확인 -> 앞뒤
    sensingCount += ( state & 0b01000000 ) ? 1 : 0;
    sensingCount += ( state & 0b00100000 ) ? 1 : 0;
    sensingCount += ( state & 0b00010000 ) ? 1 : 0;
    if ( sensingCount > 1 )    // 두 위치값이 동시에 입력되면 변동 중인 경우
        return result;

    // 변속 중인 상태 확인 -> 좌우
    sensingCount = 0;
    sensingCount += ( state & 0b00001000 ) ? 1 : 0;
    sensingCount += ( state & 0b00000100 ) ? 1 : 0;
    sensingCount += ( state & 0b00000010 ) ? 1 : 0;
    sensingCount += ( state & 0b00000001 ) ? 1 : 0;
    if ( sensingCount > 1 )    // 두 위치값이 동시에 입력되면 변동 중인 경우
        return result;

    // 모두 통과 되었다면 단수 확인 과정, 확인 불가시 '-' 처리가 default
    for ( i=0; i<sizeof(stateData)/sizeof(stateData[0]); i++ )
    {
        if ( stateData[i].state == state )
        {
            result = stateData[i].position;
            break;
        }
    }

    return result;
}

//******************************************************************************
// getTransmissionValue로 부터 받아진 결과값을 입력하면 포트출력 값을 돌려준다
//******************************************************************************
typedef struct
{
    char    mode;
    char    segmentData;
} modeToSegment;

char getSegmentData( char currentMode )
{
    const modeToSegment    modeToSegmentData[] = {
        { '-', SEGMENT_MINUS },    // -
        { 'N', SEGMENT_0 },    // 0
        { '1', SEGMENT_1 },    // 1
        { '2', SEGMENT_2 },    // 2
        { '3', SEGMENT_3 },    // 3
        { '4', SEGMENT_4 },    // 4
        { '5', SEGMENT_5 },    // 5
        { '6', SEGMENT_6 },    // 6
        { 'B', SEGMENT_BACK },    // R == P.
        { 'E', SEGMENT_ERROR },    // E.
    };
    int        i;
    char    segmentData = SEGMENT_ERROR;    // Default to Error


    // 입력값에 대응하는 Segment값을 가져온다
    for ( i=0; i<sizeof(modeToSegmentData)/sizeof(modeToSegmentData[0]); i++ )
    {
        if ( modeToSegmentData[i].mode == currentMode )
        {
            segmentData = modeToSegmentData[i].segmentData;
            break;
        }
    }

    return segmentData;
}

/*******************************************************************************
 segmentData를 포트를 이용하여 출력한다
 이어 위치가 변경될 경우 깜빡이는 효과를 시차를 이용하여 보여준다
 중립(0)상태가 일정시간 지속될 경우 animation 효과(뱅글뱅글)를 보여준다

 변속 조작 진행 상태('-')의 경우 이전 단수가 서서이 사라지는 형태로 처리한다.
*******************************************************************************/
void displaySegmentData( char segmentData )
{
    const char segmentAnimationData[] = {        // 왼쪽으로 회전하는 에니메이션
        0b00000001,
        0b00000010,
        0b00000100,
        0b00010000,
        0b00100000,
        0b01000000,
    };
    static char prevData = 0;
    static unsigned long int    timingCount = 0;
    static unsigned int            rotateTimingCount = 0;
    static char dimmingMode = 0;


    // 특정 단수 --> '-' 즉 기어 조작 진행 중인 경우
    // '-' 표시 전에 이전 단수를 희미하게 사라지게 한다
    if ( ( prevData != segmentData ) && ( SEGMENT_MINUS == segmentData ) )
    {
        if ( !dimmingMode )
        {
            dimmingMode = 1;    // Dimming mode전환
            timingCount = 0;
            PORTB = 0;            // Clear
        }

        if ( timingCount < 1000 )        // 조작 직후 이전 기어 위치 유지
            PORT_SEG = SEGOUT( prevData );
        else
        if ( timingCount < 3000 )
            PORT_SEG = ( 90 < timingCount % 100 ) ? SEGOUT(0) : SEGOUT(prevData);
        else
        if ( timingCount < 5000 )
            PORT_SEG = ( 70 < timingCount % 100 ) ? SEGOUT(0) : SEGOUT(prevData);
        else
        if ( timingCount < 7000 )
            PORT_SEG = ( 50 < timingCount % 100 ) ? SEGOUT(0) : SEGOUT(prevData);
        else
        if ( timingCount < 9000 )
            PORT_SEG = ( 30 < timingCount % 100 ) ? SEGOUT(0) : SEGOUT(prevData);
        else
        if ( timingCount < 11000 )
            PORT_SEG = ( 10 < timingCount % 100 ) ? SEGOUT(0) : SEGOUT(prevData);
        else
        if ( timingCount < 13000 )
            PORT_SEG = ( 5 < timingCount % 100 ) ? SEGOUT(0) : SEGOUT(prevData);
        else
        {
            PORT_SEG = (char)SEGOUT(SEGMENT_MINUS);    // '-' 최종 반영
            prevData = segmentData;    // '-'로 이전값 설정
            dimmingMode = 0;        // timingCount reset용
        }
       
        // 효과 과정 까지 timingCount 증가
        ++timingCount;
        return;
    }
    else
    {    // Dimming 도중에 다른 기어로 진입시에 reset 처리하여 차후 기어 조작 진행 상태 반영
        if ( dimmingMode )
        {
            dimmingMode = 0;
            timingCount = 0;
        }
    }


    // 일반적인 변동 상황
    if ( prevData != segmentData )
    {
        PORT_SEG = SEGOUT(segmentData);    // Invert for common Anode LED
        prevData = segmentData;
        timingCount = 0;        // For effect
        rotateTimingCount = 0;
    }
    else
    {
        if ( timingCount < 1000 )
            PORT_SEG = ( 100 < timingCount % 200 ) ? SEGOUT(prevData) : SEGOUT(0);
        else if ( timingCount < 3000 )
            PORT_SEG = ( 200 < timingCount % 500 ) ? SEGOUT(0) : SEGOUT(prevData);
        else if ( timingCount < 6000 )
            PORT_SEG = ( 300 < timingCount % 1000 ) ? SEGOUT(0) : SEGOUT(prevData);
        else if ( timingCount < 10000 )
            PORT_SEG = SEGOUT(prevData);
       
        // 효과 과정 까지 timingCount 증가
        if ( timingCount < 600000 )
        {
            ++timingCount;
            if ( (timingCount > 50000) && (SEGMENT_BACK == prevData) )
            {
                //PORT_SEG = ( 4000 < timingCount % 6000 ) ?  ~0 : ~prevData;
                if ( 4000 < timingCount % 6000 )
                {
                    PORT_SEG = SEGOUT(0);
                    PORTB = 0xFF;
                }
                else
                {
                    PORT_SEG = SEGOUT(prevData);
                    PORTB = 0;               
                }
            }
        }
        // 효과 완료 시점
        else
        {
            if ( SEGMENT_0 == prevData )
            {
                static int segmentIndex = 0;

                // 0 인 경우 뱅글뱅글 돌아가는 animation 출력 시작
                if ( ++rotateTimingCount > 20000 )
                {
                    PORT_SEG = SEGOUT(segmentAnimationData[segmentIndex]);
               
                    if ( ++segmentIndex >= sizeof(segmentAnimationData)/sizeof(segmentAnimationData[0]) )
                        segmentIndex = 0;
                    rotateTimingCount = 0;
                }
            }

        }

    }
   
}

//******************************************************************************
//******************************************************************************
int    main( void )
{
    const unsigned int    inOutTime = 1000;
    unsigned long int    timingCount = 0;
    char    currentMode = 'N';
    char     segmentData = 0;

    init();

    DDRA = 0x00;
    DDRC = 0xFF;
    DDRF = 0xFF;
    DDRB = 0xFF;

    PORTA = 0xFF;        // Input port에 1을 setting하여 Internal pull-up 저항을 활성화
    PORTC = 0x00;

    PORT_SEG = 0xFF;        // Common anode 이므로 1상태가 off 가 된다
    PORTB = 0x00;        // 버즈 출력 시도

    while ( 1 )
    {
        char input = 0;

        if ( 0 == timingCount % inOutTime*10 )
        {
            input = ~PINA;    // N==1 접속==0
            PORTC = input;

            // 상태값으로 부터 기어 단수 위치를 판단한다.
            currentMode = getTransmissionValue( input );
            segmentData = getSegmentData( currentMode );
        }

        displaySegmentData( segmentData );
       




        if ( ++timingCount >= 60000 )
            timingCount = 0;
    }


}

다음글은 최종 완성품 사진과 실제 동작사진, 나머지 동영상 모음을 올려 드리도록 하겠습니다.

+ Recent posts