www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ATtiny13 - 2 Funktionen spielen verrückt


Autor: Squat *** (squat)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe ein Problem mit der AD-Wandlung beim ATtiny13 bzw. spielen 
meine zwei Funktionen errückt, wenn ich den ADC mitlaufen lasse.

Ich möchte an PB3 (ADMUX=11) die Batteriespannung messen. Leider bekomme 
ich das nicht mehr hin. Ich hatte eine erste Messung auf einem ATmega8 
hinbekommen und ich bin auch der Meinung, dass es bei dem ATtiny als DIP 
Version auch schon funktionierte.

Jetzt habe ich eine Platine angefertigt und den Quelltext angepasst. 
Jedoch funktioniert die Messung nicht mehr richtig.

Beide Funktionen (Lauflicht und Blicklicht) funktionieren einzelt, wenn 
diese aufgerufen werden (die Andere auskommentieren oder wenn ich für 
result einen Wert einsetze), jedoch wenn diese zusammen über dem ADC 
ausgewählt werden sollen (Größer / Kleiner als 410), dann laufen beide 
Funktionen in einander ab.
//ICC-AVR application builder :
// Target : T13
// Crystal: 9.6000Mhz

#include <iot13v.h>
#include <macros.h>

unsigned char count10 = 0;
unsigned char countV = 0;
unsigned char blink_state = 0;
unsigned char countLED = 0;
unsigned char i = 0;
unsigned int result;

/* PORT-Zuweisung */

//#define PB5 
#define R_LED4 PB4
//#define AD_W PB3
#define R_LED1 PB2
#define R_LED2 PB1
#define R_LED3 PB0

void port_init(void)
{
 PORTB = (1<<PB5)|(1<<PB3);
 DDRB = (1<<DDB4)|(1<<DDB2)|(1<<DDB1)|(1<<DDB0);
}

//TIMER0 initialize - prescale:1024
// WGM: Normal
// desired value: 10mSec
// actual value:  9,920mSec (0,8%)
void timer0_init(void)
{
 TCCR0B = 0x00; //stop
 OCR0A = 0x5D;
 OCR0B = 0x5D;
 TCNT0 = 0xA3; //set count
 TCCR0A = 0x00; 
 TCCR0B = 0x05; //start timer

}

#pragma interrupt_handler timer0_ovf_isr:iv_TIM0_OVF
void timer0_ovf_isr(void)
{
 TCNT0 = 0xA3; //reload counter value
 ADW();
// result = 411;
 if (result <= 410) {Warning();}
 if (result > 410) {rul();} 
}


//ADC initialize
// Conversion time: 104uS
void adc_init(void)
{
 ADCSRA = 0x00;  //disable adc
 ADMUX = 0x00; 
 ACSR  = 0x80;
 ADCSRB = 0x00;
 ADCSRA = 0x86;//01
}

void rul(void)
{ 
}             //

void Warning(void)
{
}

void ADW(void)
{
//----------------------------------------------------------------------------//
       //         AD-Wandlung
//----------------------------------------------------------------------------//
       // Dummy-Readout
       ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);  // ADC aktivieren, Frequenzvorteiler: setzen auf 64 (8 MHz / 64 = 125 kHz) und ADC aktivieren
       ADMUX = 0x11;                      // Kanal waehlen (ADC3)
       ADMUX |= (1<<REFS0);                // interne Referenzspannung nutzen 
       ADCSRA |= (1<<ADSC);               // eine ADC-Wandlung
       while(!(ADCSRA & (1<<ADIF)));          // auf Abschluss der Konvertierung warten (ADIF-bit)
       result = ADC;                    // ADC muss einmal gelesen werden,
                                        // sonst wird Ergebnis der nächsten Wandlung
                                        // nicht übernommen.
       
       //ADC-Messung mit arithmetischen Mittel aus 4 Messungen
       result = 0;
       for (i=0;i<4;i++)
            {
          ADCSRA |= (1<<ADSC);             // Messung ausfuehren
        while ( ADCSRA & (1<<ADSC) );        // Konvertierung abwarten
        result += ADC;                     // aufaddieren zur Mittelwertbildung (ADC auslesen der zwei Bits
        }
        ADCSRA &= ~(1<<ADEN);               // ADC ausschalten
        
        result /= 4;                 // 

}          
            


