//====================================================== // (c)2008-9 Matthew Ford // Forward Computing and Control Pty. Ltd. // www.forward.com.au // All rights reserved // // You may freely copy and distribute this program for // commercial and non-commercial use provided:- // i) this copyright notice remains attached // ii) you clearly mark any changes you make to the code //====================================================== // // BluetoothLedDriver.asm // // Main: ATtiny84 // mode: ISP mode // // Program: ATtiny84 // Flash - Input HEX File: yourdir/ThreeLevelDriver.hex // EEPROM - not used // // Fuses: // SPIEN ticked // BODLEVEL Brown Out detection at VCC = 1.8V // CKDIV8 ticked // SUT_CKSEL Int RC Osc. 8Mhz; .... 6CK/14CK +64mS // Auto Read ticked // Smart Warnings ticked // Verify After programming ticked // then Progam the fuses // // LockBits: // LB no lock features enabled // // Advanced: // Calibrate of frequency 8.0Mhz // Do Not write any value to the Cal byte. startup handles this // // HW Setting // VTarget 5.1 // VREF 5.1 (not used) // Clock Generator 3.686Mhz (max) // // Auto: // Erase Device // Check Signature // Program FLASH // Verify FLASH //--------------------------------------------------------- // // The constants N and C determine the data rate. C selects clock frequency // the following expression yields the data rate: // // Clk_freq // BAUD = ------ x 1/N // C // // // Absolute minimum value for N*C is about 10 (which causes the // timer to interrupt to be set again before the interrupt is finished). // Absolute maximum is 170. // (Caused by the 1.5bit-lenght that is necessary to receive bits correctly.) // // The RS232 uses SERIAL_OUT_PIN as the transmit pin // The receive pin is SERIAL_IN_PIN // You need to adjust the other .equ to match the pins you choose. // // // Since this RS232 module is half duplex, it can either send or recieve data. // It can't do both simoutaneausly. // // To use first call RS232_INIT. (with interrupts disabled) // Then either call RS232_SET_TO_RECEIVE to wait for a character to arrive // or RS232_TRANSMIT to transmit the bytes to have previously stored in the // RS232_BUFFER_?? To transmit you also need to set the RS232_SEND_COUNT // which is the number of bytes to be send from 1 to RS232_MAX_SEND_BUFFER // // //************************************************************************* // Mod 1 -- Corrected check on pin level on entering PIN_CHANGE_INT, added defines // corrected number of Globals, limited max current to 1000 .include "tn84def.inc" // start interrupt routine .macro SAVE_SREG push Temp in Temp, SREG // save the SREG push Temp .ENDMACRO // end interrupt routine .macro RESTORE_SREG pop Temp out SREG, Temp // restore the SREG pop Temp .ENDMACRO // set the uC clock calibration // I have allowed for two steps incase you need to move the default more the 0x20 // my default was 0x9E so only one step was needed // in this case just set step 2 to equal step 1 .equ OSC_CAL_STEP_1 = 0xA5 // first adjustment from the default .equ OSC_CAL_STEP_2 = 0xA5 // second adjustment from the default. // 9600 at 8MHz .equ N=103 // == ((8000000/9600) / C) - 1 .equ C=8 // counter prescaler == Divisor // ((0< low -> high on pin input (ie 1 -> 0) idle is 1 // So check for port input == 1 at top and just return if not. // Otherwise start the timer to sample in 1.5 bits // Disable this pin interrupt and clear the interrupt flag // // RS232_STATUS_SET_TO_RECEIVE is cleared by this routine // RS232_STATUS_BUSY is set by this routine // RS232_STATUS_SEND is cleared by this routine // // When receive completes RS232_STATUS_DATA_RECEIVED will be set // Clear this flag when you have handled the data // //************************************************************************* PIN_CHANGE_INT: SAVE_SREG // save Temp and SREG // see if pin low or high sbic SERIAL_IN_PORTIN, SERIAL_IN_PIN // if SERIAL_IN_PIN low == 1 (set) just return rjmp END_PIN_CHANGE_INT // else want to get the timer started as close as possible // to the leading edge on the input // start it with divide by 8 // this assumes that OCR0A is set to some resonably high value // and that timer0 is cleared, RS232_INIT and RS232_STOP should do this in Temp, TCCR0B sbr Temp, (1< 9 stop // else set up to send next bit cpi RS232BitCount, 9 breq TIMER0_CMP_A_STOP_BIT // == 9 send stop bit brsh TIMER0_CMP_A_STOP_SEND // > 9 stop // else set next bit to next bit in byte // the code below takes the same time for either 0 or 1 next bit lds Temp, RS232_SEND_BYTE sbrc Temp,0 sbr RS232StatusReg,(1< '9' then should be A..F? brlo END_CONVERT_TEMP_TO_HEX // no not > '9' subi Temp,-7 ; add 7 for A to F END_CONVERT_TEMP_TO_HEX: ret /******************************************/ // LOAD_SEND_BUFFER // Must call this with interrupts disabled // as EEPROM reads should not be interrupted // // Utility function to initialize the transit buffer // with the chars // Received 0x crlf // // uses r16,r17,r26,r27,r30,r31 /******************************************/ LOAD_SEND_BUFFER: ldi ZH, high(RS232_BUFFER_1+1) // add 1 due to -X in ST -X,Temp below ldi ZL, low(RS232_BUFFER_1+1) // add 1 due to -X in ST -X,Temp below ldi XH, high(example_data) ldi XL, low(example_data) // Set pointer to EEPROM data ldi Temp, 15 mov Temp2, Temp LOAD_SEND_BUFFER_LOOP: sbic EECR,EEPE //if EEPW not clear rjmp LOAD_SEND_BUFFER_LOOP // wait more out EEARH, XH out EEARL, XL // Set the EEPROM's address sbi EECR,EERE // Send the Read strobe in Temp,EEDR // Put data in temp register st -Z, Temp adiw XH:XL, 1 //inc eeprom address dec Temp2 brne LOAD_SEND_BUFFER_LOOP ret //----------------------------------------- // ADC interrupt, result available // removed this interrupt // just check if conversion start still set. //----------------------------------------- //------------------------------------------- // LED_CONTROL // update the led control if adc trigger set //----------------------------------------- LED_CONTROL: sbic ADCSRA, ADSC rjmp END_LED_CONTROL // ADSC still set no reading yet // else have reading // get it // clear the interrupt flag sbi ADCSRA, ADIF in ADCLow,ADCL in ADCHigh,ADCH // start another one now sbi ADCSRA, ADSC sts ADC_HIGH,ADCHigh sts ADC_LOW,ADCLow cpi Torch_State, Torch_State_OFF breq CURRENT_HIGH // if led off set output low // else led should be on sub ADCLow, SP_low sbc ADCHigh, SP_High brge CURRENT_HIGH // branch if ADC >= setpoint (signed comparison) // else ADC < setpoint CURRENT_LOW: // ADC < setpoint ==> make output high sbi PORTB, uC_OUTPUT_B // set output high rjmp END_LED_CONTROL CURRENT_HIGH: // ADC > setpoint ==> make output low cbi PORTB, uC_OUTPUT_B // set output low rjmp END_LED_CONTROL // all paths jmp to here END_LED_CONTROL: ret //------------------------------- //-------------------------- // TIMER1_2mS // called each main loop // but only call Switch_Debounce if trigger // has been set by timer interrupt //------------------------- TIMER1_2mS: sbrs TRIGGER_Flags, TRIGGER_Timer1 rjmp END_TIMER1_2mS // else changed clear trigger now cbr Trigger_Flags, (1< off PROCESS_SWDown_TRIGGER_TORCH_HIGH: // high -> off, variable -> off ldi New_TORCH_State, Torch_State_OFF rjmp END_PROCESS_SWDown_TRIGGER PROCESS_SWDown_TRIGGER_TORCH_OFF: // off -> low ldi New_TORCH_State, Torch_State_LOW rjmp END_PROCESS_SWDown_TRIGGER PROCESS_SWDown_TRIGGER_TORCH_LOW: // low -> med ldi New_TORCH_State, Torch_State_MED rjmp END_PROCESS_SWDown_TRIGGER PROCESS_SWDown_TRIGGER_TORCH_MED: // med -> high ldi New_TORCH_State, Torch_State_HIGH // rjmp END_PROCESS_SWDown_TRIGGER drop through END_PROCESS_SWDown_TRIGGER: ret //------------------------------------- //------------------------------------- //UPDATE_TORCH_STATE // Set the new torch state and load current setpoint //------------------------------------- UPDATE_TORCH_STATE: mov TORCH_State, New_TORCH_State // update state to new state rcall LOAD_CURRENT_SETPOINT // load new setpoint ret //------------------------------------- //------------------------------------- //LOAD_CURRENT_SETPOINT // Loads the current setpoint base on the torch state // Also set SW_LedOn depending on TORCH_State //------------------------------------- LOAD_CURRENT_SETPOINT: cpi TORCH_State, Torch_State_OFF breq SET_TORCH_OFF // else cpi TORCH_State, TORCH_State_LOW breq SET_TORCH_LOW // else cpi TORCH_State, TORCH_State_MED breq SET_TORCH_MED // else cpi TORCH_State, TORCH_State_HIGH breq SET_TORCH_HIGH INVALID_STATE: // else invalid turn off ldi TORCH_State, TORCH_State_OFF // set valid state // drop through to set off SET_TORCH_OFF: clr SP_High clr SP_Low rjmp END_LOAD_CURRENT_SETPOINT SET_TORCH_LOW: ldi SP_High, high(CURRENT_SP_LOW) ldi SP_Low, low(CURRENT_SP_LOW) rjmp END_LOAD_CURRENT_SETPOINT SET_TORCH_MED: ldi SP_High, high(CURRENT_SP_MED) ldi SP_Low, low(CURRENT_SP_MED) rjmp END_LOAD_CURRENT_SETPOINT SET_TORCH_HIGH: ldi SP_High, high(CURRENT_SP_HIGH) ldi SP_Low, low(CURRENT_SP_HIGH) rjmp END_LOAD_CURRENT_SETPOINT END_LOAD_CURRENT_SETPOINT: ret //------------------------------------- //------------------------------------- //PROCESS_RECEIVED_CHAR // 0,1,2,3 to set level // char in Temp //------------------------------------- PROCESS_RECEIVED_CHAR: cpi Temp, 48 // '0' breq PROCESS_RECEIVED_CHAR_TORCH_OFF // else cpi Temp, 49 // '1' breq PROCESS_RECEIVED_CHAR_TORCH_LOW // else cpi Temp, 50 // '2' breq PROCESS_RECEIVED_CHAR_TORCH_MED // else cpi Temp, 51 // '3' breq PROCESS_RECEIVED_CHAR_TORCH_HIGH // else cpi Temp, 117 // 'u' breq PROCESS_RECEIVED_CHAR_TORCH_UP cpi Temp, 85 // 'U' breq PROCESS_RECEIVED_CHAR_TORCH_BIGUP cpi Temp, 100 // 'd' breq PROCESS_RECEIVED_CHAR_TORCH_DOWN cpi Temp, 68 // 'D' breq PROCESS_RECEIVED_CHAR_TORCH_BIGDOWN // else invalid char so just ignore rjmp END_PROCESS_RECEIVED_CHAR PROCESS_RECEIVED_CHAR_TORCH_OFF: // 0 == off ldi New_TORCH_State, Torch_State_OFF rjmp PROCESS_RECEIVED_CHAR_UPDATE PROCESS_RECEIVED_CHAR_TORCH_LOW: // 1 == low ldi New_TORCH_State, Torch_State_LOW rjmp PROCESS_RECEIVED_CHAR_UPDATE PROCESS_RECEIVED_CHAR_TORCH_MED: // 2 == med ldi New_TORCH_State, Torch_State_MED rjmp PROCESS_RECEIVED_CHAR_UPDATE PROCESS_RECEIVED_CHAR_TORCH_HIGH: // 3 == high ldi New_TORCH_State,Torch_State_HIGH // rjmp PROCESS_RECEIVED_CHAR_UPDATE PROCESS_RECEIVED_CHAR_UPDATE: rcall UPDATE_TORCH_STATE rjmp END_PROCESS_RECEIVED_CHAR PROCESS_RECEIVED_CHAR_TORCH_UP: PROCESS_RECEIVED_CHAR_TORCH_BIGUP: ldi TORCH_State, Torch_State_VARIABLE ldi Temp,10 add SP_Low, Temp // add 10 clr Temp adc SP_High, Temp // add carry rjmp END_PROCESS_RECEIVED_CHAR PROCESS_RECEIVED_CHAR_TORCH_DOWN: PROCESS_RECEIVED_CHAR_TORCH_BIGDOWN: ldi TORCH_State, Torch_State_VARIABLE subi SP_Low, 10 // subtract 10 sbci SP_High, 0 // subtract carry // is it still posative brpl END_PROCESS_RECEIVED_CHAR // else limit to zero ldi SP_Low, 0 ldi SP_High, 0 ldi TORCH_State, Torch_State_OFF rjmp END_PROCESS_RECEIVED_CHAR END_PROCESS_RECEIVED_CHAR: // limit setpoint to CURRENT_SP_MAX mov Temp, SP_Low subi Temp, low(CURRENT_SP_MAX) mov Temp, SP_High sbci Temp, high(CURRENT_SP_MAX) brlo RET_PROCESS_RECEIVED_CHAR // else if > load CURRENT_SP_MAX instead ldi SP_Low, low(CURRENT_SP_MAX) ldi SP_High, high(CURRENT_SP_MAX) RET_PROCESS_RECEIVED_CHAR: ret //------------------------------------- //************************************************************************* // // RS232 controlled Led Driver Program // As well as allowing the led level to be controlled by the push button // this program changes the level based on the character sent via RS232. // 0 => off // 1 => low, 2 => med, 3 => high // u or U increment setpoint by 10 // d or D decrement setpoint by 10 // limit SP at 0 and 1023 // // Each 0.5sec send back via RS232 the ADC reading // //************************************************************************* RESET: cli // disable interrupts // set clock to 8Mhz i.e. remove div8 that is set by programmer ldi Temp, (1<