최초 수동기어 인디케이터 만들기 글을 올린지가 2009년이었네요.
지금이 2013년이니 4년이 다 되어 갑니다.
지금 올리는 글은 최초 버전에서 조금 더 개선 한 버전으로, 최초 버전 이후 곧 새로 만들기는 했으나 블로그에 포스팅은 하지 않은 회로와 code로 되어 있어 이제서야 글로 올립니다.
상세 내용은 이미 공유 드린 예전 자료를 한번 쭈욱 읽어 주시기 바랍니다.
최초 내용은 AVR을 처음 학습 하던 관계로 무려 MEGA128 모듈을 사용 했습니다만, 이번 내용은 AVR의 좀더 저렴하고 DIY 하기 쉬운 8535L 을 이용하는 내용입니다.
다만 8535L 은 현 시점에 구입이 어려운 관계로, 보드 구성은 MEGA128 모듈을 그대로 사용하시고 Port배치와 chip 간의 차이점을 확인 후 아래 공유 드리는 code를 아주 약간만 수정해서 재사용 가능 하리라 생각 합니다.
추가로 LED에 TR를 달아서 PWM으로 밝기 제어를 해서 최초의 code상 밝기 제어와 비교하여 우수한 표시 안정성을 제공 합니다. 또한 효과도 더욱 멋지게 바뀌었습니다.
회로 구성을 먼저 올리고, 이어서 code를 올려 드립니다.
8MHz 진동자를 사용하는 것을 조심 하셔야 겠습니다.
그래서 16MHz의 모듈을 사용 하신다면 code상에 timing 관련 값을 2배 정도 높여 사용 하셔야 합니다.
상세 내용은 회로도와 code를 보세요. code에 충분한 comment가 달려 있답니다.
[회로도] AVR 8535L 8MHz 진동자와 3.3V LM2576 을 모두 함게 구성 했습니다.
또한 LED 밝기 조절을 위해 A1015 TR하나를 이용한 PWM 제어를 하게 됩니다.
소스코드 파일 다운로드:
소스코드 전체 내용
iocompat.h
- /*
- * ----------------------------------------------------------------------------
- * "THE BEER-WARE LICENSE" (Revision 42):
- * <joerg@FreeBSD.ORG> wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
- * ----------------------------------------------------------------------------
- *
- * IO feature compatibility definitions for various AVRs.
- *
- * $Id: iocompat.h,v 1.6.2.1 2008/03/17 22:08:46 joerg_wunsch Exp $
- */
- #if !defined(IOCOMPAT_H)
- #define IOCOMPAT_H 1
- /*
- * Device-specific adjustments:
- *
- * Supply definitions for the location of the OCR1[A] port/pin, the
- * name of the OCR register controlling the PWM, and adjust interrupt
- * vector names that differ from the one used in demo.c
- * [TIMER1_OVF_vect].
- */
- #if defined(__AVR_AT90S2313__)
- # define OC1 PB3
- # define OCR OCR1
- # define DDROC DDRB
- # define TIMER1_OVF_vect TIMER1_OVF1_vect
- #elif defined(__AVR_AT90S2333__) || defined(__AVR_AT90S4433__)
- # define OC1 PB1
- # define DDROC DDRB
- # define OCR OCR1
- #elif defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || \
- defined(__AVR_AT90S4434__) || defined(__AVR_AT90S8535__) || \
- defined(__AVR_ATmega163__) || defined(__AVR_ATmega8515__) || \
- defined(__AVR_ATmega8535__) || \
- defined(__AVR_ATmega164P__) || defined(__AVR_ATmega324P__) || \
- defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || \
- defined(__AVR_ATmega1284P__)
- # define OC1 PD5
- # define DDROC DDRD
- # define OCR OCR1A
- # if !defined(TIMSK) /* new ATmegas */
- # define TIMSK TIMSK1
- # endif
- #elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) || \
- defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__)
- # define OC1 PB1
- # define DDROC DDRB
- # define OCR OCR1A
- # if !defined(TIMSK) /* ATmega48/88/168 */
- # define TIMSK TIMSK1
- # endif /* !defined(TIMSK) */
- #elif defined(__AVR_ATtiny2313__)
- # define OC1 PB3
- # define OCR OCR1A
- # define DDROC DDRB
- #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || \
- defined(__AVR_ATtiny84__)
- # define OC1 PA6
- # define DDROC DDRA
- # if !defined(OCR1A)
- # /* work around misspelled name in avr-libc 1.4.[0..1] */
- # define OCR OCRA1
- # else
- # define OCR OCR1A
- # endif
- # define TIMSK TIMSK1
- # define TIMER1_OVF_vect TIM1_OVF_vect /* XML and datasheet mismatch */
- #elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || \
- defined(__AVR_ATtiny85__)
- /* Timer 1 is only an 8-bit timer on these devices. */
- # define OC1 PB1
- # define DDROC DDRB
- # define OCR OCR1A
- # define TCCR1A TCCR1
- # define TCCR1B TCCR1
- # define TIMER1_OVF_vect TIM1_OVF_vect
- # define TIMER1_TOP 255 /* only 8-bit PWM possible */
- # define TIMER1_PWM_INIT _BV(PWM1A) | _BV(COM1A1)
- # define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
- #elif defined(__AVR_ATtiny26__)
- /* Rather close to ATtinyX5 but different enough for its own section. */
- # define OC1 PB1
- # define DDROC DDRB
- # define OCR OCR1A
- # define TIMER1_OVF_vect TIMER1_OVF1_vect
- # define TIMER1_TOP 255 /* only 8-bit PWM possible */
- # define TIMER1_PWM_INIT _BV(PWM1A) | _BV(COM1A1)
- # define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
- /*
- * Without setting OCR1C to TOP, the ATtiny26 does not trigger an
- * overflow interrupt in PWM mode.
- */
- # define TIMER1_SETUP_HOOK() OCR1C = 255
- #elif defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || \
- defined(__AVR_ATtiny861__)
- # define OC1 PB1
- # define DDROC DDRB
- # define OCR OCR1A
- # define TIMER1_PWM_INIT _BV(WGM10) | _BV(PWM1A) | _BV(COM1A1)
- /*
- * While timer 1 could be operated in 10-bit mode on these devices,
- * the handling of the 10-bit IO registers is more complicated than
- * that of the 16-bit registers of other AVR devices (no combined
- * 16-bit IO operations possible), so we restrict this demo to 8-bit
- * mode which is pretty standard.
- */
- # define TIMER1_TOP 255
- # define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
- #elif defined(__AVR_ATmega32__) || defined(__AVR_ATmega16__)
- # define OC1 PD5
- # define DDROC DDRD
- # define OCR OCR1A
- #elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) || \
- defined(__AVR_ATmega165__) || defined(__AVR_ATmega169__) || \
- defined(__AVR_ATmega325__) || defined(__AVR_ATmega3250__) || \
- defined(__AVR_ATmega645__) || defined(__AVR_ATmega6450__) || \
- defined(__AVR_ATmega329__) || defined(__AVR_ATmega3290__) || \
- defined(__AVR_ATmega649__) || defined(__AVR_ATmega6490__) || \
- defined(__AVR_ATmega640__) || \
- defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || \
- defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
- # define OC1 PB5
- # define DDROC DDRB
- # define OCR OCR1A
- # if !defined(PB5) /* work around missing bit definition */
- # define PB5 5
- # endif
- # if !defined(TIMSK) /* new ATmegas */
- # define TIMSK TIMSK1
- # endif
- #else
- # error "Don't know what kind of MCU you are compiling for"
- #endif
- /*
- * Map register names for older AVRs here.
- */
- #if !defined(COM1A1)
- # define COM1A1 COM11
- #endif
- #if !defined(WGM10)
- # define WGM10 PWM10
- # define WGM11 PWM11
- #endif
- /*
- * Provide defaults for device-specific macros unless overridden
- * above.
- */
- #if !defined(TIMER1_TOP)
- # define TIMER1_TOP 1023 /* 10-bit PWM */
- #endif
- #if !defined(TIMER1_PWM_INIT)
- # define TIMER1_PWM_INIT _BV(WGM10) | _BV(WGM11) | _BV(COM1A1)
- #endif
- #if !defined(TIMER1_CLOCKSOURCE)
- # define TIMER1_CLOCKSOURCE _BV(CS10) /* full clock */
- #endif
GearPositionIndicatorLib.h
- #ifndef __GETPOSITIONINDICATOR_H__
- #define __GETPOSITIONINDICATOR_H__
- #define PORT_SEG PORTA // 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
- unsigned char GetGearPosition( unsigned char state );
- char getSegmentData( char currentMode );
- void displaySegmentDataNoEffect( char segmentData );
- #endif // of __GETPOSITIONINDICATOR_H__
GearPositionIndicatorLib.c
- /*******************************************************************************
- (C) 2009 Seung-Won Lee http://whoisit.tistory.com SoftWareYi@gmail.com
- 수동기어 단수표시 장치
- @File GearPositionIndicator.c
- *******************************************************************************/
- #include <avr/io.h>
- #include "GearPositionIndicatorLib.h"
- 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
- //******************************************************************************
- // Port로 부터 입력된 기어 위치값으로 부터 현재 기어 위치를 char 문자로 돌려준다
- // 중립='N'
- // 각 단수='1'~'6'
- // 후진:'B'
- // 기어조작중:'-'
- // 에러상태:'E'
- //******************************************************************************
- typedef struct
- {
- unsigned char state;
- unsigned char position;
- } stateToPosition;
- unsigned char GetGearPosition( unsigned 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 = '-';
- char sensingCount = 0;
- unsigned char 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.
- };
- unsigned char 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;
- }
- void displaySegmentDataNoEffect( char segmentData )
- {
- // 일반적인 변동 상황
- PORT_SEG = SEGOUT(segmentData); // Invert for common Anode LED
- }
GearPositionIndicator8535L.c
- /*
- ATMega 8535L 을 이용한 수동기어 인디케이터
- 기존 ATMEGA128에서 구현한 기능 + PWM 출력을 조절하여 표시 변동시에 부드러운 밝기 변동 처리가 목적
- Port A: 7 Segment 출력
- Port C: 포지션 센서 입력
- Port D: PWM 출력을 이용하여 TR을 거쳐서 7 Segment 밝기 조절 처리용으로 사용
- 이전과 같이 Loop의 단순 timing으로 효과 극대화가 곤란
- 새로운 구현에서는 밝기 조절은 Timer1을 이용하여 diming 처리
- 이어 포지션 변경으 아래와 같은 시나리오롤 처리하자.
- */
- #include <inttypes.h>
- #include <avr/io.h>
- #include <avr/interrupt.h>
- #include <limits.h>
- #include "iocompat.h"
- #include "GearPositionIndicatorLib.h"
- #define SIZEOF(x) (sizeof(x)/sizeof(x[0]))
- #define TIMER0_PWM_RATIO_MAX 4096
- typedef enum
- {
- DIMMING_OFF,
- DIMMING_UP,
- DIMMING_DOWN,
- DIMMING_FLASH,
- DIMMING_CHANGE_ALARM,
- DIMMING_DOWN_FAST,
- _DIMMING_FINISH,
- } LedDimmingMode;
- static unsigned char LastPinInput = 0xff; // 이전 입력값
- // LED brightness control variables
- static char currentDimmingMode = DIMMING_UP;
- unsigned char dimmingIndex = 0;
- short int brightIndex = 0; // [0..1] 0 for full power brightness / 1 for >> 1 == /2
- /*
- TCNT1 == Timer/Counter Register (현재 count 값 저장)
- 0되었을 경우 BOTTOM 신호 발생
- 0xFFFF 되었거나 OCR1A/B, ICR1 에 설정된 값에 도달하면 TOP 신호 발생
- --> BOTTOM / TOP 신호에 의해 카운트를 업/다운 혹은 리셋으로 처리
- TCCR1AB == Timer/Counter Control Register
- ICR1 == Input Capture Register
- 입력캡쳐핀 ICx(타이머1의 경우에는 아날로그 비교기의 출력신호도 가능)
- 으로 들어오는 신호변화를 검출하여 일어나는 입력캡쳐시 TCNT1의 카운터 값을 저장하는 16비트 레지스터로
- 이때 ICFx 플랙이 세트되고 입력캡쳐인터럽트가 요청된다. 어떤 신호의 주기 측정에 응용될 수 있다.
- OCR1AB == Output Compare Register
- TCNT1 값과 출력 비교되기 위한 16bit data값 저장 / TCNT와 같을경우 OCF 플랙설정, 출력비교 인터럽터 요청,
- 출력핀 OC1A/B 단자로 데이타 출력
- ICP1 == Input Capture Pin 1: PD6 pin for Timer/Counter1
- OC1A == 출력핀
- OC1B == 출력핀
- */
- /*******************************************************************************
- Timer 1 Duty cycle 을 조절하여 LED 발기값을 조절 한다
- [0..256] 받아서, 추가 곱하기 scaling 하여 처리하자
- *******************************************************************************/
- void SetLedBrightness( int brightness )
- {
- brightness = 0xFF - ((brightness & 0xFF)>> brightIndex);
- OCR1A = brightness << 4 | 0x0f;
- }
- /*******************************************************************************
- INT0 (PORT D0) - 스위치 입력 처리
- *******************************************************************************/
- ISR( INT0_vect , ISR_BLOCK )
- {
- /* if ( 0 == brightIndex )
- brightIndex = 1;
- else
- brightIndex = 0;
- */
- if ( ++brightIndex > 2 )
- brightIndex = 0;
- LastPinInput = 0xff; // 강제로 이전 pin값을 무효화 하여 기어 위치 재출력 로직타게 한다.
- }
- /*******************************************************************************
- Timer 0 - 기어 포지션 센서 값 확인 핸들러 주기 설정
- *******************************************************************************/
- void Timer0_Init(void)
- {
- // 아래 두 문장은 직접 관련 없는 사항
- DDRB = 0x08; // Enable output for OC0(PB3)
- PORTD = 0x00; // Clear
- // Configure Timer/Counter Control Register for 0
- // Fast PWM mode with Clear OC0 on Compare Match, set OC0 at TOP / Clock to No prescaling -> /64 divide
- //TCCR0 = (1<<WGM01) | ( 1<<WGM00) | ( 1<<COM01) | (1<<CS01) | (1<<CS00);
- // Normal mode for event looping logic
- // 8MHz 0.125us == 0.000125 ms
- // to /256 then 256*256(count) =
- // to /1024 then 1024*256(count) = 0.000125 * 1024*256 = 32.768 ms = [ 0.032 sec ] * 31 = 0.992 sec ! (31 회 핸들링 시 마다 1초에 가까움)
- //
- // TCCR0 = 0x04; /256 divide
- // = 0x05; /1024 divide
- TCCR0 = 0x05; // Use /1024 divide
- TIMSK = TIMSK | (1<<OCIE0); // OCR0 match 시에 match intrrupt 발생!
- // Output Compare Register - OCR0
- OCR0 = 255; //126; //255; // It's 8 bit!(Max 255)
- }
- /*******************************************************************************
- 확실한 dimming 처리를 위해서는 Timer 1의 16bit 해상도를 사용해야 완전 어두움을 구현 가능!
- 밝기 조절은 Timer1 사용
- *******************************************************************************/
- void Timer1_PWM_Init(void)
- {
- //--------------------------------------------------------------------------
- // PortD Pin 4 == OC1B / Pin 5 == OC1A ==> 00110000 == 0x30
- DDRD = 0x30; // Enable output for OC1A|OC1B
- PORTD = 0x00; // Clear
- //--------------------------------------------------------------------------
- // Set TCCR1A/B == Timer/Counter Control Register
- // Fast PWM Mode --> Need WGM13, WGM12, WGM11
- // No prescaling --> CS10
- // /8 prescaling --> CS11
- // Clear OC1A/OC1B on Compare Match, set OC1A/OC1B at BOTTOM
- // Non-Inverting Mode
- TCCR1A = (1<<COM1A1)|(1<<COM1B1)|(1<<WGM11);
- //TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS10);
- TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);
- // ICR1, TOP Value!
- // Crystal = 4MHz, 0.25us
- // //0.25us * 40000 = 10msec, Frequency = 100Hz
- // ICR1 = 39999; //40000 - 1
- // //Duty Cycle = 50%, 40000/2
- // OCR1A = 19999; //20000 - 1
- // //Duty Cycle = 25%, 40000/4
- // OCR1B = 9999; //10000 - 1
- //--------------------------------------------------------------------------
- // Crystal = 8MHz -> 0.125us (0.000000125 sec / 0.000125 ms)
- // For 240Hz need 0.0042 ms / 80000 for 100Hz, 20000 for 400Hz
- //ICR1 = 16384-1; // 0x4000 2^15 -->> 이 값을 높여 버리면! - 전체적인 밝기 조절이 가능!!!!!!!!!!!!!!!!!!!
- ICR1 = TIMER0_PWM_RATIO_MAX - 1; // TIMER0_PWM_RATIO_MAX = 4096
- OCR1A = 0; //TIMER0_PWM_RATIO_MAX - 1; // OCR1A PWM MAX for dimming, 0 for Max Brightness
- OCR1B = TIMER0_PWM_RATIO_MAX - 1;
- }
- /*******************************************************************************
- *******************************************************************************/
- void Timer1_PWM_Init2(void)
- {
- /*
- DDRB = 0xff;
- PORTB = ~0x08;
- // PORTB = 0x00;
- TCCR1A = TIMER1_PWM_INIT;
- TCCR1B = TIMER1_CLOCKSOURCE;
- OCR0 = 0;
- */
- }
- /*******************************************************************************
- *******************************************************************************/
- void Timer2_Init(void)
- {
- // Normal mode for event looping logic
- // 8MHz 0.125us to /1024 then 1024*88(count) = 11Hz
- TCCR2 = 0x07; // /1024 [CS2,CS1,CS0] = 111
- TIMSK = TIMSK | (1<<OCIE2); // OCR2 match 시에 match intrrupt 발생!
- // Output Compare Register - OCR0
- OCR2 = 255; // COMP 핸들러에서 TCNT2를 0 으로 초기화하여 fine tunning 값으로 사용
- }
- /*******************************************************************************
- Interrupt vector for Timer 0 overflow signal
- *******************************************************************************/
- /*ISR( TIMER0_OVF_vect, ISR_BLOCK )
- {
- // 아직 활성화 하지 않음.
- }*/
- //******************************************************************************
- // getTransmissionValue로 부터 받아진 결과값을 입력하면 포트출력 값을 돌려준다
- //******************************************************************************
- void SetLedDimming( LedDimmingMode mode )
- {
- currentDimmingMode = mode;
- dimmingIndex = 0;
- // 위 설정값은 TIMER2_COMP_vect 에서 처리함.
- }
- //******************************************************************************
- // Gear위치를 LED로 출력
- //******************************************************************************
- void SetLedDisplayChar( char gearPosition )
- {
- displaySegmentDataNoEffect( getSegmentData( gearPosition ) );
- }
- /*******************************************************************************
- Control LED dimming effects
- LED 밝기값을 시나리오별로 조절하는 역할을 한다.
- 현재 대충~ 30 회당 1초로 확인 됨. // 100회당 3.3 초
- --> 10회당 1초로 할까? --> 이게 만만해 보임 좀 버번 거리게 보일 수도...
- --> 100회당 1초로 할까? --> 100Hz > 좀 부하가 있을 느낌
- *******************************************************************************/
- //typedef struct
- //{
- // unsigned char index; // 0 then stop the effect progressing
- // unsigned char brightness; // -1 then restart the effect - it reset dimmingIndex
- //} indexToBrightness;
- ISR( TIMER2_COMP_vect, ISR_BLOCK )
- {
- const int // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
- dimmingUpTbl[] = { 20, 40, 60, 80, 100, 150, 200, 255, 255, 255, -1, -1 };
- const int
- dimmingDownTbl[] = {200, 200, 190, 180, 170, 160, 150, 140, 130, 120, 110, 100, 90, 80, 75, 70, 65, 60, 55, 50,
- 45, 40, 35, 30, 27, 24, 21, 19, 18, 17, 16, -1, -1 };
- const int
- dimmingFlashbl[] = {255, 255, 200, 160, 100, 30, 0, 0, 120, 250, -2 , -2 };
- const int
- dimmingChangeAlarmTbl[] =
- {255, 0, 255, 0, 255, 200, 150, 100, 50, 100, 150,200, 250, 200, 170, 200, 255, -1, -1 };
- const int
- dimmingDownFastTbl[] =
- {100, 100, 180, 255, 200, 150, 100, 70, 50, 30, 20, 10, 0, -1, -1 };
- // static unsigned char flash = 0; // For debugging
- static unsigned char ISRdelay = 0;
- int brightness;
- ++ISRdelay;
- if ( ISRdelay < 3 )
- return;
- else
- ISRdelay = 0;
- TCNT2 = 0; // Timer reset for resolution tunning
- //__________ 디버깅용 LED 깜빡임
- // ++flash;
- // if ( 5 == flash )
- // PORTB = PORTB | 0x01;
- // else if ( 10 == flash )
- // {
- // PORTB = PORTB & ~0x01;
- // flash = 0;
- // }
- //^^^^^^^^^^
- brightness = 0;
- switch ( currentDimmingMode )
- {
- case DIMMING_OFF: // Just turn off the LED
- SetLedBrightness( 0 );
- currentDimmingMode = _DIMMING_FINISH;
- dimmingIndex = 0;
- return;
- break;
- case DIMMING_UP:
- brightness = dimmingUpTbl[dimmingIndex];
- if ( -1 == brightness ) // -1 to finish effect scenario
- {
- currentDimmingMode = _DIMMING_FINISH;
- dimmingIndex = 0;
- return;
- }
- break;
- case DIMMING_DOWN:
- brightness = dimmingDownTbl[dimmingIndex];
- if ( -1 == brightness ) // -1 to finish effect scenario
- {
- currentDimmingMode = _DIMMING_FINISH;
- dimmingIndex = 0;
- return;
- }
- break;
- case DIMMING_FLASH:
- brightness = dimmingFlashbl[dimmingIndex];
- if ( -2 == brightness ) // -2 to repeat the effect
- {
- dimmingIndex = 0;
- return;
- }
- break;
- case DIMMING_CHANGE_ALARM:
- brightness = dimmingChangeAlarmTbl[dimmingIndex];
- if ( -1 == brightness ) // -1 to finish effect scenario
- {
- currentDimmingMode = _DIMMING_FINISH;
- dimmingIndex = 0;
- return;
- }
- break;
- case DIMMING_DOWN_FAST:
- brightness = dimmingDownFastTbl[dimmingIndex];
- if ( -1 == brightness ) // -1 to finish effect scenario
- {
- currentDimmingMode = _DIMMING_FINISH;
- dimmingIndex = 0;
- return;
- }
- break;
- case _DIMMING_FINISH:
- currentDimmingMode = _DIMMING_FINISH;
- return; // 더 이상 counting 하지도, 동작 수정 안함.
- break;
- }
- SetLedBrightness( brightness );
- ++dimmingIndex;
- }
- /*******************************************************************************
- *******************************************************************************/
- /*******************************************************************************
- Interrupt vector for Timer 0 compare matching
- 기어 센서 상태값을 확인하고, 전환 시나리오별로 처리 한다.
- *******************************************************************************/
- #define TIMEOUT_TO_1SEC (31) // 30은 1분 이하로 오차 발생, 31이 좀더 근접하나 상세 TUNING을 tcnt0 를 통할 필요 있음
- ISR( TIMER0_COMP_vect, ISR_BLOCK )
- {
- //__________ 디버깅용 LED 깜빡임
- {
- static char flash = 0;
- if ( ++flash > TIMEOUT_TO_1SEC )
- {
- flash = 0;
- PORTB ^= 0x01;
- }
- }
- //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- // Timer 해상도를 좁히기 위해 OCR0 값을 설정하고, 이 code를 활성화 하고 값을 높이면 timer 분주를 빨리함 (시작 카운트 값 설정)
- TCNT0 = 3;
- {
- static char LastGearPosition = '-'; // 이전 기어 상태 (LastPinInput으로 부터 변환된 값)
- static unsigned long int InputChangeTimeCount = 0; // 기어 변속 이후 경과시간 - 0: Timer 중지, 1~ Counting 계속
- static unsigned long int IdleTimeCount = 0; // Idle time 경과시간 - 0: Timer 중지, 1~ Counting 계속
- unsigned char NewPinInput = ~PINC & 0x7f; // 현재 Pin 값 받기
- unsigned char NewGearPosition = GetGearPosition( NewPinInput ); // Gear 위치값으로 환산
- if ( LastPinInput != NewPinInput ) // 센서 입력 변동?
- {//----- 센서 변동 -----------------------------------------------------
- LastPinInput = NewPinInput; // 이전값 갱신
- InputChangeTimeCount = 1; // 변동 시간 count 시작
- IdleTimeCount = 0; // Idle time count 중지
- switch ( NewGearPosition )
- {
- case '-' : // 기어 전환중, 중립상태 시나리오
- case 'N' : // 다른 기어로 전환이 곧 될 수 있기에 Dimming처리고 어둡게 함
- // 0~6 에서
- if ( '-' != LastGearPosition ) // 이전엔 기어위치였으나, 전환 하려는 경우임
- {
- SetLedDimming( DIMMING_DOWN ); // 천천히 어두워 지게 함
- LastGearPosition = NewGearPosition; // 기어값만 갱신
- // 변동 시간 count 계속 동작하게 둠. 0 상태에서 에니메이션 시나리오 가능케 함
- // LED 표시는 수정하지 않음
- }
- break;
- case 'E' : // 입력 에러E / 후진(B)시는 즉각적으로, 또한 깜빡임 처리
- case 'B' :
- LastGearPosition = NewGearPosition; // 마지막 기어 위치값 갱신
- SetLedDisplayChar( NewGearPosition ); // LED 표시 갱신
- InputChangeTimeCount = 0; // 변동 시간 count 중지 -> 무변동 상테에서 시나리오 멈춤
- SetLedDimming( DIMMING_FLASH ); // 반복적인 깜빡임
- break;
- default: // 나머지 기어값인 1 ~ 6 의 경우 기어 위치 확정 표시
- LastGearPosition = NewGearPosition; // 마지막 기어 위치값 갱신
- SetLedDisplayChar( NewGearPosition ); // LED 표시 갱신
- InputChangeTimeCount = 0; // 변동 시간 count 중지 -> 무변동 상테에서 시나리오 멈춤
- SetLedDimming( DIMMING_CHANGE_ALARM ); // 깜빡이면서 밝게 표시
- break;
- }
- }
- else
- {//----- 변화가 없는 경우 ----------------------------------------------
- // '-' 혹은 '0' 가 계속 유지 되고 있다면 특정 시간 이후에 '-' / '0'을 표시한다
- //----- 기어 변경 후 일정 시간 지남 확인 카우트 동작 진행
- if ( InputChangeTimeCount )
- {
- // 0 표시는 즉각적으로 갱신하지 않고, 1초간 대기 후 갱신한다.
- // 왜냐면 기어 변속중에 항상 0 위치를 거치게 되므로 잦은 갱신을 하지 않아 자연스럽게 다음 기어를 표시하게 된다.
- //----- N(중립) 에서 1초 초과시에 진짜 N(0)으로 표시 갱신
- if ( ('N' == NewGearPosition) && (InputChangeTimeCount > 1*TIMEOUT_TO_1SEC) )
- {
- InputChangeTimeCount = 0; // 이후 Timer 작동 중지
- IdleTimeCount = 1; // Idle time count 시작 -> Idle animation 처리 용
- LastGearPosition = NewGearPosition; // 기어 포지션 갱신
- SetLedDisplayChar( NewGearPosition ); // LED 표시 갱신
- SetLedDimming( DIMMING_CHANGE_ALARM ); // 깜빡이면서 밝게 표시
- }
- //----- 기어 이동중(기어 물림 위치가 아닌경우)이라면 3초간 기존 상태 유지(위에서 dimming down) 후, 새값으로 갱신
- else if ( ('-' == NewGearPosition) && ( InputChangeTimeCount > 3*TIMEOUT_TO_1SEC ))
- {
- InputChangeTimeCount = 0; // 이후 Timer 작동 중지
- //IdleTimeCount = 1; // Idle time count 시작 X
- LastGearPosition = NewGearPosition; // 기어 포지션 갱신
- SetLedDisplayChar( NewGearPosition );
- SetLedDimming( DIMMING_UP ); // 천천히 밝게 표시
- }
- else
- {
- if ( ++InputChangeTimeCount >= ULONG_MAX ) // Counting
- InputChangeTimeCount = 0; // Overflow 시에 더이상 이어변동 후 타이머 동작은 작동 안함.
- }
- }
- //----- 이어 변경 직후 타이밍 동작 완료 후, Idling 상태에서 0 위치에서 animation 표시 등 부가 동작 수행
- else
- if ( IdleTimeCount && 'N' == NewGearPosition )
- { // 0 상태에서 장시간 대기중일 경우 Rotation animation 처리하기~
- const char segmentAnimationData[] = { // 오른쪽으로 회전하는 에니메이션에 해당한 segment LED 출력 bit
- 0b01000000, // 0b01000100,
- 0b00100000, // 0b00100010,
- 0b00010000, // 0b00010001,
- 0b00000100, // 0b01000100,
- 0b00000010, // 0b00100010,
- 0b00000001, // 0b00010001,
- };
- //----- 30초 초과시 부터 idle animation시작
- if ( IdleTimeCount >= 30*TIMEOUT_TO_1SEC )
- {
- static unsigned char prevAniIndex = 6;
- char bDisplayIdleMinTime = 0;
- unsigned char aniIndex = ( (IdleTimeCount - 1 ) / TIMEOUT_TO_1SEC ) % 6;
- unsigned char i;
- //----- {{110723 lsw2000}} 1분, 2분, 3분, 최고 6분 까지에 대해 Idle 경과 시간 표시! / 웜업, 후열용 참고 시간 표시
- if ( IdleTimeCount < (6*60 +10) *TIMEOUT_TO_1SEC )
- {
- for ( i=1; i<=6; i++ )
- if ( i*60*TIMEOUT_TO_1SEC <= IdleTimeCount &&IdleTimeCount < (i*60 +(4+i)) *TIMEOUT_TO_1SEC ) // 최초 4초부터 1초식 증가하여 표시
- {
- if ( i*60*TIMEOUT_TO_1SEC == IdleTimeCount ) // 숫자 전화, 효과 설정은 단 1회만, 이후 자동
- {
- SetLedDisplayChar( '0' + i ); //
- SetLedDimming( DIMMING_FLASH ); // 반복적인 깜빡임
- }
- bDisplayIdleMinTime = 1;
- }
- }
- else
- //----- 7분 경과부터 매 1분 마다 0 표기 깜빡이기
- if ( (7*60) *TIMEOUT_TO_1SEC <= IdleTimeCount )
- {
- if ( IdleTimeCount % (60*TIMEOUT_TO_1SEC) < (5*TIMEOUT_TO_1SEC) ) // 매붖 최초 5초 동안
- {
- if ( 0 == IdleTimeCount % (60*TIMEOUT_TO_1SEC) )
- {
- SetLedDisplayChar( 'N' );
- SetLedDimming( DIMMING_FLASH ); // 반복적인 깜빡임
- }
- bDisplayIdleMinTime = 1;
- }
- }
- //----- 경과 분수 표시 타이밍이 아닌 경우 animation 표시
- if ( !bDisplayIdleMinTime && ( prevAniIndex != aniIndex ) )
- {
- prevAniIndex = aniIndex;
- PORT_SEG = ~segmentAnimationData[ aniIndex ]; // ~for Negative output
- SetLedDimming( DIMMING_DOWN_FAST ); // 새 위치 표시 후 어두워지게 효과 처리
- }
- }
- if ( ++IdleTimeCount >= ULONG_MAX ) // Overflow 대비, animation 계속하게 함.
- IdleTimeCount = 30*TIMEOUT_TO_1SEC;
- }
- // TODO: 기어 상태에서 장시간 있을 경우 살짝 깜빡임성 표시로 환기 시키기
- }
- }
- }
- /*******************************************************************************
- Main enterance
- *******************************************************************************/
- int main( void )
- {
- cli();
- Timer0_Init(); // 노브 조작 감지(센서 감지 루프)
- Timer1_PWM_Init(); // PWM ouput 조작
- Timer2_Init(); // 깜빡임, 밝기 시나리오 흐름
- //----- INT0 ----------
- GICR |= 1<<INT0; // INT0 enable
- MCUCR = 2; // 하강엣지에 인트럽트 발생
- DDRA = 0xFF; // PortA: 출력 / 7 Segment
- DDRB = 0xFF; // PortB: Timer and Debug signal
- DDRC = 0x00; // PortC: 입력 / 기어 포지션 센서
- //DDRD = ~0x04; // PortD2(INT0) 입력 활성화
- PORT_SEG = 0xFF; // Common anode 이므로 1상태가 off 가 된다
- PORTB = 0xFF; // + 인가
- PORTC = 0xFF; // Input port에 1을 setting하여 Internal pull-up 저항을 활성화
- PORTD = 0x04; // PD2(INT0) Pull-up
- sei(); // 주 동작은 Timer handler에서 수행 됨.
- // Timer0: Main loop
- // Timer1: PWM power 조절
- // Timer2: LED 깜빡인 control
- SetLedBrightness(10); // First Full brightness.
- while ( 1 )
- {
- }
- return 0;
- }
.
'DIY' 카테고리의 다른 글
[물품] (Original) ATMEL - AVR ISP MKII (AVRISP2) (0) | 2013.06.08 |
---|---|
[DIY] WIZ AVRISP hex file for recovery (0) | 2013.05.15 |
[DIY] 알루미늄 필름이 붙어있는 단열 보온 시트 재료 - 정전기 방출 아이디어! (0) | 2013.02.03 |
[DIY] 차량용 바이러스 닥터 (삼성전자/시거짹/컵홀더용) (0) | 2013.02.03 |
[DIY] 공구 상자 대신 - ART-KIT40 (0) | 2013.02.03 |