Forum: Mikrocontroller und Digitale Elektronik Programm funktioniert.aber wieso?


von digitalespannung (Gast)


Lesenswert?

Hallo

Ich habe mein erstes Programm in C geschrieben.

Das Programm funktioniert folgerndermassen:

1. Status= 0 -> Warten auf irgendein Zeichen vom UART.
2. Status= 1 -> Starte Motor.
3. Status= 3 -> Warte auf Positive Flanke an INT1 Pin.
4. Status= 4 -> Messe bei Pegeländerung an INT0 Pin.
5. usw.

Nun zu meiner Frage:
Beim Status 3 und 4 macht der Controller nichts anderes als auf einen 
Interrupt zu warten.

In dieser Zeit wollte ich eigentlich eine leere Schlaufe aufrufen oder 
eine Schleife mit einem Delay. Das funktioniert allerdings nicht weill 
dann anscheinend die Interrupts nicht mehr aufgerufen werden.

Im jetztigen Programm rufe ich die Funktion statussend() auf und in 
dieser Funktion ist der einzige Befehl: Printf
dieser Befehl bewirkt nichts.

Wieso funktioniert das ganze nicht wenn ich einfach ein _delay(ms) in 
die Funktion einfüge? Gibt es einen Befehl wie in assembler das NOP der 
einfach nichts macht ?

Danke für eure Hilfe.





1
/*
2
 * Telefon.c
3
 *
4
 * Created: 05.08.2013 13:44:22
5
 */ 