//call this routine to initialize all peripherals
void main(void)
{
 //stop errant interrupts until set up
 CLI(); //disable all interrupts
 port_init();
 timer0_init();
 adc_init();

 MCUCR = 0x00;
 TIMSK0 = 0x02; //timer interrupt sources
 GIMSK = 0x00; //interrupt sources
 SEI(); //re-enable interrupts
 //all peripherals are now initialized

while(1);
}

Auch wenn ich
  if (result <= 410) {Warning();}
  else{rul();}
statt der zwei if-Abfragen einsetze spielen diese verrückt.

Habe ich bei der AD-Wandlung einen Gedankenfehler (läuft diese so 
schnell ab, dass mehere Ergebnisse Zustande kommen)oder woran könnte das 
liegen?

Vielen Dank für eure Tipps.

Autor: Grrrr (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Im Timerinterrupt Wandlungen starten, mitteln und dann abhängig vom 
Ergebnis noch ein Blink- oder Lauflicht. Alles im selben Interrupt. Und 
das ganze ist schon mal in einem DIP-Gehäuse gelaufen. Hmmm. 
Interessant.

Also, tut mir ja leid, aber das kann leider so nicht funktionieren.

Lies mal die Tutorials hier und fang mal mit einfachen Sachen an.

Dann erfährst Du u.A. folgendes:
Interrupts sind so kurz wie möglich. Volatile. Flags setzen. Aktionen in 
main. AD-Wandlung läuft sowieso mit eigenem Interrupt. Auch 
kontinuierlich.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> läuft diese so
> schnell ab, dass mehere Ergebnisse Zustande kommen

Dein Timer-Interrupt kommt alle 10 ms.  Mehr lässt sich zu diesem Punkt 
nicht sagen, da Du den Source von Warning() und rul() nicht mitlieferst.

Weitere Punkte, ohne Anspruch auf Vollständigkeit:

In der ADC-Routine setzt Du ADIF nicht zurück, also wird es immer 
gesetzt sein und das Ergebnis der Wandlung wird gar nie abgewartet. 
Wieso wartest Du nicht auf das Rücksetzen von ADSC -- da musst Du nichts 
von Hand zurücksetzen?  Falls Du doch bei ADIF bleiben willst: mach Dich 
kundig, wie es rückgesetzt wird.  Nein, nicht indem man 0 reinschreibt.

Du erledigst viel zu viel im Interrupt, das ist keine sinnvolle 
Programmstruktur.  Setze im Interrupt maximal ein Flag, ob das Resultat 
über der Schwelle liegt, und mache die weitere Verarbeitung im 
Hauptprogramm.

Autor: Floh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Meine Tips zum Programm wären:

Den Timerinterrupt wegschmeißen.
ADC auf freerunning laufen lassen.
in nem ADC-Interrupt die Mittelbildung machen.
In der main einfach nur abfragen, ob der Mittelwert grad kleiner oder 
größergleich 410 ist und dann in der main die funktionen aufrufen.

Autor: Squat *** (squat)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank erstmal. Ich habe zwar noch nicht die Lösung gefunden, weiß 
aber schonmal, woran es liegen könnte.

Den Interrupt nutze ich um das Rundumlicht sowie das Blinklicht anzu 
steuern. Diese sollen im 100ms bzw 200ms Takt durchlaufen.

Da die Funktionen rul() und Warning() gehen, hatte ich diese nicht 
eingefügt.

zur Vollständigkeit bringe ich diese hier nochmal an.
void rul(void)
{
  switch (blink_state)
 {

  case 0: // R_LED1
  {
   PORTB &= ~((1<<R_LED2) | (1<<R_LED3) | (1<<R_LED4));
   PORTB |= (1<<R_LED1);
   break;  
  }
  case 1: // R_LED2 ein
  {
   PORTB &= ~((1<<R_LED1) | (1<<R_LED3) | (1<<R_LED4));
   PORTB |= (1<<R_LED2);
   break;  
  }  
  case 2: // R_LED3 ein
  {
   PORTB &= ~((1<<R_LED1) | (1<<R_LED2) | (1<<R_LED4));
   PORTB |= (1<<R_LED3);
   break;  
  }  
  case 3: // R_LED4 ein
  {
   PORTB &= ~((1<<R_LED1) | (1<<R_LED2) | (1<<R_LED3));
   PORTB |= (1<<R_LED4);
   break; 
  } 
 }
countLED++;
if (countLED>10) 
{
countLED=0;
blink_state++;
}             //
if (blink_state>3) blink_state=0;     // Lauflicht neustarten
}
oid Warning(void)
{
    if (countV>50) // LEDs aus
             {
        PORTB &= ~((1<<R_LED1) | (1<<R_LED2) | (1<<R_LED3) | (1<<R_LED4));
        }
    if (countV>99) // LEDs aus
             {
        PORTB |= ((1<<R_LED1) | (1<<R_LED2) | (1<<R_LED3) | (1<<R_LED4));
        countV=0;
        }
countV++;
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Squat *** schrieb:
> Vielen Dank erstmal. Ich habe zwar noch nicht die Lösung gefunden, weiß
> aber schonmal, woran es liegen könnte.

Lass dir doch mal den ADC Wert direkt auf die LED ausgeben (die obersten 
4 Bit werden wohl genügen).
Ich wette mit dir, dass du keinen stabilen Messwert hast, sondern das 
deine LED mehr oder eniger zufällige Zahlen anzeigen werden.

Warum das so ist?
Dafür gibt es mehrere Möglichkeiten. Anfangen würde ich mit der 
Referenzspannung.

Autor: Squat *** (squat)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, ich habe den Fehler gefunden.

Es lag einfach an der Kanalauswahl. Bin ich gerade zufällig drauf 
gestoßen, als ich mir ein anderes Programm angeschaut habe.

Richtig muss es
ADMUX = 0x03;
 lauten.

Vielen Dank an alle Beteiligten!!


Gerne würde ich noch mehr aus diesem Threat herausziehen.

Zum einen habe ich jetzt das Problem, dass in der Übergangsphase 
zwischen Rundumlicht und Blicklicht die Spannung einbricht, sobald das 
Blicklicht beginnt. Um dieses Problem zu umgehen ist entweder auf der 
Hardwareseite ein Kondensator zu verbauen. Oder softwaremäßig die 
Messung gezielt zwischen den Blinken anzusetzen.
Ich habe es vorerst mit dem ELko versucht, jedoch half das nicht. 
Daraufhin habe ich mir gedacht, ich rufe die ADC nur solange auf, wie 
die Rundumleuchte an ist. Sobald einmal die Spannung zu niedrig ist, 
wird nur noch das Blicklicht ausgeführt. Das funktioniert soweit auch, 
jedoch nach dem ersten Blicken bricht die Spannung soweit zusammen, dass 
der ATtiny neustartet. Und so beginnt das Spiel wieder von vorne. Wenn 
ich aber die Spannung weiter nach unten schraube, Blicken irgendwann die 
Lämpchen, so wie geplan.

Es geht also um den Übergang.

In der Software hatte ich schonmal probiert die Mittelwertbildung zu 
erweitern. Bis 50 geht dass gut. Bei Hundert klappt es nicht mehr und er 
Blink nur noch (blinkt einmalig und dann startet der Prozessor neu egal 
wie viel Spannung anliegt.

Ich weiß leider nicht, wie ich dieses Problem umgehen kann.



Und noch eine kleine Frage am Rande. Wie kann ich im Hauptprogramm 
erfragen, ob der Timer übergelaufen ist.

Und nochmals besten Dank für die Tipps.

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.