Zeitabhängige Spannungssteuerung

Gast #3106299
Lesenswert?

Guten Morgen,

ich habe ein kleines Problem mit einer Schaltung:

Ich habe eine Solar-Steuerung über einen Mikrokontroller realisiert. 
Dazu wird die Ausgangsspannung nach einem DC-DC-Wandler zu einer 
bestimmten Zeit über ein Relais an den Endverbraucher zugeschaltet. 
Dabei ruft der µC über einen TWI (DS1307) die aktuelle Zeit ab, und soll 
je nachdem das Relais schalten.

Sobald ich die Zeit neu einstelle läuft das Programm eine Zeit lang so, 
wie es soll. (Das Relais wird pünktlich geschaltet, und schaltet auch 
pünktlich zum gewünschten Zeitpunkt wieder ab)

Jedoch bleibt der µC nach einer Zeit verweilen am aktuellen Zustand, bis 
er neu eingestellt wird. Ich vermute, dass der µC sich aufhängt. Ich 
schätze, dass es am Code liegt, ich habe bereits 3 µC getestet, alle mit 
dem gleichem Ergebnis.

Mein Code:
1
#define   F_CPU   3686400
2
#include <avr\io.h>
3
//---------------------------------------------------------------------------------
4
// TWI-Funktionssammlung
5
#ifndef TWI_CLOCK
6
#define TWI_CLOCK 100000 // Geschwindigkeit des TWI-Busses
7
#endif
8
// TWCR - Control-Register-Bits
9
#define _TWINT 0b10000000
10
#define _TWEA 0b01000000
11
#define _TWSTA 0b00100000
12
#define _TWSTO 0b00010000
13
#define _TWWC 0b00001000
14
#define _TWEN 0b00000100
15
#define _TWIE 0b00000001
16
uint8_t   twiAdr = 0xD0;
17
void twiInitMaster(uint8_t twiAdr);
18
#include <util\delay.h>
19
#include <util\delay.h>
20

21
//------------------------------------------------------------------------
22
// twiInitMaster
23
//------------------------------------------------------------------------
24
void twiInitMaster(uint8_t twiAdr)
25
{
26
  // Clock
27
  TWBR=((F_CPU/TWI_CLOCK)-16)*2;
28
  // TWI-Status-Register (Vorteiler)
29
  TWSR=0;
30
  // Bus-Addr
31
  TWAR=twiAdr;
32
  // Enable
33
  TWCR=_TWINT|_TWEN;
34
}
35
//------------------------------------------------------------------------
36
// Start TWI (ohne Interrupt)
37
//------------------------------------------------------------------------
38
void twiStart()
39
{
40
  uint8_t x=TWCR;
41
  x&=_TWEN|_TWIE;    // nur Beibehalten von Enable und InterruptJ/N 
42
  TWCR=x|_TWINT|_TWSTA;
43
  // warten bis fertig
44
  while( !(TWCR & _TWINT))
45
  {}
46
}
47
//------------------------------------------------------------------------
48
// Stopp TWI (ohne Interrupt)
49
//------------------------------------------------------------------------
50
void twiStop()
51
{
52
  uint8_t x=TWCR;
53
  x&=_TWEN|_TWIE;    // nur Beibehalten von Enable und InterruptJ/N 
54
  TWCR=x|_TWINT|_TWSTO;
55
}
56
//------------------------------------------------------------------------
57
// Write Byte per TWI (ohne Interrupt)
58
// PE: data = zu sendende Daten
59
//     ackn = wenn !=0 wird Acknowledge (=TWEA) gesetzt
60
//------------------------------------------------------------------------
61
void twiWriteByte(uint8_t data, uint8_t ackn)
62
{
63
  TWDR=data;      // Daten bereitlegen
64
  // Befehl zusammenstellen
65
  uint8_t x=TWCR;
66
  x&=_TWEN|_TWIE;      //nur Beibehalten von Enable und InterruptJ/N 
67
  x|=_TWINT;
68
  if(ackn)   
69
    x|=_TWEA;   // evt. TWEA setzen, für Datenanforderung
70
  TWCR=x;         // senden
71
  // warten bis fertig
72
  while( !(TWCR & _TWINT))
73
  {}
74
}
75
//------------------------------------------------------------------------
76
// Read Byte per TWI (ohne Interrupt)
77
// PE: ackn = wenn !=0 wird Acknowledge (=TWEA) gesetzt
78
// PA: Data
79
//------------------------------------------------------------------------
80
int twiReadByte(uint8_t ackn)
81
{
82
  // Befehl zusammenstellen
83
  uint8_t x=TWCR;
84
  x&=_TWEN|_TWIE;      //nur Beibehalten von Enable und InterruptJ/N 
85
  x|=_TWINT;
86
  if(ackn)   
87
    x|=_TWEA;   // evt. TWEA setzen, für Datenanforderung
88
  TWCR=x;         // senden
89
  // warten bis fertig
90
  while( !(TWCR & _TWINT))
91
  {}
92
  return TWDR;
93
}
94

