; smart-led-1.0.asm; Tri-color LED Controller with Serial Interface ; Phil Ray (08/2000) ; Developed on the at90s2343, but intended for the ATtiny22 ;--- GENERAL DEFINATIONS --- .equ clk_freq = 1 ; System clock frequency in MHz ; Note: This value is not used by any of the ; routines, but it serves as an important reminder ; that the uC's RC clock runs at about 1MHz @ 5v. .equ half_bit = 45 ; This is the time in timer ticks (about 1 uS) ; that the UART_delay routine has to waste to ; generate a half bit delay @ 9600bps. You may need to ; change it for your particular chip. ;--- REGISTER DEFINITIONS --- .def sreg_cpy = r0 ; Holds a copy of the SREG when needed .def dlay_cnt1 = r1 ; For our short delay loop .def dlay_cnt2 = r2 ; (same as above) .def red_br = r16 ; Red LED brightness level (0=off 31=99.97%) .def green_br = r17 ; Green LED brightness level .def blue_br = r18 ; Blue LED brightness level .def tmp = r19 ; macro.asm scratchpad register .def bitcnt = r20 ; Bit counter for serial input .def temp = r21 ; Temp storage for serial input routine .def Rxbyte = r22 ; Holder of the received byte .def delay_reg = r23 ; Holder of a delay amount for UART_delay .def timer_cpy = r24 ; Snapshot of the timer for later compare .def state_cnt = r25 ; State counter (0...31) .def result = r26 ; multiplication result ;--- PORT B DEFINITIONS --- .equ red_LED = 0 ; The red LED output pin 0=on, 1=off .equ green_LED = 1 .equ blue_LED = 2 .equ RxD = 3 ; Serial data input pin 1=mark, 0=space .equ test = 4 ; test output (short positive pulse when reading RxD) ; pb76543210 .equ portB_defs = 0b00010111 ; (unused pins left as inputs) ;--- INTERRUPT VECTORS --- .org 0 RESET_V: rjmp main ; RESET vector EXT_INT0_V: rjmp INTR0 ; Int 0 vector TIM0_OVF_V: rjmp TIMR0_OVF ; Timer 0 overflow vector ;--- INCLUDES --- .include "../include/2343def.inc" ; (standard Atmel file) .include "../include/macro.asm" ; Some useful little AVR macros ;--- LOCAL MACROS ; These are to project-specific to be listed in the ; macros.asm file .macro all_on ; turn all LEDs on cbi PORTB, red_LED cbi PORTB, green_LED cbi PORTB, blue_LED .endmacro .macro all_off ; Turn 'em all off sbi PORTB, red_LED sbi PORTB, green_LED sbi PORTB, blue_LED .endmacro .macro red_off sbi PORTB, red_LED .endmacro .macro green_off sbi PORTB, green_LED .endmacro .macro blue_off sbi PORTB, blue_LED .endmacro ;--- THE PROGRAM --- main: SP_load RAMEND ; initialize stack pointer DDRB_write portB_defs ; set up port B all_off ; turn off all LEDs ldi red_br, 0 ; set starting brightness values ldi green_br, 0 ldi blue_br, 0 ; This was added to make the uC let us know that it had ; started properly, as in using a '2343 in RC mode. ; It just flashes the green LED once for about 65mS. ; (Sort of a power-on greeting) cbi PORTB, green_LED ; turn green on dlay: ; short delay dec dlay_cnt1 brne dlay dec dlay_cnt2 brne dlay green_off ; turn green off TIMR0_div_1 ; set up and start timer0 TIMR0_INT_ENABLE sei ; enable interrupts again: clr result ; Zero out the multiply reg. rcall getdigit ; wait for 1'st digit (10's) ; we need to multiply Rxbyte by 10 and store it ; somewhere. The '2343/tiny22 doesn't have "mul", so we ; do it by shifting left once (*2) and then multiply ; by 5 using repeated addition. lsl Rxbyte ; multiply by 2 ldi tmp, 5 ; number of additions multiply_cont: add result, Rxbyte dec tmp ; done? brne multiply_cont ; No. do more additions rcall getdigit ; wait for the next digit (1's) add result, Rxbyte ; add it to the result ; This bit of code checks to see if the user-supplied ; brightness value was > 31. If so, we just treat it ; as desiring full brightness and set it to 31. cpi result, 32 ; value out of range? (>31) brlo get_alpha ; No. get color letter ldi tmp, 31 ; Yes. mov result, tmp ; set brightness to max get_alpha: rcall getchar ; wait for a char cpi Rxbyte, 'r' ; char = ASCII 'r'? breq update_red_br ; yes. update red's brightness level cpi Rxbyte, 'R' ; char = ASCII 'R'? brne check_for_green ; no. go try next color update_red_br: mov red_br, result ; update red_br with new brightness rjmp again check_for_green: cpi Rxbyte, 'g' ; (same for 'g' and 'G') breq update_green_br cpi Rxbyte, 'G' brne check_for_blue update_green_br: mov green_br, result rjmp again check_for_blue: cpi Rxbyte, 'b' ; (same for 'b' and 'B') breq update_blue_br cpi Rxbyte, 'B' brne done_testing update_blue_br: mov blue_br, result done_testing: rjmp again ;--- SUBROUTINES --- getdigit: ; Gets a char, makes sure it's an ASCII digit, and converts it to ; an integer byte rcall getchar ; wait for a char cpi Rxbyte, 0x30 ; char < ASCII "0"? brlo getdigit ; yes. ignore it cpi Rxbyte, 0x3a ; char > ASCII "9"? brsh getdigit ; Yes. ignore it subi Rxbyte, 0x30 ; convert to integer byte ret getchar: ; Gets a char via serial input ; Adapted from Atmel's avr305 Application Note ldi bitcnt, 9 ; 8 data bit + 1 stop bit getchar1: sbic PINB, RxD ; Wait for start bit rjmp getchar1 cli ; shut off interrupts ldi delay_reg, half_bit rcall UART_delay ; 1/2 bit delay getchar2: ldi delay_reg, 2 * half_bit ; 1 bit delay rcall UART_delay clc ; clear carry sbi PORTB, test ; show bit-sampling sbic PINB, RxD ; if RX pin high sec ; set carry cbi PORTB, test ; show end-of-bit-sampling dec bitcnt ; If bit is stop bit breq getchar3 ; return ; else ror Rxbyte ; shift bit into Rxbyte rjmp getchar2 ; go get next getchar3: clr tmp ; load the timer with 0 out TCNT0, tmp sei ; turn on interrupts ret UART_delay: ; Generates delays based on value in delay_reg ; Updates the LED and then wastes the remainder ; The LED update frequency changes during serial receive, but ; its duty cycle (and thus its brightness) does not. clr tmp ; load the timer with 0 out TCNT0, tmp rcall update_led ; service the LED waste_time: in timer_cpy, TCNT0 ; take a snapshot of the timer cp timer_cpy, delay_reg ; enough time elapsed? brlo waste_time ; no. waste more time ret update_led: ; numbers in () are worst case number of clock cycles in sreg_cpy, SREG ; (1) preserve SREG cpi state_cnt, 32 ; (1) state_counter overflow? brne red_test ; (2) no. continue clr state_cnt ; (1) yes, reset state_counter to 0 all_on ; (6) and turn all LEDs on red_test: cp state_cnt, red_br ; (1) red_br > state_counter? brcs green_test ; (2) yes. leave LED on, next test red_off ; (2) no. turn off LED green_test: cp state_cnt, green_br ; (same for green) brcs blue_test green_off blue_test: cp state_cnt, blue_br ; (same for blue) brcs colors_done blue_off colors_done: inc state_cnt ; (1) next state out SREG, sreg_cpy ; (1) restore SREG ret ; (5) ;--- INTERRUPT SERVICE ROUTINES --- INTR0: reti ; (not used) TIMR0_OVF: ; Timer overflow sends us here every 256us except when ; receiving serial data, in which case the serial subr ; handles updates. rcall update_led reti ; EOF: smart-led-1.0.asm