www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ICP mit ATmega88 und Multiplexer


Autor: Patrick R. (pat711)
Datum:

Bewertung
0 lesenswert
nicht 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:
Sensor1: 359       Sensor2: 360       Sensor3: 364       
Sensor1: 363       Sensor2: 362       Sensor3: 362       
Sensor1: 364       Sensor2: 364       Sensor3: 367       
Sensor1: 364       Sensor2: 366       Sensor3: 366       
Sensor1: 361       Sensor2: 361       Sensor3: 362       
Sensor1: 360       Sensor2: 361       Sensor3: 363       
Sensor1: 359       Sensor2: 363       Sensor3: 364       
Sensor1: 364       Sensor2: 365       Sensor3: 365       
Sensor1: 363       Sensor2: 363       Sensor3: 366       
Sensor1: 362       Sensor2: 362       Sensor3: 359       
Sensor1: 236       Sensor2: 215       Sensor3: 238       
Sensor1: 363       Sensor2: 362       Sensor3: 360       
Sensor1: 359       Sensor2: 363       Sensor3: 363       
Sensor1: 363       Sensor2: 364       Sensor3: 366       
Sensor1: 360       Sensor2: 359       Sensor3: 364       
Sensor1: 362       Sensor2: 361       Sensor3: 363      
Sensor1: 364       Sensor2: 363       Sensor3: 366       
Sensor1: 364       Sensor2: 364       Sensor3: 367       
Sensor1: 215       Sensor2: 224       Sensor3: 221       
Sensor1: 359       Sensor2: 359       Sensor3: 363       
Sensor1: 360       Sensor2: 360       Sensor3: 363       
Sensor1: 358       Sensor2: 358       Sensor3: 364       
Sensor1: 362       Sensor2: 362       Sensor3: 364       
Sensor1: 361       Sensor2: 361       Sensor3: 362       
Sensor1: 359       Sensor2: 358       Sensor3: 364       
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:
#include <avr/io.h>
#include <util/delay.h> 
#include <avr/interrupt.h>
#include <stdlib.h>

#define F_CPU 12000000L 
#define BAUD 9600L 

#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde 
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))         //reale Baudrate 

#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille 

#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10)) 
#error Systematischer Fehler in der Baudrate größer 1% und damit zu hoch! 
#endif 

#define Adresse_A   PC0
#define Adresse_B   PC1
#define Adresse_c   PC2

#define Signal    PD2

#define Taster    PC3

unsigned int high_byte = 0;
unsigned int low_byte = 0;
unsigned int periodendauer = 0;
unsigned int signal[3];
int counter = 0; 
unsigned int kanal = 1;
unsigned int messung = 0;



ISR (TIMER1_CAPT_vect)              //4)Führe diese ISR bei Overflow von TCNT0 aus
{
  low_byte = ICR1L;
  high_byte = ICR1H;
  periodendauer = (high_byte * 256) + low_byte;
  signal[kanal -1] = periodendauer;
  kanal ++;
  TCNT1H = 0;
  TCNT1L = 0;
  ICR1H  = 0;
  ICR1L  = 0;
  if (kanal == 4)
  {
    kanal = 1;
  }
}


int uart_putc(unsigned char c)
{
  while (!(UCSR0A & (1<<UDRE0)))  /* warten bis Senden moeglich */
  {
  }                             
 
  UDR0 = c;                      /* sende Zeichen */
  return 0;
}
 
 
void uart_puts (char *s)
{
  while (*s)
  {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
    uart_putc(*s);
    s++;
  }
} 


int main(void)
{ 
  DDRC |= (1<<PC0) | (1<<PC1) | (1<<PC2);
  PORTC &= !((1<<PC0) | (1<<PC1) | (1<<PC2));
  /* Signal und Tasterpin bleiben Eingänge und kriegen Pull-Ups */
  PORTD |= (1<<Signal); // Pull-Up Signal
  PORTC |= (1<<Taster); // Pull-Up Taster
    
  /*Timer initialisieren und aktivieren */
  
  TCCR1B = (1<<CS11);    // Prescaler 8 /  12MHz:8 = 1,5MHz
  TIMSK1|=(1<<ICIE1);    // ICP aktivieren
  
  
  
  /* UART initialisieren und aktivieren */
  UBRR0H = UBRR_VAL >> 8; 
  UBRR0L = UBRR_VAL & 0xFF; 
    
    
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);        //UART einschalten 
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);        //Asynchron 8N1 
  
  /* INT0 interruptpin initialisieren */
  
  EICRA |= (1<<ISC00) | (1<<ISC01);
  EIMSK |= (1<<INT0);
  
  sei();           // Interrupts aktivieren
    
  
  char s[7];
    
  while (1)
  {  
    if(!(PINC&(1 << PC3)))
    {
      for (int count = 1; count < 4; count++)
      {
        uart_puts("Sensor");
        uart_puts( itoa( count, s, 10 ) );
        uart_puts(": ");
        uart_puts( itoa( signal[count - 1], s, 10 ) );
        uart_puts("       ");
      }
      while(!(PINC&(1 << PC3)));
    }
    if (kanal == 1)
    {
      PORTC &= ~((1<<PC0) | (1<<PC1) | (1<<PC2));   // Alle Adressleitungen auf 0
    }
    else if(kanal == 2)
    {
      PORTC |= (1<<PC0);
      PORTC &= ~((1<<PC1) | (1<<PC2));          // A auf 1 B und C auf 0
    }
    else if(kanal == 3)
    {
      PORTC |= (1<<PC1);
      PORTC &= ~((1<<PC2) | (1<<PC0));            // B auf 1 A und C auf 0
    }
  }

  return 0;                      
}

