#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <avr/fuse.h>
#include <avr/signature.h> // gepatcht: "const" weg!
FUSES={
0b11100010, // kein Taktteiler, normaler Startup, RC-Oszillator 8 MHz
0b01111101, // kein RESET, EEPROM-Inhalt behalten, Brownout bei 2,7 V
0b11111111, // Keine Selbstprogrammierung
};
/*
Hardware: ATtiny44
1 Ucc 5P
2 PB0 K Tasten, H-aktiv
3 PB1 K2 Spaltendekoder Katode2
4 PB3 K1 Spaltendekoder Katode1
5 PB2 K0 Spaltendekoder Katode0
6 PA7 OC0B Transistor Hochspannungsgenerator
7 PA6 OC1A Lautsprecher-Ausgang
8 PA5 Strommess-Bereichsumschaltung, nur L oder Z
9 PA4 ADC4 Strommessung
10 PA3 ADC3 Spannungsmessung
11 PA2 AIN1 Schnelle Strommessung Transistor-Emitter
12 PA1 C Schieberegister-Takt '164 auf Anzeige-Platine
13 PA0 D Schieberegister-Daten, H-aktiv
14 GND 00
Schieberegister-Anschluss (Schieberichtung MSBfirst)
3 0 QA a „1/s“ S5
4 1 QB b - S6
5 2 QC c „1/min“ S4 = P/R
6 3 QD d - S3 = PfeilRechts
10 4 QE e „1/h“ S1 = PfeilLinks
11 5 QF f - S2 = PfeilHoch
12 6 QG g - -
13 7 QH dp - -
Bedienkonzept: ↑ = drücken (L-H), ↓ = Loslassen (H-L), [p = vorhergehender Zustand]
Weiter: Links Hoch Rechts P/R Timeout
[0] Intro/Aus
xxxoFF Sollspannung anzeigen, aber ausgeschaltet, "off" blinkend
xx oFF (wenn dritte Kommastelle Null, bspw. "4.0 off" bei Prüfprogramm 4 kV)
[1] ↓ - ↓ - - (ODER)
sonst wie [1]
[1] Normalmodus (beides anzeigen wie eine Laborspannungsquelle):
xxxyyy Anzeige Spannung (in kV) und Strom (in mA)
Blinkender Punkt dazwischen
[0] - ↑ - - -
[2] ↓ - - - - (UND)
[3] - - ↓ - -
[4] H - ↓ - -
[5] ↓ - H - -
[6] - H - - 0,5 s
[7] L - L ↑ -
[8] H - L ↑ -
[9] L - H ↑ -
[A] H - H ↑ -
[2] Nur Spannung anzeigen (PfeilLinks)
u xxxx (aktuelle) Spannung in Volt
[1] ↓ - - - - (UND)
sonst wie bei [1]
[3] Nur Strom anzeigen (PfeilRechts)
i xxxx (aktueller) Strom in mA/µA
[1] - - ↓ - - (UND)
sonst wie bei [1]
[4] Nur Spannung ODER Strom anzeigen, je nach Begrenzung
u xxxx (aktuelle) Spannung in Volt
i xxxx (aktueller) Strom in mA/µA
[1] H - ↓ - - (UND)
[1] ↓ - H - -
sonst wie bei [1]
[5] Widerstand / Leitwert / Leistung anzeigen
r xxxx (aktueller) Widerstand in kΩ, MΩ
G xxxx (aktueller) Leitwert in µS, nS
P xxxx (aktuelle) Leistung in mW, W
[r-G-P] - - ↓ - - (UND)
sonst wie bei [1]
[6] Prüfmodus (PfeilHoch)
yyy tt Anzeige Strom (in mA/µA) und Restzeit (in s)
Ende durch Timeout (Okaypiep), Überstrom (Fehlerpiep) oder beliebige Taste
[p] ↑ ↑ ↑ ↑ - (ODER)
PASS t*s
FAIL Überstrom
Während dieser Text dasteht, ist die Spannungsquelle abgeschaltet,
sofern der vorhergehende Zustand [0] ist.
[p] - - - - 15 s
[7] Einstellmodus (Taste P/R)
U xxxx (maximale) Spannung in Volt
I xxxx (maximaler) Strom in mA/µA (mit verschieblicher Kommastelle)
bspw. "01.23" mit blinkender "3", dann PfeilHoch: "1.234" mit blinkender "4".
Jedoch nicht wenn vorn keine Null steht!
bspw. "9.234" mit blinkender "9", dann PfeilRechts: "10.23" mit blinkender "0".
t xx Prüfzeit in s
Hell/dunkel (nicht ein/aus!) blinkende Ziffer
Stellenauswahl mit PfeilHoch
Zahl hoch/runter mit PfeilLinks, PfeilRechts
Beenden mit P/R (1 s) oder Timeout 15 s, immer mit Speichern
[z-] ↑ - - - -
[d+] - ↑ - - -
[z+] - - ↑ - -
[U-I-t] - - - ↑ -
[p] - - - H 0,5 s
[p] - - - - 15 s
[8] Einstellen PID-Reglerparameter Spannungsreglung (PfeilLinks und P/R)
P xxxx hexadezimaler vzl. P-Parameter
I xxxx hexadezimaler vzl. I-Parameter (I hier linksbündig)
d xxxx hexadezimaler vzl. D-Parameter
Bedientasten wie im Einstellmodus
[9] Einstellen PID-Reglerparameter Stromreglung (PfeilRechts und P/R)
wie [8]
[A] dritte Hintertür (PfeilLinks+PfeilRechts und P/R)
yymmdd Firmware-Versionsanzeige
hhmmss Betriebsstundenzähler
RES x Auswahl Betriebsart nach Reset (0, 1, 2, 3, 4 oder 5)
LEDs: 1/s kV (sonst V)
1/min mA
1/h µA
*/
// 4 Tasten
#define KL 0x10 // Key Left
#define KU 0x04
#define KR 0x08
#define KP 0x20
// 3 LEDs
#define LS 0x01
#define LM 0x04
#define LH 0x10
typedef unsigned char byte;
typedef unsigned short word;
// Ziffern
#define _0 0x3F
#define _1 0x06
#define _2 0x5B
#define _3 0x4F
#define _4 0x66
#define _5 0x6D
#define _6 0x7D
#define _7 0x07 // Alternative 0x27
#define _8 0x7F
#define _9 0x6F
#define DP 0x80 // Dezimalpunkt (zum VerODERn)
// Buchstaben
#define _A 0x77
#define _b 0x7C // kann mit Z6 verwechselt werden
#define _C 0x39
#define _c 0x58
#define _d 0x5E
#define _E 0x79
#define _F 0x71
#define _G 0x3D
#define _H 0x76
#define _h 0x74
#define _I 0x06 // wie Z1
#define _i 0x04
#define _J 0x1E
#define _L 0x38
#define _n 0x54
#define _O 0x3F // wie Z0
#define _o 0x5C
#define _P 0x73
#define _q 0x67 // kann mit Z9 verwechselt werden
#define _r 0x50
#define _S 0x6D // wie Z5
#define _t 0x78 // umgekehrtes F
#define _U 0x3E
#define _u 0x1C
#define _Y 0x6E
// Sonstiges
#define MI 0x40 // Minus
#define __ 0x08 // Unterstrich
// Ziffern-zu-Siebensegment-Tabelle
PROGMEM const byte CodeTab[16]={
/* 0..7 */ _0,_1,_2,_3,_4,_5,_6,_7,
/* 8..F */ _8,_9,_A,_b,_C,_d,_E,_F};
static byte digits[7]; // Index 6 = zusätzliche LEDs
static volatile byte keystate;
static volatile byte keychange;
static byte digit; // 0..7, 7 = Tastenabfrage, in den oberen 3 Bits
static byte dim; // Bit 7:5 = gedimmte Stelle, Bit 4:0 = Helligkeit
static volatile byte t122; // Sub-Teiler zur Zeitmessung (Tonlänge, Timeout)
// Interruptfrequenz 8 MHz / 256 = 31,25 kHz
// Software-Teiler /256 = 122 Hz (LED-Bildwiederholfrequenz)
ISR(TIM0_OVF_vect) {
byte d=++digit;
if (d==dim) PORTB=0x0E; // Aus nach Zeit
if (d&0x1F) return; // Software-Teiler /32
PORTB=0x0E; // Nicht vorhandene Katode ansteuern: Alle LEDs aus
if (d==0xE0) {
PORTA&=~0x01; // Schieberegister leeren
byte b=6;
do{
PORTA|= 0x02;
PORTA&=~0x02;
}while(--b);
PORTA|=0x01; // Einzelnes gesetztes Bit durchschieben
b=0x01; // Endemarker
do{
PORTA|= 0x02;
PORTA&=~0x01;
b<<=1;
PORTA&=~0x02;
if (PINB&0x01) ++b; // Taste einlesen
}while(!(b&0x40)); // bis 6 Bits drin sind
b&=~0x40;
keychange|=keystate^b;// geänderte Bits der Hauptschleife melden
keystate=b; // Tastenzustand
++t122;
return; // Display bleibt dunkel
}
d>>=5;
byte b=digits[d];
byte i=8;
do{
if (b&0x80) PORTA|=0x01; else PORTA&=~0x01;
PORTA|= 0x02; // Takt
b<<=1;
PORTA&=~0x02;
}while(--i);
b=0;
if (d&1) b|=0x04;
if (d&2) b|=0x08;
if (d&4) b|=0x02;
PORTB=b; // Gemeinsame Katode ansteuern
}
// Bei Überstrom OC0B deaktivieren
ISR(ANA_COMP_vect) {
TCCR0A=0x03; // Fast-PWM belassen
}
static word akku[2]; // nur für ISR
static volatile word voltage,current;
static byte adc_cnt; // nur für ISR
static volatile bool adc_int;
static volatile bool i_lim; // im Strombegrenzungsmodus
ISR(ADC_vect,ISR_NOBLOCK) {
if ((ADMUX^=0x07)&0x04) akku[0]+=ADC;
else akku[1]+=ADC;
if (!(adc_cnt+=2)) {
voltage=akku[0];
current=akku[1]; // mit 37 Hz ein neuer 16-Bit-Wert
akku[0]=akku[1]=0;
adc_int=true;
}
}
EMPTY_INTERRUPT(PCINT0_vect)
static void setstr_P(const byte str[6]) {
memcpy_P(digits,str,6);
}
union regler_t{
word pid[3];
struct{
word p,i,d;
};
};
EEMEM struct config_t{
word max_u; // U in V (0 .. 9.999 kV) 4.000 kV
word max_i; // I in 1/10 µA (0 .. 999.9 µA) 100.0 µA
word max_p; // P in 1/10 mW (0 .. 999.9 mW) 10.00 mW
word max_t; // T in s (0 .. 999 s) 60 s
byte startstate;
byte rsv;
regler_t reg_u,reg_i;
}config_e={
4000,1000,100,60,0,0,
{{0x100,0,0}},
{{0x100,0,0}},
};
config_t config;
// Warte t/122 Sekunden (also max. 2 Sekunden)
static void pause(byte t) {
t+=t122;
do sleep_cpu(); while(t!=t122);
}
static void hexout(word value,byte start, byte len) {
do{
digits[start++]=pgm_read_byte(CodeTab+(value>>12))|(len==1?DP:0);
value<<=4;
}while(--len);
}
// vzl. Zahl mit Komma rechtsbündig ausgeben ohne (oder mit) führenden Nullen
// zlen = Ziffernlänge inkl. führender Nullen, sollte >= dp sein
// (die führenden Nullen werden zum Editieren benötigt)
// glen = Displaylänge, Digits nach vorn werden abgeschaltet
static void numout(word value, char dp=0, byte d0pos=5, char zlen=0, char glen=3, byte base=10) {
do{
byte z=value%base;
value/=base;
z=pgm_read_byte(CodeTab+z);
if (!dp) z|=DP;
digits[d0pos]=z;
dp--;
d0pos--; // nach links
glen--;
}while (--zlen>=0 || value);
for(;glen>0;glen--) {digits[d0pos]=0; d0pos--;}
}
static byte state,prev;
// vzl. Gleitkommazahl dreistellig ausgeben
// Mit dp<0 und value<1000 ohne Dezimalpunkt
// Bei value>1000 automatische Anpassung des Dezimalpunktes und
// Weglassen nachfolgender Stellen
static void dezout(word value,char dp=0,byte d0pos=2) {
for (;value>=1000;value/=(byte)10) if (--dp<0) {
dp+=3;
digits[6]|=d0pos==5?LM:LS; // "kV" oder "mA" ein
}
if (!state && d0pos==2 && dp && !value%(byte)10) {
value/=(byte)10;
digits[2]=0;
dp--;
d0pos--;
}
numout(value,dp,d0pos,dp);
}
// Tastenbehandlung; serialisiert Tastenereignisse
// Returnwert:
// Bit 7 = KeyUp
// Bit 6 = KeyDown
// Bit 3:0 = Tastennummer (hier nur 0..3)
static byte keyEventToState() {
byte kc=keychange;
byte ks=keystate;
keychange=0;
if (kc&~ks) return 0xFF; // Loslass-Ereignisse ignorieren
if (~ks&kc&KL) return 2;
if (~ks&kc&KR) return 3;
if (ks&KL && ~ks&kc&KR) return 4;
if (ks&KR && ~ks&kc&KL) return 5;
if (ks&kc&KU) return 6;
if (ks&kc&KP) return 7+(ks&KL?1:0)+(ks&KR?2:0);
return 1;
}
static void waitEvent() {
do sleep_cpu();
while (!keychange);
}
static void waitEvent(byte t) {
t+=t122;
do sleep_cpu();
while (!keychange && t122!=t);
}
//int main() __attribute__((noreturn));
int main() {
PORTA = 0x00;
DDRA = 0xC3;
PORTB = 0x0E;
DDRB = 0x0E;
TIMSK0 = 0x01; // Überlauf-Interrupt für LED-Multiplex
OCR0B = 64;
TCCR0A = 0x23; // Schnelle PWM mit 256 Schritten
TCCR0B = 0x01; // maximale Taktfrequenz (F_CPU)
ADMUX = 0x83; // mit Kanal 3 beginnen
ADCSRA = 0xEF; // A/D-Wandler mit Vorteiler 64, Abtastrate = 4,8 kSa/s
ADMUX = 0x84; // für nächste Wandlung Kanal 4
DIDR0 = 0xFF; // Kein digitaler Eingang auf Port A
PRR = 0x0A; // Timer1 und USI deaktivieren
MCUCR = 0x20; // Sleep: Timer muss weiter laufen
eeprom_read_block(&config,&config_e,sizeof config);
if (config.startstate<=5) state=config.startstate;
sei(); // Interrupts aktivieren
// static PROGMEM const byte strHello[6]={_H,_E,_L,_L,_O,0};
// setstr_P(strHello);
word time,*ref,max,udv=1;
byte blinkdigit=5;
for(;;) {
byte st=0xFF;
switch (state) {
case 0x00: {
dezout(config.max_u);
digits[3]=_o;
digits[4]=_F;
digits[5]=_F;
waitEvent();
/* if (keystate&keychange&(KL|KR)) st=1;
else*/ st=keyEventToState();
}break;
case 0x01: {
dezout(keystate/*voltage*/);
dezout(keychange/*current*/,0,5);
waitEvent(122/4); // warte mit Timeout
if (keychange) { // jede Taste bewirkt Statusänderung??
st=keyEventToState();
if (st==1) st=0; // Umschalten 1-0 mit derselben Taste
}
}break;
case 0x02: {
digits[0]=_u;
numout(voltage,0,5,0,5);
waitEvent(122/4);
if (keychange) {
st=keyEventToState();
if (st==state&0x0F) st=1;
}
}break;
case 0x03: {
digits[0]=_i;
numout(current,0,5,0,5);
waitEvent(122/4);
if (keychange) {
st=keyEventToState();
if (st==state&0x0F) st=1;
}
}
case 0x04: {
bool b=i_lim;
digits[0]=b?_u:_i;
numout(b?voltage:current,0,5,0,5);
waitEvent(122/4);
if (keychange) {
st=keyEventToState();
if (st==state&0x0F) st=1;
}
}break;
case 0x05: digits[0]=_r; numout(voltage/current,0,5,0,5); goto state5;
case 0x15: digits[0]=_G; numout(current/voltage,0,5,0,5); goto state5;
case 0x25: digits[0]=_P; numout(voltage*current,0,5,0,5); state5: {
waitEvent(122/4);
if (keychange) {
st=keyEventToState();
if (st==state&0x0F) st=1;
}
}break;
case 0x06: time=config.max_t<<1; state=0x16; // nobreak
case 0x16: {
dezout(current);
numout(time>>1,-1);
waitEvent(122/2);
if (i_lim) {
state=0x26;
static PROGMEM const byte strFail[6]={_F,_A,_I,_L};
setstr_P(strFail);
}else if(keychange) {
st=keyEventToState();
if (st==6) st=prev;
}else if (!--time) {
state=0x26;
static PROGMEM const byte strPass[6]={_P,_A,_S,_S};
setstr_P(strPass);
}
}break;
case 0x26: {
waitEvent();
st=keyEventToState();
if (st==6) st=prev;
}break;
case 0x07: digits[0]=_U; ref=&config.max_u; max=9999; goto state7;
case 0x17: digits[0]=_I; ref=&config.max_i; max=9999; goto state7;
case 0x27: digits[0]=_P; ref=&config.max_p; max=9999; goto state7;
case 0x37: digits[0]=_t; ref=&config.max_t; max=999; state7: {
if (udv>max) {udv=1; blinkdigit=5;}
numout(*ref,0,5,0,5);
waitEvent();
byte kc=keychange;
byte ks=keystate;
keychange=0;
if (ks&kc&KP) {
state=state+0x10&~0x40;
}else if (ks&kc&KL) {
*ref-=udv;
if (*ref>max) *ref=0;
}else if (ks&kc&KR) {
*ref+=udv;
if (*ref>max) *ref=max;
}else if (ks&kc&KU) {
udv*=10; blinkdigit--;
if (udv>max) {udv=1; blinkdigit=5;}
}
}break;
}
if (st!=0xFF) {
prev=state; // Anzeige wechseln
state=st;
}
digits[6]^=LM;
// if (adc_int) {
// hexout(voltage,0,3);
// hexout(current,3,3);
// adc_int=false;
// }
// pause(30);
}
}
Detected encoding: UTF-8 | 0
|