6
7
#include <avr/io.h>
8
#include <stdint.h>
9
#include <stdio.h>
10
#include <avr/interrupt.h>
11
#include <inttypes.h>
12
13
#ifndef F_CPU
14
#define F_CPU 14745600
15
#endif
16
#define UART_BAUD_RATE 115200
17
18
#include <util/delay.h>
19
20
uint8_t senden;            //wert der an Schieberegister gesendet wird
21
uint8_t empfang;
22
uint8_t tmp_sreg;          //SREG zwischenspeicher in interrupts
23
uint8_t status;            //status des Programms
24
uint16_t counter;          //counter 16bit gross
25
// Hilfsmakro zur UBRR-Berechnung ("Formel" laut Datenblatt)
26
#define UART_UBRR_CALC(BAUD_,FREQ_) ((FREQ_)/((BAUD_)*16L)-1)
27
28
//*******************************************************Interrupts*********************************************
29
//Interrupt Flanke an INT0 A&B Signal Encoder
30
//lese A/D Wandleraus und speichere in S-ram
31
ISR (INT0_vect)
32
{
33
  uint8_t werth;            // Speicher für A/D Wert high
34
  uint8_t wertl;            //Speicher für A/D Wert low
35
  
36
  tmp_sreg = SREG;      //SREG sichern
37
  uint8_t temp;        // lokale Variable
38
  
39
  counter++;              //counter +1
40
  
41
  //A/D Wandlung und speichern in S-ram Funktioniert!
42
  
43
//  PORTD = 0b01000000;          //CS SChieberegister auf OFF A/D auf ON
44
  PORTB |= (1<<PB4) ;          //Setze PB4 auf 1 CONV von A/D Wandler
45
  PORTB &= ~ (1<<PB4) ;        //Setze PB4 auf 0 CONV von A/D Wandler
46
  PORTB |= (1<<PB4) ;          //Setze PB4 auf 1 CONV von A/D Wandler
47
  _delay_ms(0.005);          //CONVERTIERUNG max 5 mikro sekunden
48
  
49
  SPDR=senden;              //Irgendwas senden um übertragung zu starten
50
  while (!(SPSR & (1<<SPIF)))      // warten bis übertragung fertig
51
  ;
52
  temp = SPDR;            // Wert in temp schreiben
53
  werth= temp<<1;            //um 1ns nach links verschieben ausgabe an PortA = highbyte
54
  
55
  SPDR = senden++;            // neuen Wert in SPDR schreiben um neue übertragung zu starten
56
  while (!(SPSR & (1<<SPIF)))      // warten bis übertragung fertig
57
  ;
58
  temp = SPDR;
59
  wertl=temp;
60
  if (temp>127)            //falls temp kleiner als 127 ist ist das MSB nicht gesetzt
61
  {
62
    werth = werth | 0b00000001;
63
  }
64
  wertl=wertl<<1;
65
  wertl = wertl & (0b11110111);        //4tes bit invertieren auf 0
66
  PORTA=werth;              //Ausgabe an PortA = Highbyte
67
  PORTC=wertl;            //Ausgabe an PortC = lowbyte
68
69
//  PORTA=counter;
70
//  PORTC=0;
71
72
  //********************************************ADWANDLER AUSGELESEN RESULTAT AN PINS DES MIKROK**************  
73
  //speichern im S-RAM
74
  PORTB &= ~ (1<<PB2);          //PB2=WE auf 0 S-ram
75
  PORTB |= (1<<PB2);            //PB2=WE auf 1 S-ram
76
77
  PORTD &= ~ (1<<PD7);          //PD7 auf 0 setzen Zähler +1 = S-ram adresse +1 = SPEICHERN
78
  PORTD |= (1<<PD7);            //PD7=zähler+1 wieder auf 1 setzen
79
80
  //******************************************SPEICHERN IN S-RAM fertig***************************************
81
  SREG = tmp_sreg;      //SREG wider herstellen
82
  
83
}
84
85
//Interrupt Funktion Positive Flanke an INT1 Z Signal Encoder
86
ISR (INT1_vect)
87
{
88
  tmp_sreg = SREG;      //SREG sichern
89
  if (status==2)              //beginne mit der Messung
90
  {
91
    GICR = (1<<INT1)|(1<<INT0);         // aktivieren von INT0 = AB Impuls
92
  }  
93
  status++;
94
  
95
  SREG = tmp_sreg;      //SREG wider herstellen
96
97
}
98
99
//**********************************************Funktionen*******************************************
100
//Motorstart Funktion
101
uint8_t startmotor()
102
{
103
  DDRA = 0xff;                  //Port A = Ausgang
104
  DDRC = 0xff;                  //Port C = Ausgang
105
  PORTD |= (1<<PD7);                //Counter auf 1 setzen bereit für negative Flanke
106
  PORTB |= (1<<PB0)|(1<<PB3)|(1<<PB1)|(1<<PB2);  //PB0 starte Motor PB3 clear counter PB1 & PB2 = OE und WE auf 1 S-ram
107
  PORTB &= ~(1<<PB3);                //clear counter wider weg
108
  counter=0;                    //zähler reseten
109
  GICR = (1<<INT1);                // aktivieren von INT1 = Z Impuls
110
  status++;                    // sollte jetzt status=2 sein
111
  return(0);
112
}
113
114
//auslesen aus S-Ram und SENDEN FUNKTION... Funktioniert!
115
uint8_t auslesen(void)
116
{
117
  uint16_t temp;                // lokale Variable
118
    cli();                  //schalte Interrupts aus
119
120
    GICR &= ~ ((1<<INT1)|(1<<INT0));     //deaktivieren von Interrupts
121
    DDRA = 0x00;              //PortA als Eingang
122
    DDRC = 0x00;              //PortC als eingang
123
    
124
    PORTB |= (1<<PB3)|(1<<PB2);        //PB3=CLR counter auf 1 PB2= WE S-ram auf 1
125
    PORTB &= ~ ((1<<PB0)|(1<<PB3)|(1<<PB1));    // stoppe Motor, Counter wider auf 0 OE s-ram auf low = ausgabe
126
      
127
  temp=counter;
128
  
129
  while(temp>=2)
130
  {
131
    while(!(UCSRA & (1<<UDRE)))        // Warten bis senden bereit
132
    ;  
133
    UDR=PINA;                //highbyte wird gesendet
134
    while(!(UCSRA & (1<<UDRE)))        // Warten bis senden bereit
135
    ;
136
    UDR=PINC;                //lowbyte wird gesendet
137
    
138
    PORTB |= (1<<PB2);            //S-ram WE auf 1
139
    PORTD &= ~ (1<<PD7);          //PD7 auf 0 setzen Zähler +1 = S-ram adresse +1
140
    PORTD |= (1<<PD7);            //PD7=zähler+1 wieder auf 1 setzen
141
    temp--;
142
  }
143
  status++;                  //status=5
144
  return(0);
145
}
146
uint8_t statussend(void)
147
{
148
  printf("m"); //eigentlich nichts machen aber es funktioniert nur mit printf
149
  
150
    return(0);
151
}
152
153
//EMPFANG FUNKTION
154
uint8_t uartempfangen (void)
155
{
156
  while(!(UCSRA & (1<<RXC))) // Warten bis empfangen
157
  ;
158
  empfang = UDR;
159
  
160
  status=1;          //stauts auf 1 setzen
161
  counter=0;
162
  sei();            //Innterrupts erlauben
163
  return (0);
164
}
165
166
//SPI
167
uint8_t shiftreg (void)
168
{
169
    
170
//PORTD = 0b00100000;          //CS SChieberegister auf OFF A/D auf ON
171
SPDR = senden;
172
while (!(SPSR & (1<<SPIF)))      // warten bis übertragung fertig
173
;
174
//0-1-0 Puls an STCP von Schieberegister um Werte zu übernehmen
175
  PORTB &= ~ (1<<PB4) ;
176
  PORTB |= (1<<PB4);
177
  PORTB &= ~ (1<<PB4) ;
178
    
179
  status=0;
180
  
181
return(0);
182
}
183
184
//ENDE FUNKTIONEN
185
int main(void)
186
{
187
    while(1)
188
    {
189
    //Serielle Schnittstelle einrichten
190
    UBRRH = (uint8_t)( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) >> 8 );
191
    UBRRL = (uint8_t)UART_UBRR_CALC( UART_BAUD_RATE, F_CPU );
192
    // Frameformat = 8 Bit
193
    UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
194
    //Datenrichtungsregister
195
    DDRA = 0x00;            //alles eingänge
196
    DDRB = 0b10111111;          //Alles Ausgänge ausser PB6
197
    DDRC = 0x00;            //alles Eingänge
198
    DDRD = 0b11100010;    
199
    
200
    PORTD = 0b00000000;          //schiberegister und CS von A/D Wandler auf ON 
201
    
202
    UCSRB = (1 << TXEN)|(1 << RXEN);  // Setzt TXEN und RXEN Bit= Transmitter Enable Reciver enable
203
    
204
    //SPI EINRICHTEN
205
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0)|(1<<CPHA); //SPI ENABLE,MASTER, set clock rate fck/16,fallende Taktflanke = einlesen
206
    SPSR = (1<<SPI2X);                // Douple speed aktivieren
