Enkel hastighetsmätare (programmeringsproblem)

Elektronikrelaterade (på komponentnivå) frågor och funderingar.
joakimholmberg
Inlägg: 18
Blev medlem: 27 januari 2009, 14:45:59

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av joakimholmberg »

Hej.
Nu har jag provat med en LED som du sa. Jag har nu kommit fram till att programmet stannar i "while (!PIR1.CCP1IF){}" efter att den har gått ett varv. Den kör inte vidare. :(
Så nu undrar jag hur detta kan vara möjligt? Finns det ingen som har haft det här problemet tidigare eller vet hur man skall göra? :roll: :)

mvh Joakim
joakimholmberg
Inlägg: 18
Blev medlem: 27 januari 2009, 14:45:59

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av joakimholmberg »

Men om nu problemet är att timern går runt och man ska räkna antalet overflow.
Jag tror inte riktigt jag förstår hur ni menar. Men är det typ såhär eller är jag helt fel ute?

Kod: Markera allt

while(1)
{
  while (!PIR1.CCP1IF){} 
  Time_Now.Byte[1] = CCPR1H; 
  Time_Now.Byte[0] = CCPR1L; 
  PIR1.CCP1IF = 0; 
  Period = Time_Now.Word - Time_Previous.Word;
  Time_Previous.Word = Time_Now.Word; 


    IntToStr(Period,svar);
   if(overflows < MAX_OVERFLOWS)
   {
        Lcd_Custom_Out(1, 1, "Pulser: ");
        Lcd_Custom_Out(2,1, svar );
        overflows=0;
   }

  if(PIR1.TMR1IF)
  {
	     if(overflows < MAX_OVERFLOWS)
	       overflows++;

	     else
	       Period = 0;

     PIR1.TMR1IF=0;
  }

}
Användarvisningsbild
Icecap
Inlägg: 26962
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av Icecap »

Om den pulslängd du ska mäta är så lång att timern inte hinner med på "ett varv" måste man ta med overflow, annars kvittar detta då matematiken blir det samma.

Jag tycker i övrigt att det är dags att du börjar använda dessa IF-som du använder till att faktisk köra en interrupt-rutin istället för att polla dom, detta gör dels att du kan få stabil mätning och inte ska tänka på hur lång tid omvandlingen till en sträng tar osv.

Men om vi ser på detta med overflow:
Du har en 16-bit tid från CCP-enheten. Om man då lägger till ytterligare 16 bit får man en 32 bitars tid. De översta 16 bit räknas bara upp för varje gång timern som CCP-enheten använder går ifrån FFFFh -> 0000h och man räknar ut perioden precis som förut fast då med 32 bit istället för 16: Period = Tid_Nu (alla 32 bit) - Tid_förut (Har per definition alltid nollar i de första 2 bytes, alltså 0000 xxxx). Sedan sparar man Tid_Nu som Tid_Förut fast man nollar de översta 16 bit som ju är overflow-räknaren.

På detta vis har man alltså expanderat en 16-bitars tidmätare till att ha 32 bit (eller hur många man nu vill ha), är det till "hemmafusk" kan man såklart nöja sig med en 8-bit overflow-räknare om man vill spara RAM.
joakimholmberg
Inlägg: 18
Blev medlem: 27 januari 2009, 14:45:59

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av joakimholmberg »

Hej.
Jag får ursäkta mig. Jag är som sagt nybörjare och har lite svårt med programmeringen.
Något jag inte förstår är hur man gör en ISR? Det sista du skrev kan jag inte riktigt sätta mig in i och förstå.

Är det såhär man ska göra?

Kod: Markera allt

while(1)
 {
  while (!PIR1.CCP1IF){}  
  Time_Now.Byte[1] = CCPR1H; 
  Time_Now.Byte[0] = CCPR1L;
  PIR1.CCP1IF = 0; 
  Period = Time_Now.Word - Time_Previous.Word; 
  Time_Previous.Word = Time_Now.Word; 

    IntToStr(Period,svar);
   if(overflows < MAX_OVERFLOWS)
   {
        Lcd_Custom_Out(1, 1, "Pulser: ");
        Lcd_Custom_Out(2,1, svar );
        overflows=0;
   }
}

    void interrupt()
   {
        if(PIR1.TMR1IF)
        {
	         if(overflows < MAX_OVERFLOWS)
	         overflows++;

	         else
            Period = 0;

         PIR1.TMR1IF=0;
         }
   }