95
//-------------------------------------------------------------------------
96
// rtcGetRtcData
97
//-------------------------------------------------------------------------
98
uint8_t rtcGetRtcData(uint8_t Register)
99
{
100
  // Adresse senden   
101
  twiStart();
102
  twiWriteByte(twiAdr,0);
103
  twiWriteByte(Register,1);
104
  // Daten lesen
105
  twiStart();
106
  twiWriteByte(twiAdr+1,1);
107
  uint8_t b = (uint8_t)twiReadByte(0);
108
  twiStop();
109
  return b;
110
}
111
//-------------------------------------------------------------------------
112
// rtcGetSeconds
113
//-------------------------------------------------------------------------
114
uint8_t rtcGetSeconds()
115
{
116
  return rtcGetRtcData(0) & 0x7F;
117
}
118
//-------------------------------------------------------------------------
119
// rtcGetMinute
120
//-------------------------------------------------------------------------
121
uint8_t rtcGetMinute()
122
{
123
  return rtcGetRtcData(1);
124
}
125
//-------------------------------------------------------------------------
126
// rtcGetHour
127
//-------------------------------------------------------------------------
128
uint8_t rtcGetHour()
129
{
130
  return rtcGetRtcData(2) & 0x3F;
131
}
132
//-------------------------------------------------------------------------
133
// rtcWaitMs
134
//-------------------------------------------------------------------------
135
void rtcWaitMs(int miliSec)
136
{
137
  _delay_loop_2( 1*(F_CPU/(1000/4)) * miliSec);   // 4 Zyklen warteschleife
138
}
139

140
//--------------------------------------------------------------------
141
// myWait_ms - Warte-Routine für x-Millisekunden (0-255)
142
// ein Millisekundenzyklus dauert 1,052 ms
143
// PE: Anzahl der zu wartenden Milisekunden
144
//--------------------------------------------------------------------
145
void myWait_ms(uint8_t dauer_ms)
146
{
147
  __asm__ volatile (
148
    "myWait_ms_0%=:\n"
149
    "push   %0\n"
150
    "ldi   %0,1\n"
151
    "myWait_ms_3%=:\n"
152
    "push   %0\n"
153
    "ldi   %0,5\n"
154
    "myWait_ms_2%=:\n"
155
    "push   %0\n"
156
    "ldi   %0,255\n"
157
    "myWait_ms_1%=:\n"
158
    "dec   %0   \n"
159
    "brne   myWait_ms_1%=\n"
160
    "pop   %0   ; Register wiederherstellen\n"
161
    "dec   %0   \n"
162
    "brne   myWait_ms_2%=\n"
163
    "pop   %0   ; Register wiederherstellen\n"
164
    "dec   %0   \n"
165
    "brne   myWait_ms_3%=\n"
166
    "pop   %0   ; Register wiederherstellen\n"
167
    "dec   %0   \n"
168
    "brne   myWait_ms_0%=\n"
169
    :
170
    :"d" (dauer_ms)
171
    );
172
}
173

174
//---------------------------------------------------------------------------
175
void waitMs(int miliSec)
176
{
177
  _delay_loop_2( 1*(F_CPU/(1000/4)) * miliSec);   // 4 Zyklen warteschleife
178
}
179

180
//------------------------------------------------------------------------
181
// Initialisierungen
182
//------------------------------------------------------------------------
183
void init()
184
{
185
  twiInitMaster(twiAdr);
186
}
187