207
    PORTB |= (1<<PB4)|(1<<PB1)|(1<<PB2) ;      //Setze PB4 auf 1 CONV von A/D Wandler PB1&2 = S-ram disabled
208
    
209
    SPDR=213;    //Dummy Daten senden
210
    
211
    //Interrupt Eingänge Encoder
212
    MCUCR = (1<<ISC00)|(1<<ISC11)|(1<<ISC10);    // ISC00 auf 1 = jede änderung an INT 0 lösst einen Interrupt aus 
213
                            // ISC11&10 auf 1 = positivie Flanke INT 1 lösst Interrupts aus
214
    status = 0 ;
215
    senden = 0b00000110;
216
    counter=0;
217
    while (1)              // unentliche schleife
218
    {    
219
    switch(status)
220
      {
221
      case 0: uartempfangen();break;          // warten bis LABVIEW bereit
222
      case 1: startmotor();break;            //Motor starten beginne Messung nach Z Impuls
223
      case 2: statussend();break;            //nichts machen warte auf Z impuls
224
      case 3: statussend();break;            //nichts machen warte auf Messung= fertig
225
      case 4: auslesen();break;            //auslesen des s-rams Senden auf schnitstelle
226
      case 5: shiftreg();break;            //irgendetwas auf den I/O Pins anzeigen
227
      default: status=0;break;            //default = neustart        
228
      }      
229
230
    }
