;----------------------------------------------------------------------
; Spannungswert (an Heißleiter oder analogem Temperatursensor) über ADC
; ermitteln und akustisch dezimal ausgeben
; Erläuterungen und Makros: www.schramm-software.de/tipps
;----------------------------------------------------------------------
; Prozessor: ATtiny13
; Takt     : 1,2 MHz
; Sprache  : Assembler
; Version  : 1.1
; Autor    : Dr. Michael Schramm, siehe www.schramm-software.de
; Datum    : Februar 2012
;----------------------------------------------------------------------
; Portbelegung:
; PB0,PB1: O  Tonsignal (Piezo-Piepser oder Lautspr. über R-C)
; PB2:     I  GND-Pegel aktiviert 1,1V-Referenz des ADCs
; PB3:     O  VDD-Pegel, solange Messung aktiv (für Sensor über R1)
; PB4:     I  Analog-Eingang für ADC-Messung
; PB5:     I  (Reset)
;----------------------------------------------------------------------
.include "tn13def.inc"
.include "../makros.inc"
; Als globale Variablen genutzte Register:
.def intreg  = r14 ;SREG-Zwischenspeicher bei Interrupt
.def timovl  = r15 ;Zähler für Timer-Overflow-Interrupts
.def r_adcl  = r24 ;Übergabe des ADC-Messwertes (Low-Byte)
.def r_adch  = r25 ;Übergabe des ADC-Messwertes (High-Byte)
.def nullreg = r31 ;(ZH) Konstante Null
;----------------------------------------------------------------------
.DSEG ;SRAM-Belegung
dezzahl:  .byte 6
tmp_word: .byte 2
;----------------------------------------------------------------------
.CSEG
;Reset- und Interruptvektoren
  rjmp start     ;0 RESET, Brown-out Reset, Watchdog Reset
  reti           ;1 INT0 External Interrupt Request 0
  reti           ;2 PCINT0 Pin Change Interrupt Request 0
  rjmp i_t0ov    ;3 TIM0_OVF Timer/Counter Overflow
  reti           ;4 EE_RDY EEPROM Ready
  reti           ;5 ANA_COMP Analog Comparator
  reti           ;6 TIM0_COMPA Timer/Counter Compare Match A
  reti           ;7 TIM0_COMPB Timer/Counter Compare Match B
  reti           ;8 WDT Watchdog Time-out
  in r_adcl,ADCL ;9 ADC ADC Conversion Complete
  in r_adch,ADCH
reti
;----------------------------------------------------------------------
zehnerpot: ;Zehnerpotenzen als 2-Byte-Zahlen für Funktion bin2dec
; Tabelle muss im Anfangsbereich des Speichers stehen, damit das
; High-Byte der Adresse 0 ist
.dw 10000,1000,100,10,1

start:
; zunächst Stromsparmaßnahmen und Initialisierung
  out_i DDRB, 0b01011  ;Datenrichtung von Port B
  out_i PORTB,0b00100  ;Startzustand (PB3 zunächst auf GND)
  out_i DIDR0,0b11011  ;PINB-Input-Buffer ausschalten
  sbi ACSR,ACD         ;Analog-Comparator ausschalten
  ldi r18,2            ;Wert für ADMUX
  sbic PINB,2          ;PB2 auf GND-Pegel?
  rjmp init2
  cbi PORTB,2          ;PB2 hochohmig, um Strom zu sparen
  sbr r18,(1<<REFS0)   ;später interne ADC-Referenzspannung aktivieren
init2:
; ADC konfigurieren mit Interrupt und Vorteiler 8 (-> 150 kHz)
  out ADMUX,r18        ;Input = ADC2 = PB4
  out_i ADCSRA,(1<<ADIE)+(3<<ADPS0)
  out_i SPL,low(RAMEND);Stackpointer setzen, 8bit-Pointer bei Tiny13
  clr nullreg          ;bleibt dauerhaft null
  out_i TIMSK0,1<<TOIE0;Timer-Overflow-Interrupt aktivieren
  sei                  ;Interrupts generell zulassen
  ldi r17,9
  rcall waitr17        ;etwa 1/2 Sekunde warten