188
//------------------------------------------------------------------------
189
// Zahlensystem-Wandler
190
//------------------------------------------------------------------------
191

192
int dectobcd(int dec)
193
{
194
  int BCD;
195
  BCD = (((dec / 10) * 16) + (dec % 10));
196
  return BCD;
197
}
198

199
int bcdtodec(int BCD)
200
{
201
  int dec;
202
  dec = ((((BCD >> 4)&0b111)*10)+(BCD&0b1111));
203
  return dec;
204
}
205

206
//------------------------------------------------------------------------
207
// Einstellung der Zeit
208
//------------------------------------------------------------------------
209

210
void define_time(int stunde, int minute, int sekunde)
211
{
212
  twiStart();
213
  twiWriteByte(twiAdr,0);
214
  twiWriteByte(0,1);
215
  sekunde = dectobcd(sekunde);
216
  twiWriteByte(sekunde,1);
217
  minute = dectobcd(minute);
218
  twiWriteByte(minute,1);
219
  stunde = dectobcd(stunde);
220
  twiWriteByte(stunde,1);
221
  twiStop();
222
}
223

224
//------------------------------------------------------------------------
225
// Ein/Ausschalten des Relais
226
//------------------------------------------------------------------------
227

228
int switcher(int stunde_on, int stunde_off)
229
{
230
  int stunde = 0;
231
  stunde = rtcGetHour();
232
  stunde = bcdtodec(stunde);
233
  if ((stunde>=stunde_on) && (stunde<stunde_off))
234
  {
235
    PORTC = 0x08;
236
  }
237
  else
238
  {
239
    PORTC = 0x00;
240
  }
241
}
242

