; vim: sts=4 sw=8 et ai fdm=marker syntax=avrasm ; ; fnordlicht firmware ; ; for additional information please ; see http://koeln.ccc.de/prozesse/running/fnordlicht ; ; (c) by Alexander Neumann ; Lars Noschinski ; Diego Biurrun ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License version 2 as ; published by the Free Software Foundation. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; ; For more information on the GPL, please go to: ; http://www.gnu.org/copyleft/gpl.html .nolist .include "m8def.inc" .list .include "fifo_handling.inc" .include "stack_handling.inc" ; compile settings ; ============================================================================= .ifndef SERIALDEBUG ; Set this to 1 for serial debugging output .define SERIALDEBUG 0 .endif .ifndef DEVELBOARD ; Set this to 1 for develboard debugging output .define DEVELBOARD 0 .endif .ifndef IR_DECODER ; Set this to 1 for (experimental) ir-decoder support .define IR_DECODER 0 .endif .ifdef IR_DECODER .ifndef IR_ADDRESS_GLOBAL .define IR_ADDRESS_GLOBAL 0x00 ; global IR address .endif .ifndef IR_ADDRESS_LOCAL .define IR_ADDRESS_LOCAL 0x05 ; local IR address .endif .ifndef IR_DEBUG ; Set this to 1 for infrared-command-debug output .define IR_DEBUG 0 ; this will send the commands received to the user .endif ; over rs232 .endif ; mark eeprom i2c setup address. set value with "make i2c-addr" ; ============================================================================= .eseg EEPROM_I2CADDR: .db 0 .dseg ; flags and aliases {{{ ; ============================================================================= ; flags (in register Flags) .equ PWMLastPulse = 0 ; if bit0 is set, we're in the last pwm pulse ; set by: timer1 interrupt ; reset by: main loop .equ PWMNewCycle = 1 ; if this flag is set, processing for the next pwm cycle ; should start. ; set by: timer1 interrupt ; reset by: main loop .equ PWMOverflowSlot = 2 ; if set, the next pwm timeslot is an overflow slot .equ ScriptEnabled = 3 ; if bit3 is set, scripts are enabled .equ FlagOF1Written = 4 ; if bit4 is set in update_timeslot_table, the timeslot ; for OF1 has already been written ; PWM properties .equ PWMChannels = 3 .equ PWMLevels = 255 ; max brightness value .equ PWMBurstLevels = 16 ; burst pulses count ; PWM output channel bits .equ PWMChannel0 = 0 .equ PWMChannel1 = 1 .equ PWMChannel2 = 2 ; LedStatusTable offsets .equ LedRecBrightness = 0 .equ LedRecTargetVal = 1 .equ LedRecFadeSpeedL = 2 ; 8.8 fixed point value, byte behind point .equ LedRecFadeSpeedH = 3 ; byte before point .equ LedRecCycleCount = 4 .equ LedRecFlags = 5 .equ LedRecSize = 6 ; LedRecFlags .equ LedFlagTargetReached = 0 ; PWMActionSlots offsets .equ ActionRecTimeSlotL = 0 .equ ActionRecTimeSlotH = 1 .equ ActionRecBitmask = 2 .equ ActionRecSize = 3 ; serial port .equ CLOCK = 16000000 .equ BAUD = 19200 .equ UBRRVAL = CLOCK/(BAUD*16)-1 ; serial fifos .equ URSize = 32 ; uart receive fifo size .equ UTSize = 128 ; i2c fifos .equ IRSize = 8 ; i2c receive fifo size .equ ITSize = 16 ; i2c transmit fifo size ; ASCII values .equ LF = 10 ; linefeed .equ CR = 13 ; carriage return ; }}} ; fifos {{{ ; ============================================================================= .dseg ; fifo: UReceiveFifo ; ========================== UReceiveFifo: .byte FifoControlSize + URSize ; fifo: UTransmitFifo ; ========================== UTransmitFifo: .byte FifoControlSize + UTSize ; fifo: IReceiveFifo ; ========================== IReceiveFifo: .byte FifoControlSize + IRSize ; fifo: ITransmitFifo ; ========================== ITransmitFifo: .byte FifoControlSize + ITSize ; }}} .cseg ; register definitions {{{ ; ============================================================================= .def IReceiveFifoWPointerL = r4 ; i2c receive fifo temporary write pointer .def IReceiveFifoWPointerH = r5 .def IReceiveFifoCounter = r6 .def l1 = r8 ; low working registers .def l2 = r9 .def l3 = r10 .def PWMNextOutputValue = r11 ; pwm output value for next pulse (non-burst mode) .def PWMBitmask = r12 ; pwm next timeslot's bitmask .def w1 = r16 ; working registers .def w2 = r17 .def w3 = r18 .def w4 = r19 .def w5 = r20 .def w6 = r21 .def Flags = r22 ; global flags .def t1w1 = r24 ; timer1 interrupt handler working registers .def t1w2 = r25 ; registers Y and Z may be used freely (although Z is used in the timer1 interrupt) ; }}} ; interrupt table {{{ ; ============================================================================= .ORG 0x0 ; reset vector rjmp reset .ORG OC1Aaddr ; timer1 CompareA interrupt handler rjmp OC1AHandler .if IR_DECODER == 1 .ORG INT0addr ; external interrupt 0 handler (ir decoder) rjmp IR_INT0Handler .ORG OC2addr ; compare interrupt handler for timer2 (ir decoder) rjmp IR_OC2Handler .ORG OVF2addr ; timer2 overflow interrupt handler (ir decoder) rjmp IR_OVF2Handler .endif .ORG URXCaddr ; USART receive complete interrupt handler rjmp URXCHandler .ORG UDREaddr ; USART data register empty handler rjmp UDREHandler .ORG TWIaddr ; TWI data handler rjmp TWIHandler .ORG INT_VECTORS_SIZE ; program start ; }}} ; SERIALDEBUG output routines {{{ ; ============================================================================= ; DEBUG_write_utf {{{ ; ============================================================================= ; saves registers and calls write_uart_transmit_fifo DEBUG_write_utf: push w2 push l1 push l2 rcall write_uart_transmit_fifo pop l2 pop l1 pop w2 ret ; }}} ; writes an immediate value .macro DEBUG_WRITE_I .if SERIALDEBUG == 1 push w1 ldi w1, @0 rcall DEBUG_write_utf pop w1 .endif .endmacro ; writes an register value .macro DEBUG_WRITE_R .if SERIALDEBUG == 1 push w1 mov w1, @0 rcall DEBUG_write_utf pop w1 .endif .endmacro ; }}} ; DEVELBOARD output routines {{{ ; ============================================================================= ; toggles a led .macro DEBUG_LED_TOGGLE .if DEVELBOARD == 1 push w1 push w2 in w1, PORTD ldi w2, (1<<@0)<<2 eor w1, w2 out PORTD, w1 pop w2 pop w1 .endif .endmacro ; switch on a led .macro DEBUG_LED_ON .if DEVELBOARD == 1 cbi PORTD, @0+2 .endif .endmacro ; switch off a led .macro DEBUG_LED_OFF .if DEVELBOARD == 1 sbi PORTD, @0+2 .endif .endmacro ; }}} ; We need the register definitions before including these files .if IR_DECODER == 1 .include "ir/ir_decoder.inc" .endif .include "script_handler.inc" .include "remote_exec_handler.inc" ; interrupt handler ; ============================================================================= ; function: reset interrupt handler {{{ ; ============================================================================= ; macro: MACRO_INIT_THREAD: initialize a thread {{{ ; ====================================== ; @0 @1 @2 @3 ; call: MACRO_INIT_THREAD ThreadNumber Handler ScriptPositionL ScriptPositionH ; uses: w1 .macro MACRO_INIT_THREAD ldi w1, @1 ; init script handler sts ScriptInfoTable+@0*ScrRecSize+ScrRecHandler, w1 ldi w1, @2 ; init script position sts ScriptInfoTable+@0*ScrRecSize+ScrRecPos, w1 ldi w1, @3 sts ScriptInfoTable+@0*ScrRecSize+ScrRecPos+1, w1 ldi w1, 0 ; init stack offset and reset flags sts ScriptInfoTable+@0*ScrRecSize+ScrRecStackOffset, w1 sts ScriptInfoTable+@0*ScrRecSize+ScrRecFlags, w1 .endmacro ; }}} reset: ; init stack ; ======================================== ldi w1, low(RAMEND) out SPL, w1 ldi w1, high(RAMEND) out SPH, w1 ; init outputs and set default output ; ======================================== ldi w1, (1< 0 ; parameters: @0: n .macro macro_delay ; do nothing for 3*n cycles ldi t1w1, @0 ; load given value 1 macro_delay_loop: dec t1w1 ; decrement given value 1 brne macro_delay_loop ; branch if nonzero 1 if false, 2 if true .endmacro ; }}} ; macro: macro_decrement_timer1_counter_register (6) {{{ ; ============================================================================= ; duration: 6 cycles ; parameters: none ; uses: t1w1, t1w2 .macro macro_decrement_timer1_counter_register ; decrement the timer1 counter register by 64000 in t1w1, TCNT1L ; load counter 1 in t1w2, TCNT1H ; 1 subi t1w1, low(64000-6) ; subtract 64000 1 sbci t1w2, high(64000-6) ; 1 out TCNT1H, t1w2 ; store counter 1 out TCNT1L, t1w1 ; 1 .endmacro ; }}} ; macro: macro_decrement_timeslot_counter (5) {{{ ; ============================================================================= ; duration: 5 cycles ; parameters: @0: used register .macro macro_decrement_timeslot_counter ; decrement timeslot counter by 1 lds @0, PWMActionSlotCounter ; load counter 2 dec @0 ; decrement counter 1 sts PWMActionSlotCounter, @0 ; store counter 2 .endmacro ; }}} ; macro: macro_prepare_next_timeslot (24) {{{ ; ============================================================================= ; duration: 24 cycles ; parameters: @0: used pointer register (either X or Y) ; uses: t1w1, t1w2, @0 .macro macro_prepare_next_timeslot ; output top, set overflow flag lds @0L, PWMActionSlotPointer ; load action slot record pointer 2 lds @0H, PWMActionSlotPointer+1 ; 2 ld t1w1, @0+ ; load new top 2 ld t1w2, @0+ ; 2 ld PWMBitmask, @0+ ; load new bitmask 2 sts PWMActionSlotPointer+1, @0H ; store pointer 2 sts PWMActionSlotPointer, @0L ; 2 ldi @0L, 0 ; 1 cp t1w1, @0L ; check if top is 0 1 cpc t1w2, @0L ; 1 (makes 17 cycles so far) breq macro_next_timeslot_prepare_overflow ; if overflow, jump away 1 if normal, 2 if overflow nop ; 1 nop ; 1 rjmp macro_next_timeslot_prepare_normal ; 2 macro_next_timeslot_prepare_overflow: ldi t1w1, low(64000) ; load next interrupt timeslot 1 ldi t1w2, high(64000) ; 1 sbr Flags, (1<= 181? brlo update_timeslot_test_of1_ret sbr Flags, (1<= l1 brsh update_timeslot_table_compare2 ; if yes, jump away mov w4, l1 ; if no, swap l1<->l2 and w1<->w2 mov l1, l2 mov l2, w4 mov w4, w1 mov w1, w2 mov w2, w4 update_timeslot_table_compare2: cp l3, l2 ; l3 >= l2 brsh update_timeslot_table_compare3 ; if yes, jump away mov w4, l2 ; if no, swap l2<->l3 and w2<->w3 mov l2, l3 mov l3, w4 mov w4, w2 mov w2, w3 mov w3, w4 update_timeslot_table_compare3: cp l2, l1 ; l2 >= l1 brsh update_timeslot_table_compare_done ; if yes, jump away mov w4, l1 ; if no, swap l1<->l2 and w1<->w2 mov l1, l2 mov l2, w4 mov w4, w1 mov w1, w2 mov w2, w4 update_timeslot_table_compare_done: ldi XL, low(PWMActionSlots) ; init X register to point to the ActionSlots table ldi XH, high(PWMActionSlots) ldi w6, 0 ; init persistent mask mov w4, l1 ; test if l1 is before OF1, if no, insert OF1 rcall update_timeslot_test_of1 cpse l1, l2 ; l1 == l2? if yes, skip next instruction rcall update_timeslot_write_timeslot_counter ; search timeslot counter for this brightness value and write it or w1, w2 ; OR masks together mov w4, l2 ; test if l2 is before OF1, if no, insert OF1 rcall update_timeslot_test_of1 cpse l2, l3 ; l2 == l3? if yes, skip next instruction rcall update_timeslot_write_timeslot_counter ; search timeslot counter for this brightness value and write it or w1, w3 ; OR masks together mov w4, l3 ; test if l3 is before OF1, if no, insert OF1 rcall update_timeslot_test_of1 mov w4, l3 ; copy w4 again, since update_timeslot_test_of1 overwrites it rcall update_timeslot_write_timeslot_counter ; search timeslot counter for this brightness value and write it ldi w4, 0xff ; insert OF1 if not already inserted... rcall update_timeslot_test_of1 ldi w4, 0 ; store OF0 st X+, w4 st X+, w4 ldi w4, 0 ; store bitmask to identify OF0 st X+, w4 sbr Flags, (1<= ch0? 1 brsh update_channel1 ; if true, jump away 1 if false, 2 if true cbr w2, (1<= ch1? 1 brsh update_channel2 ; if true, jump away 1 if false, 2 if true cbr w2, (1<= ch2? 1 brsh update_store_output ; if true, jump away 1 if false, 2 if true cbr w2, (1<= target, store current brightness ; else we missed the target us_target_missed: mov w1, w2 ; set the target value us_store: cp w1, w2 ; compare current brightness and target brightness brne us_store2 ; if not equal, just store the new brightness value ldd w4, Y+LedRecFlags ; load led flags sbr w4, (1<