Source file: /~heha/ewa/PowerZ/PowerZu8.zip/PowerZ.cpp

/* Sollte man so einen Leistungs-MOSFET zur Z-Diode machen können?
   Erst mal mit ATtiny25 und seiner High-Speed-PLL
   Eigentlich müsste auch ein ATtiny13 reichen
1	PB5	!RESET	O	frei — oder meldet Abschalten der Quelle nach Timeout
2	PB3	-	O	H zündet Überspannungsthyristor
3	PB4	ADC2	I	Misst Hochspannung per Spannungsteiler 640:1
4	GND	-	-	Masseanschluss
5	PB0	-	O	L meldet Begrenzungseinsatz an Optokoppler
6	PB1	OC1A	O	Gatespannung für Leistungs-MOSFETs (ATtiny13: OC0B)
7	PB2	ADC1	I	Misst Strom per Shuntwiderstand 1 Ω (pro Transistor), also 1 V/A
8	Ucc	-	-	Speisespannung, aus Hochspannung per Längsregler „verheizt“
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#define UZ 500.0		// Simulierte Z-Spannung in Volt
#define PMAX 150.0		// Max. Dauerleistung am Transistor in Watt
#define SIGLEN 2.0		// Melde-Zeit in s (Monoflop-Verhalten)
	// wenn der Thyristor zündet dann kürzer weil die Speisespannung zusammenbricht
#define STOPDLY 0.5		// Wartezeit bis zur Notabschaltung: „Alle Maschinen Stop“


FUSES={0b11000001,0b01010100,0b11111111};
// Unterspannungsdetektor auf = 4,3 V, EEPROM-Speicher behalten, RESET deaktivieren
// Taktfrequenz = 16 MHz mit 64-MHz-PLL, kürzeste Hochlaufzeit

volatile uint8_t vu,vi;		// Messwert für Spannung bzw. Strom
// vu = U*256/Teiler/Uref
// vi = I*256*Rshunt/Uref
// vp = U*I * Rshunt/Teiler * (256/Uref)²
// GCC-Fehler: Register gehen nicht korrekt, da diese vor dem Sleep-Befehl geladen werden.

#define VU (uint8_t)(UZ*256/640/1.1)		// Z-Schwelle als Messwert (181)
#define VP (uint16_t)(PMAX/640*65536/1.1/1.1)	// Leistungsgrenze als Produkt von vu und vi (12694)
#define SL (uint16_t)(SIGLEN*F_CPU/128/13/2)	// Startwert für Zykluszähler zur Signalisierung (Monoflop)
#define SD (uint16_t)(STOPDLY*F_CPU/128/13/2)	// Startwert für Zykluszähler

ISR(ADC_vect) {
 uint8_t v=ADCH;
 if (v==0xFF) {
// Wenn die Spannung die Referenzspannung von 1,1 Volt erreicht (egal ob Spannungss- oder Strommessung)
  PORTB|=0b00001000;		// Thyristor durchzünden
  sleep_cpu();			// Mikrocontroller stoppen (bei gesperrten Interrupts bleibt sleep hängen)
 }
 if (ADMUX&1) {	// die LAUFENDE Messung misst ADC1?
  ADMUX=0b10100010;
  vu=v;		// Das VORHERGEHENDE Ergebnis kommt von ADC2: Spannung
 }else{
  ADMUX=0b10100001;
  vi=v;		// Das VORHERGEHENDE Ergebnis kommt von ADC1: Strom
 }
}


int main() {
 MCUCR= 0b00100000;		// Sleep aktivieren
 PRR  = 0b00000110;		// Timer0 und USI wird nicht gebraucht
 sei();
// Ports initialisieren
 PORTB= 0b00100001;
 DDRB = 0b00101011;
// D/A-Wandler (PWM-Generator) starten
 PLLCSR=0b00000111;	// PCKE setzen
 OCR1C=255;
 TCCR1= 0b01100001;	// Schnelle PWM an OC1A mit f = 64MHz/256 = 250 kHz
// A/D-Wandler im Dauerlauf starten, misst Spannung und Strom im Wechsel mit 8 Bit Auflösung
 ADMUX= 0b10100001;	// Wert links ausgerichtet
 ADCSRA=0b11111111;	// Teiler 128 -> Wandlerfrequenz (2 Kanäle) = 16 MHz/128/13/2 = 4,8 kHz
 ADMUX= 0b10100010;	// nächste Wandlung vorbereiten
// Hauptschleife
 uint16_t sl=0,sd=0;
 for(;;) {
  sleep_cpu();
  sleep_cpu();		// 2 Wandlungsvorgänge abwarten: 4,8 kHz bzw. 208 µs
// Innerhalb von 208µs * 255 = 53 ms ist (theoretisch) die Gatespannung von 0 bis 5 V durchgelaufen.
// Der wirksame Stellbereich wird wohl eher ΔU = 2 V sein, somit ~ 20 ms oder 1 Netzperiode.
// Die Fage ist nur, ob die SOA-Kriterien im Fall von Überschwingen eingehalten werden.
// Die Pulsweite wird hier ständig moduliert.
  uint8_t _vu=vu;
  uint8_t _vi=vi;
  char dir=0;
  if (_vu<VU) {		// Unterspannung
   if (!_vi) dir=1;	// ein bisschen Strom fließen lassen um an die Gate-Schwelle zu gehen
   else dir=-1;		// sonst Gatespannung reduzieren
   if (sl && !--sl) PORTB|=0x01;	// Melder verzögert abschalten
   PORTB|=0x20;		// Keine Notabschaltung (mehr)
   sd=SD;		// Notabschaltverzögerung neu starten
  }else{		// Überspannung!
   uint16_t vp=_vu*_vi;	// einfache Multiplikation (ob der Compiler das merkt? Nein, er nimmt eine 16x16-Routine)
   if (vp<VP) dir=1;	// Gatespannung erhöhen
   else dir=-1;		// Gatespannung herabsetzen
   PORTB&=~0x01;	// melden
   sl=SL;
   if (sd && !--sd) PORTB&=~0x20;	// nach StopDelay Notabschaltung aktivieren
  }
  int t=OCR1A;		// D/A-Ausgabewert modifizieren ohne Überläufe zu riskieren
  t+=dir;
  if (t<0) t=0;
  if (t>255) t=255;
  OCR1A=t;
 }
 return 0;
}
Detected encoding: UTF-80