243
/////////////////////////////////////////////////////////////////////////////
244
// Main-Funktion
245
/////////////////////////////////////////////////////////////////////////////
246
main()
247
{
248
  //Variablen-Initialisierung
249
  int zeit1,zeit2;
250

251
  init();               // Initialisierungen
252

253
  //Zur Zeiteinstellung Kommentare entfernen!
254
  /*zeit1 = rtcGetHour();
255
  zeit2 = rtcGetMinute();
256

257
  
258
  if ((zeit1==0)&&(zeit2==0))
259
  {
260
    define_time(8,19,31);          
261
  }*/
262
  
263
  // Alternativ: direkte Überschreibung
264
  // define_time(10,10,15);
265
  
266
  DDRC = 0x08;
267

268
  while (true)              // Mainloop-Begin
269
  {
270
    switcher(11,13);
271
    _delay_ms(1000);
272
  }

Das ganze wurde zunächst mit dem Code-Wizard von myAVR erstellt, da ich 
die gleichen Bauteile der AVR-Boards verwendet habe.

Weiß jemand Rat?

Grüße
#3106340
Lesenswert?

> Ich vermute, dass der µC sich aufhängt.

Wär möglich.
Relais-Spulen können schon ordentlich Schmutz auf den 
Versorgungsspannungen erzeugen, wenn sie schalten.



Hast du ein Anzeigeelement, wie zb in LCD im System?
Dann könntest du dir dort laufend Ausgaben machen und zb auch die 
Uhrzeit von der RTC ausgeben lassen, dann würde man schon mehr sehen.

Wenn nicht, dann hilft es auch, sich eine LED an einen Port zu verkabeln 
und die in der Hauptschleife blinken zu lassen. Wenn das Blinken 
aufhört, dann ist der µC höchst wahrscheinlich abgeschmiert.
Aber um ehrlich zu sein würde ich mir als allererstes in ein neues 
Projekt eine Möglichkeit für Ausgaben einbauen. UART ist gut, LCD ist 
meistens besser. Ohne eine Chance sich auch mal Variablenwerte life und 
in Farbe direkt am Objekt ansehen zu können, ist alles andere immer ein 
Stochern im Nebel.
#3106367
Lesenswert?

Hallo  AtroCty,

mir gefallen deine delays nicht. Verwende doch die eingebauten 
Funktionien.

Ich habe in meinem Code in grauer Vorzeit die i2c Funktionien 
umgeschrieben. Der Code blockt nämlich für immer, wenn etwas bei der i2c 
Kommunikation nicht stimmt. D.h. alle while(!blahh); habe ich ersetzt 
durch
while{!blahhh){
if(!timeout--) break;
}

Der Sinn ist ein ungenaues timeout (weil abhängig von der 
Ausführungszeit der Schleife). Wenn die entsprechend vorinitialisierte 
Variable abgelaufen ist wird im Code weiter gemacht. Das hat sich als 
sehr sinnvoll erwiesen, weil manchmal einfach i2c Nachrichten verloren 
gehen / sinnlose states angenommen werden.
Gast #3106460
Lesenswert?

@Buchegger:

Beim Relais habe ich eine Freilaufdiode verbaut, der sollte diese 
Schmutzspannungen eigentlich abfangen...

Nein, ich habe kein LCD verbaut, und ja, es ist leider ein "stochern", 
weil dies eigentlich nur eine simple Funktion bieten sollte ohne weitere 
Modifikationen.

@Anon Ymous:

Wie genau würde der Code dann oben aussehen?
Ich weiß leider nicht genau wie du das meinst...
#3106485
Lesenswert?

Du hast zb hier
1
int twiReadByte(uint8_t ackn)
2
{
3
  // Befehl zusammenstellen
4
  uint8_t x=TWCR;
5
  x&=_TWEN|_TWIE;      //nur Beibehalten von Enable und InterruptJ/N 
6
  x|=_TWINT;
7
  if(ackn)   
8
    x|=_TWEA;   // evt. TWEA setzen, für Datenanforderung
9
  TWCR=x;         // senden
10
  // warten bis fertig
11
  while( !(TWCR & _TWINT))
12
  {}
13
  return TWDR;
14
}

mit dem while eine potentielle Endlosschleife, wenn aus irgendeinem 
Grund _TWINT nicht so tut, wie es soll.

Ich kenn jetzt das Timing nicht, aber man kann mit Sicherheit ein 
zeitliche Obergrende festlegen, nach der diese Schleife und damir der 
Read als gescheitert zu betrachten ist (jetzt mal unabhängig von der 
Ursache warum das so ist). Ich sach jetzt einfach mal als Hausnummer: 
wenn sich nach 100 Millisekunden da noch nichts getan hat, dann ist da 
was schief gelaufen.
Also macht man sich eine Beschränkung rein, die diese Schleife auf jeden 
Fall nach 100ms abwürgt.
1
int twiReadByte(uint8_t ackn)
2
{
3
  // Befehl zusammenstellen
4
  uint8_t x=TWCR;
5
  x&=_TWEN|_TWIE;      //nur Beibehalten von Enable und InterruptJ/N 
6
  x|=_TWINT;
7
  if(ackn)   
8
    x|=_TWEA;   // evt. TWEA setzen, für Datenanforderung
9
  TWCR=x;         // senden
10
 
11
 // warten bis fertig
12
  uint8_t timeout = 100;
13
  while( !(TWCR & _TWINT) && timeout > 0)
14
  {
15
    _delay_ms( 1 );
16
  }
17

18
  // warum gings aus der Schleife raus. Hat es etwa zu lange
19
  // gedauert?
20
  if( timeout == 0 )
21
    return 0;
22

23
  // Nö. Alles ok. TWDR enthält das Ergebnis
24
  return TWDR;
25
}
wie du dann weiter mit dem Fehlerfall umgehst, das musst du selbst 
entscheiden. Sicherheitshalber solltest du ihn aber auf jeden Fall 
melden! Zb. in dem eine LED an geht!

> weil dies eigentlich nur eine simple Funktion bieten sollte ohne weitere

Es ist NIE einfach nur 'simpel'. Sobald man reale Projekte machen will, 
abseits vom Übungstisch, kommt man um eine Ausgabemöglichkeit nicht rum. 
In einfachsten Fall sind das ein paar LED, dann braucht man aber immer 
noch Phantasie um sich zu überlegen, wie man die jetzt sinnvoll 
einsetzen kann um sich besondere Zustände anzeigen zu lassen. Oder aber 
im einfachsten Fall eine UART oder ein LCD. Eine UART kann man fast 
immer nachrüsten, sofern die Pins frei sind. Das muss ja nicht schön 
sein - es muss zweckmässig sein. 2 fliegend verlötete Drähte auf eine 
kleine Hilfsplatine mit einem MAX232 (oder einem FT232) und von dort 
weiter zum PC und schon ist man im Geschäft.

Und im übrigen:
Wie stellst du eigentlich die Schaltzeiten ein? Wie handhabst du die 
Sommer/Winterzeit Umstellung. Was machst du, wenn bei der RTC mal die 
Batterie leer ist?
Gast #3112456
Lesenswert?

Und zusammenkopiert ist es nicht wirklich, das meiste des Codes kommt 
aus eigener Hand bis auf die Teile des twi's, die sind vom CodeWizard 
vorgegeben. Und da es genau an dem Teil anscheinend hapert würde ich 
schon eine Erklärung bevorzugen, da ich schon gerne die Funktionsweise 
erkennen können würde, jedoch ist mir die Arbeitsweise des µC noch ein 
wenig schleierhaft...
#3112464
Lesenswert?

MaWin schrieb:
>> und kauf dir ein C-Buch.
>> Das ist ja furchtbar. Mit Code zusammenkopieren wirst du nicht weit
>> kommen.
>
> Was bitte gefällt dir an dem Code nicht ?
> Daß er nicht wie dein BASCOM aussieht ?

Ich verwende kein BASCOM.
UNd mit 'furchtbar' war eigentlich gemeint, dass man in einem Vorschlag, 
der sich über 3 Zeilen Code hinzieht nicht selbst die Stelle findet, an 
der man ein -- einfügen kann bzw. muss, um eine bereits vorgegebene 
Funktionalität (Timeout einer potentiellen Endlosschleife) zu 
realisieren.
Gast #3112494
Lesenswert?

Ich verstehe die Wirkungsweise dieses timeouts einfach nicht. Wenn ich 
an einer Stelle den Timer wieder resete würde mir das einleuchten, denn 
da ist die Position der Dekrementierung klar.

€DIT: Ich könnt mich selber für meine Blödheit schlagen g
Hatte das einfach nicht gerallt, dass das damit einfahc verhindert wird, 
dass die Schleife aufhören soll zu wirken sobald er keine Daten 
empfängt.

So ergibt das natürlich wieder Sinn. Ist einfach zu heiß hier in der 
Werkstatt ;)
Gast #3112505
Lesenswert?

