//======================================================
//   (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
//======================================================
// BasicLedDriver.asm
//
// Main:  ATtiny84
//      mode: ISP mode
//
// Program:  ATtiny84
//    Flash - Input HEX File: yourdir/BasicLedDriver.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
//---------------------------------------------------------

.include "tn84def.inc"

.equ CURRENT_SP = 1000  // the current setpoint, 1000 = full scale current, approx 500mA

.equ SW_B = PB1  // define name for switch input pin
.equ uC_OUTPUT_B = PB0  // define name for uC output pin

.def Temp = r16// Temporary register

.def ADCLow = r18 // low byte of adc
.def ADCHigh = r19 // high byte of adc

; ***** INTERRUPT VECTORS ************************************************
.org 0x0000
   rjmp RESET
.org ADCCaddr  // = 0x000d	; ADC Conversion Complete
   rjmp  ADC_INT

//---------------------------------------------
//  Main code
//  Program starts here
//---------------------------------------------
RESET:
    cli  // disable interrupts
    // set clock to 4Mhz so can run down to 1.8V
    ldi Temp, (1<<CLKPCE)
    out CLKPR, Temp  // enable clock change for next 4 cycles
    ldi Temp, (1<<CLKPS0) // divide by 2 for 4Mhz clock  
    out CLKPR,Temp  // set 4Mhz clock

    // uC_OUTPUT_B (PB0) is an output for driving the led  DDB0=1, PB0=0 low OFF to start
    // SW_B (PB1) is the switch input  DDB1=0, PB1=1 with pullup
    ldi Temp,(1<<SW_B)
    ldi r17, (1<<uC_OUTPUT_B)
    out PORTB,Temp
    out DDRB,r17

    // Enable ADC
    ldi Temp, (1<<ADEN) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS0) 
    // (1<<ADEN) enable ADC
    //  ADSC zero do not start conversion yet
    //  ADATE zero do not trigger conversion
    //  (1<<ADIE) enable ADC interrupt need I-bit in SREG set also
    //  (1<<ADPS2) (1<<ADPS0) pre-scale for 4Mhz clock and <200Khz ==> >20 pre-scale say 32
    //   i.e. 1,0,1  for 4Mhz/32 = 125Khz
    out ADCSRA, Temp       // set the  ADCSR

    // set Vref 1.1V (1,0) and the ADC mux inputs and gain (101101) PA2+ve, PA1-ve, x20
    ldi Temp, (1<<REFS1) | (1<<MUX5) | (1<<MUX3) | (1<<MUX2) | (1<<MUX0)
    out ADMUX, Temp

    sei  // enable interrupts
E_LOOP:
    // set up for ADC conversion and then sleep
    ldi Temp, (1<<SM0) | (1<<SE)
    out MCUCR, Temp   // ADC noise reduction rest zero, enable sleep
    sleep     // goto sleep  
    // wake up on ADC interrupt 
    // on completion of interrupt returns here
    rjmp E_LOOP  // loop for next ADC 
//*************************** END_RESTART_CODE **********************


//-------------------------------------
//  ADC interrupt, result available
//-------------------------------------
ADC_INT:
    // save ADC results
    in ADCLow,ADCL
    in ADCHigh,ADCH
   
    subi ADCLow, low(CURRENT_SP) 
    sbci ADCHigh, high(CURRENT_SP)
    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_ADC_INT

CURRENT_HIGH:
    // ADC > setpoint  ==> make output low
    cbi  PORTB, uC_OUTPUT_B   // set output low
    rjmp END_ADC_INT

// all paths jmp to here
END_ADC_INT:
reti  // interrupts enabled here
//----END ADC_INT -------------------------------------------------