// todo: Variable messung? Kanal variable
//      auf ICP anpassen

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


MfG Pat711

Autor: Patrick R. (pat711)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: ICP (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Patrick R. (pat711)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: ICP (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
#define Adresse_c PC2
dann kann der Compiler Adresse_C nicht finden! C ist case sensitive

Grüße

Autor: Patrick R. (pat711)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <util/delay.h> 
#include <avr/interrupt.h>
#include <stdlib.h>

#define F_CPU 12000000L 
#define BAUD 9600L 

#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde 
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))         //reale Baudrate 

#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille 

#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10)) 
#error Systematischer Fehler in der Baudrate größer 1% und damit zu hoch! 
#endif 

#define Adresse_A   PC0
#define Adresse_B   PC1
#define Adresse_c   PC2

#define Signal    PD2

#define Taster    PC3

unsigned int high_byte = 0;
unsigned int low_byte = 0;
unsigned int periodendauer = 0;
unsigned int ergebnis[3];
int counter = 0; 
unsigned int kanal = 1;
int alt = 0;
unsigned int uart = 0, messung = 0;



ISR (TIMER1_CAPT_vect)              //4)Führe diese ISR bei Overflow von TCNT0 aus
{
  low_byte = ICR1L;
  high_byte = ICR1H;
  periodendauer = (high_byte * 256) + low_byte - (alt);  // Differenz des Timers zum letzten Interrupt berechnen
  alt = (high_byte * 256) + low_byte;          // Aktuellen Timerstand abspeichern
  messung ++;
  if (messung == 4)
  {
    ergebnis[kanal -1] = periodendauer;            // Das Ergebnis in der zum Kanal passenden Variablen speichern
    kanal ++;                        // nächsten Kanal wählen
    if (kanal == 4)
    {
      kanal = 1;
    }
    if (kanal == 1)
    {
      PORTC &= ~((1<<PC0) | (1<<PC1) | (1<<PC2));   // Alle Adressleitungen auf 0
    }
    else if(kanal == 2)
    {
      PORTC |= (1<<PC0);
      PORTC &= ~((1<<PC1) | (1<<PC2));          // A auf 1 B und C auf 0
    }
    else if(kanal == 3)
    {
      PORTC |= (1<<PC1);
      PORTC &= ~((1<<PC2) | (1<<PC0));            // B auf 1 A und C auf 0
    }
    messung = 0;
  }
}

ISR (TIMER1_OVF_vect)
{
  alt = 0-(65535 - alt);      
}

int uart_putc(unsigned char c)
{
  while (!(UCSR0A & (1<<UDRE0)))  /* warten bis Senden moeglich */
  {
  }                             
 
  UDR0 = c;                      /* sende Zeichen */
  return 0;
}
 
 
 
void uart_puts (char *s)
{
  while (*s)
  {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
    uart_putc(*s);
    s++;
  }
} 


int main(void)
{ 
  DDRC |= (1<<PC0) | (1<<PC1) | (1<<PC2);
  PORTC &= !((1<<PC0) | (1<<PC1) | (1<<PC2));
  /* Signal und Tasterpin bleiben Eingänge und kriegen Pull-Ups */
  PORTD |= (1<<Signal); // Pull-Up Signal
  PORTC |= (1<<Taster); // Pull-Up Taster
    
  /*Timer initialisieren und aktivieren */
  
  TCCR1B = (1<<CS11);    // Prescaler 8 /  12MHz:8 = 1,5MHz
  TIMSK1|=(1<<ICIE1);    // ICP aktivieren
  
  
  
  /* UART initialisieren und aktivieren */
  UBRR0H = UBRR_VAL >> 8; 
  UBRR0L = UBRR_VAL & 0xFF; 
    
    
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);        //UART einschalten 
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);        //Asynchron 8N1 
  
  
  sei();           // Interrupts aktivieren
    
  
  char s[7];
    
  while (1)
  {  
    if(uart >= 60000)        // Bei jedem Timerdurchlauf 1 mal ausführen
    {                  // Taster entfernt, da nicht unbedingt notwendig
      for (int count = 1; count < 4; count++)
      {
        uart_puts("S");
        uart_puts( itoa( count, s, 10 ) );
        uart_puts(": ");
        uart_puts( itoa( ergebnis[count - 1], s, 10 ) );
        uart_puts("   ");
      }
      uart = 0;
    }
    uart ++;
    
  }

  return 0;                      
}

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 ^^

Autor: ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Patrick R. (pat711)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.