Forum: Mikrocontroller und Digitale Elektronik ICP mit ATmega88 und Multiplexer


von Patrick R. (pat711)


Lesenswert?

Guten Abend zusammen,

ich habe mich daran Versucht drei Frequenzen mittels ICP auszuwerten.
Daher habe ich das Datenblatt zur Hand genommen und nach diesem meinen 
Code geschrieben.
Im Datenblatt steht unter anderem, dass die Auswertung mit ICP nicht 
empfohlen wird, wenn sich das Signal stetig wandelt (oder hab ich das 
falsch verstanden:Using the Input Capture unit in any mode of operation 
when the TOP value (resolution) is
actively changed during operation, is not recommended.?)
Dies ist beim Multiplexen ja der Fall. Daher dachte ich mir ich wähle 
mit dem Multiplexer die gewünschte Frequenz, die gemessen werden soll 
und warte 4 Perioden ab. Alle außer der 3. werden verworfen.
Das Problem bei der Sache ist nun allerdings, dass alle drei werte der 
veschiedenen Frequenzen nahezu identisch sind.
Hier mal die Ausgabe des USART:
1
Sensor1: 359       Sensor2: 360       Sensor3: 364       
2
Sensor1: 363       Sensor2: 362       Sensor3: 362       
3
Sensor1: 364       Sensor2: 364       Sensor3: 367       
4
Sensor1: 364       Sensor2: 366       Sensor3: 366       
5
Sensor1: 361       Sensor2: 361       Sensor3: 362       
6
Sensor1: 360       Sensor2: 361       Sensor3: 363       
7
Sensor1: 359       Sensor2: 363       Sensor3: 364       
8
Sensor1: 364       Sensor2: 365       Sensor3: 365       
9
Sensor1: 363       Sensor2: 363       Sensor3: 366       
10
Sensor1: 362       Sensor2: 362       Sensor3: 359       
11
Sensor1: 236       Sensor2: 215       Sensor3: 238       
12
Sensor1: 363       Sensor2: 362       Sensor3: 360       
13
Sensor1: 359       Sensor2: 363       Sensor3: 363       
14
Sensor1: 363       Sensor2: 364       Sensor3: 366       
15
Sensor1: 360       Sensor2: 359       Sensor3: 364       
16
Sensor1: 362       Sensor2: 361       Sensor3: 363      
17
Sensor1: 364       Sensor2: 363       Sensor3: 366       
18
Sensor1: 364       Sensor2: 364       Sensor3: 367       
19
Sensor1: 215       Sensor2: 224       Sensor3: 221       
20
Sensor1: 359       Sensor2: 359       Sensor3: 363       
21
Sensor1: 360       Sensor2: 360       Sensor3: 363       
22
Sensor1: 358       Sensor2: 358       Sensor3: 364       
23
Sensor1: 362       Sensor2: 362       Sensor3: 364       
24
Sensor1: 361       Sensor2: 361       Sensor3: 362       
25
Sensor1: 359       Sensor2: 358       Sensor3: 364       
26
Sensor1: 363       Sensor2: 362       Sensor3: 361

wie man sehen kann ist der wert immer um die 360 bricht allerdings auch 
immer wieder in den 200-er bereich zusammen. Kann es sein dass ich in 
meinem Code nen Fehler habe und immer 3 mal die Werte des gleichen 
Sensors angezeigt werden aber immer wieder ein anderer Sensor ausgelesen 
wird?

