Forum: Mikrocontroller und Digitale Elektronik Programm Simulation für Schaltung bei geringer Spannung vom Akku


von spitzname (Gast)


Lesenswert?

Hallo

ich wollte eine analoge Schaltung bauen um einen Bleiakku bei geringer 
Spannung abzuschalten. Am Ende kam das Ergebnis die Schaltung nicht 
analog zu bauen sondern mit einem uC. Die Schaltung sollte jetzt fertig 
sein und bin gerade dabei ein Programm zu schreiben. Es wurde ein 
Atmega8 verwendet. Dabei wird bei vollem Akku eine Lampe auf HIGH 
gesetzt, geblinkt wird bei fast leerem Akku und bei leerem Akku 
abgeschalten.

Jetzt habe ich versucht das Programm zu simulieren ob es auch 
funktioniert.

Beim Programm werden die Ports, ADC und Timer-Interrupt initialisiert. 
Vom ADC-Wert ist abhängig ob die PINs auf HIGH gesetzt werden oder 
blinken soll. Dabei wird sleep_down() verwendet.

Es bleibt aber nach dem ersten sleep_down() hängen. Wie kann ich das 
Programm bei der Simulation von Microchip Studio wieder aus dem 
sleep_down() wecken.

Danke!

LG
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/sleep.h>
4
5
volatile unsigned char zaehler;
6
7
uint16_t adc_read()
8
{
9
  uint16_t adc_measurement = 0;
10
  
11
  ADCSRA |= (1 << ADSC);      //Startet Konvertierung
12
  while(ADCSRA & (1 << ADSC)){
13
  }                //Wartet bis Konvertierung fertig ist
14
    
15
  adc_measurement =  ADCL;        //Speichert Register in adc_value
16
  adc_measurement = (ADCH << 8) + adc_measurement;  
17
  
18
  return adc_measurement;
19
}
20
21
ISR (TIMER1_COMPA_vect)
22
{
23
  zaehler++;
24
}
25
26
27
int main(void)
28
{
29
    /* Replace with your application code */
30
  
31
  DDRD = (1 << DDD1) | (1 <<DDD0);
32
  PORTD = (1 << PD1) | (1 << PD0);
33
  
34
  ADMUX |= (1 << REFS1) | (1 << REFS0);
35
  ADMUX &= ~((1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0));
36
  ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADPS1) | (1 << ADPS0);
37
  ADCSRA &= ~(1 << ADPS2);
38
  
39
  ADCSRA |= (1 << ADSC);      //Startet Konvertierung
40
  while(ADCSRA & (1 << ADSC)){
41
  }                //Wartet bis Konvertierung fertig ist
42
  
43
  TCCR1A &= ~((1 << WGM11) | (1 << WGM10));        //CTC Mode
44
  TCCR1B |= (1 << WGM12) | (1 << CS12) | (1 << CS10);    //1024 Prescaler
45
  TCCR1B &= ~((1 << WGM13) | (1 << CS11));
46
  TIMSK |= (1 << OCIE1A);
47
  
48
  OCR1A = 250;
49
  
50
  sei();
51
  
52
  
53
  
54
  
55
  uint16_t adc_value = 0;
56
  int zustand = 0;
57
  
58
  
59
    while (1) 
60
    {
61
    adc_value = adc_read();
62
    
63
    if(adc_value >= 940)
64
    {
65
      if(zustand == 0)
66
      {
67
        PORTD = (1 << PD1) | (1 << PD0);
68
      }
69
      set_sleep_mode(SLEEP_MODE_PWR_SAVE);
70
      sleep_mode();
71
    }
72
    else if((adc_value <= 939) && (adc_value >= 926))
73
    {
74
      PORTD |= (1 << PD0);  
75
      
76
      if(zustand == 0)
77
      {
78
        zustand = 1;
79
      }
80
      
81
      if(zustand == 1)
82
      {
83
        if (zaehler == 60)
84
        {
85
          PORTD &= ~(1 << PD1);
86
        }
87
        else if (zaehler == 61)
88
        {
89
          zaehler = 0;
90
          PORTD |= (1 << PD1);
91
        }
92
      }
93
      set_sleep_mode(SLEEP_MODE_PWR_SAVE);
94
      sleep_mode();
95
    }
96
    else if (adc_value <= 925)
97
    {
98
      zustand = 2;
99
      PORTD &= ~((1 << PD0) | (1 << PD0));
100
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
101
      sleep_mode();
102
    }
103
    else
104
    {
105
      PORTD &= ~((1 << PD0) | (1 << PD0));
106
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
107
      sleep_mode();
108
    }  
109
    }
