problem med tidsintervall

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Stene
Inlägg: 276
Blev medlem: 13 maj 2008, 19:21:52
Ort: Uppsala

problem med tidsintervall

Inlägg av Stene »

Hej!
Som en nybörjare så har jag fått ett problem men ett program som jag har skrivit. Jag har "försökt" att skriva ett program som ska läsa av ett varvtal med exakt lika långt tidsintervall mellan avläsningarna. Jag får så otroligt oregelbunden avläsning.
Meningen med detta är att jag ska använda det till en bromsbänk. Jag ska då använda skillnaderna mellan 2st mätvärden för att beräkna ut effekten. Problemet är nu att vissa avläsningar blir med täta avläsningar o sen kommer några som det är längre tid emellan. Resultatet blir att grafen jag sedan gör blir väldigt zick zack.
Processorn jag använder är en 16f628 och den kör jag på 20Mhz. Jag använder mig av PICkit 2 och UART Tool som finns i det programmet. Där loggar jag dom mätvärdena som jag får och sedan importerar jag det till ett excel dokument som jag sedan gör en graf på. Jag har provat att öka överföringshastigheten men det gör ingen skillnad. Kör nu på 9600 baud.
Är det någon som har ett förslag på vad jag har gjort för fel? Finns säkert många!

Kod: Markera allt



void main( void)
{
   char i;
   unsigned long T1, t1, t2;
   unsigned long  k;
   TRISB.3 = 1;                      /* CCP1-pin is input            */
   initserial();
   delay10(10);                      /* 1 sek delay                  */

    T1CON = 0b00.11.0.0.0.1 ;        /*  prescal 1:8  */
    CCP1CON = 0b00.00.0101 ;  
    OPTION.7 =0;  
                      
while(1)
  {
       
/* Väntar på signal, sen mäter värdet */

    
    CCP1IF = 0 ;                     /* reset flag            */
    while (CCP1IF == 0 );            /* wait for capture      */
    t1  = CCPR1H*256;
    t1 += CCPR1L;
    CCP1IF = 0 ;                     /* reset flag            */
    while (CCP1IF == 0 );            /* wait for next capture */
    t2  = CCPR1H*256;
    t2 += CCPR1L;
                    

   delay10(50); 

/* Calculations  */
   
    T1 = t2 - t1;                    /* calculate period                 */
    k = 37520000/T1;                 /* calculate frequency              */
    
   
/* print variable           */
    unslongtoa(k);
    string_out1();
    putchar('\n'); 
        
  }
}

void initserial( void )            /* initialise serialcom port */
{
   SPEN = 1;
   BRGH = 1;                        /* Async high speed   */
   TXEN = 1;                        /* transmit enable    */
   SPBRG = 130 -1;                  /* 9600 Baud @ 20 MHz-clockfrequency */
   CREN = 1;                        /* Continuous receive */
   RX9  = 0;                        /* 8 bit reception    */
   TRISB.2 = 0;                     /* serial_out is output */  
   TRISB.1 = 1;                     /* serial_in is input   */  
}


bit putchar( char d_out )           /* sends one char */
{
   if(d_out == '\0') return 0;     /* refuse to send 0 "end of string" */
   while (!TXIF) ;                 /* wait until previus character transmitted   */
   TXREG = d_out;
   return 1;                       /* done */
}

/* If two (or more) char's are recieved, but not read, the reciever will be locked! */
/* To unlock the reciever, run function OverrunRecover()                            */

void OverrunRecover(void)
{  
   char trash;
   trash = RCREG;                 /* the two char's that locked the reciever  */
   trash = RCREG;                 /* are read and ignored                     */
   CREN = 0;                      /* the unlock procedure ...                 */
   CREN = 1;
}

void unslongtoa(unsigned long u16)
{
   char i,temp;
   RPM[4] = '\0';
   for (i = 3; ;i--)
      {
       temp = u16 % 10;
       temp += '0';
       RPM[i]=temp;
       if (i==0) break;
       u16 /= 10;
      }
}