Hier der dazugehörige Code:
1
#include <avr/io.h>
2
#include <util/delay.h> 
3
#include <avr/interrupt.h>
4
#include <stdlib.h>
5
6
#define F_CPU 12000000L 
7
#define BAUD 9600L 
8
9
#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde 
10
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))         //reale Baudrate 
11
12
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille 
13
14
#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10)) 
15
#error Systematischer Fehler in der Baudrate größer 1% und damit zu hoch! 
16
#endif 
17
18
#define Adresse_A   PC0
19
#define Adresse_B   PC1
20
#define Adresse_c   PC2
21
22
#define Signal    PD2
23
24
#define Taster    PC3
25
26
unsigned int high_byte = 0;
27
unsigned int low_byte = 0;
28
unsigned int periodendauer = 0;
29
unsigned int signal[3];
30
int counter = 0; 
31
unsigned int kanal = 1;
32
unsigned int messung = 0;
33
34
35
36
ISR (TIMER1_CAPT_vect)              //4)Führe diese ISR bei Overflow von TCNT0 aus
37
{
38
  low_byte = ICR1L;
39
  high_byte = ICR1H;
40
  periodendauer = (high_byte * 256) + low_byte;
41
  signal[kanal -1] = periodendauer;
42
  kanal ++;
43
  TCNT1H = 0;
44
  TCNT1L = 0;
45
  ICR1H  = 0;
46
  ICR1L  = 0;
47
  if (kanal == 4)
48
  {
49
    kanal = 1;
50
  }
51
}
52
53
54
int uart_putc(unsigned char c)
55
{
56
  while (!(UCSR0A & (1<<UDRE0)))  /* warten bis Senden moeglich */
57
  {
58
  }                             
59
 
60
  UDR0 = c;                      /* sende Zeichen */
61
  return 0;
62
}
63
 
64
 
65
void uart_puts (char *s)
66
{
67
  while (*s)
68
  {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
69
    uart_putc(*s);
70
    s++;
71
  }
72
} 
73
74
75
int main(void)
76
{ 
77
  DDRC |= (1<<PC0) | (1<<PC1) | (1<<PC2);
78
  PORTC &= !((1<<PC0) | (1<<PC1) | (1<<PC2));
79
  /* Signal und Tasterpin bleiben Eingänge und kriegen Pull-Ups */
80
  PORTD |= (1<<Signal); // Pull-Up Signal
81
  PORTC |= (1<<Taster); // Pull-Up Taster
82
    
83
  /*Timer initialisieren und aktivieren */
84
  
85
  TCCR1B = (1<<CS11);    // Prescaler 8 /  12MHz:8 = 1,5MHz
86
  TIMSK1|=(1<<ICIE1);    // ICP aktivieren
87
  
88
  
89
  
90
  /* UART initialisieren und aktivieren */
91
  UBRR0H = UBRR_VAL >> 8; 
92
  UBRR0L = UBRR_VAL & 0xFF; 
93
    
94
    
95
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);        //UART einschalten 
96
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);        //Asynchron 8N1 
97
  
98
  /* INT0 interruptpin initialisieren */
99
  
100
  EICRA |= (1<<ISC00) | (1<<ISC01);
101
  EIMSK |= (1<<INT0);
102
  
103
  sei();           // Interrupts aktivieren
104
    
105
  
106
  char s[7];
107
    
108
  while (1)
109
  {  
110
    if(!(PINC&(1 << PC3)))
111
    {
112
      for (int count = 1; count < 4; count++)
113
      {
114
        uart_puts("Sensor");
115
        uart_puts( itoa( count, s, 10 ) );
116
        uart_puts(": ");
117
        uart_puts( itoa( signal[count - 1], s, 10 ) );
118
        uart_puts("       ");
119
      }
120
      while(!(PINC&(1 << PC3)));
121
    }
122
    if (kanal == 1)
123
    {
124
      PORTC &= ~((1<<PC0) | (1<<PC1) | (1<<PC2));   // Alle Adressleitungen auf 0
125
    }
126
    else if(kanal == 2)
127
    {
128
      PORTC |= (1<<PC0);
129
      PORTC &= ~((1<<PC1) | (1<<PC2));          // A auf 1 B und C auf 0
130
    }
131
    else if(kanal == 3)
132
    {
133
      PORTC |= (1<<PC1);
134
      PORTC &= ~((1<<PC2) | (1<<PC0));            // B auf 1 A und C auf 0
135
    }
136
  }
137
138
  return 0;                      
139
}
140
141
// todo: Variable messung? Kanal variable
142
//      auf ICP anpassen

der Verlauf der Signale folgt gleich noch den muss ich von nem anderen 
PC aus hochladen.


MfG Pat711

von Patrick R. (pat711)


Angehängte Dateien:

Lesenswert?

Hier noch die Aufnahme des Signalverlaufs

Signal 1: TXD des Controllers
Signal 2: Frequenz 1
Signal 3: Frequenz 2
Signal 4: Frequenz 3
Signal 5: Adresse A des Multiplexers
Signal 6: Adresse A des Multiplexers
Signal 7: Adresse A des Multiplexers
Signal 8: ICP - Eingang des Controllers

MfG Pat711

