; **************************************************************
; * Sinus-Generator mit einem AVR-Timer/Counter als Pulsweiten-*
; * Modulator, einem 3-fach-RC-Filter und einem Emitterfolger  *
; * Geschrieben fuer einen ATtiny13 mit 9,6MHz internem Takt-  *
; * oszillator, Ausgang ist OC0B (PB1, DIL-Pin 6), Version 1,  *
; * Sinus eingestellt auf 1754,4 Hz, Aufloesung 32 Stufen,     *
; * CTC=171, RC-Filter: R=2k2, C=2n2, Emitterfolger: Re=1k,    *
; * BC183B, hFE=260                                            *
; * (C)2005 by http://www.avr-asm-tutorial.net                 *                                                           *
; **************************************************************
;
.NOLIST
.INCLUDE "tn13def.inc"
.LIST
;
; ***********************************************
;               S C H A L T B I L D
; ***********************************************
;
;       10k    ___________
;       ___  1/           |8
;+5V O-|___|-|RES      VCC|--O+5V                     +5V
;T-Kurz __   2|   ATMEL    |7                           O
;   |--O  O--|PB3      PB2|--O PTT-Ausgang              |C
;T-Lang __  3|  ATtiny13  |6  ___     ___     ___    B|/ BC
;   |--O  O--|PB4      PB1|--|___|-O-|___|-O-|___|-O--| 183B
;           4|    PDIP    |5   R   |   R   |   R   |  |\ E
;      |--O--|GND      PB0|--     ---     ---     ---   O--O NF-
;            |____________|      C---    C---    C---   |    aus
;                                  |       |       |   +-+
;                                 ---     ---     ---  | |Re
;                                                      +-+
;                                                       |
;                                                      ---
;
; ***********************************************
;        E I N S T E L L U N G E N
; ***********************************************
;
; Anderbare Einstellungen, maximum = 37 Sekunden!
;
.EQU cKurz = 2 ; 2 Sekunden Dauer fuer kurzes Signal
.EQU cLang = 10 ; 10 Sekunden Dauer fuer langes Signal
.EQU bKurz = 3 ; Portbit fuer Taster kurze Signale
.EQU bLang = 4 ; Portbit fuer Taster lange Signale
.EQU bTx   = 2 ; Portbit fuer PTT-Ausgang aktiv high
.EQU bPwm  = 1 ; Portbit fuer PWM-Ausgang, nicht aenderbar
;
; ***********************************************
;    R E G I S T E R - D E F I N I T I O N E N
; ***********************************************
;
; verwendet: R0 fuer Lesen aus Sinustabelle
; frei: R1..R14
.DEF rSreg = R15 ; Status retten bei Interrupts
.DEF rmp = R16 ; Multipurpose Register ausserhalb Int
.DEF rimp = R17 ; Multipurpose Register innerhalb Int
; frei: R18..R23
.DEF rDauerL = R24 ; Signaldauer-Zaehler, LSB
.DEF rDauerM = R25 ; dto., MSB
; frei: X, Y = R26..R29
; verwendet: Z = R31:R30 fuer Tabelle-Lesen
;
; ***********************************************
;          K O N S T A N T E N
; ***********************************************
;
; Nicht veraenderbare Konstanten
;
.EQU cClock = 9600 ; Prozessortaktfrequenz in kHz
.EQU cCtc   = 171 ; TC0-CTC
.EQU cKSin = cKurz * 1754 ; Anzahl Sinus bei kurzem Signal
.EQU cLSin = cLang * 1754 ; Anzahl Sinus bei langem Signal
.IF (cKSin>65536)||(cLSin>65536) ; Falsche Parameter
	.ERROR "Zeit zu lang!"
	.ENDIF
;
; Reset- and interrupt vectors
;
.CSEG
.ORG $0000
	rjmp main ; Reset Vektor
	reti ; INT0-Interrupt, nicht verwendet
	rjmp PcInt ; PIN-Change-Interrupt
	rjmp Tc0OvfInt ; TC0-Overflow-Interrupt
	reti ; EERDY-Interrupt, nicht benutzt
	reti ; ANACOMP-Interrupt, nicht benutzt
	reti ; TC0COMPA-Interrupt, nicht benutzt
	reti ; TC0COMPB-Interrupt, nicht benutzt
	reti ; WDT-Interrupt, nicht benutzt
	reti ; ADC-Interrupt, nicht benutzt
;
; *****************************************************
;  I N T E R R U P T - S E R V I C E - R O U T I N E N
; *****************************************************
;
; Pin-Change-Interrupt, startet Signal
;
PcInt:
	in rSreg,SREG ; Status sichern
	tst rDauerL ; Signal schon gestartet?
	brne PcIntR ; ja, ignoriere Signal
	tst rDauerM ; teste auch MSB
	brne PcIntR ; schon gestartet
	sbis PINB,bKurz ; Kurzes Signal auf 1?
	rjmp PcIntK ; nein, starte kurze Signaldauer
	sbic PINB,bLang ; Langes Signal auf 0?
	rjmp PcIntR ; kein aktives Signal, ignoriere Int
	ldi rDauerM,HIGH(cLSin) ; setze Zaehler auf Lang
	ldi rDauerL,LOW(cLSin)
	rjmp PcIntS ; Starte Signalausgabe