void string_out1( void ) 
{
   char i;  
   for( i = 0; putchar( RPM[i] ); i++); 
 }
 
void delay10( char n)
/*
  Delays a multiple of 10 milliseconds using the TMR0 timer
  Clock : 4 MHz   => period T = 0.25 microseconds
  1 IS = 1 Instruction Cycle = 1 microsecond
  error: 0.16 percent. B Knudsen.
*/
{
    char i;

    OPTION = 7;
    do  {
        i = TMR0 + 39;              /* 256 microsec * 39 = 10 ms */
        while ( i != TMR0)
            ;
    } while ( --n > 0);  
}  
Senast redigerad av Stene 9 september 2009, 19:16:46, redigerad totalt 1 gång.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: problem med tidsintervall

Inlägg av sodjan »

Det hela ser ut som om du bara har flyttat ett "PC-programmering" över till en PIC.
Konstruktioner som t.ex "k = 37520000/T1" ser inte speciellt "microcontroller-friendly" ut.
Att ha flera programvaru-delayer kan också ställa till det. Bättre att ha det hela
timerstyrt.

Så mitt intryck är att det hela inte är genomtänkt riktigt. Om det *är* det eller inte vet
jag inte, jag orkar inte kolla igenom koden för att försöka lista ut vad du försöker göra.
Antingen får du kommentera bättre så det framgår eller försöka beskriva på annat sätt
vad koden försöker göra...

Sen behöver du jobba lite med din beskrivning av problemet :

> Jag får så otroligt oregelbunden avläsning.

*Hur* oregelbunden ? D.v.s vad betyder "otroligt" just för dig och i detta sammanhang ?

> Problemet är nu att vissa avläsningar blir med täta avläsningar...

Vad betyder "täta" i detta sammanhang ?

> ...o sen kommer några som det är längre tid emellan.

*Hur* mycket längre ? Och hur många är "några" ?
Användarvisningsbild
Marta
EF Sponsor
Inlägg: 7487
Blev medlem: 30 mars 2005, 01:19:59
Ort: Landskrona
Kontakt:

Re: problem med tidsintervall

Inlägg av Marta »

Först så är det ett asynkront förlopp Du mäter, är problemet det jitter som uppstår p.g.a. väntan på att det skall komma två pulser från givaren så måste Du tänka om på hur Du vill lösa detta. Alternativen är att räkna utifrån de tider som pulserna faktiskt råkar komma, eller ha givare som ger så tät signal att jittret blir försumbart.

Eller är problemet att det ibland kommer ett skräpvärde som om tiden för ett varv var jättelång? Jag behärskar inte C och har ingen aning om hur många bitar det är i en "unsigned long", men Du måste använda 16-bit variabler när Du räknar ut skillnaden mellan två "captures", alternativt lägga till en AND-instruktion för att rensa överskottsbitarna. Annars blir resultatet blaj varje gång Timer1 har slagit runt under mätintervallet.

Använd sedan en hårdvarutimer för att ge mätintervallet med exakt timing för att få bort det jitter som garanterat uppstår när Du använder mjuktimers. Låt bli onödig bearbetning av mätvärdena, t.ex. divisionen Du har stoppat in. Överlåt det till PC'n istället för att bränna massor av kapacitet i PIC'en. Dessutom har Du där antagligen en icke försumbar variabel fördröjning som ger talvärdesberoende jitter.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 47013
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: problem med tidsintervall

Inlägg av TomasL »

Ett stort problem med C, när det gäller portering är just längden på variabler.
Variabellängden är kompilatorspecifik, så unsigned long är olika långa beroende på vilken kompilator programmet ursprungligen skrevs för.
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: problem med tidsintervall

Inlägg av Icecap »

Som påpekat är du på helt fel väg!