von ICP (Gast)


Lesenswert?

1. lass den TIMER1 durchlaufen und ermittle nur die Differenz zum 
Vorhergehenden ICP wert
2. Möglichen Überlauf von TIMER1berücksichtigen!
3. Taste entprellen.
4. Multiplexer noch im Interrupt umschalten, nur so kannst du sicher 
sein dass nach dem hochzählen der Kanalnummer auch der Multiplexer 
umgeschaltet ist. Die ausgabe nach dem Tasterdruck braucht "unendlich" 
lange
5. Nach dem umschalten mind. 1 bis 2 Messwerte verwerfen.
6. Wo ist die ISR für den INT0?
7. #defines GROSS schreiben erhöht die Lesbarkeit
8. #defines VERWENDEN

viel Erfolg

von Patrick R. (pat711)


Lesenswert?

Vielen Dank für deine Antwort ich werde meinen Code danach überarbeiten.
Hier noch ein paar Anmerkungen:

1.Um nicht die Zeit mit ein zu rechnen?
2.
3.Das hat erst mal zeit, da es nicht weiter problematisch ist, wenn das 
Ganze mehrmals gesendet wird.
4.
5. Hatt ich vor war allerdings in diesem Code noch nicht drin und den 
neueren hab ich so verbock dass ich ihn verworfen hab ;-)
6. Die Initialisierung stammt noch aus früheren versuchen, das muss ich 
noch raus schmeißen
7.
8.da hatte ich ein problem mit da Adresse_C irgendwie nicht erkannt 
worden war und der Compiler dauernd meckerte die Variable sei ihm nicht 
bekannt

MfG Pat711

von ICP (Gast)


Lesenswert?

1. Um arbeit zu sparen.
3. 51 Zeichen bei 9600Baud sind 54ms ohne die Takte fürs umrechnen. Da 
geht ganz schön zeit drauf. Nimm die Lösung zur Tastenentprellung aus 
dem Artikelbereich. (Nicht die Anfängerlösung mit delay) -> Es kann 
nicht warten

6. Der ATmega springt irgend wohin wenn die ISR  Routine fehlt. Lass den 
Timer0 drin und mach damit die Entprellung. Oder nimm den vorteiler bei 
Timer1 raus und mach die Tasten in jedem 2 Überlauf mit. Den Überlauf 
von Timer1 musst du eh noch berücksichtigen.......

8 #define funktioniert, wenn nicht dann liegts das Problem wo anders. 
Deshalb hatte ich Punkt 7 aufgeführt.

Wenn da steht
1
#define Adresse_c PC2
dann kann der Compiler Adresse_C nicht finden! C ist case sensitive

Grüße

von Patrick R. (pat711)


Angehängte Dateien:

Lesenswert?

Guten Abend

ich habe meinen Code nochmals überarbeitet und unter anderem den Taster 
komplett hinaus geworfen, da dieser ja nicht das elementare an dem code 
ist. Das Ziel ist ja eigentlich eine Saubere ICP - Auswertung außerdem 
kommen so die ergebnisse regelmäßig.

Hier das Ergebnis:

Das Bild zeigt die gesendeten Daten Visualisiert.