; Messung durchführen
  sbi PORTB,3          ;VDD an PB3, Strom an für Heißleiter oder LMx35
  sbi ADCSRA,ADEN      ;ADC einschalten (schaltet ggf. int. Ref.spg. ein)
  for r16,30,wait_ref_lp       ;gut 70 µs warten, damit sich ggf. die
  next_down r16,wait_ref_lp    ;interne Referenzspannung stabilisiert
  out_i MCUCR,(1<<SE)+(1<<SM0) ;Noise-reduction-Modus
  sleep                        ;Warten auf ADC-Ergebnis (1. Messung)
; die erste Messung war nur zum 'Warmlaufen' des ADCs
  out MCUCR,r16                ;Noise-reduction-Modus
  sleep                        ;Warten auf ADC-Ergebnis (2. Messung)
  cbi ADCSRA,ADEN              ;ADC ausschalten
  cbi PORTB,3                  ;Stromfluss durch R1 + Sensor stoppen
; Messwert akustisch ausgeben
  ldi_hl x,24      ;Adresse von R24 (= R_ADCL)
  ldi_hl y,dezzahl ;Speicherbereich für Dezimalzahl-Ergebnis
  rcall bin2dec
  tst r18          ;Sonderfall 0 berücksichtigen
  brne ziff_ausg
    st y,r18       ;Dezimalziffer 0 schreiben
    inc r18        ;1 Ziffer ausgeben, also '0'
  ziff_ausg:       ;R18 Ziffern ab (Y) ausgeben
    ld r0,y+
    rcall zifferton
  next_down r18,ziff_ausg
; Ausschalten
  out_i MCUCR,(1<<SE)+(1<<SM1) ;Power-down-Modus
  sleep                        ;Tiefschlaf, warten auf Reset

; ******************* Unterprogramme / Funktionen *********************

tonsignal: ; *** Tonsignal an PB0/PB1 ausgeben
; (schlichte Realisierung über Zählschleife)
; R16: Periodenlänge als Vielfaches von 100 µs
; R17: 1/10 der Anzahl der zu erzeugenden Perioden
  push xl
  push xh
  push r20
  push r2
  sbi PORTB,0              ;Startzustand der Ports B0 und B1
  tonsiglp:
    for r20,10,ton_10per   ;10 Perioden erzeugen
      for xh,2,ton_period  ;für die beiden halben Perioden
        mov r2,r16         ;R16 halbe Perioden erzeugen
        wt_halb_per:       ;1/2 Periode erzeugen
          for xl,19,wt50lp ;50 µs warten (bei Taktfrequenz 1,2 MHz)
          next_down xl,wt50lp
        next_down r2,wt_halb_per
        ldi xl,3
        out PINB,xl        ;Ports B0 und B1 umschalten
      next_down xh,ton_period
    next_down r20,ton_10per
  next_down r17,tonsiglp
  cbi PORTB,0              ;B0 und B1 auf 0 (spart ggf.
  cbi PORTB,1              ;Energie am Lautspr.)
  pop r2
  pop r20
  pop xh
  pop xl
ret
;----------------------------------------------------------------------
zifferton: ; *** R0 als Tonsignalfolge ausgeben
  push r0
  push r16
  push r17
  tst r0
  brne ziff_pieps
  ldi r16,20         ;500 Hz für die Ziffer 0
  ldi r17,25         ;250 Perioden = 1/2 Sekunde
  inc r0             ;damit die folgende Schleife 1mal durchlaufen wird
  rjmp ausg_ziff_ton ;Sprung in die Schleife ...
  ziff_pieps:        ;R0 Piepser für eine Dezimalziffer
    ldi r16,10       ;1 kHz
    ldi r17,25       ;250 Perioden = 1/4 Sekunde
    ausg_ziff_ton:
    rcall tonsignal
    ldi r17,5
    rcall waitr17    ;nach jedem Pieps 5/18 Sek. warten (knapp 300 ms)
  next_down r0,ziff_pieps
  ldi r17,11
  rcall waitr17      ;nach jeder Ziffer zusätzlich gut 600 ms Pause
  pop r17
  pop r16
  pop r0