110
}

von Timo N. (tnn85)


Lesenswert?

"Only an External Reset, a Watchdog Reset, a Brown-out Reset, a Two-wire 
Serial Interface
address match interrupt, or an external level interrupt on INT0 or INT1, 
can wake up the MCU"

Aus dem ATmega 8 Datasheet.

Was soll in deinem Code denn den µC wieder aus dem Sleep rausholen?

spitzname schrieb:
> if(zustand == 0)
>       {
>         zustand = 1;
>       }
>
>       if(zustand == 1)
>       {
>         if (zaehler == 60)
>         {
>           PORTD &= ~(1 << PD1);
>         }
>         else if (zaehler == 61)
>         {
>           zaehler = 0;
>           PORTD |= (1 << PD1);
>         }
>       }

Versteh ich nicht. Da wird er zuerst in die erste if-Bedingung 
reingehen, zustand auf 1 setzen und deswegen dann auch gleich die zweite 
if-Bedingung erfüllen. Was soll das "zustand"-Setzen dann bringen?

Allgemein wäre es vielleicht zum Stromsparen sinnvoll den Watchdog als 
Timer zu verwenden. Ich denke wenn du irgendwas blinken lassen willst 
mit nem Sekundentakt, dann brauchst du da nicht unbedingt 
Mikrosekundengenauigkeit und die Clock vom Watchdog wird dir reichen. 
Problem ist halt beim ATmega8, dass er dir den µC wieder resettet und du 
nicht wie beim ATmega328p einfach nur ein Interrupt auslösen kannst. Das 
sollte man eventuell im Programm irgendwie lösen.

von spitzname (Gast)


Lesenswert?

Ich habe gelesen dass der Timer2 aktiv ist. Und Timer 2 löst ja ein 
Interrupt aus.
Bei ADC Noise Reduction Mode funktioniert es auch nicht aber bei 
sleep_idle wird es aufgeweckt.

Timo N. schrieb:
> Was soll in deinem Code denn den µC wieder aus dem Sleep rausholen?

Ich dachte der Timer2 Interrupt weckt den uc aus dem Sleep wieder auf. 
Ich habe aber bemerkt dass nur sleep_idle den Timer2 aufweckt.

Timo N. schrieb:
> Versteh ich nicht. Da wird er zuerst in die erste if-Bedingung
> reingehen, zustand auf 1 setzen und deswegen dann auch gleich die zweite
> if-Bedingung erfüllen. Was soll das "zustand"-Setzen dann bringen?

Es wurde gesagt dass die Spannung vom Akku doch schwankt. Wenn es aber 
schon einmal geblinkt hat da es an der Grenze liegt vom blinken und fix 
leuchten, soll es das blinken auch fortsetzen.

Jedoch ist mir jetzt aufgefallen dass es noch nicht funktioniert wenn 
zähler==60 ist aber die erste if-Anweisung ausgeführt wird.

LG

von Timo N. (tnn85)


Lesenswert?

spitzname schrieb:
> Ich habe gelesen dass der Timer2 aktiv ist. Und Timer 2 löst ja ein
> Interrupt aus.

Dein Timer 2 bekommt standardmäßig die CLK von clk_IO. Aber die Clock 
ist laut Table 14 auf Seite 35 im Datenblatt deaktiviert.

https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2486-8-bit-AVR-microcontroller-ATmega8_L_datasheet.pdf

Nur im IDLE sleep mode ist diese CLK überhaupt aktiv (es sei denn du 
hast für den Timer einen speziellen Oszillator).

Long story short: Dein Timer 2 läuft gar nicht.

Außerdem ist der Timer2 Interrupt auch als "Weck"-Quelle laut Table 14 
gar nicht vorgesehen. Dein µC wacht also nie auf.

spitzname schrieb:
> Es wurde gesagt dass die Spannung vom Akku doch schwankt. Wenn es aber
> schon einmal geblinkt hat da es an der Grenze liegt vom blinken und fix
> leuchten, soll es das blinken auch fortsetzen.

Das hat mit dem von mir angesprochenen Problem nichts zu tun.
Wenn er er einmal in diesen Bereich kommt:
1
else if((adc_value <= 939) && (adc_value >= 926))
dann kannst du auch direkt zustand = 1 setzen. Die Abfrage mit
1
if(zustand == 0)
bringt hier nichts, da du dort sowieso gleich zustand = 1 setzt und er 
damit gleich in den nächsten if-Block reinspringt.

von spitzname (Gast)


Lesenswert?

Danke! Super, für die hilfreichen Antworten.

Ich habe den Code erweitert.

Beim Debugging sind noch Fragen aufgetaucht:

1)In der Funktion adc_read() beim I/O ADC muss ich jedes mal nach 
"while(ADCSRA & (1 << ADSC))" einen Wert für ADC eingeben da der Wert 
nach kurzer Zeit erlöscht. Kann man bei Microchip Studio einen Wert für 
ADC Bits speichern damit es beim Debugging nicht immer erlöscht beim 
Interrupt.

2)Ich habe mir bei Watch 1 die Variablen zaehler, adc_value, 
adc_measuremnt, zustand, usw. erstellt. Jedoch wird die Variable 
"adc_measuremnt" nicht angezeigt obwohl es richtig ausgeführt wird. Es 
steht immer "Unknown identifier" bzw. "Optimized away".
Weißt du vielleicht den Grund wieso kein Wert angezeigt wird?

 3)Gibt es irgendwo eine Zeitanzeige wieviel sek. vergehen bis es aus 
dem sleep_idle() geweckt wird.

Hier ist der aktuelle Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/sleep.h>
4
5
volatile unsigned char zaehler;
6
7
void uc_init()
8
{
9
  DDRD = (1 << DDD1) | (1 << DDD0);
10
  PORTD = (1 << PD1) | (1 << PD0);
11
    
12
  ADMUX |= (1 << REFS1) | (1 << REFS0);
13
  ADMUX &= ~((1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0));
14
  ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADPS1) | (1 << ADPS0);
15
  ADCSRA &= ~(1 << ADPS2);
16
    
17
  ADCSRA |= (1 << ADSC);      //Startet Konvertierung
18
  while(ADCSRA & (1 << ADSC)){
19
  }                //Wartet bis Konvertierung fertig ist
20
    
21
  TCCR1A &= ~((1 << WGM11) | (1 << WGM10));        //CTC Mode
22
  TCCR1B |= (1 << WGM12) | (1 << CS12) | (1 << CS10);    //1024 Prescaler
23
  TCCR1B &= ~((1 << WGM13) | (1 << CS11));
24
  TIMSK |= (1 << OCIE1A);
25
    
26
  OCR1A = 250;
27
    
28
  sei();
29
}
30
31
uint16_t adc_read()
32
{
33
  uint16_t adc_measurement = 0;
34
  int adc_mittelwert = 0;
35
  int i = 0;
36
  
37
  for(i=0; i <= 4; i++)
38
  {
39
    ADCSRA |= (1 << ADSC);      //Startet Konvertierung
40
    while(ADCSRA & (1 << ADSC)){
41
    }                //Wartet bis Konvertierung fertig ist
42
    
43
    adc_measurement =  ADCL;        //Speichert Register in adc_value
44
    adc_measurement = (ADCH << 8) + adc_measurement;  
45
    adc_mittelwert |= adc_measurement;
46
  }
47
  
48
  adc_mittelwert = adc_mittelwert / 5;
49
  
50
  return adc_mittelwert;
51
}
52
53
void lampe_leuchten()
54
{
55
  PORTD = (1 << PD1) | (1 << PD0);  
56
}
57
58
void lampe_blinken()
59
{
60
  PORTD |= (1 << PD0);
61
  
62
  if (zaehler == 60)
63
  {
64
    PORTD &= ~(1 << PD1);
65
  }
66
  else if (zaehler == 61)
67
  {
68
    zaehler = 0;
69
    PORTD |= (1 << PD1);
70
  }
71
}
72
73
void lampe_abschalten()
74
{
75
  PORTD |= (1 << PD0);
76
  PORTD &= ~(1 << PD1);
77
}
78
79
void schaltung_abschalten()
80
{
81
  PORTD &= ~((1 << PD0) | (1 << PD1));
82
}
83
84
85
ISR (TIMER1_COMPA_vect)
86
{
87
  zaehler++;
88
}
89
90
91
int main(void)
92
{
93
    /* Replace with your application code */
94
95
  uint16_t adc_value = 0;
96
  int zustand = 0;
97
  
98
  uc_init();
99
  
100
  
101
    while (1) 
102
    {
103
    adc_value = adc_read();
104
    
105
    if(adc_value >= 940)
106
    {
107
      if(zustand == 0)
108
      {
109
        lampe_leuchten();
110
      }
111
      else if(zustand == 1)
112
      {
113
        lampe_blinken();
114
      }
115
      
116
      set_sleep_mode(SLEEP_MODE_IDLE);
117
      sleep_mode();
118
    }
119
    else if((adc_value <= 939) && (adc_value >= 926))
120
    {
121
      if(zustand == 0)
122
      {
123
        zustand = 1;  
124
      }
125
      
126
      if(zustand == 1)
127
      {
128
        lampe_blinken();
129
      }
130
      else if(zustand == 2)
131
      {
132
        lampe_abschalten();
133
      }
134
      
135
      set_sleep_mode(SLEEP_MODE_IDLE);
136
      sleep_mode();
137
    }
138
    
139
    else if ((adc_value <= 925) && (adc_value >= 915))
140
    {
141
      zustand = 2;
142
      
143
      lampe_abschalten();
144
  
145
      set_sleep_mode(SLEEP_MODE_IDLE);
146
      sleep_mode();
147
    }
148
    else
149
    {    
150
      schaltung_abschalten();
151
            
152
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
153
      sleep_mode();
154
    }  
155
    }
156
}

von Manfred (Gast)


Lesenswert?

spitzname schrieb:
> ich wollte eine analoge Schaltung bauen um einen Bleiakku bei geringer
> Spannung abzuschalten. Am Ende kam das Ergebnis die Schaltung nicht
> analog zu bauen sondern mit einem uC.

Weshalb macht Du unter anderem Namen einen neuen Thread auf?

Beitrag "Bleiakku bei geringer Spannung abschalten"

von spitzname (Gast)


Lesenswert?

Hallo,

hat noch jemand Tipps auf diese Unklarheiten?

1)In der Funktion adc_read() beim I/O ADC muss ich jedes mal nach
"while(ADCSRA & (1 << ADSC))" einen Wert für ADC eingeben da der Wert
nach kurzer Zeit erlöscht. Kann man bei Microchip Studio einen Wert für
ADC Bits speichern damit es beim Debugging nicht immer erlöscht beim
Interrupt.

2)Ich habe mir bei Watch 1 die Variablen zaehler, adc_value,
adc_measuremnt, zustand, usw. erstellt. Jedoch wird die Variable
"adc_measuremnt" nicht angezeigt obwohl es richtig ausgeführt wird. Es
steht immer "Unknown identifier" bzw. "Optimized away".
Weißt du vielleicht den Grund wieso kein Wert angezeigt wird?

3)Gibt es irgendwo eine Zeitanzeige wieviel sek. vergehen bis es aus
dem sleep_idle() geweckt wird. Hätte gerne eine genaue Zeitanzeige.

4)Atmel wurde ja von Microchip übernommen. Kann man mit dem PICkit3 bzw. 
MPLAB ICD 3 die Atmel uC flashen oder brauchen die einen Programmer für 
Atmel?
Die 4 Serie kann Atmel und PIC flashen jedoch weiß ich nicht ob auch bei 
der älteren Serie ein Update gemacht wurde.

Danke!

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
Noch kein Account? Hier anmelden.