Hallo, ich habe folgendes Problem. Ich möchte eine Schaltung mit einem Attiny realisieren welche nach jedem Neustart des Systems einen anderen Zustand einnimmt. Neustart heißt die Spannungsversorung wird komplett getrennt und anschließend wieder verbunden. Es gibt insgesamt 3 Zustände. Nach dem ersten Einschalten soll Zustand 1 aktiviert werden. Nach dem nächsten aus/einschalten Zustand 2, nach dem nächsten aus/einschalten Zustand 3. Danach soll bei jedem weiteren aus/einschalten zwischen Zustand 2 und 3 toggeln. Das lässt sich ja noch gut mit einer Flag realsieren realsieren, ich hoffe nur dass der EEPROM wirklich an die 100k Schreibzyklen mitmacht. Das eigentliche Problem ist nun folgendes. Ich möchte dass nach einer gewissen Zeit im ausgeschalteten Zustand, also zB. 5 Minuten der Mikrocontroller nach dem Einschalten wieder in den Zustand 1 wechselt. Ich weiß nur nicht wie ich das Clever lösen kann. Was mir einfallen würde: Spannung an einem Kondensator messen-> nach gewisser Zeit bestimmter Pegel unterschritten. Oder irgendwie den µC über einen Goldcap solange versorgen und einen Timer laufen lassen. So recht gefällt mir das aber nicht. Wie könnte ich Das am geschicktesten lösen? Ihr habt da hoffentliche bessere Vorschläge? Allgemein ist nur wenig Platz für zusätzliche Bauteile vorhanden. Danke schonmal Gruß Ansgar
>Wie könnte ich Das am geschicktesten lösen? Ihr habt da hoffentliche >bessere Vorschläge? Controller nicht wirklich ausschalten und neu starten sondern in den Sleep Modus setzen. Ansonsten wird das wohl eher eklig.
danke für deine Antwort. Leider ist das Gerät Batteriebetrieben, und ich kann/will aus verschiedenen Gründen das Umschalten zwischen den Zuständen nur durch den Reset mittels "Spannung weg" realisieren. Ein Taster kommt nicht in Frage. Danke
Was soll dieser tiny-Scheiss. Nimm einen richtigen Mega16 oder groesser. Die 2 Euro mehr koennen's nicht sein.
EEPROM durch einen MCP79410 EEPROM/RAM/RTC ersetzen. Beim Power-Off solltest Du schauen, ob Du einen Power-Off Interrupt hinbekommst, bei dem Du den aktuellen Zeitstempel im SRAM des MCP speicherst. Wenn Du das nicht schaffst, machst Du das halt periodisch alle 15s. Beim Wiederanschalten prüfst Du den Zeitstempel gegen die aktuelle Zeit und führst die entsprechende Aktion aus. Den letzten Zustand speicherst Du auch im gepufferten SRAM. fchk
Der Mehrpreis ist sicher nicht der Grund für den Tiny, sondern der nicht vorhandene Platz und der geringere Stromverbrauch. Wie könnte denn ein Atmega mein Problem "lösen"? Danke Ansgar
>Leider ist das Gerät Batteriebetrieben
Ist doch ok. Im Sleep Modus zieht der uC nur ein
paar uA. Wenn überhaupt.
Für den Zustandswechsel wie geschrieben das EEPROM nutzen... für den Rückfall nach 5min folgende Idee. Mosfet der über den µC eingeschaltet werden kann und einen Kondensator lädt... im laufenden Betrieb evtl. immer mal wieder Nachladen, damit die Spannung am Kondensator nahe Betriebsspannung bleibt... wenn der µC abgeklemmt wird, entlädt sich der Kondensator langsam ... über den Widerstand/Kondensator kann man die Entladezeit festlegen. Ein Komperator oder Schmitt-Trigger vergleicht dann beim Einschalten die Spannung ...
@Frank Vielen Dank für Deine Antwort. Leider benötige ich dafür wieder einige zusätzliche Bauteile, Quarz usw. Den MCP müsste ich dann wohl noch über einen Goldcap versorgen, oder gibts da was besseres? @Holger wie gesagt ich muss zum Wechseln zwischen den Zuständen die Spannungsversorung trennen. Oder kann ich den Tiny 5 Minuten über einen Goldcap versorgen und einen Timer laufen lassen? Danke Ansgar
@skyperhh ja an sowas ähnliches hatte ich auch gedacht. Aber ich weiß nicht wie zuverlässig das Ganze funktionieren würde. Danke Ansgar
>@Holger >wie gesagt ich muss zum Wechseln zwischen den Zuständen die >Spannungsversorung trennen. >Oder kann ich den Tiny 5 Minuten über einen Goldcap versorgen und einen >Timer laufen lassen? Den kannst du im Sleep Modus ein paar Tage mit Goldcap laufen lassen.
> Das lässt sich ja noch gut mit einer Flag realsieren realsieren, ich > hoffe nur dass der EEPROM wirklich an die 100k Schreibzyklen mitmacht. Die minimale Anzahl an Lösch/Schreibzyklen ist über den gesamten Temperaturbereich garantiert, in diesem Fall für die Maximaltemperatur. Bei 25°C sind (nicht garantiert) 1...5 Mio. Zyklen drin. Du könntest z.B. nach einem Ausschalten die EEPROM-Bytes sukzessive auf 0 setzen, die Null-Bytes zählen und das Ergebnis der Modulo 3 - Division als Startzustandsindikator verwenden. Das setzt voraus, daß du nach dem Abschalten noch genügend 'Saft' zum Schreiben und ggf. vollständigen Löschen des EEPROMs hast. Meine Firma verwendet eine ähnliche Methode in Geräten, die in Millionenstückzahlen verkauft werden.
ansgar schrieb: > ja an sowas ähnliches hatte ich auch gedacht. Aber ich weiß nicht wie > zuverlässig das Ganze funktionieren würde. Warum sollte das ganze nich funktionieren? Kleines C wählen, großer Widerstand...
@ frame könntest Du das Verfahren mal näher erleutern? @skyperhh bei einigen Versuchen hat der Kondensator die Spannung keine 5 Minuten gehalten. Möglicherweiße muss ich da mal noch verschiedene Typen testen.
ansgar schrieb: > @Frank > > Vielen Dank für Deine Antwort. Leider benötige ich dafür wieder einige > zusätzliche Bauteile, Quarz usw. 1* 32kHz Quarz. 1* 100n Abblockkondensator 2* Pullup-R für den I2C Das wars. > Den MCP müsste ich dann wohl noch über einen Goldcap versorgen, oder > gibts da was besseres? Dein Gerät ist doch ohnehin batteriebetrieben, oder? Dann trenne nur die eigentliche Stromversorgung, nicht aber die Versorgung zu VBat vom MCP. fchk
Frank K. schrieb: > 1* 32kHz Quarz. 1* 100n Abblockkondensator 2* Pullup-R für den I2C > Das wars. Ich weiß nicht, ich finde das reichlich kompliziert... Für die gewünschte Funktion reicht ein ATtiny13A – ohne Quarz. Als Puffer für die Versorgungsspannung tuts für 5 Minuten im Sleepmode ein kleiner (!) Elko. EEPROM wird nicht benötigt. Der ATtiny13A besitzt einen internen 128-kHz-Oszillator, den man per Vorteiler bis auf 500 Hz runtertakten kann. Dann braucht der Mikrocontroller eh kaum noch Strom. Das Programm sollte kaum größer werden als ungefähr 30 Assembler-Zeilen. Also, alles halb so wild, außer dem Mikrocontroller brauchst du eigentlich nur nen kleinen Elko, um die Spannung zu halten, und einen Kondensator in Reihe zum externen Interrupt-Eingang (mit Pulldown) – zum Aufwecken.
> @ frame > könntest Du das Verfahren mal näher erleutern? Eigentlich ist die Methode ursprünglich für Flash ausgelegt, den man nur blockweise löschen kann. Dabei werden Datenblöcke sukzessive in den Flash geschrieben, bis ein Block voll ist. Dann wird der nächste begonnen, und der übernächste voreilend gelöscht. Beim letzten Block wird dann wieder am Anfang begonnen. Für deinen Fall wäre das simpler, z.B.: Flash ist im unbeschriebenen Zustand 0xFF. Beim allerersten Start sind alle (verwendeten) Zellen 0xFF. Beim ersten Ausschalten setzt du die erste Zelle auf 0x00. Beim zweiten Ausschalten setzt du die erste Zelle wieder auf 0xFF, und die zweite auf 0x00. Und so weiter. Nach der letzten Zelle beginnst du wieder von vorn. Das verteilt die Schreibzyklen gleichmäßig auf die Zellen, die du dafür verwenden willst - was auch den einzigen Vorteil gegenüber einem Zähler in einer Zelle darstellt. Aus der Position der 0x00-Zelle kannst du die Anzahl bisheriger Starts ableiten, z.B. durch Modulo-Division. Aber, wie gesagt, meine Firma nutzt Controller mit für 100k spezifizierten EEPROM-Schreibzyklen, bisher ohne Probleme. 100.000 Starts sind schon relativ viel, rechne das doch mal mit deiner angesetzten Schalthäufigkeit nach.
frame schrieb: > Das setzt voraus, daß du nach dem Abschalten noch genügend 'Saft' zum > Schreiben und ggf. vollständigen Löschen des EEPROMs hast. das brauchst du nicht. folgender Ablauf: Epprom inital nullen startup: erste "nicht-0"-Zelle mit aktuellem Modus beschreiben. irgendwann saft weg nächster Startup. erste freie Zelle suchen, modus aus voriger Zelle berechnen und wieder wegschreiben. Wenn ende des EEPROMS erreicht wieder komlett nullen und modus in erste Zelle schreiben. so schreibt man immer nur ins eeprom, wenn man sicher Saft hat und verteilt die Schreibzugriffe auch optimal. Das alles löst aber das 5min. Problem nicht. Ich würde versuchen einen Kondensator zu nutzen. wichtig ist, dass der µC den Spannungswegfall registriert und (zB per FET) sämtliche anderen Verbraucher vom Strom trennt. dann power down sleep aufsetzen und per Watchdog-Interupt weckenlassen und die ca 5min zählen. dannach einfach den internen Modus wieder auf Startwert setzen, falls vor dem irgendwann folgenden Brown-Out wieder angeschalten werden sollte. Da braucht man auch gar kein eeprom.
Das Verfahren kommt von einem Gerät, daß diverse Zustände erst bei Netz-Aus und nicht dauernd sichert, weil wegen sich permanent ändernder Daten sonst die garantierte Anzahl an Lösch/Schreibzyklen schnell erreicht wäre. Das Gerät muß aber einen Standard erfüllen, der die persistente Abspeicherung dieser Daten beinhaltet. Deswegen ist die Netzversorgung auch auf eine Stützzeit von >300ms ausgelegt, um alle Daten sicher speichern zu können. In wie weit diese Umstände auf das diskutierte Gerät zutreffen, muß der Fragesteller entscheiden.
Wenn eine Echtzeituhr (Batteriegepuffert mit kleinem RAM-Bereich) zu viel Aufwand ist, mach doch so: Mach dem ersten einschalten läuft Betriebsart 1, danach legt sich der Controller schlafen und wird vom Timer regelmässig kurz geweckt. Wenn in der Zwischenzeit wieder Eingeschaltet wird, wechselt der Controller in Betriebsart 2/3/2/3 etc. Nach 5 min im Sleep/Aufwach-Betrieb schaltet sich der Controller aus und beim nächsten Einschalten ist daher wieder Betriebsart 1 dran. Dann brauchst Du eine Pufferung, die höchstens Strom für einige Minuten Sleep-Mode (5 Sleep plus Aufwachphasen) puffern muss.
Hallo, danke für eure Unterstützung. Ich habe die Anforderungen jetzt etwas geändert. Es gibt nur noch zwei Zustände. Die Zustände sollen nur zwei Ports umschalten. Dass Trennen der Versorgungsspannung wird durch den ext. Interrupt detektiert, da er auf die fallende Flanke reagiert. Der Interrupt lässt dann den Mode wechseln. Der Attiny wird während der Off Zeit aus einem Elko gepuffert. Damit der Elko reicht wird wechselt er in den Sleepmode Idle. Jetzt möchte ich allerdings dass IMMER Mode 1 aktiviert wird wenn die Offzeit länger als 8 Sekunden dauert. Dauert die Offzeit weniger als 8 Sekunden soll man durch trennen/wiederherstellen der Spannungsversorung die Stufen wechseln können. Ich habe dazu noch den Timer aktiviert, und wollte das mit dem Timeroverflow Interrupt machen. Leider bekomme ich es nicht hin dass die 8 Sekunden nur während der Offzeit gelten, sondern nur so dass er immer nach 8 Sekunden schaltet. Ich kann leider keine zusätzlichen Bauteile mehr einbauen, und muss das über die Software hinbiegen. Ich hoffe ihr könnt mir nochmal helfen. Anbei mal mein letzter Codestand, so funktioniert der Timeroverflow natürlich gar nicht, war nur ein Versuch. Vielen Dank Ansgar
1 | //Includes
|
2 | #include <avr/io.h> |
3 | #include <stdint.h> |
4 | #include <avr/interrupt.h> |
5 | #include <avr/eeprom.h> |
6 | #include "defines.h" |
7 | #include <avr/sleep.h> |
8 | |
9 | /**********************************************/
|
10 | #define F_CPU 128000UL // 128kHz CPU Takt
|
11 | #include <util/delay.h> |
12 | |
13 | /**********************************************/
|
14 | //Variablen
|
15 | volatile uint8_t Zaehler = 0; |
16 | volatile uint8_t Mode =1; |
17 | volatile uint8_t b = 0; |
18 | volatile bool Abbruch = true; |
19 | |
20 | //uint8_t EStatus EEMEM = 0; //EEPROM
|
21 | |
22 | |
23 | |
24 | /***********************************************/
|
25 | |
26 | //Hauptprogramm
|
27 | int main(void) |
28 | {
|
29 | |
30 | /*************Zuordnungen********************/
|
31 | |
32 | |
33 | |
34 | DDRB = ( 1 << PB0 ) | ( 1 << PB2 );// Ausgänge PB0 und PB2 |
35 | PORTB=0b00000000; //Ausgänge: LOW / Eingänge: Pullups deaktiviert |
36 | |
37 | TCCR0B= (1<<CS02) |(0<<CS01) | (1<<CS00); // Prescaler 1024 |
38 | |
39 | |
40 | |
41 | GIMSK |= (1<<INT0); //externer INT0 auf PB1 freigeben |
42 | MCUCR |= (1<<ISC01) | (0<<ISC00); //Interrup INT0: fallende Flanke |
43 | |
44 | |
45 | // Analogcomparator ausschalten
|
46 | |
47 | ACSR = 0x80; |
48 | |
49 | //Interrupts zulassen
|
50 | sei(); |
51 | |
52 | //Sleepmode "IDLE2" wählen
|
53 | set_sleep_mode(SLEEP_MODE_IDLE); |
54 | |
55 | |
56 | while(1) |
57 | {
|
58 | switch (Mode) |
59 | {
|
60 | case 1: //100% |
61 | |
62 | {
|
63 | Abbruch = true; |
64 | Mode = 2; |
65 | while(Abbruch) //Endlosschleife bis Abbruch durch Interrupt INTO |
66 | {
|
67 | TIMSK0 &=~ (1<<TOIE0); //Timer Overflow Interrupt sperren |
68 | |
69 | PORTB &= ~(1<<PB0); //Analogschalter 1 geschlossen |
70 | PORTB |= (1<<PB2); //Analogschalter 2 geöffnet |
71 | |
72 | sleep_mode(); //schlafen |
73 | }
|
74 | |
75 | }
|
76 | |
77 | break; |
78 | |
79 | |
80 | |
81 | |
82 | |
83 | |
84 | |
85 | case 2: //25% |
86 | {
|
87 | Abbruch = true; |
88 | Mode = 1; |
89 | while(Abbruch) //Endlosschleife bis Abbruch durch Interrupt INTO |
90 | {
|
91 | TIMSK0 &=~ (1<<TOIE0); //Timer Overflow Interrupt sperren |
92 | |
93 | PORTB |= (1<<PB0); //Analogschalter 1 geöffnet |
94 | PORTB &= ~ (1<<PB2); //Analogschalter 2 geschlossen |
95 | |
96 | sleep_mode(); //schlafen |
97 | }
|
98 | |
99 | }
|
100 | |
101 | break; |
102 | |
103 | |
104 | |
105 | |
106 | |
107 | |
108 | |
109 | default:
|
110 | PORTB &= ~(1<<PB0); //Analogschalter 1 geschlossen |
111 | PORTB |= (1<<PB2); //Analogschalter 2 geöffnet |
112 | |
113 | |
114 | }
|
115 | |
116 | }
|
117 | |
118 | return 0; |
119 | }
|
120 | //Ende Hauptprogramm
|
121 | |
122 | |
123 | |
124 | |
125 | /*****************************************************************/
|
126 | //Interrupt Routine
|
127 | |
128 | ISR(TIM0_OVF_vect) // Timer0 Overflow Interrupt wird alle 2048ms aufgerufen |
129 | {
|
130 | //PORTB |=(1<<PB3);
|
131 | if (Zaehler < 3) |
132 | {
|
133 | Zaehler++; |
134 | |
135 | }
|
136 | else { |
137 | Zaehler = 0; |
138 | Mode=1; //nach 8s Mode 1 aktivieren |
139 | }
|
140 | |
141 | |
142 | |
143 | |
144 | }
|
145 | |
146 | |
147 | ISR(INT0_vect) // Interrupt fallende Flanke PB1 |
148 | {
|
149 | Abbruch = false; |
150 | Zaehler = 0; |
151 | _delay_ms(200); |
152 | TCNT0 = 0; |
153 | TIMSK0 |= (1<<TOIE0); //Overflow Interrupt zulassen |
154 | |
155 | }
|
Ich habe mir gerade überlegt dass ich nach dem Interrupt fallende Flanke, den Interrupt auf steigende Flanke umstelle. Somit könnte ich feststellen ob ich wieder eingeschaltet habe oder nicht. Was meint ihr? Danke Ansgar
Hallo, ich hab die Software jetzt so hinbekommen dass es scheinbar das macht was ich wollte. Habt ihr noch Verbesserungsvorschläge? Danke Gruß Ansgar
1 | //Includes
|
2 | #include <avr/io.h> |
3 | #include <stdint.h> |
4 | #include <avr/interrupt.h> |
5 | #include <avr/eeprom.h> |
6 | #include "defines.h" |
7 | #include <avr/sleep.h> |
8 | |
9 | /**********************************************/
|
10 | #define F_CPU 128000UL // 128kHz CPU Takt
|
11 | #include <util/delay.h> |
12 | |
13 | /**********************************************/
|
14 | //Variablen
|
15 | volatile uint8_t Zaehler = 0; |
16 | volatile uint8_t Mode =1; |
17 | volatile uint8_t b = 0; |
18 | volatile bool Abbruch = true; |
19 | |
20 | //uint8_t EStatus EEMEM = 0; //EEPROM
|
21 | |
22 | |
23 | |
24 | /***********************************************/
|
25 | |
26 | //Funktionen
|
27 | void mode1(void) //Mode1 |
28 | {
|
29 | PORTB &= ~(1<<PB0); //Analogschalter 1 geschlossen |
30 | PORTB |= (1<<PB2); //Analogschalter 2 geöffnet |
31 | }
|
32 | |
33 | void mode2(void) //Mode2 |
34 | {
|
35 | PORTB |= (1<<PB0); //Analogschalter 1 geöffnet |
36 | PORTB &= ~ (1<<PB2); //Analogschalter 2 geschlossen |
37 | }
|
38 | |
39 | void interruptchange(void) //Externen Interrupt umschalten fallende/steigende Flanke |
40 | {
|
41 | if ( !( MCUCR & (1<<ISC00) ) ) //falls Interrupt fallende Flanke |
42 | {
|
43 | MCUCR |= (1<<ISC00); //Interrupt auf steigende Flanke umstellen |
44 | Zaehler = 0; |
45 | TCNT0 = 0; |
46 | TIMSK0 |= (1<<TOIE0); //Overflow Interrupt zulassen |
47 | }
|
48 | else if(MCUCR & (1<<ISC00) ) //falls Interrupt steigende Flanke |
49 | {
|
50 | MCUCR &= ~ (1<<ISC00); //Interrupt auf fallende Flanke umstellen |
51 | TIMSK0 &=~ (1<<TOIE0); //Timer Overflow Interrupt sperren |
52 | Abbruch = false; |
53 | }
|
54 | }
|
55 | //Funktionen Ende
|
56 | |
57 | |
58 | //Hauptprogramm
|
59 | int main(void) |
60 | {
|
61 | |
62 | /*************Zuordnungen********************/
|
63 | |
64 | |
65 | |
66 | DDRB = ( 1 << PB0 ) | ( 1 << PB2 ) | (1<<PB3);// Ausgänge PB0 und PB2 |
67 | PORTB=0b00000000; //Ausgänge: LOW / Eingänge: Pullups deaktiviert |
68 | |
69 | TCCR0B= (1<<CS02) |(0<<CS01) | (1<<CS00); // Prescaler 1024 |
70 | |
71 | GIMSK |= (1<<INT0); //externer INT0 auf PB1 freigeben |
72 | MCUCR |= (1<<ISC01) | (0<<ISC00); //Interrup INT0: fallende Flanke |
73 | |
74 | |
75 | // Analogcomparator ausschalten
|
76 | |
77 | ACSR = 0x80; |
78 | |
79 | //Interrupts zulassen
|
80 | sei(); |
81 | |
82 | //Sleepmode "IDLE" wählen
|
83 | set_sleep_mode(SLEEP_MODE_IDLE); |
84 | |
85 | |
86 | while(1) |
87 | {
|
88 | switch (Mode) |
89 | {
|
90 | case 1: //100% |
91 | |
92 | {
|
93 | Abbruch = true; |
94 | Mode = 2; |
95 | while(Abbruch) //Endlosschleife bis Abbruch durch Interrupt INTO |
96 | {
|
97 | mode1(); |
98 | sleep_mode(); //schlafen |
99 | }
|
100 | |
101 | }
|
102 | |
103 | break; |
104 | |
105 | |
106 | |
107 | |
108 | |
109 | |
110 | |
111 | case 2: //25% |
112 | {
|
113 | Abbruch = true; |
114 | Mode = 1; |
115 | while(Abbruch) //Endlosschleife bis Abbruch durch Interrupt INTO |
116 | {
|
117 | mode2(); |
118 | sleep_mode(); //schlafen |
119 | }
|
120 | |
121 | }
|
122 | |
123 | break; |
124 | |
125 | default:
|
126 | PORTB &= ~(1<<PB0); //Analogschalter 1 geschlossen |
127 | PORTB |= (1<<PB2); //Analogschalter 2 geöffnet |
128 | |
129 | |
130 | }
|
131 | |
132 | }
|
133 | |
134 | |
135 | |
136 | return 0; |
137 | }
|
138 | //Ende Hauptprogramm
|
139 | |
140 | |
141 | /*****************************************************************/
|
142 | //Interrupt Routinen
|
143 | |
144 | ISR(TIM0_OVF_vect) // Timer0 Overflow Interrupt wird alle 2048ms aufgerufen |
145 | {
|
146 | |
147 | |
148 | if (Zaehler < 3) |
149 | {
|
150 | Zaehler++; |
151 | |
152 | }
|
153 | else { |
154 | Zaehler = 0; |
155 | |
156 | PORTB |=(1<<PB3); |
157 | Mode=1; //nach 8s Mode 1 aktivieren |
158 | }
|
159 | |
160 | |
161 | |
162 | |
163 | }
|
164 | |
165 | |
166 | ISR(INT0_vect) // Interrupt fallende/steigende Flanke PB1 |
167 | {
|
168 | |
169 | _delay_ms(200); |
170 | interruptchange(); |
171 | |
172 | }
|
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.