mvh Joakim
Användarvisningsbild
Icecap
Inlägg: 26962
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av Icecap »

Detta är saxat ur ett program som mäter just så:

Kod: Markera allt

#define false 0
#define true  1

typedef unsigned char byte;
typedef unsigned int  word;
typedef unsigned long dword;

typedef union
  {
  word Word:
  byte Byte[2];
  } T_W_AND_B;

typedef union
  {
  dword DW;
  word  WO[2];
  byte  BY[4];
  } T_ALL_ACCESS;

T_ALL_ACCESS Pulse_Now;
T_ALL_ACCESS Pulse_Previous;
byte Overflow;dword Difference;

struct
  {
  byte Running : 1;
  byte Update  : 1;
  byte Timeout : 1;
  } Flag;

void interrupt(void)
  {
  if(PIR1.TMR1IF)
    { // Timer1 FFFF->0000 interrupt
    PIR1.TMR1IF = false; // Acknowledge interrupt
    Overflow++;
    if(Overflow > 7) Flag.Timeout = true; // 7 is in my case
    }
  if(PIR1.CCP1IF)
    { // Pulse came on Capture
    if(!Flag.Update)
      {
      Pulse_Now.BY[0] = CCPR1L; // Transfer the value, low byte
      Pulse_Now.BY[1] = CCPR1H; // Transfer the value, high byte
      Pulse_Now.BY[2] = Overflow;
      Overflow = 0;
      Flag.Update = true;
      }
    PIR1.CCP1IF = false; // Acknowledge interrupt
    }
  }
  
void main(void)
  {
  Initialize();
  while(true)
    {
    if(Flag.Update)
      {
      // Now calculate
      Pulse_Now.BY[3] = 0; // If a 16 bit overflow counter is not needed
      Difference = Pulse_Now.DW - Pulse_Previous.DW;
      Pulse_Previous.DW = Pulse_Now.DW;
      Flag.Update = false; // Now it's calculated, allow the next measurement to begin
      // Now Difference is a 32 bit variabel that contains the total pulse length
      // Do Your calculation with Difference here
      }
    if(Flag.Timeout)
      {
      // Timed out, do what You need to do here
      } // END if(Flag.Update)
    } // END mainloop
  }

void Initialize(void)
  {
  TRISA        =  0xEF; // All input but PORTA.4
  TRISB        =  0x08; // All output but PORTB.3
  CMCON        =  0x07; // No comparator inputs
  CCP1CON      =  0x05; // Set CCP1 to capture all rising edges and use Timer 1 as timebase
  PIE1         =  0x05; // Allow Timer 1 & CCP1 interrupts
  PIR1         =  0x00; // Erase any latent interrupts
  INTCON       =  0xC0; // Allow interrupts
  T1CON        =  0x31; // Set Timer 1 to lowest possible speed (125KHz which anyway is too high) and start it counting
  PORTA        =  0x00; // Set to all '0'
  PORTB        =  0x04; // Set to all '0' but PORTB.2 (Ser out)
  Flag.Running = false; // Initial state
  Flag.Update  = false; // Initial state
  }
Jag garanterar inte något i detta, min egentliga applikation gjorde lite annat men det är på ett ungefär ett fungerande program, designad till en PIC16F628A med intern 4MHz klocka. Du kan behöva ändra inställningar på portar osv.

EDIT: observera att jag aldrig använder några av dessa lcdout() osv då jag inte har koll på deras exakta funktion, tid och resurser de tar i anspråk, till detta projekt hade jag egna rutiner som skrev ut decimalt och skickade på UART'en + att den mätte mellan stigande och fallande flank så den bit är skapligt omgjort.
joakimholmberg
Inlägg: 18
Blev medlem: 27 januari 2009, 14:45:59

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av joakimholmberg »

Hej.
Tack för ditt inlägg och ditt fina program. Jag har gjort om det så det passar med min PIC och LCD :tumupp:

Jag är lite osäker på hur jag räknar ut hur ofta det blir overflow? I ditt fall skrev du 7 när du har en intern 4MHz klocka?

