DIY - TPMS(Tire Pressure Monitoring System) value display for RENAULT and NISSAN via CAN(Controller Area Network)





두번째 글 입니다.

이 글에서는 전체적인 모듈 구성도, Display 처리 logic 을 제외한 CAN data 처리 부분 code까지 살펴 봅니다.


먼저 모듈 구성도를 보겠습니다. code 구성 전에 회로를 구성하게 되고 회로에 맞추어 code를 구성해야 합니다. 물론 구성 전에 개별 port를 할당과 연결은 미리 고민 후 할당 해야겠습니다.

저의 경우 AT90CAN128 모듈의 포트 위치에 따라서 TPMS 표시 모듈을 먼저 구성하여 포트가 연속이지는 않지만 해당 모듈의 기판에서는 물리적으로 연속하여 배치 되어 실제 pin header 로 연결 용이성에 최우선 순위로 결정 했습니다.

추가로 처리한 Accelerator / RPM bar-graph는 남는 반대편 port에 구성 하였습니다.

실제 모습은 첫번째 글을 참고 하시면 되겠습니다.


[모듈 구성도] 


OBDII Socket 

4선을 연결 하면 됩니다. 저의 경우 고장난 충전용 5pin USB 케이블의 선재만 이용 했습니다.

+ / - / D+/ D- 를 그대로 활용하여 VCC(8) / GND(4,5) / CANH(6) / CANL(14) 와 연결 합니다.

여기서 주목 할 사항은 Renault / Nissan 차량에서 OBDII Socket의 8번 pin을 사용하면 항시 전원이 아닌 시동 전원(IGN2)에 연결하게 되는 것입니다.

혹시나 ELM327 호환 OBDII 장치에 대해 상시전원을 막으려면 16pin 연결된 전원 선을 8pin으로 옮기면 방전 걱정을 덜게 됩니다.

아! OBDII 소켓은 별로로 구입해서 선을 연결해야 됩니다. 저는 이 소켓에 regulator까지 함께 장착 했습니다.


Switching regulator 

차량 전원은 +12 ~ 14.8 V 의 고전압(TTL회로에 적합하지 않은) 입니다. 직접 AT90CAN128 Module에 연결할 수 없고, 5V 로 낮추어 공급 해야 합니다.

요즘은 OBDII Socket 내부에 넣을 정도로 작은 switching regulator 모듈을 판매하고 있습니다. 이러한 부품을 사용 합니다.

7805 같은 linear regulator 는 여러 이유로 추천 드리지 않습니다. (12V 에서는 고열로 인한 방열 문제 등등)


AT90CAN128 module 

전원 +5V 는 앞서 switching regulator와 연결하고, CAN 라인은 Socket 으로부터 직접 연결 하면 됩니다. (6, 14 번 핀)

Tire 공기압 표시를 위한 7 segment 모듈과 연결은 Port F / A / C 를 통합니다. 총 24pin 연결하도록 2열 pin header , socket 으로 기판을 직접 결합 하게 됩니다.

Port B / E 는 accelerator, RPM gauge bar graph 표현 모듈과 연결 합니다. 8bit는 공통이고 총 4개 모듈을 제어 하게 합니다. 마찬가지로 4번 각각 선택 해야 총 40개의 LED를 표현하게 됩니다.


7 segment display module 

Port F 와여 연결선은 모든 7 segment 모듈에 공통으로 연결합니다. (한 시점에 한개의 자리만 표시)

Port A, C 를 개별 7 segment 하나의 단위와 차례로 연결하여 총 15자리 수를 순차적으로 선택 하게 합니다.


HW 준비 설명은 이렇게 서둘러(!) 마무리 하겠습니다.

100% 똑같이 만들기는 곤란하기에, 핵심만 전달 드립니다. (포트 역할, 전체 결선 방법)



AT90CAN128 을 활용한 CAN data수신 

AT90CAN128 은 칩 이름만 보면 아시겠지만 CAN 통신 기능이 이미 chip 수준에 내장된 MICOM입니다. 다만 물리적인 CAN 라인과 연결한 트랜시버는 추가로 부착 하는 정도의 수고가 필요 합니다. 제가 사용한 모듈에는 이미 부착 되어 단지 차량 CANH, CANL 라인과 연결만 하면 됩니다.

하지만 CAN 통신 처리가 그냥 되지는 않고, Atmel 에서 제공하는 규약에 맞추어 register 설정, interrupt 설정 등을 통해 동작이 되어야 하는데 - Atmel 에서 제공하는 library가 - 시원찮습니다. 특히나 최신 Atmel studio 에서는 제대로 import 하는 것만 해도 일이더군요.

