diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/idp8_rx.asm | 481 | ||||
-rw-r--r-- | firmware/idp8_tx.asm | 321 |
2 files changed, 802 insertions, 0 deletions
diff --git a/firmware/idp8_rx.asm b/firmware/idp8_rx.asm new file mode 100644 index 0000000..cad5ae8 --- /dev/null +++ b/firmware/idp8_rx.asm @@ -0,0 +1,481 @@ + +; ************************************* +; * IDP8 (RX) assembler source * +; * Written by: Dylan Muller * +; * Target architecture: AVR ATtiny85 * +; ************************************* + +; IDP8 = Infrared Data Protocol 8 + + .NOLIST + .INCLUDE "tn85def.inc" + .LIST + + .equ CLK_DEL = 99 + .equ TIM_THRESH = 5 + + ;f(t_timeout) = 25.5ms (25500μs) * TIM_THRESH + ; The desired packet timeout time must be multiplied by 25.5ms as a result of register (R25) overflow , + ; used in conjunction with R26 to achieve longer time delays (see below) + ; and because our clock/counter has a 100us resolution. + ; The relative time-out delay is achieved by overflowing the 8-bit t_timeout(R25) register and thereafter + ; incrementing t_timeout_2(R26), continuously comparing R26 with TIM_THRESH, + ; until we exceed our delay threshold. + +.def packet_info = R24 +.def dinv_info = R19 +.def t_timeout = R25 +.def t_timeout_2 = R26 + +; Protocol definitions (Specified as a multiple of 100 microseconds) +; This is our chosen timer resolution +.equ DPACKET_HEADER = 30 ; 3ms (3000μs) +.equ DPDATA_0 = 20 ; 2ms (2000μs) +.equ DPDATA_1 = 10 ; 1ms (1000μs) +; Defines pulse delay tolerance +.equ SIG_TOLERANCE = 1 ; +/- 100μs + + ; Reserved registers + .def pflags = R21 + .def tlapse = R20 + + ;TX packet data to be compared with +.equ I0_PACKET_DATA = 0x1 ; 001 (last bit)(middle bit)(first bit) LSB +; Least significant bit first +.equ I1_PACKET_DATA = 0x4 ; 100 +.equ I2_PACKET_DATA = 0x5 ; 101 +.equ I3_PACKET_DATA = 0x7 ; 111 + +; packet_info (R24) register explanation +; 7 6 5 4 3 2 1 0 (bit positions) LSB +; - - - - - - - - (packet_info) register +; - - - - (packet_info mask bits) +; The packet_info mask bits determine which bit should be validated upon a +; jump to pk_dtest +; 0 = Packet header validated +; 1 = Data bit 1 validated +; 2 = Data bit 2 validated +; 3 = Data bit 3 validated +; 4 = Data bit 1 value +; 5 = Data bit 2 value +; 6 = Data bit 3 value +; 7 = Reserved + +; dinv_info (R19) register explanation +; 7 6 5 4 3 2 1 0 (bit positions) LSB +; - - - - - - - - (packet_info) register +; The dinv_info (data inverted) information register is used to +; capture and validate the 3 inverted data bits which are transmitted after +; the 3 normal data bits, this mechanism is used to verify data integrity. + +; 0 = INV Data bit 1 validated +; 1 = INV Data bit 2 validated +; 2 = INV Data bit 3 validated +; 3 = Reserved +; 4 = INV Data bit 1 value +; 5 = INV Data bit 2 value +; 6 = INV Data bit 3 value +; 7 = Reserved + +; pflags (R21) register explanation +; 7 6 5 4 3 2 1 0 (bit positions) LSB +; - - - - - - - - (pflags) register + +; 0 = Last pin status of pin 0 (PINB0) +; 1 = tlapse register available for processing +; Status bit 1 is set upon a low-high logic transition on input pin 0 (PINB0) + +.CSEG +.ORG 0x00 +; Define ISR (Interrupt Service Routine) vectors +; Reset vector +rjmp reset +reti +; PCINT0 (Pin change interrupt) vector +rjmp pc_int +reti +reti +reti +reti +reti +reti +reti +;TIMER0_COMPA - Timer 0 (Output Compare Match A) ISR +rjmp tim0_compa +;TIMER0_COMPA - Timer 0 (Output Compare Match B) ISR +rjmp tim0_compb ; Timer compare interrupt + +;.org 0xF +; Reset vector sub-routine +reset: +; Stack initialize +ldi R16, low(RAMEND) ; +out SPL, R16 +; Enable output drivers +ldi R16, 0x1E +out DDRB, R16 +; Initialize CTC mode + (CLK/8) prescaler select (TIMER 0) +ldi R16, (1 << WGM01) +out TCCR0A,R16 +ldi R16, (1 << CS01) +out TCCR0B,R16 +ldi R16, CLK_DEL +out OCR0A, R16 +ldi R16, CLK_DEL +out OCR0B, R16 +;Enter power reduction mode | Disable: USI +ldi R16, (1 << PRUSI) +out PRR, R16 +; Enable pin change interrupts for wake up +; Set PCINT masks via PCMSK +ldi R16, (1 << PCIE) +out GIMSK, R16 +ldi R16, (1 << PCINT0) +out PCMSK, R16 +sei ; Enable interrupts + +; Main loop +main: +; Jump if we have a reading +sbrc pflags,0x1 +rjmp smp_match +rjmp main + +set_packet: +ldi packet_info, 0x1 +rjmp smp_end +packet_reset: +clr packet_info +rjmp smp_end + +smp_match: +cli +in R22, SREG +; Has the packet header been validated? +; i.e the packet header validation bit in the packet_header register +; been set? +sbrc packet_info, 0 +rjmp pd_aquire +; If not test for packet header +pkheader_test: +cpi tlapse, (DPACKET_HEADER - SIG_TOLERANCE) +brge pkht_upper +rjmp smp_end +pkht_upper: +cpi tlapse, (DPACKET_HEADER + (SIG_TOLERANCE+1)) +brlo set_packet + +; Our packet header has been validated at this stage. +pd_aquire: +; Test if any of the 3 data bits in our packet have been processed. +; If not jump to the respective handler routines +sbrs packet_info, 1 +rjmp data_1 +sbrs packet_info, 2 +rjmp data_2 +sbrs packet_info, 3 +rjmp data_3 +; Testing remaining 3 inverted data bits +sbrs dinv_info, 0 +rjmp dinv_1 +sbrs dinv_info, 1 +rjmp dinv_2 +sbrs dinv_info, 2 +rjmp dinv_3 + + +; At this stage all the data bits have been +; successfully captured and validated. +; We are ready to process our captured packet +rjmp data_proc + + +; Handler routines pass the relevant bit mask of the packet_info +; register to the pk_dtest sub-routine. +; This sub-routine's purpose is to validate the pulse delay +; between high-low and low-high logic transitions (stored in tlapse) +; on pin 0 (PINB0) +; Furthermore the sub-routine uses this information to differentiate between logic 1 +; and logic 0 bit information provided the pulse delay is within configured bounds +; for those respective logic levels. +; The relevant packet bits are then set accordingly and a jump to smp_end +; is executed +data_1: +ldi R23, 0x1 +rjmp pk_dtest +data_2: +ldi R23, 0x2 +rjmp pk_dtest +data_3: +ldi R23, 0x4 +rjmp pk_dtest + +dinv_1: +ldi R23, 0x1 +rjmp dinv_test +dinv_2: +ldi R23, 0x2 +rjmp dinv_test +dinv_3: +ldi R23, 0x4 +rjmp dinv_test + +; Process data packet +data_proc: +; Optional unused status bit. Not needed in this case +;sbr packet_info, 0x80 +;extract data bit values +andi packet_info, 0x70 +ldi R16, 4 +shift_4: +lsr packet_info +dec R16 +brne shift_4 +; Compare packet data +mov R16, packet_info +cpi R16, I0_PACKET_DATA +breq led +mov R16, packet_info +cpi R16, I1_PACKET_DATA +breq led2 +mov R16, packet_info +cpi R16, I2_PACKET_DATA +breq led3 +mov R16, packet_info +cpi R16, I3_PACKET_DATA +breq led4 +rjmp smp_clear + +;data handler 1 +led: +ldi R16, 0x2 +out PORTB, R16 +rjmp smp_clear +;data handler 2 +led2: +ldi R16, 0x4 +out PORTB, R16 +rjmp smp_clear +;data handler 3 +led3: +ldi R16, 0x10 +out PORTB, R16 +rjmp smp_clear +;data handler 4 +led4: +ldi R16, 0x8 +out PORTB, R16 + +smp_clear: +clr t_timeout +clr t_timeout_2 +; Activate TIMER0_COMPB (tim0_compb) ISR +in R16, TIMSK +ori R16, (1<< OCIE0B) +out TIMSK, R16 +rjmp packet_reset + +smp_end: +clr tlapse +clr R23 +; Clear second bit in pflags +andi pflags, 0x1 +out SREG, R22 +sei +rjmp main + +; Packet data test sub-routine +; R23 = packet_info bit mask. This mask describes +; which data bit is to be processed if it has not already. +; As noted above, the individual bit meanings in the packet_info register +; are as follows, starting with the least significant bit (LSB) +; 7 6 5 4 3 2 1 0 (bit positions) LSB +; - - - - - - - - (packet_info) register +; - - - - (packet_info bit mask bits) +; The packet_info mask bits determine which bit should be validated upon a +; jump to pk_dtest +; 0 = Packet header validated +; 1 = Data bit 1 validated +; 2 = Data bit 2 validated +; 3 = Data bit 3 validated +; 4 = Data bit 1 value +; 5 = Data bit 2 value +; 6 = Data bit 3 value +; 7 = Reserved + +; After the sub-routine has finished execution +; the relevant bits in the packet_info register +; would have been set +pk_dtest: + +; Shift R23 one bit to the left +; All bit shifting found in this sub-routine +; is used for setting the correct bits in the +; packet_info register +lsl R23 +; Perform validation via boundary checks, +; taking into account pulse delay tolerances +test_high: +cpi tlapse, (DPDATA_1 - SIG_TOLERANCE) +brge dhtest_upper +rjmp test_low +dhtest_upper: +cpi tlapse, (DPDATA_1 + (SIG_TOLERANCE+1)) +brlo dset_high +test_low: +cpi tlapse, (DPDATA_0 - SIG_TOLERANCE) +brge dltest_upper +rjmp packet_reset +dltest_upper: +cpi tlapse, (DPDATA_0 + (SIG_TOLERANCE+1)) +brlo dset_low +rjmp packet_reset + +; The tlapse register (pulse delay) has been validated to logic 1 +; the requested validation bit, set by the relevant mask, +; is now set as well as the respective data value bit. +dset_high: +or packet_info, R23 +; shift 3 left +ldi R16, 3 +shift_3: +lsl R23 +dec R16 +brne shift_3 +or packet_info, R23 +rjmp smp_end + +; The tlapse register (pulse delay) has been validated to logic 0 +; the requested validation bit, obtained by the relevant mask, +; is now set and the data value bit cleared. +dset_low: +or packet_info, R23 +rjmp smp_end + +dinv_reset: +clr packet_info +clr dinv_info +rjmp smp_end + + +dinv_test: +dinvtest_high: +cpi tlapse, (DPDATA_1 - SIG_TOLERANCE) +brge dhtest_invupper +rjmp invtest_low +dhtest_invupper: +cpi tlapse, (DPDATA_1 + (SIG_TOLERANCE+1)) +brlo dinv_hcheck +invtest_low: +cpi tlapse, (DPDATA_0 - SIG_TOLERANCE) +brge dltest_invupper +rjmp dinv_reset +dltest_invupper: +cpi tlapse, (DPDATA_0 + (SIG_TOLERANCE+1)) +brlo dinv_lcheck +rjmp dinv_reset + +dinv_lcheck: +or dinv_info, R23 +rjmp dinv_checkl +dinv_hcheck: +or dinv_info, R23 +lsl R23 +ldi R16, 3 +shiftinv_3: +lsl R23 +dec R16 +brne shiftinv_3 +or dinv_info, R23 +dinv_checkl: +mov R16, packet_info +andi R16, 0x70 +and R16, R23 +cp R16, R23 +brne dinvchk_end +rjmp dinv_reset +dinvchk_end: +rjmp smp_end + + +; PCINT0 ISR +; Pin change interrupt service routine +; This ISR is used for timing the delay between +; high-low and low-high state transitions. +; This time delay is then stored in the tlapse register +pc_int: +in R22, SREG ; Save SREG +sbrc pflags, 0 +rjmp pcint_htol + +; Status bit 0 in pflags was low before interrupt +pcint_ltoh: +sbic PINB, 0 +sbr pflags, 1 + +sbrs pflags, 0 +rjmp pcint_ret +in R16, TIMSK +andi R16, (1 << OCIE0B) +out TIMSK, R16 +; Signal reading status +ori pflags, 0x2 +rjmp pcint_ret + +; Status bit 0 in pflags was high before interrupt +pcint_htol: +sbis PINB, 0 +cbr pflags, 1 + +sbrc pflags, 0 +rjmp pcint_ret +in R16, TIMSK +ori R16, (1 << OCIE0A) +out TIMSK, R16 +clr R16 +clr tlapse +out TCNT0, R16 + +pcint_ret: +out SREG, R22 +reti + +; TIMER0_COMPA - Timer 0 (Output Compare Match A) ISR +; This ISR is used as the 100μs clock counter +tim0_compa: +inc tlapse +reti + +; TIMER0_COMPB - Timer 0 (Output Compare Match B) ISR +; This ISR is activated upon setting any of the output +; bits in the PORTB register. +; The ISR serves as a timeout function ,setting all output +; bits in the PORTB register to a logic 0 if a packet is not +; received within the specified time threshold. +; The TIM_THRESH preprocessor definition defines this time threshold. +tim0_compb: +in R22, SREG ; Save status register +; Increment until t_timeout overflows +inc t_timeout +brne tim0_ret +inc t_timeout_2 +; If t_timeout_2 matches our threshold value carry +; on with execution +cpi t_timeout_2, TIM_THRESH +brlo tim0_ret + +; Clear respective registers +clr R16 +out PORTB, R16 +out TCNT0, R16 +out TIMSK, R16 +clr tlapse +clr t_timeout +clr t_timeout_2 +clr pflags +clr packet_info +clr dinv_info + +tim0_ret: +out SREG, R22 ; Restore status register +reti diff --git a/firmware/idp8_tx.asm b/firmware/idp8_tx.asm new file mode 100644 index 0000000..4361696 --- /dev/null +++ b/firmware/idp8_tx.asm @@ -0,0 +1,321 @@ + +; ************************************* +; * IDP8 (TX) assembler source * +; * Written by: Dylan Muller * +; * Target architecture: AVR ATtiny85 * +; ************************************* + +; IDP8 = Infrared Data Protocol 8 + + .NOLIST + .INCLUDE "tn85def.inc" + .LIST + + ; OCR1C timer value + ; T_TOP ~ 50% (duty cycle) 38095 Hz square output on 0C1A + ; Fo = Frequency output on 0C1A + ; Fo = (8000000)/(2*(104+1)) + +.equ T_TOP = 0x68 +.def tlapse = R21 + +; Protocol definitions (Specified in 100 microseconds) +; This is our chosen timer resolution +.equ DPACKET_HEADER = 30 ; 3ms (3000μs) +.equ DPDATA_0 = 20 ; 2ms (2000μs) +.equ DPDATA_1 = 10 ; 1ms (1000μs) +.equ TX_SLEEP = 200 ; 20ms +; Time our 38kHz burst pulse is high (IR receiver specific) +.equ TXPULSE_HIGH = 6 ; 0.6ms (600μs) + +; Define packet data to be transmitted for respective inputs +.equ I0_PACKET_DATA = 0x1 ; 001 (last bit)(middle bit)(first bit) LSB + ; Least significant bit first +.equ I1_PACKET_DATA = 0x4 ; 100 +.equ I2_PACKET_DATA = 0x5 ; 101 +.equ I3_PACKET_DATA = 0x7 ; 111 + +; Define input bit masks for PORTB +.equ I0_MASK = (1 << PB0) +.equ I1_MASK = (1 << PB2) +.equ I2_MASK = (1 << PB3) +.equ I3_MASK = (1 << PB4) + + +.CSEG +.ORG 0x00 + +; Define ISR (Interrupt Service Routine) vectors +; Reset vector +rjmp reset +reti +reti ; PCINT0 dummy vector for wake-up +reti +reti +reti +reti +reti +reti +reti +;TIMER0_COMPA - Timer 0 (Output Compare Match A) ISR +rjmp tim0_compa ; + + + +;.org 0xF +; Reset vector ISR +reset: +; Stack initialize +ldi R16, low(RAMEND) ; +out SPL, R16 + +; Load timer 1 TOP value +ldi R16, T_TOP +out OCR1C, R16 + + +; Initialize CTC mode + (CLK/8) prescaler select (TIMER 0) +ldi R16, (1 << WGM01) +out TCCR0A,R16 +ldi R16, (1 << CS01) +out TCCR0B,R16 + +; Load OCR0A value for 0.1ms (100us) resolution on timer 0 +; Each compare match calls the OCIE0A vector's ISR +; which increments the time lapsed (tlapse). +ldi R16, 99 ; 0.1ms +out OCR0A, R16 + +; Initialize CTC mode + (CLK/1) prescaler select (TIMER 1) +ldi R16, (1 << CS10) | (1 << CTC1) +out TCCR1, R16 + +; Enable output driver (0C1A) +sbi DDRB, 1 + +;Enter power reduction mode | Disable: USI +ldi R16, (1 << PRUSI) +out PRR, R16 + +; Enable pin change interrupts for MCU wake up +; Set PCINT masks via PCMSK +ldi R16, (1 << PCIE) +out GIMSK, R16 +ldi R16, (1 << PCINT0) | (1 << PCINT2) | (1 << PCINT3) | (1 << PCINT4) +out PCMSK, R16 + +sei ; Enable interrupts + +; Main loop +main: +; Enters idle-sleep and waits for input +; Sleep mode is terminated upon an external pin change event/interrupt. + +ldi R16, (1 << SE) ; Set SE bit + Idle mode +out MCUCR, R16 +sleep +clr R16 +out MCUCR, R16 + +; Pin status checks +; Jumps to respective stub if relative status port (PINB) bit(s) are set + +sbic PINB, 0 +rjmp istub0 +sbic PINB, 2 +rjmp istub1 +sbic PINB, 3 +rjmp istub2 +sbic PINB, 4 +rjmp istub3 + +rjmp main + + +; 0C1A output connect | Mode: Toggle on compare +oc1a_enable: +in R16, TCCR1 +ori R16, (1 << COM1A0) +out TCCR1, R16 +clr R16 +out TCNT1, R16 +ret + +; 0C1A output disconnect +oc1a_disable: +in R16, TCCR1 +andi R16, ~(1 << COM1A0) +out TCCR1, R16 +ret + +; Timer sub-routines for generating pulse delays +; specified in our protocol definition +; These functions are used when sending data packet(s) + +; Reset time +tim0_reset: +clr R16 +out TCNT0, R16 +clr tlapse +ret + +; TIMER0_COMPA enabled +tim0_start: +ldi R16, (1 << OCIE0A) +out TIMSK, R16 +rcall tim0_reset +ret +; TIMER0_COMPA disabled +tim0_stop: +clr R16 +clr tlapse +out TIMSK, R16 +ret + +; R23 = Time to wait in number of .1ms (100μs) +tim0_wait: +push R16 +rcall tim0_start +tim0_loop: +cp tlapse,R23 +brne tim0_loop +rcall tim0_stop +pop R16 +ret + +; * Pulse sub-routine * +; This sub-routine is responsible for the repeated +; transmission of data packets. +; R20 = pin mask +; R22 = packet data + +pulse_ms: +push R16 +in R16, SREG ; Save status register +push R20 +push R22 + +; Continuously transmit data packets until input release +pulse_loop: +rcall transmit_packet +ldi R23,TX_SLEEP +rcall tim0_wait + +in R19, PINB +and R19, R20 +brne pulse_loop + +pop R22 +pop R20 +out SREG, R16 +pop R16 +ret + +; TIMER0_COMPA - Timer 0 (Output Compare Match A) ISR +; This ISR is used as the 100μs clock counter +tim0_compa: +inc tlapse +reti + +; R22 = Packet data +transmit_packet: +; Transmit packet header +rcall tx_ph + +; Send all 3 data bits + +sbrc R22, 0 +rcall txd1 +sbrs R22, 0 +rcall txd0 + +sbrc R22, 1 +rcall txd1 +sbrs R22, 1 +rcall txd0 + +sbrc R22, 2 +rcall txd1 +sbrs R22, 2 +rcall txd0 + +; Send all 3 data bits inverted + +sbrc R22, 0 +rcall txd0 +sbrs R22, 0 +rcall txd1 + +sbrc R22, 1 +rcall txd0 +sbrs R22, 1 +rcall txd1 + +sbrc R22, 2 +rcall txd0 +sbrs R22, 2 +rcall txd1 + +rjmp tx_ret + +; Generate 38kHz IR burst +tx_38irp: +rcall oc1a_enable +ldi R23, TXPULSE_HIGH +rcall tim0_wait +rcall oc1a_disable +ret +txd1: +; Transmit logic 1 +rcall tx_38irp +ldi R23, DPDATA_1 +rcall tim0_wait +ret + +txd0: +; Transmit logic 0 +rcall tx_38irp +ldi R23, DPDATA_0 +rcall tim0_wait +ret + +tx_ph: +; Transmit packet header +rcall tx_38irp +ldi R23, DPACKET_HEADER +rcall tim0_wait +ret + +tx_ret: +; Transmit stop pulse +rcall tx_38irp +ret + +; Input stub handlers & masks +istub0: + +ldi R20, I0_MASK +ldi R22, I0_PACKET_DATA +rcall pulse_ms +rjmp main + +istub1: + +ldi R20, I1_MASK +ldi R22, I1_PACKET_DATA +rcall pulse_ms +rjmp main + +istub2: + +ldi R20, I2_MASK +ldi R22, I2_PACKET_DATA +rcall pulse_ms +rjmp main + +istub3: + +ldi R20, I3_MASK +ldi R22, I3_PACKET_DATA +rcall pulse_ms +rjmp main |