231
  }
232
    return 0;    
233
}

von Karl H. (kbuchegg)


Lesenswert?

digitalespannung schrieb:

> 1. Status= 0 -> Warten auf irgendein Zeichen vom UART.
> 2. Status= 1 -> Starte Motor.
> 3. Status= 3 -> Warte auf Positive Flanke an INT1 Pin.
> 4. Status= 4 -> Messe bei Pegeländerung an INT0 Pin.
> 5. usw.
>
> Nun zu meiner Frage:
> Beim Status 3 und 4 macht der Controller nichts anderes als auf einen
> Interrupt zu warten.
>
> In dieser Zeit wollte ich eigentlich eine leere Schlaufe aufrufen oder
> eine Schleife mit einem Delay. Das funktioniert allerdings nicht weill
> dann anscheinend die Interrupts nicht mehr aufgerufen werden.

Wozu dann der ganze Interrupt-Käse?

Mach eine Schleife, in der der Pin abgefragt wird, und wenn sich der 
Pegel ändert, dann hast du dein Ereignis. Dazu braucht man keinen 
Interrupt.

Interrupt und warten, das beißt sich. Interrupts machen nur Sinn, wenn 
das Programm in der Zwischenzeit etwas völlig anderes macht (oder wenn 
man, wie beim Input Capture zyklengenau arbeiten muss).

von Karl H. (kbuchegg)


Lesenswert?