그래서 별도의 library를 독일의 한 community(https://www.mikrocontroller.net/topic/98697) 에서 구하여 약간의 수정으로 사용하게 되었습니다.


**별도 압축파일로 제공하는 Atmel Studio용 project파일의 can.h / can.c 파일을 참고 하시기 바랍니다.

AT90CAN_TPMS_2016-09-06_003533.7z




Main 함수

인트로 보여주기, 안내 메시지, can 초기화(InitCAN()), CAN data 받기용 interrupt 설정(set_interrupt_handler) 과정을 차례로 수행 합니다.

그리고 실제 동작은 interrupt 루틴인 canInterruptHander 함수에서 하고, main은 while (true)로 대기 상태(무한 loop) 처리가 됩니다. [MICOM은 무한 루프라고 열내거나 처리하다 죽지 않습니다^^]



CAN 초기화 - QM5/KOLEOS는 일반적인 속도인 500kbps를 사용 합니다. 

초기화를 완료 하면 segment display로 잠깐 결과를 보여 주게 됩니다(이건 나중에 별도 설명)



CAN data 수신 interrupt handler  - 모든 data를 수신 처리 합니다.

CAN data가 수신 될 경우 - 사실 mask 를 0으로 처리 해서 CAN으로 뿌려지는 모든 data가 수신 되게 됩니다. AT90CAN128은 충분한 속도와 반응으로 수신 처리를 하게 됩니다.

can_get_id() 함수를 통해서 CAN id를 받고, data는 can_get_data() 함수로 8 byte 배열에 수신된 data를 받게 됩니다.

이후는 잘 '요리'하는 일이죠!


우리의 관심 data인 TPMS 수치 값은 CAN id 0x385 를 가지며 길이는 8 입니다.

차량 속도: CAN id 0x284 

스티어링 앵글(운전석 핸들의 각도): CAN id 0x002 

Throttle(Accelerator), RPM : CAN id 0x180 

기어 중립 상태 여부 : CAN id 0x358 


** QM5/KOLEOS CAN data 별도 페이지로 안내 드릴 예정 입니다.

우선 TPMS CAN data 수신에 대한 처리 부분을 좀 더 보도록 하겠습니다.



TPMS data 수신 후 처리 - checkTPMSAndDisplay() 함수

8byte data 중 3번째~6번째 byte data가 각각 FR, FL, RR, RL 의 타이어 압력 값이며 code에서 보는 것과 같이 raw 수치에 0.25 를 곱한 값이 최종 압력 단위 psi(pound per square inch / 제곱 인치 당 파운드) 값이 됩니다. 즉 resolution이 0.25 psi가 됩니다.


* 10.0f 한 것은 7 segment display에서 3자리만 출력 되는 관계로 소수점 1자리만 유효하고 그에 대한 scale 처리를 한 것입니다. 예로서 32.25 라면 32.2 만 표현 할 수 있는 상태고, 실제 display 루틴은 정수 값만 받아 처리 하도록 두어서 *10 하여 최종 "322"를 만들어 전달 합니다.

물론 출력할 때 "." 위치를 마지막 자리 수 앞에 두어 최종 결과는 "32.2" 로 보이게 만듭니다.


이 최종 출력은 showATPMSValue 함수에서 처리를 하게 됩니다.

약간의 Magic(마법?!)이 이 함수에 포함 되어 있습니다. 

먼저 이전 값을 기억 했다가(static 변수) 수치가 변동 되는 시점에 값을 갱신함과 동시에 깜빡임 처리를 하게 됩니다. 이 깜빡임은 bit flag MultiSegmentDisplay::ATTR_BLINK 으로 표현되고, "4" 값에 의해 지속 시간이 전달 됩니다. 실제 처리는 MultiSegmentDisplay class의 timer interrupt 에서 약 4초간 깜빡이게 사전 code가 구성 되어 있습니다. (다음 글에서 이 부분을 더 자세히 볼 예정 입니다)



속도 표시 - displaySpeed() 함수

차량의 현재 속도는 CAN id 0x284 에 담겨져 오고 5번째, 6번째 byte data의 조합으로 계산 됩니다.

계산 수식은 Speed = (D5*2)+(D6/128) 로 처리 됩니다. 실제 code상에서는 소수점 2자리를 표현하기 위해 100배의 값이 두 byte로 표현 하는 것으로 아래 처럼 계산처리가 가능 한 것입니다.

  speedx100 = (data[4] << 8 | data[5]);


이곳에서도 Magic(?!) 으로 소수점 단위의 절반 값에 대해서만 값 변동을 처리하여 빈번한 속도 표시 변동을 막는 기법을 사용 하고 있습니다.

표시는 7 segment 중간에 있는 3자리 수에 표시를 하게 됩니다.



스티어링 앵글(운전석 핸들 각도) - displaySteeringAngle() 함수

이 부분은 매우 큰 관심을 받을 만한 값으로 생각 됩니다. 

이미 제 블로그에 공유 드렸던 "[DIY] 차량용 코너링 램프 컨트롤러 제작" 에서 휠각을 판단하기 위해 별도의 센서를 설치하는 고난도(?)의 DIY를 소개 드렸는데, CAN 통신을 통해서 한번에 휠각을 실시간으로 0.1 도의 정확도로 받아 올 수 있는 길이 있는 것입니다.

이번 TPMS diy를 완성 하기 까지 여전히 제 차에는 앞서 직접 DIY한 코너링 램프 컨트롤러가 동작 중입니다. 언젠가는 CAN 연결을 통한 제어로 바꿀 수도 있을 것 같습니다 (귀찮아서 언제 쯤....)



수동 기어 단수 표시, Accelerator, RPM 상태 표시 - checkEngineParameter() 함수

처음 계획은 TPMS로 부터의 개별 tire psi 값 출력이었습니다. 항상 그렇지만 하나가 되니 추가로 무언가 하고 싶더군요(실은 예전 부터 계획했고, 부품도 함께 구입 했다는).

특히 AT90CAN128의 엄청난 수의 port를 남겨 둘 수 없는 마음에 - 가속 패달(Accelerator)과 엔진 회전수(RPM)을 그럴 듯 한 Bar LED 모듈를 사용한 bar graph 로 표현하고자 하여 만든 함수 입니다.

RPM, 가속 페달 값은 아래와 같이 계산이 되는 것입니다.

    uint16_t RPM = (data[0] << 7 | data[1]) >> 2; // Engin RPM = (D1*128+D2)/4

    uint16_t throttle = (data[5] << 2) | ((data[6] & 0xC0) >> 6);


특이점으로 수동 기어 단수를 현재 RPM과 속도로 부터 유추하여 디스플레이어 가운데 부분에 보여 주는 code도 담겨져 있습니다.

자동이던 수동이던 기어비는 이미 정해 져 있습니다(무단 변속기 제외). 이 기어비 값을 현재 RPM과 속도로 부터 역으로 유추하여 보여 주는 것이죠! - 단 클러치를 밝고 있는 경우 속도와 RPM은 비례 관계가 아니므로 정확한 실제 기어 위치를 알 수는 없습니다. 하지만 충분히 실용적인 표시가 가능 하고, clutch 동작 시점에만 표시 하기에 엉뚱한 값 표시를 최대한 억제 하게 됩니다.

해당 함수는 getTransmissionInformation() 를 살펴 보면 되겠습니다.



이상 주요 함수를 돌아 보았습니다.

다음 글에서는 7 segment 15개와 bar LED 10 x 5 에 대한 driver code구성을 살펴볼 예정 입니다.


AtmelStudio 7로 작성한 project와 source 코드는 앞서 언급 드린 첨부 파일을 다운로드 받으셔서 컴파일, 실행, 확인을 하실 수 있습니다.

AtmelStudio 7 은 Atmel에서 배포하는 무료 AVR 통합 환경 입니다. Visual studio IDE를 사용하여 만들어 졌고 AVR 개발에는 매우 유용한 도구 입니다.


AT90CAN_TPMS_2016-09-06_003533.7z


**개인 사용자라면 누구나 자유롭게 사용 가능 하며 출처 표기 정도는 해 주시면 감사 하겠습니다.

  단, 상업적인 목적으로는 허용하지 않습니다.



DIY - TPMS(Tire Pressure Monitoring System) value display for RENAULT and NISSAN via CAN(Controller Area Network)


직접 만드는 TPMS 값 표시기 입니다 - QM5/KOLEOS 용 이기는 한데 RENAULT, NISSAN 차량에 동일 CAN ID를 사용하는 차량이라면 호환 가능 할 것으로 예상 합니다.


*TPMS 장착 차량에서 개별 타이어 공기압 수치를 보기 위한 장치를 만드는 것이며 TPMS 미 장착 차량에 대한 추가 장착(개조)은 아닙니다. CAN ID 0x385 를 사용한다면 다른 차량에서도 정상 동작 할 듯 합니다.


[사진1] DIY TPMS module (케이스 만들기 전 마지막 확인 과정 중 촬영 / 클릭하면 원본 크기)
  오른쪽 Bar LED는 최초 계획에서 추가한 Accelerator, RPM 표시기 입니다(Shift down indicator 포함)

DIY - TPMS value display module


[동영상]


QM5(KOLEOS)에는 TPMS(Tire Pressure Monitoring System)가 선택 혹은 기본 사양으로 장착 되는 차량 입니다. 현 시점 기준으로 9년을 넘기고 있는 모델임에 불구하고 국내 차량 기준으로는 좀 빠른 시점에 장착이 되었다고 볼 수 있습니다 (BMW 같은 차량은 이미 오래오래 전에 기본 장착을 하고 있었죠)


그런데 QM5 TPMS display는 계기판에서 전담하고 있는데, 여러 글자를 표시할 수 있음에도 불구하고 개별 타이어의 공기압은 제공 해 주지 않습니다. 다만 공기압이 기준 수치 보다 낮아 졌을 때 경고 메시지 혹은 파열등에 의한 공기압 미확인 메시지 등만 제공을 하고 있죠.

아래 사진 처럼 말이죠.


[사진2] 뒷쪽왼편(RL) 타이어 공기압 부족 상황(이때 나사못(피스)에 의해 공기가 조금씩 빠지던 상황)

QM5/KOELOS TPMS warning messageTire pressure adjust needed


기억은 정확하지 않은데, 25psi 정도로 떨어져야 저 메시지를 보여 주게 됩니다.

이미 너무 많은 공기압이 빠진 상태에서 저 메시지를 보게 됩니다. 사전에 TPMS 에서 보고하는 수치값을 보여 준다면 미리 대비할 수 있을텐데 말이죠.

특히 비교적 최근 차량인 SM5의 경우 MMI 선택에 의해서 현재 개별 타이어 공기압 수치 확인이 가능 합니다.


그래서 TPMS 역시 별도의 MICOM으로 구성 된 것이 확인 되고(당연히) 이 또한 CAN network상에 연결 되어 있습니다. CANCLIP 으로도 확인이 가능한 내용이랍니다.


[사진3] Renault 진단 장비인 CANCLIP 에서 확인한 현재 공기압

  (RL만 1.57 bar == 23 psi 정도로 너무 낮은 상태)


위의 사진2 / 사진3과 같은 상황에서와 같이 실 사용에서 현재 공기압이 무지 궁금하고 그렇다고 매번 CANCLIP 장비(노트북 + 별도 연결용 CANCLIP 기기)를 연결해서 확인 해야 하기에 별도 TPMS display module 을 만들고자 한 꿈으로 이렇게 사진1과 같은 최종(케이스 뺀) 물건을 만들기에 이르렀습니다.


LCD로 보기에는 시인성이 떨어지기에 소형 LED 7 segment 를 여러개 조합하여 바퀴 4개에 대한 TPMS 값을 확실히 보이게 만드는 것이 목적 입니다.


사용한 모듈과 부품 등등은 아래와 같습니다.

* AT90CAN128 Module - Interboard 에서 만든 AT90CAN128BK2-ECO 모듈(CAN 트랜시버 내장)

* 3 자리 7 segment - Aliexpress 에서 찾은 매우 작은 크기의 3자리 표시 모듈

  (RED 3 Bit Digital Tube 0.25 inch LED Display 15*8*4mm)

* 만능 기판 - 3자리 7 segment 총 5개를 적절히 배치 할 크기

* 모듈과 만능기판을 연결할 2pin 핀과 핀헤더 소켓

* 배선용 배선

* 추가: 가속패달 표시와 RPM 표시(+Shift down 인디케이션)를 위한 10bit bar-graph LED 모듈



AT90CAN128 Module 은 아래 처럼 생겼습니다 - COM port 기판 표시와 실제 번호가 다른 것으로 기억.

[사진4] AT90CAN128 module


원래는 회로도를 그리고 하려고 했으나...

특정 port를 통으로 7 Segment LED에 연결 하는 것이 핵심이라... 넘어가고(2년 동안 해서 지쳤습니다)

작업 사진 몇 개와 실제 중요 code 구성을 안내 드리고자 합니다.

동일하게 작업하기에는 쉽지 않기에, 기본을 알려 드리고 참고 자료로서 개인별 응용이 더 맞을 듯 합니다.


위 보드만 있으면 남은 일은 전원, CAN 연결, LED 배선이 남은 일입니다. 물론 프로그래밍이 남았죠. 이 부분은 아래 설명과 함께 첨부 파일을 보신다면 절반은 해결 한 것이라고 해도 과언이 아닐 듯 합니다.


LED 배치와 배선 작업은 아래처럼 시작과 끝을 내었습니다.

무려 3x5 = 15 자리 segment를 제어 해야 하는데, 밝기를 위해 별도 트랜지스터 사용하기에는 보드 크기와 배선 난위도, 전력 고려 등등 너무 과하기에, 트랜지스터를 사용 하지 않고 8bit 출력 port에 AT90CAN128 개별 출력(40mA 한계) 전력을 넘지 않을 저항값을 선택 하게 되었습니다.

다시 말해 8개의 LED를 동시에 켜되 총 40mA 를 넘지 않게 즉 개별 LED 당 4.4 mA 를 넘지 않게 해야 합니다.

이를 위해 680옴 저항을 사용 하면 4.2mA 를 소모하게 되고 8개 모두 사용 하더라도 35mA 미만으로 소모 하기에 15자리 segment를 순차적으로 표시(15자리 중 순간적으로 하나만 표시)하게 되면 별도 TR 없이도 구동이 가능 하게 됩니다.

대신 duty cycle이 무려 1/15 라서 밝기는 줄어 들 것이라는 .... (최종 완성시에 문제 없었습니다)


말보다는 사진 나갑니다~ (클릭하면 아름다운 대형 크기로!)

[사진5] LCD 배치 - 느끼기에 균형미가 넘치게 자~알


[사진6] 뒷면에는 AT90CAN128 모듈과 직접 연결할 핀헤드를 자알~ 위치
          (고도의 도킹 시나리오 준비 필요?!)


[사진7] 공통 출력(8선)을 모두 이어 나갑니다 (거짓말 조금 하면 하루 작업 입니다 ㅠ)


[사진8] 전류 제한을 위한 공통라인 8선에 저항 부착(680옴- 검갈빨주노초)


[사진9] 개별 segment 선택 용 배선 15개 까지 추가 하면 - 인간의 한계를 시험 하는 느낌이 옵니다.


[사진10, 11] CAN 없이 Segment display 루틴 부터 만들고 배선을 제대로 했나 확인 합니다.


문자열 data도 넣어서, 영문 표현을 시도 해 봅니다^^


[사진12,13,14] AT90CAN128 보드와 디스플레이 기판 결합

핀 헤더는 디스플레이 기판에서, 소켓은 AT90128 모듈에 장착하여 서로 연결합니다.


뒷쪽은 아래와 같습니다. - 왼쪽이 CAN 라인, 우측이 전원, 헤더는 AVR ISP 프로그래머와 연결


약간 다른 각도~


위의 사진과 같이 핀헤더로 기성품인 AT90CAN128 모듈과 디스플레이 모듈을 결합 하게 됩니다.

7 Segment 표시를 위해 아래의 AVR 포트를 할당 했습니다. (code 참조 가능)
  PORTF : Segment out (수 표시용 7 세그먼트 7bit + 점(dot) 1bit)

  PORTA : Digit 0 ~ 7 (처음 8자리 표시)

  PORTC : Digit 8 ~ 15 (나머지 7자리 표시)

  ** [사진 4]에서 보시면 아시겠지만 기판 포트 위치에 맞추어 A,C,F 포트를 할당 하게 된 것입니다.


그리고 위 사진에 없지만 나중에 추가 연결한 악셀레이터(Accelerator) 와 RPM 표시를 위해 아래의 포트를 할당 했습니다.

  PORTB : 8 개 LED 개별 표시

  PORTE : 8 개 묶음 표시


LCD 모듈을 사용하면 포트 2개 정도(8bit + 8bit) 으로 해결 될 수 있겠지만 낮/밤 시인성을 위해 7 segment를 사용한 이상 엄청난 수의 출력 포트를 사용 하게 됩니다. AT90CAN128의 풍부한 포트를 잘 활용 했다고 볼 수 있습니다.


[사진15] 처음 디버깅용 MCP2515 + AVR 조합의 CAN data 전송, 확인(to BT), PC to CAN 전송 용 모듈들 - 한쌍(두줄)의 CAN 네트웍으로 연결 된 상태 입니다.

위 등장 모듈은 별도 글에서 설명 드릴 예정 입니다.


실제 모듈 구성과 기본 code 구성은 다음 글로 이어집니다.



01234567891011121314


간만에 QM5 전용 DIY 내용을 정리 합니다.


리어 하이트센서 추가 장착과 활성화를 어떻게 하느냐의 내용입니다.

RE 모델(초기년식은 꼭 RE+)이라면 그냥 넘어 가시고, 그렇지 않은 일반 모델에 바이제논 램프를 변경 장착 하셨다면 참고하실 만합니다.


법상으로 바이제논 램프를 장착 하게 된다면 반드시(?) 광축(빛의 높낮이) 자동 조절 장치를 달아야 하고, 만일 추가로 장착할 경우 램프 뿐만 아니라 지금 언급 드리는 리어 하이트센서도 함께 장착 후 구조변경 까지 마쳐야 제대로 된 마무리라고 할 수 있겠습니다.


이때까지의 개조에서 QM5에서 리어하이트 센서 장착과 활성화는 제대로 정리 되거나 알려지지 않아서 이번 기회에 QM5 자체 부품과 르노 CANCLIP으로 활성화 까지 완료 하는 것을 정리 합니다.


지금 보는 글에서는 재료 준비, 배선, 활성화 단계 까지만 보고, 다음 글에서 리어하이트 센서 장착과 추가 배선 처리를 정리 합니다.


동작 동영상 추가로 올립니다.


순서 

  1. 리어 하이트 센서에 연결에 필요한 각종 전원, 신호선 확보
  2. 신호선 연결


필요 부품 

  1. 리어 하이트 센서(Rear height sensor) PART NO. 53820-JD00A 와 전용 고정 볼트 01125-N8021(55082-1363R) 2개
  2. 현대 제네시스 오토레벨링센서 배선 PART NO. 921933M090 
  3. Scotch Lock Quick Splice Wire Connectors - 기존 배선을 찝어서 손쉽게 배선 따오는 부품 5개.
  4. 왼쪽 앞 배선 소켓에서 후륜 까지 연장 케이블과 관련 연결 부자재
  5. 추가로 헤드램프 하이트 조절 연결용 커넥터(유사한 것 구하세요)와 추가 연장 배선 등등


배선 부위 확인 

리어 하이트센서 연결을 위한 신호 라인은 R2 / R462 두 곳 만으로 모든 연결이 가능 합니다.

아래 이미지에서 실제 R462는 위에서 두번째가 아닌 아래에서 두번째 입니다 (위아래 반대)


리어 하이트 센서에 6선을 잘 연결 해야하는 것이고 핵심은 R2 / R462 의 특정 라인과 잘 추가 연결만 하면 되는 것입니다.


아래 이미지 두장으로 요약 했습니다. (클릭 하면 더 큰 크기)


*전체 배선도와 배선 대상 커넥트와 핀배치도


필요한 신호선은 R2에서 4선, R462에서 1선 총 5선 연결이 필요 합니다.

나머지 한 선은 출력으로서 제논램프의 광축 조절 단자의 가운데 핀으로 연결합니다.

광축 조절 단자의 전원(+/GND) 처리는 여기서 상세히 다루지 않습니다 - 헤드라이트 신호와 연결하는 것이 가장 적은 비용으로(바로 옆에 있기에) 해결 할 수 있을 것 같습니다. 꼭 AP44와 연동 할 필요는 없습니다.


중간 커넥터인 현대 제네시스 오토레벨링 배선을 사용하는 이유는 리어 하이트 센서와 100% 동일 규격의 커넥트를 저렴한 가격으로 해결이 되기 때문입니다. 리어하이트 센서를 보면 센서 자체는 동일 회사에서 만드는 듯 하더군요 - 부차 적인 기구와 K 단자를 통한 프로토콜 정도만 다르겠죠?

반대선 배선은 적당히 절단 후 QM5의 해당 신호와 배선 하면 되겠습니다. 저는 Test용으로 손쉽게 연결을 할 수 있게끔 터미널 형태 단자를 사용하여 진행 했습니다.

최종적으로는 별도의 연장 케이블과 연결 예정으로 위 test는 참고로만 참조 하세요.



*실제 연결 상태와 관련 참고 내용 


앞서 준비물로 언급드린 스카치락을 적극적으로 활용하여 기존 커넥터 배선으로 부터 손쉽게 추가 선을 연결하였습니다.(윗쪽 사진) / 아래 왼쪽 파란색 두개는 original 3M 스카치락으로 도어 연동 부저 DIY 때 연결한 것들입니다.

이 작업을 위해 플라이어(뺀치)로 단자를 찝는 정도의 노력이 필요 합니다. 다만 연결 단자 처리는 단자 찝는 전용 툴을 사용 하셔야 합니다. (하나 사셔야죠, 진정한 DIY를 위해!)


아래 사진이 모든 출연진이 나온 사진 입니다.

최종적으로는 Test용 결선 터미널 대신 연장 케이블(6선 연결 가능한)과 중간 연결을 위한 추가 단자 처리가 있을 겁니다. (예정)


위 내용으로 배선에는 어려움이 없는 충분한 정보 전달이 되었을 것으로 생각 됩니다.


다음편 내용은 언제 마무리 될지 몰라서(센서 설치를 하려면 궁극적으로 리프트에 올려서 작업이 필요 합니다) 미리, CAN CLIP으로 활성화를 위한 내용을 짧막하게 요약하고 마무리 하겠습니다.


CAN CLIP실행 후, ECU 강제 인식을 선택 후 시작합니다. (새로 추가한 부품 강제 인식)

그러면 이때 까지 잡히지 않았던 1372 가 화면 가운데에 보이게 됩니다.

1372 선택 후 계속 진행 하여 진단 메뉴에 가면, 초기화가 되지 않은 상태라 에러값으로 처리 되어 있답니다.

아래의 두가지 command를 차례대로 진행 해 주세요 (CAN CLIP 사용법은 저도 기억이...)

  1. VP009: EEPROM 테이터 설정 (정지 상태 / 이그니션 ON)

  2. VP007: 시스템  (정지, 이그니션 ON / 센서 에러 없는 상태(지우세요) / 공차상태!!! / 평지!!! / 정상 공기압!!!)

위 까지 했다면 모든 것이 OK!

최종 확인은 tester기로 리어하이트센서 5번 단자 출력을 + 테스트봉으로 연결 / -는 8번 MZS와 연결하여 하이트 센서를 살짝 회전시켜 봅니다.

그러면 10초 정도 후에 전압값이 변경되어 출력 되고 이까지 확인 되면 모든 과정이 성공 한 것입니다.


나머지는 실전 리어 서스펜션에 센서 설치와 연장선 처리만 남게 되는 것이죠.


오늘도 즐 DIY 되시길~ / 2편은 언제 올릴지 기약이 없습니다, 누가 리프트 좀 빌려주시면... ^^


IKEA LAPPLAND TV수납유닛, 화이트 (402.851.57) 3개 한셋!


TV와 함께 주변 기기, 책, 기타 악세사리를 두기 적당한 TV 수납 장식함 입니다.

장식장은 호불호가 있기에... 조립과 사용 결과 나름의 활용성은 좋은 것 같습니다. (처음에는 저도 제 취향이 아니라고 생각 했지만, 실제 사용 해 보니 쓸만 합니다)


조심해야 할 사항을 정리 하면 아래와 같습니다.

  1. 모두 3개를 픽업 하여야 합니다. 실제 제가 픽업 할 때 보니 1번만 한개 남고 2, 3번 박스는 5개씩이 있더라는 우스운 상황이... 아마도 1번만 들고 조립 하러 집에 가신 듯 합니다 @@
  2. 기본적으로 TV는 벽쪽(장식장 뒷면)에 거치해야 합니다. 바닥에 둘 경우 정 중앙에 TV 무게가 위치해야 하며 다리 형식으로 바깥으로 무게가 쏠리는 스탠드 TV의 경우 반드시 추가 받침대를 사용 해야 합니다. - 완성 사진 구조를 보시면 아시겠지만 정중앙만 무게 지탱이 가능한 구조라 그냥 사용하면 처짐 현상이 발생 합니다.



박스 3개 한셋 입니다. 아래 사진의 맨 우측 3개가 LAPPLAND TV 수납 유닛 구성품 입니다.


매장 창고에서 아래의 3가지 포장 박스 모두 챙겨 오세요 이 3개 모두가 한 셋 입니다.

1번만 가져 가져 가셔도 금액 계산 되고 그냥 집에 까지 가실 수도 있답니다.


2번 상자


3번 상자



각종 체결 부품 등 확인 체결 부위가 많은 만큼 부품수도 많고 다양 합니다.

앞서 BEKANT 책상 조립때는 주지 않던 조임 부품 손 보호용 플라스틱 부품도 여기서는 포함 되어 있네요.


외각 프레임은 MDF보다는 강한 파티클보드 형태의 소재를 사용 합니다.


길고 덩치 큰 자재인 관계로 바닥에는 별도 보호를 위해 포장 박스와 포장 보호재 혹은 다른 방법으로 바닥 보호와 목재 상처를 꼭 방지 해야 합니다.


여차저차 설명서 보고 조립하니 아래와 같이 완성!

책상보다는 조금 더 어렵습니다. 특히 상호간 체결시에 약간의 어려운 부분도 있습니다. 잘 보시고 차근 차근 하면 OK (시간도 좀 걸립니다)


상판 체결부위 참고 사진 가장 긴 체결 부품으로 고정 합니다.


거실에 위치 잡기


최종 TV 설치 --> 이렇게 TV를 바닥에 두면 큰일(?) 납니다.

아래의 TV의 경우 벽으로 고정 하지 않고 바닥에 두었는데, 보시면 아시겠지만 - 다리가 4가닥으로 뻗어서 세로 판재가 있지 않은 부분에 하중이 가해 집니다.

아래 처럼 두실 거라면, TV 다리에 추가의 판재(휘지 않는)를 넣어야 TV를 받치는 하판이 아래로 휘지 않게 됩니다.


맨 아래 왼쪽의 수납 함은 별도 구매 하여야 합니다!

LEKMAN (202.471.37) 33 x 35 x 33 cm 의 플라스틱 상자 입니다. 


참고 하세요~.

012345678910


.

최초 수동기어 인디케이터 만들기 글을 올린지가 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


GearPositionIndicatorLib.h


GearPositionIndicatorLib.c


GearPositionIndicator8535L.c



소스코드 전체 내용

iocompat.h

  1. /*
  2.  * ----------------------------------------------------------------------------
  3.  * "THE BEER-WARE LICENSE" (Revision 42):
  4.  * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
  5.  * can do whatever you want with this stuff. If we meet some day, and you think
  6.  * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
  7.  * ----------------------------------------------------------------------------
  8.  *
  9.  * IO feature compatibility definitions for various AVRs.
  10.  *
  11.  * $Id: iocompat.h,v 1.6.2.1 2008/03/17 22:08:46 joerg_wunsch Exp $
  12.  */
  13.  
  14. #if !defined(IOCOMPAT_H)
  15. #define IOCOMPAT_H 1
  16.  
  17. /*
  18.  * Device-specific adjustments:
  19.  *
  20.  * Supply definitions for the location of the OCR1[A] port/pin, the
  21.  * name of the OCR register controlling the PWM, and adjust interrupt
  22.  * vector names that differ from the one used in demo.c
  23.  * [TIMER1_OVF_vect].
  24.  */
  25. #if defined(__AVR_AT90S2313__)
  26. #  define OC1 PB3
  27. #  define OCR OCR1
  28. #  define DDROC DDRB
  29. #  define TIMER1_OVF_vect TIMER1_OVF1_vect
  30. #elif defined(__AVR_AT90S2333__) || defined(__AVR_AT90S4433__)
  31. #  define OC1 PB1
  32. #  define DDROC DDRB
  33. #  define OCR OCR1
  34. #elif defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || \
  35.       defined(__AVR_AT90S4434__) || defined(__AVR_AT90S8535__) || \
  36.       defined(__AVR_ATmega163__) || defined(__AVR_ATmega8515__) || \
  37.       defined(__AVR_ATmega8535__) || \
  38.       defined(__AVR_ATmega164P__) || defined(__AVR_ATmega324P__) || \
  39.       defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || \
  40.       defined(__AVR_ATmega1284P__)
  41. #  define OC1 PD5
  42. #  define DDROC DDRD
  43. #  define OCR OCR1A
  44. #  if !defined(TIMSK)           /* new ATmegas */
  45. #    define TIMSK TIMSK1
  46. #  endif
  47. #elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) || \
  48.       defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__)
  49. #  define OC1 PB1
  50. #  define DDROC DDRB
  51. #  define OCR OCR1A
  52. #  if !defined(TIMSK)           /* ATmega48/88/168 */
  53. #    define TIMSK TIMSK1
  54. #  endif /* !defined(TIMSK) */
  55. #elif defined(__AVR_ATtiny2313__)
  56. #  define OC1 PB3
  57. #  define OCR OCR1A
  58. #  define DDROC DDRB
  59. #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || \
  60.       defined(__AVR_ATtiny84__)
  61. #  define OC1 PA6
  62. #  define DDROC DDRA
  63. #  if !defined(OCR1A)
  64. #    /* work around misspelled name in avr-libc 1.4.[0..1] */
  65. #    define OCR OCRA1
  66. #  else
  67. #    define OCR OCR1A
  68. #  endif
  69. #  define TIMSK TIMSK1
  70. #  define TIMER1_OVF_vect TIM1_OVF_vect /* XML and datasheet mismatch */
  71. #elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || \
  72.       defined(__AVR_ATtiny85__)
  73. /* Timer 1 is only an 8-bit timer on these devices. */
  74. #  define OC1 PB1
  75. #  define DDROC DDRB
  76. #  define OCR OCR1A
  77. #  define TCCR1A TCCR1
  78. #  define TCCR1B TCCR1
  79. #  define TIMER1_OVF_vect TIM1_OVF_vect
  80. #  define TIMER1_TOP 255        /* only 8-bit PWM possible */
  81. #  define TIMER1_PWM_INIT _BV(PWM1A) | _BV(COM1A1)
  82. #  define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
  83. #elif defined(__AVR_ATtiny26__)
  84. /* Rather close to ATtinyX5 but different enough for its own section. */
  85. #  define OC1 PB1
  86. #  define DDROC DDRB
  87. #  define OCR OCR1A
  88. #  define TIMER1_OVF_vect TIMER1_OVF1_vect
  89. #  define TIMER1_TOP 255        /* only 8-bit PWM possible */
  90. #  define TIMER1_PWM_INIT _BV(PWM1A) | _BV(COM1A1)
  91. #  define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
  92. /*
  93.  * Without setting OCR1C to TOP, the ATtiny26 does not trigger an
  94.  * overflow interrupt in PWM mode.
  95.  */
  96. #  define TIMER1_SETUP_HOOK() OCR1C = 255
  97. #elif defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || \
  98.       defined(__AVR_ATtiny861__)
  99. #  define OC1 PB1
  100. #  define DDROC DDRB
  101. #  define OCR OCR1A
  102. #  define TIMER1_PWM_INIT _BV(WGM10) | _BV(PWM1A) | _BV(COM1A1)
  103. /*
  104.  * While timer 1 could be operated in 10-bit mode on these devices,
  105.  * the handling of the 10-bit IO registers is more complicated than
  106.  * that of the 16-bit registers of other AVR devices (no combined
  107.  * 16-bit IO operations possible), so we restrict this demo to 8-bit
  108.  * mode which is pretty standard.
  109.  */
  110. #  define TIMER1_TOP 255
  111. #  define TIMER1_CLOCKSOURCE _BV(CS12) /* use 1/8 prescaler */
  112. #elif defined(__AVR_ATmega32__) || defined(__AVR_ATmega16__)
  113. #  define OC1 PD5
  114. #  define DDROC DDRD
  115. #  define OCR OCR1A
  116. #elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) || \
  117.       defined(__AVR_ATmega165__) || defined(__AVR_ATmega169__) || \
  118.       defined(__AVR_ATmega325__) || defined(__AVR_ATmega3250__) || \
  119.       defined(__AVR_ATmega645__) || defined(__AVR_ATmega6450__) || \
  120.       defined(__AVR_ATmega329__) || defined(__AVR_ATmega3290__) || \
  121.       defined(__AVR_ATmega649__) || defined(__AVR_ATmega6490__) || \
  122.       defined(__AVR_ATmega640__) || \
  123.       defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || \
  124.       defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
  125. #  define OC1 PB5
  126. #  define DDROC DDRB
  127. #  define OCR OCR1A
  128. #  if !defined(PB5)             /* work around missing bit definition */
  129. #    define PB5 5
  130. #  endif
  131. #  if !defined(TIMSK)           /* new ATmegas */
  132. #    define TIMSK TIMSK1
  133. #  endif
  134. #else
  135. #  error "Don't know what kind of MCU you are compiling for"
  136. #endif
  137.  
  138. /*
  139.  * Map register names for older AVRs here.
  140.  */
  141. #if !defined(COM1A1)
  142. #  define COM1A1 COM11
  143. #endif
  144.  
  145. #if !defined(WGM10)
  146. #  define WGM10 PWM10
  147. #  define WGM11 PWM11
  148. #endif
  149.  
  150. /*
  151.  * Provide defaults for device-specific macros unless overridden
  152.  * above.
  153.  */
  154. #if !defined(TIMER1_TOP)
  155. #  define TIMER1_TOP 1023       /* 10-bit PWM */
  156. #endif
  157.  
  158. #if !defined(TIMER1_PWM_INIT)
  159. #  define TIMER1_PWM_INIT _BV(WGM10) | _BV(WGM11) | _BV(COM1A1)
  160. #endif
  161.  
  162. #if !defined(TIMER1_CLOCKSOURCE)
  163. #  define TIMER1_CLOCKSOURCE _BV(CS10) /* full clock */
  164. #endif
  165.  