ret
;----------------------------------------------------------------------
; Warte-Routinen sind energiesparend mit Timer + SLEEP-Modus realisiert
waitr17: ;  *** R17 * 1/18 Sekunde warten
  push r16
  clr timovl ;Überlauf-Reg. für den Timer, wird in Int.-Rout. inkrementiert
  out TCNT0,nullreg       ;Counter-Wert nullsetzen
  out_i TIMSK0,(1<<TOIE0) ;Timer-Overflow-Interrupt aktivieren
  out_i TCCR0B,(1<<CS02)  ;Timer starten mit Systemtakt / 256 = 4,6875 kHz
  wtr17lp:
    out_i MCUCR,(1<<SE)   ;Idle-Modus
    sleep                 ;CPU schläft, wartet auf Interrupt (Timer)
	cp timovl,r17         ;r17 Timer-Überläufe erreicht?
  brcs wtr17lp            ;noch nicht => weiter warten
  out TCCR0B,nullreg      ;Timer stoppen
  pop r16
ret
;----------------------------------------------------------------------
bin2dec: ; *** Dezimaldarstellung einer 2-Byte-Binärzahl berechnen
; Input:  X zeigt auf Beginn der Binärzahl (LSB) im SRAM
;         Y zeigt auf Beginn des SRAM-Speicherbereichs der Dezimalzahl
; Output: Dezimalzahl ab (Y), eine Dez.ziffer pro Byte, Start mit MSD
;         R18 = Anzahl der Dezimalziffern (0 bei 0)
; Binärzahl (X) wird zu null abgebaut
  push r0
  push r1
  push r10
  push r11
  push r16
  push r17
  push zl
  push zh
  clr r18
  mov r11,xl             ;R11 = Kopie von XL
  ldi_hl z,(zehnerpot*2) ;mal 2 wegen wortweiser Adressierung im Flash
  for r16,5,b2d_nxt_ziff ;R16 = Abwärtzzähler für Zehnerpotenzen
    clr r10              ;R10 = aktuelle Ziffer
    push yl
    ldi yl,low(tmp_word)
; hier wird ausgenutzt, dass Yh im kleinen SRAM immer gleich bleibt (0)
    for r17,2,b2d_pot_copy
      lpm r0,z+          ;Zehnerpotenz vom Flash ins SRAM kopieren
      st y+,r0
    next_down r17,b2d_pot_copy
    mov xl,r11
    adiw x,2
   b2d_nxt_test:
    for r17,2,b2d_compare
      ld r0,-y           ;Zehnerpotenz
      ld r1,-x           ;Rest der Ausgangszahl
      cp r1,r0
      brcs b2d_ziff_ok   ;kein weiterer Abzug möglich
      brne b2d_subtract
    next_down r17,b2d_compare
   b2d_subtract:
    mov xl,r11
    ldi yl,low(tmp_word)
    rcall x_min_y
    inc r10
    rjmp b2d_nxt_test
   b2d_ziff_ok:
    pop yl
    tst r18
    brne b2d_st_ziff
    tst r10
    breq b2d_ziff_fertig
    b2d_st_ziff:
    st y+,r10
    inc r18
  b2d_ziff_fertig:
  next_down r16,b2d_nxt_ziff
  sub yl,r18             ;Y wieder auf Dezimalzahlbeginn
  pop zh
  pop zl
  pop r17
  pop r16
  pop r11
  pop r10
  pop r1
  pop r0
ret
;----------------------------------------------------------------------
x_min_y: ; *** Subtraktion, 2-Byte-Integer (X) <- (X) - (Y)
; r0, r1 werden verändert
; x und y zeigen anschließend hinter die Zahlen, sind also um 2 erhöht
  push r17
  clc
  for r17,2,xminy_lp
    ld r0,x
    ld r1,y+
    sbc r0,r1
    st x+,r0
  next_down r17,xminy_lp
; sbiw x,2
; sbiw y,2
  pop r17
ret

; ************************ Interrupt-Routinen *************************

i_t0ov: ; *** Timer/Counter Overflow
; Prescaler=256 => 18,3 Overflow-Interrupts pro Sekunde
  in intreg,sreg  ;Statusregister merken
  inc timovl      ;Zähler für (in etwa) 18tel Sekunden
  out sreg,intreg ;Statusregister wiederherstellen
reti

; ******************************* ENDE *******************************
