Hög hastighet

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

Du ska använda den interna räknaren TMR1, en 16-bitars räknare. När den är konfigurerad och påslagen ökar den automatiskt. En en gång per instruktionscykel till exempel. Läs om den i databladet. CCP-enheten är nog lämplig att använda också.

Sen är det det inte bra att ligga och vänta i ett interrupt. Interrupten ska bara göra något fort, t.ex. spara timer-/CCP-värdet samt sätta en flagga och sedan hoppa ut.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> TMR0++;

Så där gör man i princip *aldrig* !
I alla fall inte för det du vill åstakomma.
Det är inte stor mening att gå in på detaljer, du
måste läsa på ordentligt om timers...

Trigga interruptet på RB0, starta timern, ställ om
interrupt flanken, återgå och vänta på nästa interrupt.
Då stoppar du (eller läser av) timern. Sen får du göra
något vettigt med resultatet...
J-R
Inlägg: 13
Blev medlem: 16 december 2007, 11:17:57
Ort: Stockholm

Inlägg av J-R »

Tack för svaren.

Läst på lite till om timers och tänker följa bearing's råd och använda Timer1 istället.

Ny kod. Tar gärna kritik så man kan förbättra koden.

Sodjan: Tanken är att trigga interrupten på RB0 (sensor 1) och avsluta interrupten mha sensor 2 på RB1.

Får jag owerflow flaggan satt på Timer1 så har mätobjektet misslyckat med att trigga sensor 2 så jag avslutar och börjar om.

Kod: Markera allt

void interrupt() {
     T1CON.TMR1ON = 1;                //Start Timer1
     INTCON.INTE=0;                   //Disable external interrupt on RB0
     while(PORTB.F1 == 0 && PIR1.TMR1IF == 0)
     {
                                      // Wait
     }
     T1CON.TMR1ON = 0;                //Stop Timer1

     TEMPH = TMR1H;                   //Calculate TMR1
     TEMPL = TMR1L;
     time = TEMPH*256;
     time += TEMPL;
}
//JR
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

För att slippa vänta i interruptrutinen skulle du kunna göra så att interrupt genereras både vid båda passeringarna. Då kommer interruptrutinerna bra sätta igång och stänga av tidtagningen. Dom kommer inte behöva vänta.

Använd t.ex. INTE för att nollställa TMR1, sen CCP1 inställd som Capture-enhet. Den kommer då spara TMR1-värdet automatisk när CCP1-pinnen ändras. I CCP1-interruptet kan du senare spara det värdet till din egen variabel.

Det tar några instruktionscykler från den faktiska flanken tills du att hamnar i INTE-interruptet där TMR1-nollställs, så om du istället för 0 laddar antalet cykel/prescalern så kommer du få mer exakt tid.

Det är även bra att använda ett TMR1-interrupt - ifall den skulle slå runt. Där kan du antingen sätta en felflagga, eller så skulle du kunna ha en annan räknare där som förlänger TMR1 till 24 eller kanske 32 bitar. (Om det skulle behövas, beror ju på hur lång tid som är rimlig.)
J-R
Inlägg: 13
Blev medlem: 16 december 2007, 11:17:57
Ort: Stockholm

Inlägg av J-R »

Jag är endast i interrupt rutinen i max 2ms. Jag ser det inte som ett problem att vänta där tills jag får andra passeringen.

Jag har ett annat (läs ett till..) problem som jag inte får till.
Jag använder Mikroc och deras inbyggda bibliotek.

Problemet är att jag inte kan visa det uträknade värdet på LCD'en.
Så här har jag tänkt.

Förutsätter 1m mellan sensorerna. 8Mhz Osc ger en intruktionscykel på 2MHz -> 0,0000005 s för varje instruktionscykel.

Kod: Markera allt

speed = (1,0/(time * 0,0000005));
        floatToStr(speed, text);        // Convert number to string
        LCD_Init(&PORTD);            // Initialize LCD connected to PORTB
        LCD_Cmd(LCD_CLEAR);          // Clear display
        LCD_Cmd(LCD_CURSOR_OFF);     // Turn cursor off
        LCD_Out_CP(text);          // Print text to LCD
time värdet kommer härifrån:

Kod: Markera allt

void interrupt() {
     T1CON.TMR1ON = 1;                //Start Timer1
     INTCON.INTE=0;                   //Disable external interrupt on RB0
     while(PORTB.F1 == 0 && PIR1.TMR1IF == 0)
     {
                                      // Wait
     }
     T1CON.TMR1ON = 0;                //Stop Timer1

     TEMPH = TMR1H;                   //Calculate TMR1
     TEMPL = TMR1L;
     time = TEMPH*256;
     time += TEMPL;
Det ända som visas på LCD'en är 0 (noll).

Om jag endast multiplicerar time med 5 (speed=time*5) så får jag rätt värde på LCD'en.

Några idéer?

//JR
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> time * 0,0000005

Vilken typ har time ?

MEn generellt sätt så är en beräkning som den där ett
tecken på att man har tänkt fel någonstans...

> Om jag endast multiplicerar time med 5 (speed=time*5) så får jag rätt värde på LCD'en.

Ja men då så... :-)
bearing
Inlägg: 11677
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Inlägg av bearing »

Istället för att göra en multiplikation i nämnaren kan du bryta ut 1/0,0000005 till 2000000. Dvs speed = 2000000/time. Då behöver du inte ens räkna med flyttal. Flyttal ska man alltid undvika tycker jag. Om du vill ha två decimaler kan du lägga till två nollor på konstanten och sätta en punkt vid utskriften.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Helt rätt. Flyttal kan med rätt design i de flesta fall undvikas helt.
Ofta kan man även kraftigt förrenkla sina beräkningar genom
smarta val av prescaler, mätintervall o.s.v. D.v.s så att själva mätningen
ger korrekt värde direkt utan omräkningar. Eller åtminstånde så att
beräkningarna sker med jämna potenser av två (oavsett om det är
multiplikationer eller divisioner) eftersom det då bara blir left/right shift
av det hela...
J-R
Inlägg: 13
Blev medlem: 16 december 2007, 11:17:57
Ort: Stockholm

Inlägg av J-R »

Tack för hjälpen Bearing, nu fungerar det!

Nu ska jag snygga till koden och lägga till lite user input möjligheter.

Återkommer säkert med fler frågor!

Sodjan:
Tanken var rätt, designen var fel. :)


//J-R
Skriv svar