kleines Update: Ich habe jetzt eine LED's an einem Ausgang verbaut, 
diese soll bei einem Fehler an dieser Stelle aufleuchten. Jedoch 
leuchtet die LED nicht, noch hat der µC seine gewünschte Funktionsweise 
konstant. (nach 30 Minuten hing er sich auf, die Zeit blieb auf dem 
Display stehen, das Relais blieb geschaltet.)
Gast #3113211
Lesenswert?

AtroCty schrieb:
> Der µC schaltet ja nur einen Transistor, nicht das Relais direkt, weil
> das der µC nicht aushalten würde.

Und was ändert das? Wenn das Relais auf der Versorgungsspannung rumsaut, 
ist das ganz egal, ob in der Ansteuerung noch ein Transistor sitzt.

Wie sieht es denn mit Abblockkondensatoren auf VCC bei µC und 
Relais/Diode aus?
Gast #3113221
Lesenswert?

Sind vorhanden, aber wie gesagt, ich habe es bereits auch mit einer 
externen Platine ohne Relais getestet mit gleichem Ergebnis.

Kann vom aktuellem Netz keine Bilder hochladen, aber ja, 
Stützkondensatoren sind vorhanden für die IC's (µC & TWI), sowie 
Freilaufdiode für das Relais. Spannung wird aus einem DC-DC-Wandler 
bezogen.
Gast #3114665
Lesenswert?

!Bump!

Bin mittlerweile echt ratlos, da der Fehler wohl definitiv am Code/µC 
aufzufinden ist...

Was ist denn an den delay-Funktionen auszusetzen?

Ebenfalls kann ich am Code keine Stelle feststellen, die den Speicher 
overflown... Würde es an den Spannungen liegen würde der µC abstürzen 
und nicht stehen bleiben.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren