Forum: Mikrocontroller und Digitale Elektronik Attiny13A, programmieren von verschiedenen abläufen


von Tobias B. (bomber28)


Lesenswert?

Hallo zusammen,
vorweg... ich bin blutiger Anfänger... habe auch schon viel gesucht, 
aber finde einfach nicht so richtig die Hilfe die ich brauche.

Folgendes, ich möchte mir gerne eine Individuelle Lichtsteuerung bauen 
mit einem Attiny13A
Was soll er können?
Verschiende Programme ablaufen lassen
Programme durch schalten mittels Taster
Das aktuelle Programm speichern. Damit er mit dem Programm wieder 
startet, wenn man Ihn wieder einschaltet...

Das habe ich bisher als minimal-version mir zusammen gebastelt.
1
#define F_CPU 4800000UL //4,8MHz
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <avr/eeprom.h>
5
6
uint8_t eeFooByte EEMEM;                              //Variable eeFooByte im EEPROM anlegen.
7
uint8_t Setting_Write;                            //Variable myWByte anlegen und den Wert 1 laden. (Wird später in EEPROM geschrieben)
8
uint8_t Setting_Read;                                 //Variable für den Wert der dann aus EEPROM gelesen wird.
9
10
int main(void)
11
{
12
  #define EEPROM_DEF 0xFF
13
  int x;
14
  
15
  
16
  
17
  Setting_Read = eeprom_read_byte (&eeFooByte);
18
  x = Setting_Read;
19
  if (x < 1) {x = 1;}
20
  
21
  //int x = 1;
22
  int k = 0;
23
  int i = 0;
24
  
25
    DDRB = 0b00000001; //PB0 als Ausgang deklarieren
26
    PORTB = 0b00000010;
27
  
28
  while (1) 
29
    {
30
    k++;
31
    i++;
32
    
33
    if (!(PINB & (1<<1))) {x++; Setting_Write = x; eeprom_write_byte(&eeFooByte, Setting_Write);  _delay_ms(500);} // Invertiert mit (!...) Taster abfragen! Wert aus Variable Setting_Write in eeFooByte (EEPROM) schreiben
34
    
35
    if (x == 1)
36
    {
37
      if (k==10) {PORTB |= (1 << PB0);}
38
      if (k==20) {PORTB &= ~(1 << PB0);}
39
      if (k==30) {PORTB |= (1 << PB0);}
40
      if (k==40) {PORTB &= ~(1 << PB0);}
41
        
42
      if (k==80) {PORTB |= (1 << PB0);}
43
      if (k==90) {PORTB &= ~(1 << PB0);}
44
      if (k==100) {PORTB |= (1 << PB0);}
45
      if (k==110) {PORTB &= ~(1 << PB0);}
46
        
47
      if (k==150) {k = 0;}
48
      
49
    }
50
    if (x == 2)
51
    {
52
      if (k==1) {PORTB |= (1 << PB0);}
53
      if (k==6) {PORTB &= ~(1 << PB0);}
54
      if (k==11) {PORTB |= (1 << PB0);}
55
      if (k==16) {PORTB &= ~(1 << PB0);}
56
      
57
      if (k==26) {PORTB |= (1 << PB0);}
58
      if (k==31) {PORTB &= ~(1 << PB0);}
59
      if (k==36) {PORTB |= (1 << PB0);}
60
      if (k==41) {PORTB &= ~(1 << PB0);}
61
      
62
      if (k==51) {k = 0;}
63
    }
64
    if (x >= 3) {x = 1;}
65
    
66
    
67
    if (i==1000) {i = 0;}  
68
    _delay_ms(25);
69
    }
70
71
}

Probleme: Er schaltet nicht zuverlässig die Programme durch, er bleibt 
hängen, so das eine LED konstant an bleibt oder gar nicht erst leuchtet.
Das Byte in den eeprom schreiben funktioniert wohl auch nicht so 
zuverlässig.

Könnt ihr mit bitte helfen??


Vielen lieben Dank

Tobias

von Stefan F. (Gast)


Lesenswert?

k, x, i sind nicht gerade aussagekräftige Dateinamen. Nutze den Platz 
für längere Namen.

Auch die Port-Zugriffe kann man mit Hilfe von Definitionen und 
Funktionen lesbarer machen. Beispiele:
1
#define BEREITSCHAFTS_LED PB3
2
PORTB |= (1<<BEREITSCHAFTS_LED);   // ein schalten
3
PORTB &= ~ (1<<BEREITSCHAFTS_LED); // aus schalten
4
5
#define Bereitschafts_LED_Ein PORTB |= (1<<PB3);
6
#define Bereitschafts_LED_Aus PORTB &= ~(1<<PB3);
7
Bereitschafts_LED_Ein;
8
Bereitschafts_LED_Aus;
9
10
static inline void Bereitschafts_LED_Ein()
11
{
12
    PORTB |= (1<<PB3);
13
}
14
15
static inline void Bereitschafts_LED_Aus()
16
{
17
    PORTB |= &= ~(1<<PB3);
18
}
19
20
Bereitschafts_LED_Ein();
21
Bereitschafts_LED_Aus();

Anstelle dieser langen "if"-Ketten halte ich "switch" für eleganter.

Bist du sicher dass die Stromversorgung Ok ist? Hast du 
Abblock-Kondensatoren? Zeige mal den Schaltplan und Fotos.

Wozu dient die Variable i?

von Tobias B. (bomber28)


Angehängte Dateien:

Lesenswert?

Da mir der letzte Spannungsteiler hops gegangen ist versorge ich den 
AtTiny13 nun über den ISP6 Anschluss,

Normal hab ich aber einen Spannungsteiler 5V, 1 100µF und 0,1µF 
Kondensator mit drinnen.

von Stefan F. (Gast)


Lesenswert?

Ich sehe da ein Problem:

Wenn k zum Beispiel gerade 14 ist und due dann von Programm X=1 nach x=2 
umschaltest, wird es ziemlich lange dauern, bis der Zähler k überläuft 
und bei 0 beginnt. Ich würde entweder bei jedem Umschalten des Programms 
k auf 0 setzen oder den letzten if Ausruck ändern.

anstatt: if (k==51) {k = 0;}
besser: if (k>=51) {k = 0;}

oder noch besser mit switch:
1
switch(k)
2
{
3
    case 1:  PORTB |=  (1 << PB0); break;
4
    case 6:  PORTB &= ~(1 << PB0); break;
5
    case 11: PORTB |=  (1 << PB0); break;
6
    case 16: PORTB &= ~(1 << PB0); break;     
7
    case 26: PORTB |=  (1 << PB0); break;
8
    case 31: PORTB &= ~(1 << PB0); break;
9
    case 36: PORTB |=  (1 << PB0); break;
10
    case 41: PORTB &= ~(1 << PB0); break;
11
    default: k = 0;
12
}

Beitrag #5545959 wurde von einem Moderator gelöscht.
von Stefan F. (Gast)


Lesenswert?

Tobias B. schrieb:
> Da mir der letzte Spannungsteiler hops gegangen ist

Stromversorgung über einen Spannungsteiler ist schon mal ein riesen 
Fehler. Verwende entweder einen aktiven Spannungsregler (wie den LM7805) 
oder schließe den Mikrocontroller direkt ans Netzteil/Batterie an, falls 
die Spannung passt.

Und nie vergessen: 100nF direkt an jedem IC von VCC nach GND.

Zeige mal den Schaltplan und Fotos.

von Tobias B. (bomber28)


Lesenswert?

Vielen Dank schonmal,
jetzt hab ich das etwas angepasst.
Mit dem Case blieb auf einmal die LED Konstant an... komisch,

Das Programme umschalten funktioniert nun reibungslos (vorerst :-)) aber 
irgendwo hab ich noch den Wurm drinnen mit dem Abspeichern und Auslesen 
des "aktiven" Programms? Wenn ich die Spannung unterbreche, dann fängt 
er wieder mit "1" an.

D.h. entweder speichert oder liest den Wert nicht, das immer mein
1
if (x < 1) {x = 1;}
 greift.


Spannungsregler ist notiert, suche mir da die passenden SMD Bauteile 
raus, 0,1µF hab ich drangehangen an VCC/GND.

Was meint ihr? Ist der 100µF übertrieben? Der ist als SMD Bauteil doch 
recht groß.

Grüße
Tobias
1
#define F_CPU 4800000UL //4,8MHz
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <avr/eeprom.h>
5
6
uint8_t eeFooByte EEMEM;                              //Variable eeFooByte im EEPROM anlegen.
7
uint8_t Setting_Write;                            //Variable myWByte anlegen und den Wert 1 laden. (Wird später in EEPROM geschrieben)
8
uint8_t Setting_Read;                                 //Variable für den Wert der dann aus EEPROM gelesen wird.
9
10
int main(void)
11
{
12
  #define EEPROM_DEF 0xFF
13
  int x;
14
  
15
  
16
  
17
  Setting_Read = eeprom_read_byte (&eeFooByte);
18
  x = Setting_Read;
19
  if (x < 1) {x = 1;}
20
  
21
  //int x = 1;
22
  int k = 0;
23
  int i = 0;
24
  
25
    DDRB = 0b00000001; //PB0 als Ausgang deklarieren
26
    PORTB = 0b00000010;
27
  
28
  while (1) 
29
    {
30
    k++;
31
    i++;
32
    
33
    if (!(PINB & (1<<1))) {x++; Setting_Write = x; eeprom_write_byte(&eeFooByte, Setting_Write); k = 0; i = 0;  _delay_ms(500);} // Invertiert mit (!...) Taster abfragen! Wert aus Variable myWriteByte in eeFooByte (EEPROM) schreiben
34
    
35
    if (x == 1)
36
    {
37
      if (k==10) {PORTB |= (1 << PB0);}
38
      if (k==20) {PORTB &= ~(1 << PB0);}
39
      if (k==30) {PORTB |= (1 << PB0);}
40
      if (k==40) {PORTB &= ~(1 << PB0);}
41
        
42
      if (k==80) {PORTB |= (1 << PB0);}
43
      if (k==90) {PORTB &= ~(1 << PB0);}
44
      if (k==100) {PORTB |= (1 << PB0);}
45
      if (k==110) {PORTB &= ~(1 << PB0);}
46
        
47
      if (k>=150) {k = 0;}
48
      
49
    }
50
    if (x == 2)
51
    {
52
      if (k==1) {PORTB |= (1 << PB0);}
53
      if (k==6) {PORTB &= ~(1 << PB0);}
54
      if (k==11) {PORTB |= (1 << PB0);}
55
      if (k==16) {PORTB &= ~(1 << PB0);}
56
      
57
      if (k==26) {PORTB |= (1 << PB0);}
58
      if (k==31) {PORTB &= ~(1 << PB0);}
59
      if (k==36) {PORTB |= (1 << PB0);}
60
      if (k==41) {PORTB &= ~(1 << PB0);}
61
      
62
      if (k>=51) {k = 0;}
63
    }
64
    if (x >= 3) {x = 1;}
65
    
66
    
67
    if (i==1000) {i = 0;}  
68
    _delay_ms(25);
69
    }
70
71
}

von Stefan F. (Gast)


Lesenswert?

Tobias B. schrieb:
> Was meint ihr? Ist der 100µF übertrieben? Der ist als SMD Bauteil doch
> recht groß.

Schau ins Datenblatt des Spannungsregler. Da wird immer ein konkreter 
Wert empfohlen und manchmal ein maximale erlaubter Wert angegeben.

Ich glaube, dass Schlüsselwort EEMEM soll vor dem Namen der Variable 
stehen.

Warum benutzt du für x eine 16 Bit (int) Variable, kopierst sie aber zum 
Speichern von und zu 8 Bit um? Vermutlich ist das jetzt nicht die 
Problemursache, aber es ist zumindest merkwürdig.

Wenn du diese EEprom Zelle aufgrund vorheriger falscher Versuche schon 
tausende male beschrieben hast, ist sie vielleicht einfach schon 
verschlissen. Probiere mal einen anderen Speicherplatz. Anstelle der 
EEMEM Variable kannst du bei eeprom_write_byte() und eeprom_read_byte() 
einfach die Nummer (=Adresse) der Speicherzelle angeben.

Zeige den Quelltext mit switch/case.
Benutze längere Variablen Namen.
Benutze #define und/oder Funktionen um den Code besser lesbar zu machen.

Ich habe keine Lust, diese Wirrwarr noch öfter analysieren zu müssen.

von Stefan F. (Gast)


Lesenswert?

> Da immer das if (x < 1) {x = 1;} greift.

Woher weißt du, dass genau diese Zeile greift? Hast du einen Debugger 
verwendet, oder wie analysierst du das? Ich würde das gerne wissen, weil 
man die weitere Vorgehensweise darauf aufsetzen kann.

Noch ein möglicher Fehler:

Beim Programmstart aktivierst du den internen Pull-Up Widerstand vom 
Taster und fragst in ohne nennenswerte Verzögerung danach ab.

Je nachdem, wie hoch die parasitäre Kapazität vom Steckbrett, Leitungen 
und Taster sind, ist der pin dann noch dabei, von Low nach High hoch zu 
kommen.

Benutze einen externen Pull-Up Widerstand mit 1 bis 4,7k Ohm und baue 
eine kleine Warteschleife ein, bevor du den Taster zum ersten mal 
abfragst.

von Stefan F. (Gast)


Lesenswert?

> default: k = 0;

Das war falsch. Das greift ja schon bei all den Zwischenständen von k, 
wo NICHTS passieren soll. So müsste es gehen:
1
switch(k)
2
{
3
    case 1:  PORTB |=  (1 << PB0); break;
4
    case 6:  PORTB &= ~(1 << PB0); break;
5
    case 11: PORTB |=  (1 << PB0); break;
6
    case 16: PORTB &= ~(1 << PB0); break;     
7
    case 26: PORTB |=  (1 << PB0); break;
8
    case 31: PORTB &= ~(1 << PB0); break;
9
    case 36: PORTB |=  (1 << PB0); break;
10
    case 41: PORTB &= ~(1 << PB0); break;
11
    case 51: k = 0;
12
}
Dann aber nicht vergessen, k zurück auf 0 zu setzen, wenn der Taster zum 
Programmwechsel gedrückt wird.

von Tobias B. (bomber28)


Lesenswert?

Hallo,

nein ich habe kein Debugger, live zu sehen was der Attiny macht wäre 
schon schön...

So ich hab jetzt das EEMEM davor gesetzt, das Beispiel hatte ich von 
http://avr-programmieren-rh.de/

Desweiteren habe ich das
1
_delay_ms(25);
 nach oben geholt, um ein wenig verweilzeit zu haben um den Taster ab zu 
fragen.

desweiteren hab ich "int" durch "uint8_t" ersetzt.

Jetzt klappt alles! VIELEN DANK EUCH! :-)


Wie meint ihr das, den Code lesbarer zu machen? Sorry für diese Frage..

Grüße
Tobias

von Tobias B. (bomber28)


Lesenswert?

Hab doch noch mal eine Frage,
wenn ich PB5 als Ausgang einstelle und dort eine LED Anschließe, macht 
der Attiny nichts mehr und die LED schimmert..

Ist ja auch der Reset Pin, muss ich da irgendwie was anders machen?

von Stefan F. (Gast)


Lesenswert?

Tobias B. schrieb:
> nein ich habe kein Debugger, live zu sehen was der Attiny macht wäre
> schon schön...

Also hast du das nur vermutet. Das solltest du auch deutlich 
hinschreiben, sonst führt deine (Fehl-)Diagnose die Helfer in die Irre.

Es ist immer Hilfreich, solche Vermutungen durch zusätzliche LED 
Anzeigen oder serielle Ausgaben zu verifizieren. Dafür brauchst du nur 
einen freien Pin. Siehe http://stefanfrings.de/avr_hello_world/

> Wie meint ihr das, den Code lesbarer zu machen? Sorry für diese Frage..

Habe ich doch geschrieben. Versuche es mal so (ungetesteter Code):
1
#define F_CPU 4800000UL //4,8MHz
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include <avr/eeprom.h>
5
6
// Die LED ist an PB0 und GND angeschlossen
7
#define LED_EIN { PORTB |= (1 << PB0); }
8
#define LED_AUS { PORTB &= ~(1 << PB0); }
9
10
// Der Taster ist an PB1 und GND angeschlossen (erfordert Pull-Up)
11
#define TASTER_GEDRUECKT (!(PINB & (1<<PB1)))
12
13
uint8_t eeBlinkMusterNr EEMEM;  // Um das Blinkmuster im EEprom zu speichern
14
uint8_t blinkMusterNr;          // Aktuell laufendes Blinkmuster (1 oder 2)
15
uint8_t step=0;                 // Schrittzähler für die Blinkmuster
16
17
18
void setup()
19
{
20
    // I/O Pins initialisieren
21
    DDRB  = 0b00000001; // PB0=Ausgang für die LED
22
    PORTB = 0b00000010; // PB1=Eingang mit Pull-Up für den Taster
23
    
24
    // Konfiguration aus EEprom lesen
25
    blinkMusterNr = eeprom_read_byte(&eeBlinkMusterNr);
26
}
27
28
29
void blinkmuster_1()
30
{
31
    switch (step)
32
    {
33
        case 10:  LED_EIN; break;
34
        case 20:  LED_AUS; break;
35
        case 30:  LED_EIN; break;
36
        case 40:  LED_AUS; break;
37
        case 80:  LED_EIN; break;
38
        case 90:  LED_AUS; break;
39
        case 100: LED_EIN; break;
40
        case 110: LED_AUS; break;
41
        case 150: step=0;
42
    }
43
}
44
45
46
void blinkmuster_2()
47
{
48
    switch (step)
49
    {
50
        case 1:   LED_EIN; break;
51
        case 6:   LED_AUS; break;
52
        case 11:  LED_EIN; break;
53
        case 16:  LED_AUS; break;
54
        case 26:  LED_EIN; break;
55
        case 31:  LED_AUS; break;
56
        case 36:  LED_EIN; break;
57
        case 41:  LED_AUS; break;
58
        case 51: step=0;
59
    }
60
}
61
62
63
int main(void)
64
{
65
    setup();
66
    
67
    // Main loop, repeats every 25ms
68
    while(1)
69
    {
70
        _delay_ms(25);
71
        
72
        // Wenn Taste gedrückt...
73
        if (TASTER_GEDRUECKT)
74
        {
75
            // dann auf das nächste Blinkmuster umschalten
76
            blinkMusterNr++;  
77
            step=0;          
78
            
79
            // Neue Blinkmuster Nr im EEprom speichern
80
            eeprom_write_byte(&eeBlinkMusterNr, blinkMusterNr);
81
            
82
            // Ein bisschen warten 
83
            // Anm. stefanus: das würde ich etwas "intelligenter" machen
84
            _delay_ms(500);
85
        }
86
        
87
        // Blinkmuster anzeigen    
88
        switch (blinkMusterNr)
89
        {
90
            case 1: blinkmuster_1(); break;
91
            case 2: blinkmuster_2(); break;
92
            default: blinkMusterNr=1;
93
        }
94
        step++;
95
    }
96
}

von Stefan F. (Gast)


Lesenswert?

Du solltest deiner LED dringend einen Vorwiderstand spendieren, wenn du 
sie nicht killen willst. Wenn du das mit vielen LEDs so (ohne 
Widerstand) machst, brennt dir sogar der Mikrocontroller durch.

von Tobias (Gast)


Lesenswert?

Stefanus, vielen Dank.
Vorwiderstand hab ich dran, 220Ohm.

Hmm woran liegt das mit PB5?

Deine code ist sehr übersichtlich, ich werde mir das wohl auch so 
angewöhnen, danke :-)

von Stefan F. (Gast)


Lesenswert?

Tobias schrieb:
> Hmm woran liegt das mit PB5?

PB5 ist der Reset Eingang.

Versuche nicht, die entsprechende Fuse zu ändern, denn ohne Reset 
funktioniert die ganze ISP Schnittstelle danach nicht mehr!

von Stefan F. (Gast)


Lesenswert?

Das Programm hat eine unschöne Stelle:

Wenn blinkMusterNr=2 aktiv ist und du dann die Taste drückst, wird die 
Variable auf 3 erhöht und das dann auch so im EEprom gespeichert.

3 ist ungültig, deswegen wird das direkt danach von den default case auf 
1 geändert.

Letztendlich wird im EEprom immer 2 oder 3 stehen, obwohl in 
Wirklichkeit die Blinkmuster 2 oder 1 aktiv sind. Ist egal, oder?

von Andreas R. (daybyter)


Lesenswert?

Ich glaub, ich würde die Blinksignale in dem Format Start/Länge in 
Arrays schreiben und dann mit ner Schleife drüber gehen, um zu schauen, 
wo ich bin? Langsamer, aber schöner... find ich...

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.