I detta är alla delay av ondo och visar att du inte har hittat rätt sätt att lösa uppgiften på.
Dessutom tror jag inte på att du klockar PIC'en med 20mhz, jag tror att det är 20MHz, "bara" en faktor 10^9 fel...

Nåväl, du ska angripa detta problem med interrupt stället! Jag har faktisk gjort något identisk med samma processor, det är interruptdriven och mäter tiden mellan 2 pulser som sedan skickas via serieporten och det fungerar kanonbra.

I detta fall ska du _först_ definiera den längste och den kortaste tid mellan 2 pulser, detta för att veta vilken upplösning som behövs och vilken variabelstorlek som gäller.

Det behövs lite variabler:
Tid_Nu, Tid_Förr, Tid_Diff.

Sedan ska interruptrutinen vid varje puls ladda in Tid_Nu från CCP-enheten, räkna ut Tid_Diff (Dit_Diff = Tid_Nu - Tid_Förr) och kopiera Tid_Nu till Tid_Förr.

Nu är det dags att markera med en flagga att det finns en ny tid att bearbeta, själva bearbetningen sköts av main-loop varför ISR'n avslutas här.

I main-rutinen kollas flaggan, är den satt görs det som göras ska med Tid_Diff och flaggan nollas, sedan är det klart.
Vill du ha extra buffer kan du i början av denna behandling kopiera Tid_Diff till en arbetsvariabel varefter du nollar flaggan, på det vis kan mainrutinen bearbeta förra värde och den kan ändå ta emot ett nytt värde utan att det skiter sig.

Om du behöver att fånga värden med större avstånd än vad som kan rymmas i en byte får du trixa lite med timern och overflow, jag har löst detta så att jag kan ha "valfri" max-tid, upp till 64 bit tidräkning fungerar.
Stene
Inlägg: 276
Blev medlem: 13 maj 2008, 19:21:52
Ort: Uppsala

Re: problem med tidsintervall

Inlägg av Stene »

Hej o tack för svaren!
Ni verkar alla överens om att jag har gjort totalt fel. Man lär sig av misstagen! Hur oregelbundet avläsningarna blir är att tex det är mätning 1: 10 rpm:s skillnad, mätning 2: 12 rpm, mätning 3: 157 rpm, mätning 3; 167 rpm, mätning 4; 8 rpm. Ni är helt eniga om att jag inte kan använda delay funktionen. Är det någon som har något förslag vad jag kan använda istället då jag behöver någon funktion som gör att jag har en exakt tidsintervall mellan avläsningarna? Jag behöver den funktionen för senare beräkningar.
Jag använder en C kompilatorn som heter B Knudsen och som jag förstår det så ska unsigned long vara en 16 bit variabel.
Jag har tänkt på att använda en interrupt men jag är osäker om jag kan använda den när jag inte kommer att använda alla pulser jag får? Jag använder ju bara 2 st pulser med ett vist tidsintervall. Har jag fel eller hur löser jag det?
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: problem med tidsintervall

Inlägg av sodjan »

För att sammanfatta...

Det kommer pulser i princip kontinuerligt.
Då och då vill du att din processor ska mäta tiden mellan två pulser.
Det behöver inte mäta tiden mellan mellan alla pulser...

Rätt så långt ?

Eller vill du räkna antalet pulser under en viss tid ?

Vad är min resp max intervallet för dina pulser ?
Och vilken upplösning vill du ha i mätningen ?
Detta påverkar konfigureringen av timern som du använder för mätningen.

Sedan, när du vill göra en mätning kan du t.ex göra :

- Vänta på en flank i signalen, nolla klockan/timern.
- Vänta på nästa flank i signalen, stoppa klockan/timern.
- Konvertera timern och skicka till överordnat system.

Eller :

Sätt upp ett interrupt från pinnen där signalen kommer in.
När ett interrupt inträffar :
- Läs av klockan/timern och spara unden värdet.
- Nolla klockan/timern så att den börjar räkna nästa intervall.
- Konvertera värdet och skicka till överordnat system (om du behöver
göra det varje gång).

