Learning IR remote decoder with ATmega8 / Обучаемый приёмник ИК-команд на ATmega8
Содержание статьи
ДобавлениеПри использовании в вашем проекте ножки SS как выхода, дальнейшее перепрограммирование становится невозможным. Для исправления этого, ножку SS (выв. 14) необходимо отсоединить от остальной части схемы. |
AddedWhen you use SS pin as output in your code (suddenly or intentionally), subsequent programming will not work anymore. To fix it, you may need to disconnect SS pin (pin 14) from rest of schematic. |
; 12 MHz xtal ; This program provides standalone IR remote learning and then decoding functionality. ; It works with common TSOP4838 or any other receiver. ; Program uses 1/16384 s time grain, to make it 'compatible' with LIRC databases when need. ; Software not stuck on any proprietary IR encoding system and should work with any IR protocol. ; However you should select proper receiver (38 / 56 kHz ... etc). Or even both in parallel. ; Code optimized for power consumption (uses sleep when awaiting for IR / button command). ; Ebay's USBASP v2.0 "LC Tech / LCSOFT" blue 44x19 mm board used as 'eval board' because ; it is cheapest possible ready-to-use board (cheaper than any other complete AVR board), ; and can be programmed using another same board. Note, for use as IR transmitter, ; you need to disconnect (cut) trace between pins 13 & 32, it is under IC body. ; Hardware also includes pushbutton (in parallel to TSOP4838, wired "OR" to save a pin ; and an interrupt). Connect it all to pin 32. ; Hardware also uses some loads like LED/relay(s). Connect load0 to pin 25, ; it connected to JP3 at LCSOFT USBASP board. ; Short pins 2 & 3 (short pin 2 to gnd) to set on/off mode switching of load0 by two different remote commands. ; Unconnect pin 2 to set toggle mode switching using one remote command. ; IR Transmitting also possible, and pin and HW Timer reserved and tested, but not done yet. ; The quantity of remembered commands is depend on EEPROM size and two commands can be used in ATmega8 ; (512 bytes of EEPROM). ; To program board, short (permamently) JP2, and cut wire #4 in 10-pin cable. ; Cutting wire not damages programming cable, it will work as before on other devices. ; Connect board to same but unmodified programmer board and use: ; sudo avrdude -p m8 -c usbasp -U flash:w:irlearn.hex ; disambiguition: not to confuse with OSCaR IR raw RX/TX interface. This projest is for standalone use. ; Author: jopka1@gmail.com, www.bdyssh.ru. License: GPL V2+ ; please see also http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=3944 .INCLUDE "m8def.inc" ; INVERTED .equ led0port = portc .equ led0ddr = ddrc .equ led0bit = 0 ; pin 23 (pc0) INVERTED ; INVERTED .equ led1port = portc .equ led1ddr = ddrc .equ led1bit = 1 ; pin 24 (pc1) INVERTED ; not use pin 14 (pb2) it is connected to reset ; pin 32 (pd2) it is connected to pb1 (pin 13) ; BUT need unconnect it! .equ usb0port = portb .equ usb0ddr = ddrb .equ usb0bit = 0 ; pin 12 (pb0) ;.equ usb1port = portb ;.equ usb1ddr = ddrb ;.equ usb1bit = 1 ; pin 13 (pb1) ; OR .equ usb1port = portd .equ usb1ddr = ddrd .equ usb1in = pind .equ usb1bit = 2 ; pin 32 (pd2 / INT0) ; UART / GPIO .equ uart0port = portd ; RX .equ uart0ddr = ddrd .equ uart0in = pind .equ uart0bit = 0 ; pin 30 (pd0) .equ uart1port = portd ; TX .equ uart1ddr = ddrd .equ uart1in = pind .equ uart1bit = 1 ; pin 31 (pd1) ; HW counter/PWM ; pin 32 (pd2) connected to pb1 (pin 13) ; BUT i unconnect it. .equ timer1port = portb .equ timer1ddr = ddrb .equ timer1bit = 1 ; pin 13 (pb1) .equ timer1in = pinb ; input of own pin - for test of end of pulse. ; mode jumpers ; main mode (mode0) .equ modesw0port = portd .equ modesw0ddr = ddrd .equ modesw0bit = 3 ; pin 1 (pd3) .equ modesw0in = pind ; mode1 .equ modesw1port = portd .equ modesw1ddr = ddrd .equ modesw1bit = 4 ; pin 2 (pd4) .equ modesw1in = pind ; load on/off outputs .equ load0port = portc .equ load0ddr = ddrc .equ load0bit = 2 ; pin 25 (pc2) connected to JP3 at LCSOFT USBASP board .equ load1port = portc .equ load1ddr = ddrc .equ load1bit = 3 ; pin 26 (pc3) .equ maxpktlen = 64 ; multiplied by 4 bytes. So 64 uses 256 (max) bytes of eeprom. (and RAM, too.) .equ eeprom_size_bytes = EEPROMEND + 1 .equ eeprom_size_packets = eeprom_size_bytes / maxpktlen / 4 .equ cmd_qty = eeprom_size_packets ; 2 for atmega8, we hope. ; timeout - q'ty of 256 times of successive '1's treated as packet end. (minimum value = 1). ; big timeout eases debugging, but treat autorepeat command of some remotes (while button hold) as part of command. ; (use short remote button press to avoid). ; too short timeout will break packet before packet end on slow remotes (with low BPS). ;.equ timeout = 16 ; 64 = 1 s, 16 = 1/4 s. .equ timeout = 2 ; to avoid 'repeat' commands concatenate. .dseg buf: .byte 512 stack: .byte 512 .cseg rjmp RESET ; Reset Handler rjmp EXT_INT0 ; IRQ0 Handler rjmp EXT_INT1 ; IRQ1 Handler rjmp TIM2_COMP ; Timer2 Compare Handler rjmp TIM2_OVF ; Timer2 Overflow Handler rjmp TIM1_CAPT ; Timer1 Capture Handler rjmp TIM1_COMPA ; Timer1 CompareA Handler rjmp TIM1_COMPB ; Timer1 CompareB Handler rjmp TIM1_OVF ; Timer1 Overflow Handler rjmp TIM0_OVF ; Timer0 Overflow Handler rjmp SPI_STC ; SPI Transfer Complete Handler rjmp USART_RXC ; USART RX Complete Handler rjmp USART_UDRE ; UDR Empty Handler rjmp USART_TXC ; USART TX Complete Handler rjmp ADC ; ADC Conversion Complete Handler rjmp EE_RDY ; EEPROM Ready Handler rjmp ANA_COMP ; Analog Comparator Handler rjmp TWSI ; Two-wire Serial Interface Handler rjmp SPM_RDY ; Store Program Memory Ready Handler EXT_INT1: TIM2_COMP: TIM2_OVF: TIM1_CAPT: TIM1_COMPA: TIM1_COMPB: TIM1_OVF: TIM0_OVF: SPI_STC: USART_RXC: USART_UDRE: USART_TXC: ADC: EE_RDY: ANA_COMP: TWSI: SPM_RDY: rjmp kernel_panic rjmp kernel_panic rjmp kernel_panic ; reti RESET: ldi r16, high(RAMEND) out SPH, r16 ldi r16, low(RAMEND) out SPL, r16 ldi r16, 0 ; pullup out ddrb, r16 ldi r16, 255 out portb, r16 ldi r16, 0 ; pullup out ddrc, r16 ldi r16, 255 out portc, r16 ldi r16, 0 ; pullup out ddrd, r16 ldi r16, 255 out portd, r16 ; mode pin(s) init cbi modesw0ddr, modesw0bit ; in sbi modesw0port, modesw0bit ; pullup cbi modesw1ddr, modesw1bit ; in sbi modesw1port, modesw1bit ; pullup ; timer1 init ; dont forget, timer stops when sleep! sbi timer1ddr, timer1bit ; r17=10, r16=0 - 2325 Hz (measured) : 12000000 / 2 / 2560 = 2343 Hz (theory) ; so 38 kHz is r17=0, r16=158 ; ldi r16, 158 ; (lo x) ; ldi r17, 0 ; (hi x) ; try to get 38 khz ldi r16, 167 ; (lo x) ldi r17, 0 ; (hi x) ; try to get 36 khz out ocr1ah, r17 out ocr1al, r16 rcall timer1_off ; the difference of full power down and standby is only 0.1 mA. ; it is very small compared to TSOP4838 current (3.3 mA). ; And after full power-down, decodind works bad for low (3,3V) power. ; Current draw in standby mode only 3,4 mA in total, for 3,3-3,6 V supply. ldi r16, 0b11100000 ; sleep enable, standby (xtal running) ; ldi r16, 0b10100000 ; sleep enable, full power down out MCUCR, r16 ldi r16, 0b01000000 ; INT0 low level out GICR, r16 ; 03/16/2014: Try to use two different modes. Selected using pin 1. ; Pin 1 unconnected: using standalone mode. ; Main task is to learning, receive, and decode command(s) and switch relay(s). ; Optionally, IR transmitting is possible and tested, but no good way to ; start transmitting in sleep mode. So if one need to transmit, it is currently done ; incompatible with sleep/learn/receive/decode, it is done with polling of pins pd0, pd1, ; to use this, one need to: ; 1. learn to some commands. ; 2. make EEPROM not clear while Flash programming, using fuse bytes. ; 3. hardly switch this code to use transmitting, recompile, program Flash. ; Pin 1 tied to GND: using host mode with rs232 RX/TX. In this mode, ; learning not possible. This mode transmit IR packet received via rs232, ; and receive and decode IR packet for transmitting it into rs232 immediately or by ; host request (depends on other mode pin). ; In this mode, not only flat (raw), but encoded NEC, RC5 and Sony autodetect/send/receive used. ; (but RC5 and SONY are under developement due to no such handsets i have). ; EEPROM not used in this mode. ; this mode is part of OSCaR universal IR daughterboard module, but quite good for ; any other DIY project. ; i make my pinout for OSCaR module compatible with USBASP boards, to eliminate any ; programmer's hair tearing during developement, according to KISS rule. sbic modesw0in, modesw0bit rjmp standalone_learning_device ; serial mode. USART_Init: ; ldi r16, low(77) ; baud rate divider: 12.0MHz, 9600bps ; ldi r17, high(77) ; 9600 bps is too slow for transmit between end of IR packet and start ; of IR autorepeat command for NEC remotes. ldi r16, low(19) ; baud rate divider: 12.0MHz, 38400bps ldi r17, high(19) out UBRRH, r17 out UBRRL, r16 ldi r16, (1< <RXEN)|(1<<TXEN) out UCSRB,r16 ldi r16, (1<<URSEL)|(3<<UCSZ0) out UCSRC,r16 rjmp oj ; echo test oj1: sbis UCSRA, RXC ; Wait for data to be received rjmp oj1 in r16, UDR ; rcall USART_Transmit ; echo back rcall USART_Transmit ; echo back rjmp oj1 ; fail-safe trap rjmp kernel_panic oj: standalone_learning_device: ; remove this for transmitter: rjmp standalone_receive_decode ; test part of code for planned ir transmitter. Only experimental use use. ; test button btn: rcall bounce_delay sbis uart0in, uart0bit rjmp pressed1 sbis uart1in, uart1bit rjmp pressed2 ;unpressed: ; rcall bounce_delay ; sbic uart1in, uart1bit ; wait for press ; rjmp unpressed rjmp btn pressed1: ldi r21, 0 ; cmd num rcall transmit rjmp btn_done pressed2: ldi r21, 1 ; cmd num rcall transmit rjmp btn_done btn_done: nop rcall delay_025s rjmp btn rjmp kernel_panic timer1_on: ldi r16, 0b01000000 out TCCR1A, r16 ldi r16, 0b00001001 ; div by 1 out TCCR1B, r16 ret timer1_off: ; wait for end of '1' pulse ;wt: ; sbic timer1pin, timer1bit ; sometimes hang... ; rjmp wt ;timer1_off_2: ; ldi r16, 0b00001000 ; out TCCR1B, r16 ldi r16, 0b00000000 out TCCR1A, r16 ldi r16, 0b00000000 out TCCR1B, r16 cbi timer1port, timer1bit ; not work. - work now. ret ; here r21 = number of command transmit: ; ldi r28, low(buf) ; RAM ; ldi r29, high(buf) ldi r30, 0 ; eeprom pkt start address ldi r31, 0 .if maxpktlen=64 mov r31, r21 ; read offset in eeprom for command .else raise_compiler_error_should_be_modify_here_to_correct_calculate_shift_for_r31. .endif ldi r22, 0 tx0: rcall timer1_on rcall eep_read mov r24, r16 rcall eep_read mov r25, r16 cpi r25, 255 ; eeprom probably empty - FF'ed ; breq kernel_panic brne pulse_txon rjmp kernel_panic ; breq not_like pulse_txon: rcall ir_delay ; 1 / 16384 s. standard LIRC grain sbiw r24, 1 cpi r24, 0 brne pulse_txon cpi r25, 0 brne pulse_txon rcall timer1_off rcall eep_read mov r24, r16 rcall eep_read mov r25, r16 cpi r25, timeout ; end of stored packet reached breq tx_done pulse_txoff: rcall ir_delay ; 1 / 16384 s. standard LIRC grain sbiw r24, 1 cpi r24, 0 brne pulse_txoff cpi r25, 0 brne pulse_txoff inc r22 ; failsafe behaviour ; cpi r22, maxpktlen*2 ; here '1's and '0's both counted in r22 so double size. cpi r22, maxpktlen brne tx0 tx_done: ret standalone_receive_decode: ; try to share learning button pin and tsop4838 pin, ; due to it use interrupt and so IC can be sleep and don't poll button. ; r23 - mode. ; r23=0 - sleep/idle - not used? ; r23=1 - normal packet receive process, do nothing, wait for end ; r23=2 - end of pkt receive, must process received data. ; r23=3 - short 0.5...5 s button press ; r23=4 - long >5s press sbis modesw0in, modesw0bit rjmp mode_rs232 ; rx mode 0 - receive -> decode -> loads control ; rx mode 1 - receive -> store flat (raw) data in EEPROM (learning) ; rx mode 2 - receive -> send raw (flat) data to rs232 mode_self: ldi r16, 0 mov r15, r16 ; rx mode rjmp md mode_rs232: ldi r16, 2 mov r15, r16 ; rx mode md: rcall led0off rcall led1off ldi r24, 0 ; cmd number for next remember/learn main: ldi r28, low(buf) ; RAM ldi r29, high(buf) ldi r22, 0 ; pkt len. rx_cont_pkt: ldi r23, 1 sei sleep ; waiting for interrupts... cli cli cpi r23, 1 breq rx_cont_pkt cpi r23, 2 breq rx_pkt_done cpi r23, 3 breq btn_shortpress cpi r23, 4 breq btn_longpress rjmp main btn_shortpress: ; short press acts as spare (emergency) control when remote is lost or not work. ; so put here action(s) same as IR decoded command reaction. ; rcall led0on ; rcall led1on rcall load0_toggle ; ... rjmp main btn_longpress: ; long button press (hold down) is enter learning mode. ; Each new command starts at N*4*maxpktlen offset in EEPROM, where N - command number. ; rcall led0off ; rcall led1on ldi r16, 1 ; mode = learn - just write next received pkt to eeprom mov r15, r16 rjmp main ; here is (we hope) packet received in RAM and what to do with it depends on current mode. rx_pkt_done: rcall led0off rcall led1off mov r16, r15 cpi r16, 2 brne rxp2 rjmp pkt_send_serial rxp2: cpi r16, 0 breq cmd_search_2 rjmp pkt_write_eep cmd_search_2: ldi r21, 0 ; number of decoded command, valid only if decoded correctly. cmd_search: rcall pkt_compare cpi r16, 1 ; match breq cmd_found inc r21 cpi r21, cmd_qty brne cmd_search rjmp cmd_search_done cmd_found: cpi r21, 0 ; cmd number breq cmd0 cpi r21, 1 ; cmd number breq cmd1 rjmp cmd_search_done cmd0: sbic modesw1in, modesw1bit rjmp cmd_toggle rcall load0_on rjmp cmd_search_done cmd_toggle: rcall load0_toggle rjmp cmd_search_done cmd1: rcall load0_off rjmp cmd_search_done cmd_search_done: rjmp main load0_toggle: mov r16, r00 cpi r16, 1 ; if it was 'on' then turn it off breq load0_off load0_on: ldi r16, 1 mov r00, r16 sbi load0ddr, load0bit sbi load0port, load0bit ; add your own functionality here: rcall led2on ret load0_off: ldi r16, 0 mov r00, r16 sbi load0ddr, load0bit cbi load0port, load0bit ; add your own functionality here: rcall led2off ret load1_toggle: mov r16, r01 cpi r16, 1 ; if it was 'on' then turn it off breq load1_off load1_on: ldi r16, 1 mov r01, r16 sbi load1ddr, load1bit sbi load1port, load1bit ; add your own functionality here: ret load1_off: ldi r16, 0 mov r01, r16 sbi load1ddr, load1bit cbi load1port, load1bit ; add your own functionality here: ret pkt_compare: ldi r28, low(buf) ; RAM ldi r29, high(buf) ldi r30, 0 ; eeprom pkt start address ldi r31, 0 .if maxpktlen=64 mov r31, r21 ; read offset in eeprom for command .else raise_compiler_error_should_be_modify_here_to_correct_calculate_shift_for_r31. .endif ldi r22, 0 eep_compare: ; '0' (active) pulse length check ; - and - ; '1' (inactive) pulse length check (now combined because almost same procedure) rcall eep_read ld r17, y+ sub r16, r17 mov r18, r16 rcall eep_read cpi r16, 255 ; eeprom probably empty - FF'ed ; breq kernel_panic breq not_like ; this should normally not occurs at '0' (active) pulse length check, ; maybe it should be added later to skip it at '0' check... *TODO* cpi r16, timeout ; end of stored packet reached - so it is match breq match ld r17, y+ ; sbc r16, r17 ; should check both bytes! but now only one check. TODO! ; ... cpi r18, 252 breq like cpi r18, 253 breq like cpi r18, 254 breq like cpi r18, 255 breq like cpi r18, 0 breq like cpi r18, 1 breq like cpi r18, 2 breq like cpi r18, 3 breq like cpi r18, 4 breq like rjmp not_like like: inc r22 ; failsafe behaviour cpi r22, maxpktlen*2 ; here '1's and '0's both counted in r22 so double size. brne eep_compare not_like: ldi r16, 0 ret match: ldi r16, 1 ret pkt_write_eep: ldi r16, 0 mov r15, r16 ; reset current mode cpi r22, 0 ; length ; breq main brne eepw rjmp main eepw: ; cli ; write received pkt from ram to eeprom. ldi r28, low(buf) ; RAM ldi r29, high(buf) ldi r30, 0 ; eeprom pkt start address .if maxpktlen=64 mov r31, r24 ; write offset in eeprom for command .else raise_compiler_error_should_be_modify_here_to_correct_calculate_shift_for_r31. .endif eepwr: rcall led0off rcall led1on ld r16, y+ rcall eep_write ld r16, y+ rcall eep_write rcall led0on rcall led1off ld r16, y+ rcall eep_write ld r16, y+ rcall eep_write dec r22 brne eepwr ; ldi r22, 0 ; index ; ldi r28, low(buf) ; RAM ; ldi r29, high(buf) ; roll place at eeprom where to write next learned command (if any) inc r24 cpi r24, cmd_qty brne n2 ldi r24, 0 n2: ;rx_cont_pkt: rcall led0off rcall led1off rjmp main rjmp main pkt_send_serial: cpi r22, 0 ; length brne ser1 rjmp main ser1: ; try to use some artificial intelligence to determine what type of encoding ; we receive. Need to distinguish at least raw (RAW), NEC (NEC), Sony (SON) ; and Philips (RC5). But i lack any handset except many "NEC" type remotes, ; so others not implemented currently. TODO. ; how to emulate unknown remote for testing this code behaviour? Just use ; two remotes in parallel (pressing button on each at same time) :-) mov r23, r22 ; save length. pkt_test_for_nec: ldi r28, low(buf) ; RAM ldi r29, high(buf) ld r16, y+ ; 9 ms should be 0.009*16384=147 cpi r16, 144 breq like_nec cpi r16, 145 breq like_nec cpi r16, 146 breq like_nec cpi r16, 147 ; 0x93 breq like_nec cpi r16, 148 breq like_nec cpi r16, 149 breq like_nec cpi r16, 150 breq like_nec rjmp pkt_test_for_sony_ ; not like NEC. like_nec: ld r16, y+ ; 2nd byte (high byte of 1st pulse) should be 0 cpi r16, 0 brne pkt_test_for_sony_ ; not like NEC. ld r16, y+ ; 1st pause: 4.5 ms should be 0.0045*16384=74 cpi r16, 70 breq like_nec_a cpi r16, 71 breq like_nec_a cpi r16, 72 breq like_nec_a cpi r16, 73 breq like_nec_a cpi r16, 74 breq like_nec_a cpi r16, 75 breq like_nec_a cpi r16, 76 breq like_nec_a cpi r16, 33 ; test for 2.25 ms pause breq like_necautorep cpi r16, 34 breq like_necautorep cpi r16, 35 breq like_necautorep cpi r16, 36 breq like_necautorep cpi r16, 37 breq like_necautorep cpi r16, 38 breq like_necautorep cpi r16, 39 breq like_necautorep rjmp pkt_test_for_sony_ ; not like NEC. pkt_test_for_sony_: rjmp pkt_test_for_sony like_necautorep: ld r16, y+ ; 4 byte (high byte of 1st pause) should be 0 cpi r16, 0 brne pkt_test_for_sony_ ; not like NEC. ; send autorep text pkt_send_serial_nec_autorep: rcall led0off rcall led1on rcall USART_Transmit ldi r16, 'R' rcall USART_Transmit ldi r16, 'E' rcall USART_Transmit ldi r16, 'P' rcall USART_Transmit rjmp pkt_send_serial_done like_nec_a: ld r16, y+ ; 4 byte (high byte of 1st pause) should be 0 cpi r16, 0 brne pkt_test_for_sony_ ; not like NEC. ; and finally here we pass all checks and hope we see valid NEC packet. pkt_send_serial_nec: rcall led0off rcall led1on ldi r28, low(buf) ; RAM ldi r29, high(buf) ldi r16, 'N' rcall USART_Transmit ldi r16, 'E' rcall USART_Transmit ldi r16, 'C' rcall USART_Transmit ldi r16, ' ' rcall USART_Transmit ld r16, y+ ; skip preamble. ld r16, y+ ld r16, y+ ld r16, y+ ; calculate and send only full bytes. ideally we should receive 4 full byte, but ; transfer may be interrupted. This code will work with different q'ty of bytes, ; it may be useful when code length changes (in future, or due to cheap remotes, ; etc). ; the standard 4 bytes is not always 2 bytes data + 2 bytes inverted (CRC). ; Example is Sanyo remote model CXPP for PLV-Z2 projector, which violates this ; rule. So we don't filter CRC errors and pass all bytes as is, user may wish ; to do any CRC checking externally, if need. nec3: ldi r18, 8 ; bits q'ty ldi r19, 0 ; result nec2: ld r16, y+ ; pulse length - not checked currently. ld r16, y+ ; pulse length - not checked currently. ld r16, y+ ; pause length ld r17, y+ ; pause length high byte not used. lsr r19 cpi r16, 18 ; middle between '0' and '1' brlo nec_rxzero nec_rxone: ori r19, 0b10000000 nec_rxzero: ; andi r19, 0b01111111 ; really not need, just for ensure. dec r18 cpi r18, 0 brne nec4 ; full byte calculated. mov r16, r19 rcall whexbyte ldi r16, ' ' rcall USART_Transmit ldi r18, 8 ; bits q'ty ldi r19, 0 ; result nec4: dec r22 brne nec2 ; test for debug: ; rjmp pkt_send_serial_raw rjmp pkt_send_serial_done pkt_test_for_sony: ; ... rjmp pkt_test_for_philips pkt_test_for_philips: ; ... rjmp pkt_send_serial_raw pkt_send_serial_raw: ldi r28, low(buf) ; RAM ldi r29, high(buf) ldi r16, 'R' rcall USART_Transmit ldi r16, 'A' rcall USART_Transmit ldi r16, 'W' rcall USART_Transmit ldi r16, ' ' rcall USART_Transmit ser2: rcall led0off rcall led1on ; ld r24, y+ ; ld r25, y+ ; rcall whexword ld r16, y+ rcall whexbyte ld r16, y+ ; currently, high byte not used. ldi r16, ' ' rcall USART_Transmit rcall led0on rcall led1off ; ld r24, y+ ; ld r25, y+ ; rcall whexword ld r16, y+ rcall whexbyte ld r16, y+ ; currently, high byte not used. ldi r16, ' ' rcall USART_Transmit ; dec r22 dec r23 brne ser2 pkt_send_serial_done: ldi r16, 13 rcall USART_Transmit ldi r16, 10 rcall USART_Transmit rcall led0off rcall led1off ; wait for last byte completely transmits before sleep: ; http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=1144804#1144804 ut2: sbis UCSRA, TXC rjmp ut2 rjmp main whexword: ; write hex word r24:r25 mov r16, r25 rcall whexbyte mov r16, r24 whexbyte: ; write hex byte r16 push r16 swap r16 rcall whexdigit pop r16 whexdigit: andi r16, 0b00001111 subi r16, 256-'0' cpi r16,'9'+1 brlo whe1 subi r16, 256-('a'-'9'-1) whe1: rcall USART_Transmit ret EXT_INT0: rcall led0on rcall led1off ldi r19, 0 ldi r18, 0 meas_zero: rcall ir_delay ; 1 / 16384 s. standard LIRC grain sbic usb1in, usb1bit rjmp meas_zero_done inc r18 cpi r18, 0 brne meas_zero inc r19 cpi r19, timeout ; max. one second (63). brne meas_zero ; if active (low) for more than timeout, it is button press which ; shared (wired-OR) with TSOP4838. ldi r23, 3 ; at least, short button press detected. rcall led0on rcall led1on ldi r18, 10 ; 10 x 0,25 = 2,5 s long press meas_zero_2: rcall delay_025s sbic usb1in, usb1bit ; rjmp meas_zero_2_done rjmp short_press dec r18 brne meas_zero_2 ldi r23, 4 ; long button press detected. rcall led0off rcall led1on ; rjmp int0_done rjmp meas_wait_for_zero_gone short_press: rcall led0off rcall led1off rjmp int0_done ; here need still to wait for low level gone. Else next time INT0 false fires. ; So, we never return if continuous low level stuck. meas_wait_for_zero_gone: ; rcall ir_delay ; this not need here really rcall bounce_delay ; and this really need. nop sbis usb1in, usb1bit rjmp meas_wait_for_zero_gone rjmp int0_done meas_zero_done: st y+, r18 ; store active (0) length to ram st y+, r19 ; rcall led0off ; rcall led1on ldi r19, 0 ldi r18, 0 meas_one: rcall ir_delay ; 1 / 16384 s. standard LIRC grain sbis usb1in, usb1bit rjmp meas_one_done inc r18 cpi r18, 0 brne meas_one inc r19 cpi r19, timeout ; max. one second (63). brne meas_one ; here is timeout of high level, it is normal at end of packet. ldi r23, 2 ; end of packet due to timeout meas_one_done: ; cpi r23, 1 ; brne int0_done st y+, r18 ; store inactive (1) length to ram st y+, r19 inc r22 cpi r22, maxpktlen brne int0_done ldi r23, 2 ; end of packet due to length int0_done: reti ; need to 12M / 16384 = 732 tact delay (61 * 12). ; but real delay (time grain for digitize packet) is not measured in hardware so may be not ; accurate. It is not any problem with self (standalone) learning and then detecting, ; but may have not ideal match to LIRC database (if used in any way). ir_delay: ; ldi r16, 61 ldi r16, 60 ir_d1: nop ; 1 tact x 9 nop nop nop nop nop nop nop nop dec r16 ; 1 tact brne ir_d1 ; 2 tact when loop nop nop nop nop nop ret bounce_delay: push r17 push r16 ldi r17,0 bounce_d1: rcall ir_delay dec r17 brne bounce_d1 pop r16 pop r17 ret delay_025s: push r18 ldi r18, 16 dly02: rcall bounce_delay dec r18 brne dly02 pop r18 ret led0on: sbi led0ddr, led0bit cbi led0port, led0bit ret led1on: sbi led1ddr, led1bit cbi led1port, led1bit ret led0off: sbi led0ddr, led0bit sbi led0port, led0bit ret led1off: sbi led1ddr, led1bit sbi led1port, led1bit ret led2off: sbi usb0ddr, usb0bit cbi usb0port, usb0bit ret led2on: sbi usb0ddr, usb0bit sbi usb0port, usb0bit ret USART_Transmit: sbis UCSRA, UDRE rjmp USART_Transmit ; http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=1144804#1144804 sbi UCSRA, TXC ; clear TXC out UDR, r16 ;ut1: ; sbis UCSRA, UDRE ; rjmp ut1 ret eep_read: sbic EECR, EEWE rjmp eep_read out EEARH, r31 out EEARL, r30 sbi EECR, EERE in r16, EEDR adiw r30, 1 ret eep_write: sbic EECR, EEWE rjmp eep_write out EEARH, r31 out EEARL, r30 out EEDR, r16 sbi EECR, EEMWE sbi EECR, EEWE adiw r30, 1 ret kernel_panic: rcall led0off rcall led1on rcall led2off rcall delay_025s rcall delay_025s rcall led0on rcall led1off rcall led2on rcall delay_025s rcall delay_025s nop rjmp kernel_panic rjmp kernel_panic ;;; sudo avrdude -p m8 -c usbasp -U eeprom:r:eep.hex:i ; sudo avrdude -p m8 -c usbasp -U eeprom:r:eee.hex:r rjmp kernel_panic ;.stop ; .end
Example of EEPROM content when learn to two commands from remote from cheap color LED strip controller.
90 00 47 00 09 00 06 00 09 00 07 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 1a 00 08 00 1a 00 08 00 19 00 09 00 19 00 09 00 19 00 09 00 19 00 09 00 19 00 08 00 1a 00 09 00 07 00 08 00 1a 00 08 00 07 00 09 00 07 00 08 00 1a 00 09 00 07 00 08 00 08 00 08 00 08 00 08 00 1a 00 09 00 07 00 08 00 1a 00 09 00 19 00 08 00 08 00 08 00 19 00 09 00 19 00 08 00 1a 00 09 00 00 02 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 46 00 09 00 07 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 19 00 08 00 1a 00 09 00 19 00 08 00 1a 00 08 00 1a 00 08 00 1a 00 08 00 1a 00 08 00 19 00 09 00 08 00 08 00 19 00 09 00 19 00 08 00 08 00 08 00 1a 00 08 00 08 00 08 00 08 00 08 00 08 00 08 00 1a 00 08 00 08 00 08 00 07 00 09 00 19 00 08 00 08 00 08 00 1a 00 08 00 1a 00 08 00 1a 00 08 00 00 02 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
Tip: It is very easy to de-solder IC, using Light Alloy solder like Rose’s Alloy or Wood Alloy. No any special tools need, just everyday soldering iron used. Additionally, IC body saved from overheat, compared to hot air or oven de-soldering.
Photos:
[nggallery id=3]