; Copyright 2021 KoroLion (https://github.com/KoroLion)

#define __SFR_OFFSET 0
#define LED_PORT 7 // led port
 
#include "avr/io.h" // #include "avr/iom328p.h"

.section .bss
lm35_port: .byte 0

.section .text

.global lm35_init
.global lm35_read

.global led_init
.global led_enable
.global led_disable


led_init:
  sbi   DDRD, LED_PORT
  ret

led_enable:
  sbi PORTD, LED_PORT
  ret
  
led_disable:
  cbi PORTD, LED_PORT
  ret


.macro pin_mode_input port
  ; sets analog port as input
  ldi r31, 1 ; means port 0
  cpi \port, 0 
  breq end_convert ; = 0 => no need to convert (already set mask to port 0)
  mov r30, \port
convert:
  lsl r31 ; logic shift left ~ r31 * 2
  dec r30
  cpi r30, 0
  brne convert
end_convert:
  com r31 ; inverting mask (00001000 -> 11110111)

  ; setting correct DDRC using mask
  in r30, DDRC
  and r30, r31 ; applying mask (11111111 and 11110111 = 11110111)
  out DDRC, r30
.endm

.macro adc_read port, res_high, res_low
  ldi r30, 0b00000000
  sts PRR, r30

  ; first two bits are for the ref voltage 
  ; 11 means using internal 1.1v atmega voltage for adc
  ; last three bits are for the analog port number
  ldi r30, 0b11000000 
  or r30, \port
  sts ADMUX, r30

  ; last 3 bits (111 means 128)
  ; ADC Prescaler Select Bits (ADPS): ADPS2, ADPS1 and ADPS0 bits are used to set circuit clock frequency. 
  ; For ADC circuitry to work at its maximum resolution needs to be supplied with 50 kHz to 200 kHz frequency as per the datasheet; 
  ; but the system clock will be generally higher (8 MHz, 10 MHz, 16 MHz etc). 
  ; To reduce it to required frequency we use ADC prescaler bits. Suppose we have system clock with frequency 10Mhz (10000000 Hz) and set division factor to 64, 
  ; then ADC clock frequency is 10000000/64 = 156250Hz = 156.25 KHz, which is between 50 to 200 KHz as mentioned in the datasheet.
  ; (http://www.robotplatform.com/knowledge/ADC/adc_tutorial_2.html)
  ; 
  ; 16 MHz / 128 = 16000000 Hz / 128 = 125000 Hz = 125 KHz => OK!
  ldi r30, 0b11000111
  sts ADCSRA, r30

wait_adc:
  lds r30, ADCSRA
  ldi r31, 0b01000000
  and r31, r30

  cpi r31, 0
  brne wait_adc

  lds \res_low, ADCL
  lds \res_high, ADCH
  ; clearing unused bits to be sure they're zero
  ldi r31, 0b00000011
  and \res_high, r31
.endm

.macro divw10 divident_high, divident_low
  ; r30 - res
  ; divident_low - quotient
  ldi r30, 0
division:
  cpi \divident_low, 10
  brlo division_low_end ; <
continue_division:
  sbiw \divident_low, 10
  inc r30
  rjmp division
division_low_end:
  cpi \divident_high, 0
  brne continue_division
  cpi \divident_low, 5
  brlo not_inc ; <
  inc r30
not_inc:
.endm 


lm35_init:
  ; r25:r24 is the first arg
  subi r24, 14 ; because A0 is D14
  pin_mode_input r24
  sts lm35_port, r24
  ret

lm35_read:
  cli ; forbids interruptions
  
  lds r24, lm35_port
  adc_read r24, r25, r24
  
  divw10 r25, r24
  mov r24, r30 ; uint8_t return value
  adiw r24, 2 ; due to LM35 formula (2C to 150C)
  
  clr r1 ; docs requirement
  sei ; allow interruptions

  ret