I inget fall behövs det delay() funktioner i koden. Gör allt händelsestyrt
istället. Delay() ska i princip aldrig användas utom till rena demo/kurs
exempel eller liknande. SÅ snart man gör något "på riktigt" så är de
bara i vägen och ör nästan alltid helt fel lösning.

Hur fungerar igentligen din kod. Försöker den mäta antalet pulser under en
viss tid ? I så fall blir det lite annorlunda. Jag ser inte heller var du nollar
räknaren mellan dina avläsningar. Det framgår inte heller att du har konfigurerat
räknaren så att det passar det förväntade mätintervallet. Rent allmänt så saknas
det beskrivning av vad koden försöker göra och hur du vill att det ska fungera...
Användarvisningsbild
Marta
EF Sponsor
Inlägg: 7487
Blev medlem: 30 mars 2005, 01:19:59
Ort: Landskrona
Kontakt:

Re: problem med tidsintervall

Inlägg av Marta »

Du skall definitivt fortsätta att använda CCP-modulen som Du gör nu. Ställ räknaren efter hur långt det maximalt kan vara mellan pulserna. För bästa noggranhet skall den räkna så snabbt som möjligt, men inte över 65535 steg på ett varv för då blir det fel utan specialhantering. Det finns ingen anledning att nolla timern, bättre då att rensa överblivna bitar med en AND 0xFFFF ifall variabeln har mera än 16 bitar.

Eventuellt kan det vara en fördel att använda prescaler på CCP'n och en lägre frekvens på timern för att få bättre precision ifall Din givare skulle ge lite jittrig signal. Då räknar den tiden på t.ex. 8 eller 16 varv istället för 1 och givarens osäkerhet får motsvarande mindre betydelse.

Låt CCP ge interrupt hela tiden och räkna ut diffen varje gång såsom Icecap skriver. En annan timer får sedan ge interrupt med fasta intervaller. Är dessa för korta så räkna ner en mjukvaruräknare. När denna går ut så läs värdet och skicka vidare precis som det är. Bättre att göra all bearbetning i PC'n. Tänk på att det är en delad variabel. CCP tappar inte precicion för att Du nollar dess interrupt enable under avläsningen för att undvika att läsa blaj om det inträffar en interrupt mitt i avläsningen. Enklare så än att dubbelbuffra.

Serieporten går också att hantera med interrupt, så kan allt ligga som interrupthanterare och det s.k huvudprogrammet bestå av en evighetsloop när initieringen är klar.

Överväg att byta till assembler, det är faktiskt mycket mera lämpat för denna typ av applikationer. Då har Du total kontroll över allt som händer och får snål kod både vad avser storlek och exekveringstid.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 47013
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: problem med tidsintervall

Inlägg av TomasL »

En variant är att använda Timer 0 med lämplig prescaler, sedan se hur långt den hinner räkna mellan interupten, typ
Interupten genereras på lämplig pinne av din signal.
Kan också vara lämpligt att räkna tiden för ett antal pulser, då kan du använda timer1 med extern klocka, och generera interupt vid overflow, med Timer0 som tidräknare.
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: problem med tidsintervall

Inlägg av Icecap »

Jag gjorde det i C och det fungerar alldeles utmärkt men OK, jag har kanske en del mer erfarenhet i sånt.

Men att bara skicka tiden mellan varje puls är en bra lösning, då kan PC'n ta hand om alla decimal-problem, med hastigheter på 8 RPM blir det decimaler att räkna med och det är enklare att låta PC-programmet buffra och räkna.

Men projektet behöver mer definitioner, det framgår ingenstans vilken noggrannhet som behövs och det har ju betydelse för hastigheten CCP'n ska köra med.

