IR Decode NEC Protocol (WinAVR + ATmega8A)•ว่างอยู่1วันหลังจากส่งงานหมด พอมีเวลาเล่น HC-SR04 โดยใช้ input capture
ลองผิดๆถูกอยู่พักนึงบวกกับได้กระทู้นี้ช่วย เลยไปได้ไวหน่อย ขอบคุณป๋า samira ด้วยครับ
www.electoday.com/bbs/viewthread.php?tid=14149&highlight=%2Bsamirahttp://www.youtube.com/v/3ViTo0AsX9M•แต่ project นี้ไม่ได้เกี่ยวอะไรกับ HC-SR04 แต่มันเป็นความต่อเนื่อง บวกกับคันมือ
อยากลอง เลยไปหยิบ remote ในกองขยะมาได้ตัวนึง ซึ่งเป็น remote dvd จีน
ใช้ nec protocol ซึ่งรูปแบบของ ir nec นี้ถูกใช้อย่างมากในสินค้าจากจีน
แทบจะทุกอย่างเลยก็ว่าได้ โดย protocol มีรูปแบบและหน้าตาดังนี้
ข้อมูลประกอบไปด้วย
1.Address
2.Logical Inverse Address
3.Command
4.Logical Inverse Command
•การตรวจสอบข้อมูล Address และ Command จะใช้การ Inverse แล้วเทียบกับ Logical Inverse
เป็นการตรวจสอบแบบง่ายๆและเพิ่มความน่าเชื่อถือของข้อมูลที่ได้รับว่ามีความถูกต้อง
•แนวการถอดระหัสหรือ algorithm ผมด้นๆมั่วๆเอาเองนึกอะไรได้ก็เขียนมันส่ง อาจจะยังไม่ค่อยดีเท่าไร
ตอนนี้ยังรับ repeat pulse ไม่ได้ เอาไว้จะปรับปรุงให้ดีขึ้นใน version ต่อๆไป
•โดยผมแบ่ง state ออกเป็น 4 state ตามภาพ โดยจะสลับบิตICES1เพื่อให้เกิด interruptที่สัญญาณ
ขอบขาขึ้นและขอบขาลง ที่เหลือดูรายละเอียดจากcodeนะครับถ้าอธิบายสงสัยจะยืดยาว
•ตอน debug ส่งออก serial port เพื่อดู key code
•ต่อกันง่ายๆผ่าน arduino v3.3 มีled4ดวงเอาไว้ลอง command
//--------------------------------------------------
//Program :IR Decode (NEC Protocol)
//Author :Somlak Mangnimit
//Date :27/09/2012
//Mcu :ATmega8A
//Xtal :16Mhz
//Compiler :WinAVR 20100110
//Version :1.0
//Note :-
//--------------------------------------------------
#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#define BAUD_RATE 9600
static int usart_putc(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(usart_putc, NULL,_FDEV_SETUP_WRITE);
volatile unsigned char buffer[4],ir_state,bit_flag,bit_cnt,byte_cnt,ndat;
volatile unsigned long BIT_BUF;
enum{rem_state1,rem_state2,rem_state3,rem_state4};
#define ICCR_RISING() TCCR1B |= (1<<ICES1);
#define ICCR_FALLING() TCCR1B &= ~(1<<ICES1);
#define ICCR_ENABLE() TIMSK |= (1<<TICIE1);
#define ICCR_DISABLE() TIMSK &= ~(1<<TICIE1);
#define LED_ON() PORTB |= (1<<PB5);
#define LED_OFF() PORTB &= ~(1<<PB5);
#define Adr buffer[0]
#define AdrInv buffer[1]
#define Cmd buffer[2]
#define CmdInv buffer[3]
//timer
void initial(void){
TCCR1B |= (1<<CS12); //prescaler clk/256
TIMSK |= (1<<TICIE1); //input capture interrupt enable
sei(); //global interrupt enable
}
//put usart
static int usart_putc(char c , FILE *stream){
if(c == '\n')
usart_putc('\r', stream);
loop_until_bit_is_set(UCSRA, UDRE);
UDR = c;
return 0;
}
//usart initial
void usart_initial(void){
DDRD |= (1<<PD1);
UBRRH = (((F_CPU/BAUD_RATE)/16)-1)>>8; // set baud rate
UBRRL = (((F_CPU/BAUD_RATE)/16)-1);
UCSRB = (1<<RXEN)|(1<<TXEN); // enable Rx & Tx
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // config USART; 8N1
stdout = &mystdout;
}
//test logical inverse
unsigned char log_inv(unsigned char a,unsigned char b){
a ^= a;
if(a==b){return 0;}
else{return 1;}
}
//main
int main(void){
DDRB |= (1<<PB5)|(1<<PB3);
DDRC |= (1<<PC3)|(1<<PC2)|(1<<PC1)|(1<<PC0);
initial();
usart_initial();
LED_ON();
_delay_ms(300);
LED_OFF();
for(;;){
ndat = 0;
while(!ndat);
if(log_inv(Adr,AdrInv)&&log_inv(Cmd,CmdInv)){
switch(Cmd){
case 12:
PORTC ^= (1<<PC0);
break;
case 6:
PORTC ^= (1<<PC1);
break;
case 5:
PORTC ^= (1<<PC2);
break;
case 79:
PORTC ^= (1<<PC3);
break;
case 1:
PORTC &= ~(1<<PC3)&(1<<PC2)&(1<<PC1)&(1<<PC0);
//end case
}
}
printf("Address = %d\nLogical Inverse Address = %d\nCommand = %d\nLogical Inverse Command = %d\n\n",
buffer[0],buffer[1],buffer[2],buffer[3]);
}
}
//reset ir state
void RESET_IR(void){
ir_state = rem_state1;
bit_flag = 0;
byte_cnt = 0;
bit_cnt = 0;
ICCR_FALLING();
LED_OFF();
}
//isr input capture
ISR(TIMER1_CAPT_vect){
LED_ON();
switch(ir_state){
case rem_state1:
ir_state = rem_state2;
ICCR_RISING();
TCNT1 = 0;
break;
case rem_state2:
BIT_BUF = ICR1;
BIT_BUF = BIT_BUF*10000/625;
if(BIT_BUF>9000-200&&BIT_BUF<9000+200){
ir_state = rem_state3;}
else{RESET_IR();break;}
ICCR_FALLING();
TCNT1 = 0;
break;
case rem_state3:
BIT_BUF = ICR1;
BIT_BUF = BIT_BUF*10000/625;
if(BIT_BUF>4400-200&&BIT_BUF<4400+200){
ir_state = rem_state4;}
else{RESET_IR();break;}
ICCR_RISING();
break;
case rem_state4:
if(!bit_flag){
bit_flag = 1;
ICCR_FALLING();
TCNT1 = 0;
}else{
bit_flag = 0;
BIT_BUF = ICR1;
BIT_BUF = BIT_BUF*10000/625;
buffer[byte_cnt] >>= 1; //LSB
if(BIT_BUF>1500&&BIT_BUF<1700){
buffer[byte_cnt] |= 0x80;}
else if(BIT_BUF>300&&BIT_BUF<600){
buffer[byte_cnt] &= ~0x80;}
else{RESET_IR();break;}
ICCR_RISING();
}
if(++bit_cnt>15){
if(++byte_cnt>3){RESET_IR();ndat = 1;}
else{bit_cnt = 0;}
}
//end case
}
TIFR |= (1<<ICF1); //clear flag by write one
}