und hier noch der Code:
1
#include <avr/io.h>
2
#include <util/delay.h> 
3
#include <avr/interrupt.h>
4
#include <stdlib.h>
5
6
#define F_CPU 12000000L 
7
#define BAUD 9600L 
8
9
#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde 
10
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))         //reale Baudrate 
11
12
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille 
13
14
#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10)) 
15
#error Systematischer Fehler in der Baudrate größer 1% und damit zu hoch! 
16
#endif 
17
18
#define Adresse_A   PC0
19
#define Adresse_B   PC1
20
#define Adresse_c   PC2
21
22
#define Signal    PD2
23
24
#define Taster    PC3
25
26
unsigned int high_byte = 0;
27
unsigned int low_byte = 0;
28
unsigned int periodendauer = 0;
29
unsigned int ergebnis[3];
30
int counter = 0; 
31
unsigned int kanal = 1;
32
int alt = 0;
33
unsigned int uart = 0, messung = 0;
34
35
36
37
ISR (TIMER1_CAPT_vect)              //4)Führe diese ISR bei Overflow von TCNT0 aus
38
{
39
  low_byte = ICR1L;
40
  high_byte = ICR1H;
41
  periodendauer = (high_byte * 256) + low_byte - (alt);  // Differenz des Timers zum letzten Interrupt berechnen
42
  alt = (high_byte * 256) + low_byte;          // Aktuellen Timerstand abspeichern
43
  messung ++;
44
  if (messung == 4)
45
  {
46
    ergebnis[kanal -1] = periodendauer;            // Das Ergebnis in der zum Kanal passenden Variablen speichern
47
    kanal ++;                        // nächsten Kanal wählen
48
    if (kanal == 4)
49
    {
50
      kanal = 1;
51
    }
52
    if (kanal == 1)
53
    {
54
      PORTC &= ~((1<<PC0) | (1<<PC1) | (1<<PC2));   // Alle Adressleitungen auf 0
55
    }
56
    else if(kanal == 2)
57
    {
58
      PORTC |= (1<<PC0);
59
      PORTC &= ~((1<<PC1) | (1<<PC2));          // A auf 1 B und C auf 0
60
    }
61
    else if(kanal == 3)
62
    {
63
      PORTC |= (1<<PC1);
64
      PORTC &= ~((1<<PC2) | (1<<PC0));            // B auf 1 A und C auf 0
65
    }
66
    messung = 0;
67
  }
68
}
69
70
ISR (TIMER1_OVF_vect)
71
{
72
  alt = 0-(65535 - alt);      
73
}
74
75
int uart_putc(unsigned char c)
76
{
77
  while (!(UCSR0A & (1<<UDRE0)))  /* warten bis Senden moeglich */
78
  {
79
  }                             
80
 
81
  UDR0 = c;                      /* sende Zeichen */
82
  return 0;
83
}
84
 
85
 
86
 
87
void uart_puts (char *s)
88
{
89
  while (*s)
90
  {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
91
    uart_putc(*s);
92
    s++;
93
  }
94
} 
95
96
97
int main(void)
98
{ 
99
  DDRC |= (1<<PC0) | (1<<PC1) | (1<<PC2);
100
  PORTC &= !((1<<PC0) | (1<<PC1) | (1<<PC2));
101
  /* Signal und Tasterpin bleiben Eingänge und kriegen Pull-Ups */
102
  PORTD |= (1<<Signal); // Pull-Up Signal
103
  PORTC |= (1<<Taster); // Pull-Up Taster
104
    
105
  /*Timer initialisieren und aktivieren */
106
  
107
  TCCR1B = (1<<CS11);    // Prescaler 8 /  12MHz:8 = 1,5MHz
108
  TIMSK1|=(1<<ICIE1);    // ICP aktivieren
109
  
110
  
111
  
112
  /* UART initialisieren und aktivieren */
113
  UBRR0H = UBRR_VAL >> 8; 
114
  UBRR0L = UBRR_VAL & 0xFF; 
115
    
116
    
117
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);        //UART einschalten 
118
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);        //Asynchron 8N1 
119
  
120
  
121
  sei();           // Interrupts aktivieren
122
    
123
  
124
  char s[7];
125
    
126
  while (1)
127
  {  
128
    if(uart >= 60000)        // Bei jedem Timerdurchlauf 1 mal ausführen
129
    {                  // Taster entfernt, da nicht unbedingt notwendig
130
      for (int count = 1; count < 4; count++)
131
      {
132
        uart_puts("S");
133
        uart_puts( itoa( count, s, 10 ) );
134
        uart_puts(": ");
135
        uart_puts( itoa( ergebnis[count - 1], s, 10 ) );
136
        uart_puts("   ");
137
      }
138
      uart = 0;
139
    }
140
    uart ++;
141
    
142
  }
143
144
  return 0;                      
145
}

Vielen Dank für die Großartige Hilfe.
Vll fallen ja noch jemandem ein paar unschönheiten an dem Code auf. 
Einige sind noch drin das weiß ich ;-)

MfG Pat711

Edit: achso das mit den defines war  so ne komische sache an der 
Schreibweise lag es nämlich nicht, ich habe es sogar mit kopieren 
versucht :). Nun hab ich die erst mal nicht mehr drin (die zum 
Multiplexer) aber des muss ich nochmal irgendwann nachschaun ^^

von ulrich (Gast)


Lesenswert?