Att expandera tidräknaren till 32 bit är enkelt: enabla och fånga TMR1 overflow och räkna upp den övre delen (bit 31:16) ett steg per overflow, detta 32-bit värde (CCPR1L & CCPR1H som bit 15:0) är sedan Tid_Nu, på detta vis kan man mäta långa tider med hög upplösning.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 47013
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: problem med tidsintervall

Inlägg av TomasL »

För att utveckla:
Du använder Timer1 för att räkna pulser från din givare.
Du använder Timer0 för att räkna tiden.

Det är bättre att räkna ett antal pulser från din givare och räkna den totala tiden för dessa.
Säg att du bestämmer dig för att använda 10 pulser, då sätter du lämpligen Timer1 till 246, vilket gör att Timer1 blir 0 efter 10 pulser.
Samtidigt som du laddar Timer1 med "246" nollställer du Timer0 och startar den.
Sedan har du en liten ISR som anropas (automatiskt av interuptlogiken) när Timer1 blir 0, då stoppar du Timer1, läser av värdet.
Nollställer Timer0 återladdar Timer1 och startar Timer0.

Sedan kan du på ett mycket enkelt sätt räkna ut med hjälp av värdet som du läste av från Timer0 vilket varvtal du har.

Du måste naturligtvis veta hur många pulser per varv du får från din givare, därför kan du behöva labba med både Timer1 prescalern och såddvärdet till timer1 för att få en vettig upplösning.
Samma gäller prescalern på Timer0 vilket beror på klockfrekvensen och upplösningen.
Naturligtvis måste alla värden på Prescalers och sådd väljas så att timer0 aldrig räknar över.

Du löser det på detta sättet med ett tiotal rader enkel C-kod
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 47013
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: problem med tidsintervall

Inlägg av TomasL »

Tillägg, på detta sättet kan du dessutom göra en automatisk skalning, dvs du kan låta programmet få ändra på skalfaktorna dynamiskt och på så sätt få en förbättrad upplösning. Dvs, vil du ha fyra gällande siffror på varvtalet så kan du enkelt få programmet att skapa det.


Edit:
Naturligtvis måste du se till att signalerna från givarna är väl filtrerade med både ett CMMR filter och ett lämpligt Lågpass- alt bandpass-filter
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: problem med tidsintervall

Inlägg av Icecap »

Hmmm... konstigt. I mitt projekt är Timer1 den som används av Capture-enheten och med så långsamma förlopp och det som blev berättat om sekvens tidigare är det tiden mellan varje puls som är viktig. Att räkna ett antal pulser under en viss tid är, vid 8 RPM, ganska tidsödande, för att räkna 8 RPM (@ 1 puls per varv) ska man räkna under 60 sekunder och allt som heter decimaler är ej möjligt att uppnå utan ett ganska komplicerat program.

Så _jag_ anser att detta att räkna pulser under en viss tid är alldeles fel väg att gå men FORTFARANDE har trådskaparen inte definierat vad som krävs vilket ju gör alla lösningar lika bra eller usla.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 47013
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: problem med tidsintervall

Inlägg av TomasL »

Det hela beror väl på hur många pulser per varv man får ut, och erforderlig samplingsperiod naturligtvis.
Mitt exempel kommer iofs från en generisk varvräknare jag gjorde.
Den skulle vara kapabel att hantera n antal cylindrar både 2 och 4 takt samt även pulser fån ÖDP-givare, med automatisk skalning och tre gällande siffror.
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: problem med tidsintervall

Inlägg av Icecap »

Exakt och då trådskaparen ju inte ger nödvändig information kan "ditt" sätt vara skitbra eller åt helvetet fel (vilket gäller "mitt" förslag också) men vi _vet_ ju inte.

Följande ska alltså beskrivas/definieras innan "rätt" väg kan definieras:
* Antal pulser från sensorn per varv.
* Vilka data som är viktiga, varje varv eller ett genomsnitt. (jag gissar på varje varv med tanke på att det är en dyno-bänk)
* Vilken upplösning som behövs
Skriv svar