PcIntK:
	ldi rDauerM,HIGH(cKSin) ; starte kurzes Signal
	ldi rDauerL,LOW(cKSin)
PcIntS:
	ldi rimp,1<<TOIE0 ; starte TC0-Interrupts
	out TIMSK0,rimp
	sbi PORTB,bTx ; starte Ausgang
PcIntR:
	out SREG,rSreg ; Status wieder herstellen
	reti ; Zurueck vom Interrupt
;
; TC0-Overflow-Interrupt, tritt bei Erreichen des
;   CTC-Wertes auf gibt naechsten Tabellenwert aus
;
Tc0OvfInt:
	in rSreg,SREG ; Status sichern
	lpm R0,Z+ ; lese Tabellenwert und erhoehe Z
	out OCR0B,R0 ; gib an TC0-COMPB weiter
	cpi ZL,LOW(2*SinTabEnde) ; Ende der Tabelle?
	brne Tc0OvfIntR
	ldi ZH,HIGH(2*SinTab) ; an den Anfang der Tabelle
	ldi ZL,LOW(2*SinTab) ; zuruecksetzen
	sbiw rDauerL,1 ; Anzahl Sinus minus Eins
	brne Tc0OvfIntR ; noch nicht, mache weiter
	ldi rimp,0 ; stelle Interrupts ab, TC0 macht allein
	out TIMSK0,rimp ; mit letzten OCR0B-Wert weiter
	cbi PORTB,bTx ; stoppe Ausgang
Tc0OvfIntR:
	out SREG,rSreg ; Status wieder herstellen
	reti ; Zurueck vom Interrupt
;
; ***********************************************
;          H A U P T P R O G R A M M
; ***********************************************
;
main:
	; Stapel initiieren
	ldi rmp,LOW(RAMEND) ; Stapelzeiger auf Ende SRAM
	out SPL,rmp
	; Taktfrequenz auf 9,6MHz
	ldi rmp,1<<CLKPCE ; Clock Prescaler Change Enable
	out CLKPR,rmp
	ldi rmp,0 ; Teiler auf 1
	out CLKPR,rmp
	; Port-Ein- und Ausgaenge konfigurieren
	ldi rmp,(1<<bPwm)|(1<<bTx) ; OC0B und Tx als Ausgang
	out DDRB,rmp ; in Richtungsregister
	ldi rmp,(1<<bPwm)|(1<<bKurz)|(1<<bLang)|(1<<bTx) ; Ausgang OC0B
	out PORTB,rmp ; und Aktiv auf 1, Pullup auf Eingaenge
	; Zaehler zu Anfang auf 38 Sekunden
	clr rDauerM
	clr rDauerL
	; Zeiger Z auf Tabellenanfang
	ldi ZH,HIGH(2*SinTab) ; Z auf Sinus-Tabelle
	ldi ZL,LOW(2*SinTab)
	lpm R0,Z+ ; ersten Wert aus Tabelle lesen
	out OCR0B,R0 ; an TC0-COMPB ausgeben
	; Timer TC0 einstellen
	ldi rmp,cCtc ; CTC auf 171 setzen
	out OCR0A,rmp
	ldi rmp,(1<<COM0B1)|(1<<WGM01)|(1<<WGM00) ; Fast PWM,
	out TCCR0A,rmp ; 0C0B bei Match auf 0, TOP=OCRA
	ldi rmp,(1<<WGM02)|(1<<CS00) ; Fast PWM, Prescaler=1,
	out TCCR0B,rmp ; Timer starten
	ldi rmp,1<<TOIE0 ; TC0-Interrupts ermoeglichen
	out TIMSK0,rmp
	; Pin-Change-Interrupts einstellen
	ldi rmp,(1<<bKurz)|(1<<bLang) ; Bits maskieren
	out PCMSK,rmp
	ldi rmp,1<<PCIE ; Pin-Change-Int einschalten
	out GIMSK,rmp
	; Sleep-Mode einstellen
	ldi rmp,1<<SE ; Sleep Enable, Mode 0
	out MCUCR,rmp
	; Interrupts generell einschalten
	sei
	; Hauptprogramm-Loop
Loop:
	sleep ; CPU schlafen legen
	nop ; Dummy nach aufwachen
	rjmp Loop ; wieder schlafen legen
;
; ***********************************************
;           S I N U S - T A B E L L E
; ***********************************************
;
; Sinustabelle fuer Pulsweiten-Modulator
;   fuer CTC=171, niedrigster COMPB-Wert=18
;
SinTab:
.DB 94, 109, 123, 136, 148, 157, 164, 169 ; 0..90 Grad
.DB 170, 169, 164, 157, 148, 136, 123, 109 ; 90..180 Grad
.DB 94, 79, 65, 52, 40, 31, 24, 19 ; 180..270 Grad
.DB 18, 19, 24, 31, 40, 52, 65, 79 ; 270..360 Grad
SinTabEnde:
;
; End of source code