Es fehlt immer noch das volatile bei einigen variablen definitioenen. 
Das kann so gut gehen, muss es aber nicht. Einige der Variablen wie 
low_byte wären besser als lokale Variablen in der ISR.

Der Zugriff auf das Ergebnis ist auch noch nicht vor einem 
zwischenzeitlichen Interrupt geschützt. Das kann zu sehr seltenen 
Fehlern führen.

Die Variable "alt" muss eigentlich unsigned int sein. Die ISR zum Timer1 
überlauf ist falsch. Zumindest wenn man alt als unsigned hat, kann die 
ISR (und die aktivierung des Interrupts) ersatzlos entfallen. Die 
Arithmetik mit vorzeichenlosen Zahlen sorgt schon dafür, dass der 
Überlauf richtig berücksichtiget wird. Das Ergebnis ist dann immer die 
positive Differenz in der Zeit.

Man könnte auch noch den Noise Cancler für die ICP Funktion einschalten, 
wenn die Pulse nicht sehr kurz ( < 1 µs) sind

von Patrick R. (pat711)


Lesenswert?

Guten Abend,

das volatile bestimmt doch, dass die variable vor jeder Verwendung neu 
aus dem Speicher gelesen werden muss. Wiso ist das bei controllern 
überhaupt wichtig? es reift ja keine andere anwendung auf den speicher 
zu???

und das mit der variablen alt versteh ich auch noch nicht ganz. Hier ein 
Beispiel:
Version mit vorzeichen:
alt = 0-(65535 - alt);
also beispielsweise wenn alt 65000 ist:
alt = 0-(65535 - 65000)
alt = -535

beim nächsten capture - interrupt dann:
neuer wert im ICR1 register = 100
dann:
periodendauer = 100 - (-535)
periodendauer = 100 + 535
periodendauer = 635


bei der version ohne vorzeichen:
variable alt = 65000
neuer wert: 100
dann:
periodendauer = 100 - 65000???
wäre ja aber ein positives ergebnis, das keine vorzeichen hat

die aktivierung des interrupts weglassen? der overflowinterrupt wird 
doch mit dem timer aktiviert oder?

den noise canceller hatte ich schon probiert hatte in der alten version 
aber keine große wirkung. ich werd den nochmal einbaun

MfG Pat711

von ulrich (Gast)


Lesenswert?

Beim µC greift keine andere Anwendung auf den Speicher zu, das ist der 
Code in der ISR.  Der Code im Hauptprogramm weiss ohne das Volatile 
nicht, das sich die Werte ändern können. Ggf wird dann zu viel 
optimiert.
Wenn man Variablen im normalen Code und der ISR nutzt muss man die 
Volatile machen.

Wenn die variabel alt mit Vorzeichen Deklariert, kann die den Wert 65000 
gar nicht annehmen. Da sind dann nur werte von -32xxx bis + 32xxx 
möglich. Wenn überhaupt, ist die ISR auch noch um 1 daneben, der 
Überlauf kommt alle 65536 Schritte. Bei Integer Werten ist der Überlauf 
auch nicht eindeutig definiert. In der Regel passiert das gleiche wie 
bei unsigned Werten, weil die Addition / Subtraktion auf ASM-ebene meist 
nicht nach signed / unsigned unterscheidet. Nur gesichert durch den 
C-Standard ist das Verhalten nicht, es kann vom Compiler abhängen.
Bei der Rechnung mit Int Werten kann auch keine Wert größer 32768 
raukommen, da wird es dann negativ.

Bei der Rechnung ohne Vorzeichen gibt es bei der Berechnung von
100-65000  gerade einen Unterlauf. Das Ergebnis ist dann
65536 + 100 - 65000 = 636
genau so wie es sein soll. Das verhalten für unsigned variablen ist so 
von C vorgegeben - das sollte also Compilerunabhängig funktionieren.

Auf die timer_overflow ISR kann man also verzichten. Man kann den Timer 
overflow interrupt auch abschalten. Eigeschatet wird der im Register
 TIMSK1 . So wie es aussieht, wird der interrupt hier auch noch gar 
nicht eingeschaltet.

Man sollte die Register am Anfang auch besser ganz definieren, und nicht 
nur einzelne Bits setzen. So stellt man sicher das die anderen Bits auch 
wirklich gelöscht sind. Es ließt sich besser, weil man weiß das es egal 
ist was vorher drin stand.

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.