GearPositionIndicatorLib.h

  1. #ifndef __GETPOSITIONINDICATOR_H__
  2. #define __GETPOSITIONINDICATOR_H__
  3.  
  4.  
  5. #define PORT_SEG                        PORTA   // Segment 출력용 port
  6. #define SEG_COMMON_ANODE        1               // Anode 공통 == + 극 공통으로 출력이 0으로 나가야 동작 하는 경우
  7.  
  8. #ifdef  SEG_COMMON_ANODE
  9.         #define SEGOUT(value)   (~value)        // Coommon Anode이므로 bit 상태를 뒤집는다
  10. #else
  11.         #define SEGOUT(value)   (value)
  12. #endif
  13.  
  14.  
  15. unsigned char GetGearPosition( unsigned char state );
  16. char getSegmentData( char currentMode );
  17. void displaySegmentDataNoEffect( char segmentData );
  18.  
  19.  
  20. #endif  // of __GETPOSITIONINDICATOR_H__


GearPositionIndicatorLib.c

  1. /*******************************************************************************
  2.  (C) 2009 Seung-Won Lee   http://whoisit.tistory.com    SoftWareYi@gmail.com
  3.  
  4.  
  5.   수동기어 단수표시 장치
  6.  
  7.   @File  GearPositionIndicator.c
  8. *******************************************************************************/
  9.  
  10. #include <avr/io.h>
  11. #include "GearPositionIndicatorLib.h"
  12.  
  13.  
  14.  
  15. const unsigned char     SEGMENT_MINUS   = 0b10000000;   // -
  16. const unsigned char     SEGMENT_0               = 0b01110111;   // 0
  17. const unsigned char     SEGMENT_1               = 0b00010100;   // 1
  18. const unsigned char     SEGMENT_2               = 0b10110011;   // 2
  19. const unsigned char     SEGMENT_3               = 0b10110110;   // 3
  20. const unsigned char     SEGMENT_4               = 0b11010100;   // 4
  21. const unsigned char     SEGMENT_5               = 0b11100110;   // 5
  22. const unsigned char     SEGMENT_6               = 0b11100111;   // 6
  23. const unsigned char     SEGMENT_7               = 0b01110100;   // 7
  24. const unsigned char     SEGMENT_8               = 0b11110111;   // 8
  25. const unsigned char     SEGMENT_9               = 0b11110110;   // 9
  26. const unsigned char     SEGMENT_BACK    = 0b11111001;   // P. for Back
  27. const unsigned char     SEGMENT_ERROR   = 0b11101011;   // E. for Error
  28. const unsigned char     SEGMENT_H3              = 0b10100010;   // 한자 3
  29. const unsigned char     SEGMENT_ALL             = 0b11111111;   // All for test
  30.  
  31.  
  32.  
  33. //******************************************************************************
  34. // Port로 부터 입력된 기어 위치값으로 부터 현재 기어 위치를 char 문자로 돌려준다
  35. //      중립='N'
  36. //      각 단수='1'~'6'
  37. //      후진:'B'
  38. //      기어조작중:'-'
  39. //      에러상태:'E'
  40. //******************************************************************************
  41. typedef struct
  42. {
  43.         unsigned char   state;
  44.         unsigned char   position;
  45. } stateToPosition;
  46.  
  47. unsigned char GetGearPosition( unsigned char state )
  48. {
  49.         const stateToPosition   stateData[] = {
  50.                 { 0b00100010, 'N' },    // Natural
  51.                 { 0b00010100, '1' },    // 1
  52.                 { 0b01000100, '2' },    // 2
  53.                 { 0b00010010, '3' },    // 3
  54.                 { 0b01000010, '4' },    // 4
  55.                 { 0b00010001, '5' },    // 5
  56.                 { 0b01000001, '6' },    // 6
  57.                 { 0b00011000, 'B' },    // Back
  58.         };
  59.         char    result = '-';
  60.         char    sensingCount = 0;
  61.         unsigned char   i;
  62.  
  63.         state = state & 0b01111111;     // 상위 1bit 사용 안함 처리
  64.        
  65.         // CheckError - 최소한 하나의 bit는 1이어야 정상이다. (실은 2개)
  66.         if ( !(0b01111111 & state) )
  67.         {
  68.                 return 'E';     // 센서로부터 아무런 입력값이 없을 경우 Error처리
  69.         }
  70.  
  71.         // 변속 중인 상태 확인 -> 앞뒤
  72.         sensingCount += ( state & 0b01000000 ) ? 1 : 0;
  73.         sensingCount += ( state & 0b00100000 ) ? 1 : 0;
  74.         sensingCount += ( state & 0b00010000 ) ? 1 : 0;
  75.         if ( sensingCount > 1 ) // 두 위치값이 동시에 입력되면 변동 중인 경우
  76.                 return result;
  77.  
  78.         // 변속 중인 상태 확인 -> 좌우
  79.         sensingCount = 0;
  80.         sensingCount += ( state & 0b00001000 ) ? 1 : 0;
  81.         sensingCount += ( state & 0b00000100 ) ? 1 : 0;
  82.         sensingCount += ( state & 0b00000010 ) ? 1 : 0;
  83.         sensingCount += ( state & 0b00000001 ) ? 1 : 0;
  84.         if ( sensingCount > 1 ) // 두 위치값이 동시에 입력되면 변동 중인 경우
  85.                 return result;
  86.  
  87.         // 모두 통과 되었다면 단수 확인 과정, 확인 불가시 '-' 처리가 default
  88.         for ( i=0; i<sizeof(stateData)/sizeof(stateData[0]); i++ )
  89.         {
  90.                 if ( stateData[i].state == state )
  91.                 {
  92.                         result = stateData[i].position;
  93.                         break;
  94.                 }
  95.         }
  96.  
  97.         return result;
  98. }
  99.  
  100. //******************************************************************************
  101. // getTransmissionValue로 부터 받아진 결과값을 입력하면 포트출력 값을 돌려준다
  102. //******************************************************************************
  103. typedef struct
  104. {
  105.         char    mode;
  106.         char    segmentData;
  107. } modeToSegment;
  108.  
  109. char getSegmentData( char currentMode )
  110. {
  111.         const modeToSegment     modeToSegmentData[] = {
  112.                 { '-', SEGMENT_MINUS }, // -
  113.                 { 'N', SEGMENT_0 },     // 0
  114.                 { '1', SEGMENT_1 },     // 1
  115.                 { '2', SEGMENT_2 },     // 2
  116.                 { '3', SEGMENT_3 },     // 3
  117.                 { '4', SEGMENT_4 },     // 4
  118.                 { '5', SEGMENT_5 },     // 5
  119.                 { '6', SEGMENT_6 },     // 6
  120.                 { 'B', SEGMENT_BACK },  // R == P.
  121.                 { 'E', SEGMENT_ERROR }, // E.
  122.         };
  123.         unsigned char   i;
  124.         char    segmentData = SEGMENT_ERROR;    // Default to Error
  125.  
  126.  
  127.         // 입력값에 대응하는 Segment값을 가져온다
  128.         for ( i=0; i<sizeof(modeToSegmentData)/sizeof(modeToSegmentData[0]); i++ )
  129.         {
  130.                 if ( modeToSegmentData[i].mode == currentMode )
  131.                 {
  132.                         segmentData = modeToSegmentData[i].segmentData;
  133.                         break;
  134.                 }
  135.         }
  136.  
  137.         return segmentData;
  138. }
  139.  
  140.  
  141. void displaySegmentDataNoEffect( char segmentData )
  142. {
  143.         // 일반적인 변동 상황
  144.         PORT_SEG = SEGOUT(segmentData); // Invert for common Anode LED
  145.        
  146. }