Vad gör CMCON? Edit. På min PIC finns det CM2CON1 som är associerad med capture och timer1.

Mvh Joakim
Användarvisningsbild
Icecap
Inlägg: 26962
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av Icecap »

CMCON finns i PIC16F628A som programmet kör på och styr vilka pinnar som ska ha analog/digital funktion. Läs gärna databladet.

Och overflowet räknade jag ut, jag räknade med den interna 4MHz oscillator, hur fort Timer1 räknades upp med de befintliga inställningar (Excel är bra!) och vad den längsta puls jag kunde behöva mäta kunde bli så 7 var rätt i det fall, vad det är för dig får du räkna ut själv.

Overflow = (Timer1 klockhastigheten * längsta pulstiden) / 65536

Längsta pulstiden beror på avrundning osv. ett exempel är att man har 1 puls/meter och ska mäta ner till 0,1km/h, då blir längsta pulsen (3600sek/t / 100m/t =) 36 sek, med en Timer1 klocka på 1MHz vill detta ge ett värde på 2255100h, klippa av de understa 16 bit och det blir 225h över (549 decimalt) och då skulle max overflow alltså bli 550 (decimalt).
joakimholmberg
Inlägg: 18
Blev medlem: 27 januari 2009, 14:45:59

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av joakimholmberg »

Hej.
Nu fungerar det med att den skriver ut och uppdaterar värden på LCD:n.
Det konstiga är att den skriver ut minustal ibland.


Det hade varit grymt om du kunde förklara hur detta fungerar? På vilket sätt får den den ihop overflow så att tiden blir rätt?

Kod: Markera allt

      Pulse_Now.BY[0] = CCPR1L; 
      Pulse_Now.BY[1] = CCPR1H; 
      Pulse_Now.BY[2] = Overflow;
Mvh Joakim
Användarvisningsbild
Icecap
Inlägg: 26962
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av Icecap »

Kod: Markera allt

typedef union
  {
  dword DW;
  word  WO[2];
  byte  BY[4];
  } T_ALL_ACCESS;
Ovanstående är en "typedef", alltså en definition av en "egen sort" variabel, den kallas T_ALL_ACCESS precis som någon kallas char, int osv.

Men den är definierat som en "union" vilket betyder att alla variabler som är definierat ligger på samma adress(er).
om vi låtsas att de alla ligger på adress 20h bara för att ta ett värde:
DW ligger på 20h-23h.
WO[0] ligger på 20h-21h, WO[1] ligger på 22h-23h.
BY[0] ligger på 20h, BY[1] ligger på 21h, BY[2] ligger på 22h, BY[3] ligger på 23h
Ser du ett mönster?

Pulse_Now.BY[0] = CCPR1L;
Pulse_Now.BY[1] = CCPR1H;
Pulse_Now.BY[2] = Overflow;
Ovanstående överför alltså 3 bytes av tidvärdet till DW och
Pulse_Now.BY[3] = 0;
nollställer alltså högsta byten i DW, på den vis har du ett 32-bitars tidvärde skapat av det 16-bitars CCP-värde samt en overflow-räknare men att överföra värden görs på byte-nivå.

Sedan räknas tiden ut vid att räkna bort förra värdet mot nuvarande värde och detta då i 32-bitars storlek,
Difference = Pulse_Now.DW - Pulse_Previous.DW;
Pulse_Previous.DW = Pulse_Now.DW;
Dessa rader är alltså i 32-bitars "matte" vilket kompilern tar hand om, hade man gjort det i ASM hade man varit tvunget att subtrahera ett 4-byte tal från ett annat 4-bytes tal samt att kopiera över 4 bytes, i ASM hade man sannolikt begränsat sig till 3 bytes om det räckte men det är en akademisk fråga, lite kostar det ju att använda C.
joakimholmberg
Inlägg: 18
Blev medlem: 27 januari 2009, 14:45:59

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av joakimholmberg »

okej. Tack för din snabba beskrivning. Då förstår jag nog lite bättre.
Men spelar det någon roll om CCPR1L kommer före CCPR1H? Borde det inte vara tvärt om?
Pulse_Now.BY[0] = CCPR1L;
Pulse_Now.BY[1] = CCPR1H;
Pulse_Now.BY[2] = Overflow;
Mvh Joakim
Användarvisningsbild
Icecap
Inlägg: 26962
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av Icecap »