1
ISR (INT0_vect)
2
{
3
  uint8_t werth;            // Speicher für A/D Wert high
4
  uint8_t wertl;            //Speicher für A/D Wert low
5
  
6
  tmp_sreg = SREG;      //SREG sichern

Lass das SREG in Ruhe.
Du programmierst nicht in C, damit du dich um Kleinkram wie SREG 
Register sichern, oder überhaupt Register sichern kümmern muss. Das 
alles geht dich nichts mehr an. Dazu hast du deinen C-Compiler.

Aber eigentlich ist der ganze Ansatz mit den Interrupts schon Käse, wenn 
du sowieso auf die jeweilige Flanke aktiv warten musst. Das 
verkompliziert dein Programm einfach nur deutlich (und eröffnet 
Potential für alle möglichen Sorten von Fehlern), ohne dass es dir auch 
nur irgendetwas bringt.


Schreib dir lieber ein paar Hilfsfunktion, wie zb zum Versenden eines 
Bytes per UART, oder zur ADC Abfrage. Davon hast du deutlich mehr.

von Ralf G. (ralg)


Lesenswert?

digitalespannung schrieb:
1
 tmp_sreg = SREG;      //SREG sichern 
2
 
3
 SREG = tmp_sreg;      //SREG wider herstellen
nimm das mal schleunigst raus, ehe es jemand sieht!

Edit: schon zu spät.

von Dietrich L. (dietrichl)


Lesenswert?

Was ich auf die Schnelle sehe:
1
uint8_t status;            //status des Programms
muss "volatile" sein, da es in "main" und einer ISR benutzt wird.

von Ralf G. (ralg)


Lesenswert?

- 'while(1)' um 'while(1)' drumherum?

digitalespannung schrieb:
> _delay_ms(0.005);          //CONVERTIERUNG max 5 mikro sekunden
Das geht?

von digitalespannung (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> ISR (INT0_vect)
> {
>   uint8_t werth;            // Speicher für A/D Wert high
>   uint8_t wertl;            //Speicher für A/D Wert low
>
>   tmp_sreg = SREG;      //SREG sichern
>
> Lass das SREG in Ruhe.
> Du programmierst nicht in C, damit du dich um Kleinkram wie SREG
> Register sichern, oder überhaupt Register sichern kümmern muss. Das
> alles geht dich nichts mehr an. Dazu hast du deinen C-Compiler.
>
> Aber eigentlich ist der ganze Ansatz mit den Interrupts schon Käse, wenn
> du sowieso auf die jeweilige Flanke aktiv warten musst. Das
> verkompliziert dein Programm einfach nur deutlich (und eröffnet
> Potential für alle möglichen Sorten von Fehlern), ohne dass es dir auch
> nur irgendetwas bringt.
>
> Schreib dir lieber ein paar Hilfsfunktion, wie zb zum Versenden eines
> Bytes per UART, oder zur ADC Abfrage. Davon hast du deutlich mehr.

Okey man sieht meinem Code wohl an das ich vorher in assembler 
programmiert habe. ;)

Alles unter INT0 ist ein auslesen eines A/D Wandlers mit SPI und das 
Ergebnis in ein S-Ram zu speichern.

Die positive Flanke an INT1 aktiviert und deaktiviert die Messung 
wieder.

Die Funktion auslesen lisst das S-Ram wider aus und sendet die Daten 
über die Schnitstelle.

von Karl H. (kbuchegg)


Lesenswert?

Summa summarum würde ich sagen:
Du hast dich da mit deiner komplexen Ablaufsteuerung einfach selbst 
ausgetrickst. Das alles ist deutlich komplizierter, nicht zuletzt durch 
die Interrupts, als es sein müsste.
Deine Problemstellung hat eine recht klare Reihenfolge in der Aktionen 
passieren müssen. Das kann man ganz problemlos ohne Statemachine und 
ohne Interrupts runterprogrammieren.

Das Geflecht aus
* Hochzählen der Status-Variablen in den einzelnen Funktionen und
  in der ISR
* Welche Interrupts sind wann aktiviert
* Was passiert mit durch die Interrupt Flags gespeichertn Interrupts

ist recht undurchdringlich, so dass es nicht so einfach ist, das alles 
auseinanderzudröseln, in welcher Reihenfolge was wann passiert.

von Karl H. (kbuchegg)


Lesenswert?

Dir scheint nicht klar zu sein, dass es je nach Anwendung nicht genügt, 
die Interrupts einfach zu deaktivieren.

Wenn in der Phase, in der die Interrupts deaktiviert sind, ein 
entsprechender Puls in der Zwischenzeit eintrifft, dann wird der 
REGISTRIERT, indem ein entsprechendes Bit gesetzt wird!
Aktivierst du danach die Interrupts wieder, dann kriegst du aufgrund 
dieser gespeicherten Registrierung SOFORT einen entsprechenden Aufruf 
der ISR!

Wenn du also darauf aus bist, dass ein entsprechender Puls am Eingang 
nur dann registriert wird, NACHDEM du den zugehörigen Intertup aktiviert 
hast, dann MUSST du sicherheitshalber diese Registrierungs-Bits auf 
jeden Fall löschen!

von digitalespannung (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Dir scheint nicht klar zu sein, dass es je nach Anwendung nicht
> genügt,
> die Interrupts einfach zu deaktivieren.
>
> Wenn in der Phase, in der die Interrupts deaktiviert sind, ein
> entsprechender Puls in der Zwischenzeit eintrifft, dann wird der
> REGISTRIERT, indem ein entsprechendes Bit gesetzt wird!
> Aktivierst du danach die Interrupts wieder, dann kriegst du aufgrund
> dieser gespeicherten Registrierung SOFORT einen entsprechenden Aufruf
> der ISR!
>
> Wenn du also darauf aus bist, dass ein entsprechender Puls am Eingang
> nur dann registriert wird, NACHDEM du den zugehörigen Intertup aktiviert
> hast, dann MUSST du sicherheitshalber diese Registrierungs-Bits auf
> jeden Fall löschen!

Stimmt! Das habe ich vergessen. Ich muss INTF1 & 2 noch auf 1 setzen um 
die Bits zu löschen.

von digitalespannung (Gast)


Lesenswert?

Dietrich L. schrieb:
> Was ich auf die Schnelle sehe:uint8_t status;            //status
> des Programms
> muss "volatile" sein, da es in "main" und einer ISR benutzt wird.

Danke! Ich habe jetzt status und counter volatile gemacht.
Jetzt funktioniert es auch mit einer leeren Schleife. Ich habe es so 
programmiert damit ich evt. noch etwas erledigen kann dort wo jetzt ein 
leerlauf ist.

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.