GearPositionIndicator8535L.c

  1. /*
  2.   ATMega 8535L 을 이용한 수동기어 인디케이터
  3.  
  4.   기존 ATMEGA128에서 구현한 기능 + PWM 출력을 조절하여 표시 변동시에 부드러운 밝기 변동 처리가 목적
  5.  
  6.   Port A: 7 Segment 출력
  7.   Port C: 포지션 센서 입력
  8.   Port D: PWM 출력을 이용하여 TR을 거쳐서 7 Segment 밝기 조절 처리용으로 사용
  9.  
  10.  
  11.   이전과 같이 Loop의 단순 timing으로 효과 극대화가 곤란
  12.   새로운 구현에서는 밝기 조절은 Timer1을 이용하여 diming 처리
  13.   이어 포지션 변경으 아래와 같은 시나리오롤 처리하자.
  14.  
  15.  
  16.  
  17. */
  18.  
  19.  
  20. #include <inttypes.h>
  21. #include <avr/io.h>
  22. #include <avr/interrupt.h>
  23. #include <limits.h>
  24. #include "iocompat.h"
  25. #include "GearPositionIndicatorLib.h"
  26.  
  27. #define SIZEOF(x) (sizeof(x)/sizeof(x[0]))
  28. #define TIMER0_PWM_RATIO_MAX    4096
  29.  
  30. typedef enum
  31. {
  32.         DIMMING_OFF,
  33.         DIMMING_UP,
  34.         DIMMING_DOWN,
  35.         DIMMING_FLASH,
  36.         DIMMING_CHANGE_ALARM,
  37.         DIMMING_DOWN_FAST,
  38.         _DIMMING_FINISH,
  39.  
  40. } LedDimmingMode;
  41.  
  42.  
  43. static unsigned char LastPinInput = 0xff;               // 이전 입력값
  44. // LED brightness control variables
  45. static char currentDimmingMode = DIMMING_UP;
  46. unsigned char dimmingIndex = 0;
  47. short int brightIndex = 0;              // [0..1] 0 for full power brightness / 1 for >> 1 == /2
  48.  
  49.  
  50. /*
  51.   TCNT1 == Timer/Counter Register (현재 count 값 저장)
  52.                 0되었을 경우 BOTTOM 신호 발생
  53.                 0xFFFF 되었거나 OCR1A/B, ICR1 에 설정된 값에 도달하면 TOP 신호 발생
  54.                   --> BOTTOM / TOP 신호에 의해 카운트를 업/다운 혹은 리셋으로 처리
  55.  
  56.  
  57.   TCCR1AB == Timer/Counter Control Register
  58.  
  59.  
  60.   ICR1 == Input Capture Register
  61.                 입력캡쳐핀 ICx(타이머1의 경우에는 아날로그 비교기의 출력신호도 가능)
  62.                 으로 들어오는 신호변화를 검출하여 일어나는 입력캡쳐시 TCNT1의 카운터 값을 저장하는 16비트 레지스터로
  63.                 이때 ICFx 플랙이 세트되고 입력캡쳐인터럽트가 요청된다. 어떤 신호의 주기 측정에 응용될 수 있다.
  64.  
  65.  
  66.   OCR1AB == Output Compare Register
  67.                 TCNT1 값과 출력 비교되기 위한 16bit data값 저장 / TCNT와 같을경우 OCF 플랙설정, 출력비교 인터럽터 요청,
  68.                 출력핀 OC1A/B 단자로 데이타 출력
  69.  
  70.  
  71.   ICP1 == Input Capture Pin 1: PD6 pin for Timer/Counter1
  72.  
  73.                
  74.   OC1A == 출력핀
  75.   OC1B == 출력핀
  76.  
  77. */
  78.  
  79.  
  80. /*******************************************************************************
  81.  Timer 1 Duty cycle 을 조절하여 LED 발기값을 조절 한다
  82.  [0..256] 받아서, 추가 곱하기 scaling 하여 처리하자
  83. *******************************************************************************/
  84. void SetLedBrightness( int brightness )
  85. {
  86.         brightness = 0xFF - ((brightness & 0xFF)>> brightIndex);
  87.         OCR1A = brightness << 4 | 0x0f;
  88. }
  89.  
  90.  
  91. /*******************************************************************************
  92.  INT0 (PORT D0) - 스위치 입력 처리
  93. *******************************************************************************/
  94. ISR( INT0_vect , ISR_BLOCK )
  95. {
  96. /*      if ( 0 == brightIndex )
  97.                 brightIndex = 1;
  98.         else
  99.                 brightIndex = 0;
  100. */
  101.         if ( ++brightIndex > 2 )
  102.                 brightIndex = 0;
  103.  
  104.         LastPinInput = 0xff;    // 강제로 이전 pin값을 무효화 하여 기어 위치 재출력 로직타게 한다.
  105. }
  106.  
  107.  
  108. /*******************************************************************************
  109.  Timer 0 - 기어 포지션 센서 값 확인 핸들러 주기 설정
  110.  
  111. *******************************************************************************/
  112. void Timer0_Init(void)
  113. {
  114.         // 아래 두 문장은 직접 관련 없는 사항
  115.         DDRB = 0x08;    // Enable output for OC0(PB3)  
  116.         PORTD = 0x00;   // Clear
  117.  
  118.         // Configure Timer/Counter Control Register for 0
  119.         // Fast PWM mode with Clear OC0 on Compare Match, set OC0 at TOP / Clock to No prescaling -> /64 divide
  120.         //TCCR0 = (1<<WGM01) | ( 1<<WGM00) | ( 1<<COM01) | (1<<CS01) | (1<<CS00);
  121.  
  122.         // Normal mode for event looping logic
  123.         // 8MHz 0.125us == 0.000125 ms
  124.         // to /256 then 256*256(count) =
  125.         // to /1024 then 1024*256(count) = 0.000125 * 1024*256 = 32.768 ms = [ 0.032 sec ] * 31 = 0.992 sec ! (31 회 핸들링 시 마다 1초에 가까움)
  126.         //
  127.         // TCCR0 = 0x04;        /256 divide
  128.         //       = 0x05;        /1024 divide
  129.  
  130.         TCCR0 = 0x05;   // Use /1024 divide
  131.         TIMSK = TIMSK | (1<<OCIE0);     // OCR0 match 시에 match intrrupt 발생!
  132.  
  133.         // Output Compare Register - OCR0
  134.         OCR0 = 255;     //126;  //255;  // It's 8 bit!(Max 255)
  135. }
  136.  
  137.  
  138. /*******************************************************************************
  139.  
  140.  확실한 dimming 처리를 위해서는 Timer 1의 16bit 해상도를 사용해야 완전 어두움을 구현 가능!
  141.  밝기 조절은 Timer1 사용
  142. *******************************************************************************/
  143. void Timer1_PWM_Init(void)
  144. {
  145.         //--------------------------------------------------------------------------
  146.         // PortD Pin 4 == OC1B / Pin 5 == OC1A  ==> 00110000 == 0x30
  147.         DDRD = 0x30;    // Enable output for OC1A|OC1B         
  148.         PORTD = 0x00;   // Clear
  149.        
  150.         //--------------------------------------------------------------------------
  151.         // Set TCCR1A/B == Timer/Counter Control Register
  152.         // Fast PWM Mode --> Need WGM13, WGM12, WGM11
  153.         // No prescaling --> CS10
  154.         // /8 prescaling --> CS11
  155.         // Clear OC1A/OC1B on Compare Match, set OC1A/OC1B at BOTTOM
  156.         // Non-Inverting Mode
  157.         TCCR1A = (1<<COM1A1)|(1<<COM1B1)|(1<<WGM11);
  158.         //TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS10);
  159.         TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11);
  160.        
  161.         // ICR1, TOP Value!
  162.         // Crystal = 4MHz, 0.25us
  163.         // //0.25us * 40000 = 10msec, Frequency = 100Hz
  164.         // ICR1 = 39999;        //40000 - 1
  165.         // //Duty Cycle = 50%, 40000/2
  166.         // OCR1A = 19999;       //20000 - 1
  167.         // //Duty Cycle = 25%, 40000/4
  168.         // OCR1B = 9999;        //10000 - 1
  169.  
  170.  
  171.         //--------------------------------------------------------------------------
  172.         // Crystal = 8MHz -> 0.125us (0.000000125 sec / 0.000125 ms)
  173.         // For 240Hz need 0.0042 ms  / 80000 for 100Hz, 20000 for 400Hz
  174.         //ICR1 = 16384-1;       // 0x4000 2^15  -->> 이 값을 높여 버리면! - 전체적인 밝기 조절이 가능!!!!!!!!!!!!!!!!!!!
  175.         ICR1 = TIMER0_PWM_RATIO_MAX - 1;        // TIMER0_PWM_RATIO_MAX = 4096
  176.         OCR1A = 0;      //TIMER0_PWM_RATIO_MAX - 1;     // OCR1A PWM MAX for dimming, 0 for Max Brightness
  177.         OCR1B = TIMER0_PWM_RATIO_MAX - 1;
  178. }
  179.  
  180.  
  181. /*******************************************************************************
  182. *******************************************************************************/
  183. void Timer1_PWM_Init2(void)
  184. {
  185. /*
  186.         DDRB = 0xff;
  187.         PORTB = ~0x08;
  188. //      PORTB = 0x00;
  189.  
  190.         TCCR1A = TIMER1_PWM_INIT;
  191.         TCCR1B = TIMER1_CLOCKSOURCE;
  192.  
  193.         OCR0 = 0;
  194. */
  195. }
  196.  
  197.  
  198. /*******************************************************************************
  199.  
  200. *******************************************************************************/
  201. void Timer2_Init(void)
  202. {
  203.         // Normal mode for event looping logic
  204.         // 8MHz 0.125us to /1024 then 1024*88(count) = 11Hz
  205.         TCCR2 = 0x07;   // /1024  [CS2,CS1,CS0] = 111
  206.         TIMSK = TIMSK | (1<<OCIE2);     // OCR2 match 시에 match intrrupt 발생!
  207.  
  208.         // Output Compare Register - OCR0
  209.         OCR2 = 255;     // COMP 핸들러에서 TCNT2를 0 으로 초기화하여 fine tunning 값으로 사용
  210. }
  211.  
  212.  
  213. /*******************************************************************************
  214.  Interrupt vector for Timer 0 overflow signal
  215.  
  216. *******************************************************************************/
  217. /*ISR( TIMER0_OVF_vect, ISR_BLOCK )
  218. {
  219.         // 아직 활성화 하지 않음.
  220. }*/
  221.  
  222.  
  223. //******************************************************************************
  224. // getTransmissionValue로 부터 받아진 결과값을 입력하면 포트출력 값을 돌려준다
  225. //******************************************************************************
  226. void SetLedDimming( LedDimmingMode mode )
  227. {
  228.         currentDimmingMode = mode;
  229.         dimmingIndex = 0;
  230.         // 위 설정값은 TIMER2_COMP_vect 에서 처리함.
  231. }
  232.  
  233.  
  234. //******************************************************************************
  235. // Gear위치를 LED로 출력
  236. //******************************************************************************
  237. void SetLedDisplayChar( char gearPosition )
  238. {
  239.         displaySegmentDataNoEffect( getSegmentData( gearPosition ) );
  240. }
  241.  
  242.  
  243. /*******************************************************************************
  244.  Control LED dimming effects
  245.  LED 밝기값을 시나리오별로 조절하는 역할을 한다.
  246.  
  247.  현재 대충~ 30 회당 1초로 확인 됨. // 100회당 3.3 초
  248.  --> 10회당 1초로 할까?  --> 이게 만만해 보임 좀 버번 거리게 보일 수도...
  249.  --> 100회당 1초로 할까? --> 100Hz > 좀 부하가 있을 느낌
  250. *******************************************************************************/
  251. //typedef struct
  252. //{
  253. //      unsigned char   index;                  // 0 then stop the effect progressing
  254. //      unsigned char   brightness;             // -1 then restart the effect - it reset dimmingIndex
  255. //} indexToBrightness;
  256.  
  257. ISR( TIMER2_COMP_vect, ISR_BLOCK )
  258. {
  259.         const int                       // 1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20
  260.                 dimmingUpTbl[] =        { 20,  40,  60,  80, 100, 150, 200, 255, 255, 255,  -1,  -1 };
  261.         const int
  262.                 dimmingDownTbl[] =      {200, 200, 190, 180, 170, 160, 150, 140, 130, 120, 110, 100,  90,  80, 75,  70,  65,  60,  55,  50,
  263.                                                           45,  40,  35,  30,  27,  24,  21,  19,  18,  17,  16, -1,  -1 };
  264.         const int
  265.                 dimmingFlashbl[] =      {255, 255, 200, 160, 100,  30,   0,   0, 120, 250,  -2 , -2 };
  266.         const int
  267.                 dimmingChangeAlarmTbl[] =
  268.                                                         {255,   0, 255,   0, 255, 200, 150, 100,  50, 100, 150,200, 250, 200, 170, 200, 255,  -1,  -1 };
  269.         const int
  270.                 dimmingDownFastTbl[] =
  271.                                                         {100, 100, 180, 255, 200, 150, 100,  70,  50,  30,  20, 10,  0,   -1,  -1 };
  272. //      static unsigned char  flash = 0;                // For debugging
  273.         static unsigned char  ISRdelay = 0;
  274.         int     brightness;
  275.  
  276.         ++ISRdelay;
  277.         if ( ISRdelay < 3 )
  278.                 return;
  279.         else
  280.                 ISRdelay = 0;
  281.  
  282.  
  283.         TCNT2 = 0;      // Timer reset for resolution tunning
  284.  
  285.         //__________ 디버깅용 LED 깜빡임
  286. //      ++flash;
  287. //      if ( 5 == flash )
  288. //              PORTB = PORTB | 0x01;
  289. //      else if ( 10 == flash )
  290. //      {
  291. //              PORTB = PORTB & ~0x01;
  292. //              flash = 0;
  293. //      }
  294.         //^^^^^^^^^^
  295.  
  296.         brightness = 0;
  297.         switch ( currentDimmingMode )
  298.         {
  299.         case DIMMING_OFF:               // Just turn off the LED
  300.                 SetLedBrightness( 0 );
  301.                 currentDimmingMode = _DIMMING_FINISH;
  302.                 dimmingIndex = 0;
  303.                 return;
  304.                 break;
  305.  
  306.         case DIMMING_UP:
  307.                 brightness = dimmingUpTbl[dimmingIndex];
  308.                 if ( -1 == brightness )                                         // -1 to finish effect scenario
  309.                 {
  310.                         currentDimmingMode = _DIMMING_FINISH;
  311.                         dimmingIndex = 0;
  312.                         return;
  313.                 }
  314.                 break;
  315.  
  316.         case DIMMING_DOWN:
  317.                 brightness = dimmingDownTbl[dimmingIndex];
  318.                 if ( -1 == brightness )                                         // -1 to finish effect scenario
  319.                 {
  320.                         currentDimmingMode = _DIMMING_FINISH;
  321.                         dimmingIndex = 0;
  322.                         return;
  323.                 }
  324.                 break;
  325.  
  326.         case DIMMING_FLASH:
  327.                 brightness = dimmingFlashbl[dimmingIndex];
  328.                 if ( -2 == brightness )                                         // -2 to repeat the effect
  329.                 {
  330.                         dimmingIndex = 0;
  331.                         return;
  332.                 }
  333.                 break;
  334.  
  335.         case DIMMING_CHANGE_ALARM:
  336.                 brightness = dimmingChangeAlarmTbl[dimmingIndex];
  337.                 if ( -1 == brightness )                                         // -1 to finish effect scenario
  338.                 {
  339.                         currentDimmingMode = _DIMMING_FINISH;
  340.                         dimmingIndex = 0;
  341.                         return;
  342.                 }
  343.                 break;
  344.  
  345.         case DIMMING_DOWN_FAST:
  346.                 brightness = dimmingDownFastTbl[dimmingIndex];
  347.                 if ( -1 == brightness )                                         // -1 to finish effect scenario
  348.                 {
  349.                         currentDimmingMode = _DIMMING_FINISH;
  350.                         dimmingIndex = 0;
  351.                         return;
  352.                 }
  353.                 break;
  354.  
  355.         case _DIMMING_FINISH:
  356.                 currentDimmingMode = _DIMMING_FINISH;
  357.                 return; // 더 이상 counting 하지도, 동작 수정 안함.
  358.                 break;
  359.         }
  360.  
  361.         SetLedBrightness( brightness );
  362.         ++dimmingIndex;
  363. }
  364.  
  365.  
  366. /*******************************************************************************
  367.  
  368. *******************************************************************************/
  369.  
  370.  
  371. /*******************************************************************************
  372.  Interrupt vector for Timer 0 compare matching
  373.  기어 센서 상태값을 확인하고, 전환 시나리오별로 처리 한다.
  374. *******************************************************************************/
  375. #define TIMEOUT_TO_1SEC (31)            // 30은 1분 이하로 오차 발생, 31이 좀더 근접하나 상세 TUNING을       tcnt0 를 통할 필요 있음
  376.  
  377. ISR( TIMER0_COMP_vect, ISR_BLOCK )
  378. {
  379.         //__________ 디버깅용 LED 깜빡임
  380.         {
  381.                 static char  flash = 0;
  382.                 if ( ++flash > TIMEOUT_TO_1SEC )
  383.                 {
  384.                         flash = 0;
  385.                         PORTB ^= 0x01;
  386.                 }
  387.         }
  388.         //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  389.  
  390.  
  391.  
  392.         // Timer 해상도를 좁히기 위해 OCR0 값을 설정하고, 이 code를 활성화 하고 값을 높이면 timer 분주를 빨리함 (시작 카운트 값 설정)
  393.         TCNT0 = 3;
  394.         {
  395.                 static char LastGearPosition = '-';                                     // 이전 기어 상태 (LastPinInput으로 부터 변환된 값)
  396.                 static unsigned long int InputChangeTimeCount = 0;      // 기어 변속 이후 경과시간 - 0: Timer 중지, 1~ Counting 계속
  397.                 static unsigned long int IdleTimeCount = 0;                     // Idle time 경과시간 - 0: Timer 중지, 1~ Counting 계속
  398.  
  399.  
  400.                 unsigned char NewPinInput = ~PINC & 0x7f;                       // 현재 Pin 값 받기
  401.                 unsigned char NewGearPosition = GetGearPosition( NewPinInput ); // Gear 위치값으로 환산
  402.  
  403.                 if ( LastPinInput != NewPinInput )              // 센서 입력 변동?
  404.                 {//----- 센서 변동 -----------------------------------------------------
  405.                         LastPinInput = NewPinInput;                     // 이전값 갱신
  406.                         InputChangeTimeCount = 1;                       // 변동 시간 count 시작
  407.                         IdleTimeCount = 0;                                      // Idle time count 중지
  408.  
  409.                         switch ( NewGearPosition )
  410.                         {
  411.                                 case '-' :      // 기어 전환중, 중립상태 시나리오
  412.                                 case 'N' :      // 다른 기어로 전환이 곧 될 수 있기에 Dimming처리고 어둡게 함
  413.                                         // 0~6 에서
  414.                                         if ( '-' != LastGearPosition )                  // 이전엔 기어위치였으나, 전환 하려는 경우임
  415.                                         {
  416.                                                 SetLedDimming( DIMMING_DOWN );          // 천천히 어두워 지게 함
  417.                                                 LastGearPosition = NewGearPosition;     // 기어값만 갱신
  418.                                                                                                                         // 변동 시간 count 계속 동작하게 둠. 0 상태에서 에니메이션 시나리오 가능케 함
  419.                                                                                                                         // LED 표시는 수정하지 않음
  420.                                         }
  421.                                         break;
  422.  
  423.                                 case 'E' :      // 입력 에러E / 후진(B)시는 즉각적으로, 또한 깜빡임 처리
  424.                                 case 'B' :
  425.                                         LastGearPosition = NewGearPosition;             // 마지막 기어 위치값 갱신
  426.                                         SetLedDisplayChar( NewGearPosition );   // LED 표시 갱신
  427.                                         InputChangeTimeCount = 0;                               // 변동 시간 count 중지 -> 무변동 상테에서 시나리오 멈춤
  428.                                         SetLedDimming( DIMMING_FLASH );                 // 반복적인 깜빡임
  429.                                         break;
  430.  
  431.                                 default:        // 나머지 기어값인 1 ~ 6 의 경우 기어 위치 확정 표시
  432.                                         LastGearPosition = NewGearPosition;             // 마지막 기어 위치값 갱신
  433.                                         SetLedDisplayChar( NewGearPosition );   // LED 표시 갱신
  434.                                         InputChangeTimeCount = 0;                               // 변동 시간 count 중지 -> 무변동 상테에서 시나리오 멈춤
  435.                                         SetLedDimming( DIMMING_CHANGE_ALARM );  // 깜빡이면서 밝게 표시
  436.                                         break;
  437.                         }
  438.                 }
  439.                 else
  440.                 {//----- 변화가 없는 경우 ----------------------------------------------
  441.                         // '-' 혹은 '0' 가 계속 유지 되고 있다면 특정 시간 이후에 '-' / '0'을 표시한다
  442.  
  443.                         //----- 기어 변경 후 일정 시간 지남 확인 카우트 동작 진행
  444.                         if ( InputChangeTimeCount )
  445.                         {
  446.                                 // 0 표시는 즉각적으로 갱신하지 않고, 1초간 대기 후 갱신한다.
  447.                                 // 왜냐면 기어 변속중에 항상 0 위치를 거치게 되므로 잦은 갱신을 하지 않아 자연스럽게 다음 기어를 표시하게 된다.
  448.  
  449.                                 //----- N(중립) 에서 1초 초과시에 진짜 N(0)으로 표시 갱신
  450.                                 if ( ('N' == NewGearPosition) && (InputChangeTimeCount > 1*TIMEOUT_TO_1SEC) )
  451.                                 {
  452.                                         InputChangeTimeCount = 0;                               // 이후 Timer 작동 중지
  453.                                         IdleTimeCount = 1;                                              // Idle time count 시작 -> Idle animation 처리 용
  454.                                         LastGearPosition = NewGearPosition;             // 기어 포지션 갱신
  455.                                         SetLedDisplayChar( NewGearPosition );   // LED 표시 갱신
  456.                                         SetLedDimming( DIMMING_CHANGE_ALARM );  // 깜빡이면서 밝게 표시
  457.                                 }
  458.                                 //----- 기어 이동중(기어 물림 위치가 아닌경우)이라면 3초간 기존 상태 유지(위에서 dimming down) 후, 새값으로 갱신
  459.                                 else if ( ('-' == NewGearPosition) && ( InputChangeTimeCount > 3*TIMEOUT_TO_1SEC ))
  460.                                 {
  461.                                         InputChangeTimeCount = 0;                               // 이후 Timer 작동 중지
  462.                                         //IdleTimeCount = 1;                                    // Idle time count 시작 X
  463.                                         LastGearPosition = NewGearPosition;             // 기어 포지션 갱신
  464.                                         SetLedDisplayChar( NewGearPosition );
  465.                                         SetLedDimming( DIMMING_UP );                    // 천천히 밝게 표시
  466.                                 }
  467.                                 else
  468.                                 {
  469.                                         if ( ++InputChangeTimeCount >= ULONG_MAX )      // Counting
  470.                                                 InputChangeTimeCount = 0;                       // Overflow 시에 더이상 이어변동 후 타이머 동작은 작동 안함.
  471.                                 }
  472.                         }
  473.                         //----- 이어 변경 직후 타이밍 동작 완료 후, Idling 상태에서 0 위치에서 animation 표시 등 부가 동작 수행
  474.                         else
  475.                         if ( IdleTimeCount && 'N' == NewGearPosition )
  476.                         {       // 0 상태에서 장시간 대기중일 경우 Rotation animation 처리하기~
  477.                                 const char segmentAnimationData[] = {           // 오른쪽으로 회전하는 에니메이션에 해당한 segment LED 출력 bit
  478.                                         0b01000000,     // 0b01000100,
  479.                                         0b00100000,     // 0b00100010,
  480.                                         0b00010000,     // 0b00010001,
  481.                                         0b00000100,     // 0b01000100,
  482.                                         0b00000010,     // 0b00100010,
  483.                                         0b00000001,     // 0b00010001,
  484.                                 };
  485.  
  486.  
  487.                                 //----- 30초 초과시 부터 idle animation시작
  488.                                 if ( IdleTimeCount >= 30*TIMEOUT_TO_1SEC )
  489.                                 {
  490.                                         static unsigned char prevAniIndex = 6;
  491.                                         char bDisplayIdleMinTime = 0;
  492.                                         unsigned char aniIndex = ( (IdleTimeCount - 1 ) / TIMEOUT_TO_1SEC ) % 6;
  493.                                         unsigned char i;
  494.  
  495.  
  496.                                         //----- {{110723 lsw2000}} 1분, 2분, 3분, 최고 6분 까지에 대해 Idle 경과 시간 표시! / 웜업, 후열용 참고 시간 표시
  497.                                         if ( IdleTimeCount < (6*60 +10) *TIMEOUT_TO_1SEC )
  498.                                         {
  499.                                                 for ( i=1; i<=6; i++ )
  500.                                                         if ( i*60*TIMEOUT_TO_1SEC <= IdleTimeCount &&IdleTimeCount < (i*60 +(4+i)) *TIMEOUT_TO_1SEC )          // 최초 4초부터 1초식 증가하여 표시
  501.                                                         {
  502.                                                                 if ( i*60*TIMEOUT_TO_1SEC == IdleTimeCount )    // 숫자 전화, 효과 설정은 단 1회만, 이후 자동
  503.                                                                 {
  504.                                                                         SetLedDisplayChar( '0' + i );                           //
  505.                                                                         SetLedDimming( DIMMING_FLASH );                         // 반복적인 깜빡임
  506.                                                                 }
  507.                                                                 bDisplayIdleMinTime = 1;
  508.                                                         }
  509.                                         }
  510.                                         else
  511.                                         //----- 7분 경과부터 매 1분 마다 0 표기 깜빡이기
  512.                                         if ( (7*60) *TIMEOUT_TO_1SEC <= IdleTimeCount )
  513.                                         {
  514.                                                 if ( IdleTimeCount % (60*TIMEOUT_TO_1SEC) < (5*TIMEOUT_TO_1SEC) )      // 매붖 최초 5초 동안
  515.                                                 {
  516.                                                         if ( 0 == IdleTimeCount % (60*TIMEOUT_TO_1SEC) )
  517.                                                         {
  518.                                                                 SetLedDisplayChar( 'N' );
  519.                                                                 SetLedDimming( DIMMING_FLASH );                         // 반복적인 깜빡임
  520.                                                         }
  521.                                                         bDisplayIdleMinTime = 1;
  522.                                                 }
  523.                                         }
  524.                                        
  525.                                         //----- 경과 분수 표시 타이밍이 아닌 경우 animation 표시
  526.                                         if ( !bDisplayIdleMinTime && ( prevAniIndex != aniIndex ) )
  527.                                         {
  528.                                                 prevAniIndex = aniIndex;
  529.                                                 PORT_SEG = ~segmentAnimationData[ aniIndex ];   // ~for Negative output
  530.                                                 SetLedDimming( DIMMING_DOWN_FAST );                             // 새 위치 표시 후 어두워지게 효과 처리
  531.                                         }
  532.                                 }
  533.  
  534.                                 if ( ++IdleTimeCount >= ULONG_MAX )                             // Overflow 대비, animation 계속하게 함.
  535.                                         IdleTimeCount = 30*TIMEOUT_TO_1SEC;
  536.                         }
  537.                         // TODO: 기어 상태에서 장시간 있을 경우 살짝 깜빡임성 표시로 환기 시키기
  538.                 }
  539.  
  540.         }
  541.  
  542.  
  543. }
  544.  
  545. /*******************************************************************************
  546.  Main enterance
  547.  
  548. *******************************************************************************/
  549. int main( void )
  550. {
  551.         cli();
  552.  
  553.         Timer0_Init();          // 노브 조작 감지(센서 감지 루프)
  554.         Timer1_PWM_Init();      // PWM ouput 조작
  555.         Timer2_Init();          // 깜빡임, 밝기 시나리오 흐름
  556.  
  557.         //----- INT0 ----------
  558.         GICR |= 1<<INT0;        // INT0 enable
  559.         MCUCR = 2;                      // 하강엣지에 인트럽트 발생
  560.  
  561.         DDRA = 0xFF;            // PortA: 출력 / 7 Segment
  562.         DDRB = 0xFF;            // PortB: Timer and Debug signal
  563.         DDRC = 0x00;            // PortC: 입력 / 기어 포지션 센서
  564.         //DDRD = ~0x04;         // PortD2(INT0) 입력 활성화
  565.  
  566.         PORT_SEG = 0xFF;        // Common anode 이므로 1상태가 off 가 된다
  567.         PORTB = 0xFF;           // + 인가
  568.         PORTC = 0xFF;           // Input port에 1을 setting하여 Internal pull-up 저항을 활성화
  569.         PORTD = 0x04;           // PD2(INT0) Pull-up
  570.  
  571.  
  572.         sei();                          // 주 동작은 Timer handler에서 수행 됨.
  573.         // Timer0: Main loop
  574.         // Timer1: PWM power 조절
  575.         // Timer2: LED 깜빡인 control
  576.  
  577.         SetLedBrightness(10);   // First Full brightness.
  578.  
  579.         while ( 1 )
  580.         {
  581.  
  582.         }
  583.  
  584.         return 0;
  585. }



.



+ Recent posts