Sättet en processor sparar större tal än det minnet medger (en 8-bit har ju plats till 255 som mest, sedan får man ta fler minneslokationer) beror lite på språket som det programmeras i samt på hårdvaran ibland, detta då vissa processorer kan ha t.ex. en 32-bit Ackumulator men 16-bit minne och då sparas datan efter "little endian" eller "big endian" som avgör om datan i en många-bytes variabel sparas med LSB eller MSB först.

I det exempel du anger betyder det att det är rätt ordning ... kanske, jag menar att ha testat detta och MikroC kör med little endian vilket betyder att i en multi-byte variabel sparas minst betydande byte på lägsta adress, alltså är exemplen rätt.

Men som sagt: jag är <ganska säker> på att det är på detta vis, får du konstiga fenomen kan det möjligen bero på detta men det fungerade iaf. på den mjukvara jag gjorde och då tror jag att det gör det fortfarande.
joakimholmberg
Inlägg: 18
Blev medlem: 27 januari 2009, 14:45:59

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av joakimholmberg »

Hej.
Tack för ditt svar och din beskrivning ännu en gång :tumupp:

Jag har funderat lite på hur det fungerar med overflow räknaren eftersom jag ibland får negativa värden.
Så jag provade ett möjligt scenario med papper och penna för att se om jag kunde få ett negativt värde.
Om jag inte har tänkt helt fel så kan värdet bli negativt om det händer följande:

Den första pulsen (P1) kanske får ett värde mellan 0-65535 ex40.000 utan att det har blivit någon overflow.
Den andra Pulsen (P2) får ett värde som är mindre än P1 ex 30.000 men eftersom den har en overflow så blir den ändå större och p2-p1 = positivt.
Men eftersom vi nollar overflow räknaren efter att ha sparat undan värdet i P2 så kan puls nummer tre (P3) också kunna få en overflow. Om P3 samtidigt får ett mindre värde än P2 så kommer p3-p2 = negativt...

Jag har gjort en liten bild för att förtydliga.

Bild

Kod: Markera allt

Pulse_Now.BY[0] = CCPR1L;
Pulse_Now.BY[1] = CCPR1H; 
Pulse_Now.BY[2] = Overflow;
Overflow = 0;
Mvh Joakim
Användarvisningsbild
Icecap
Inlägg: 26962
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av Icecap »

Du får negativa värden för att du skriver ut med förtecken (signed values)!

Om vi lekar med att det är en räkning på 40000 för varje puls behöver du först och främst inte att extenda räkningen till att ha overflow alls vilket ger att du bara behöver en 16-bit värde men då du ju INTE har angivit vad din långsammaste puls du ska mäta är i Timer-counts tog jag det säkra för det osäkra.

Men vi räknar lite 16-bit matte och utgår ifrån att allt startar precis samtidig.
Förra Tid nu Diff
00000 40000 40000
40000 80000 Det går ju inte eller hur? Nästa steg har jag plockat bort så att det blir rätt:
40000 14464 -25536 (med förtecken) men utan förtecken är det (9C40h =) 40000
14464 54464 40000
54464 94464 Samma här, nästa rad gäller:
54464 28928 -25536 (vilket ger 9C40h =) 40000

Felet är alltså att du använder förtecken och inget annat.

Gissa själv varför jag aldrig använder dessa "fina" rutiner som finns till att skriva ut med...
joakimholmberg
Inlägg: 18
Blev medlem: 27 januari 2009, 14:45:59

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av joakimholmberg »

Hej.
Okej, det var tråkigt att höra. Men tack för din förklaring.
Finns det något annat sätt att få det att fungera utan att behöva göra om allt? Det känns som att det är väldigt nära att allt ska fungera nu så det hade varit trevligt om någon kunde förklara hur man kan lösa detta?

Mvh Joakim
Användarvisningsbild
Icecap
Inlägg: 26962
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Enkel hastighetsmätare (programmeringsproblem)

Inlägg av Icecap »

Tiden som mäts är alltså rätt, det är din uträkning eller din visning som är fel. Gör alltså om din visning eller posta hur du gör.
Skriv svar