Fler problem med avbrottsrutin * LÖST VIDEO BIFOGAD*

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
SeniorLemuren
Inlägg: 8407
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Fler problem med avbrottsrutin * LÖST VIDEO BIFOGAD*

Inlägg av SeniorLemuren »

Håller på att programmera en varvräknare som består av en hallgivare samt en PIC 16F877A och en LCD. Programmerar i Hi-Tech C ver 9.83 vilket jag är ganska novis i ännu.

Hallgivaren är inte kopplad utan jag sätter porten RC2 hög med en sladdstump för att testa programmet.

Jag har fått programmet att fungera så långt att det skriver den fasta texten till displayen. Vad jag inte fått till är utskrift av rpm från själva avbrottsrutinen.

Jag har följt instruktionerna i Microchip Tips ‘n Tricks Tips #2 Men jag kan inte se att det blir avbrott när jag sätter porten CCP1 (pin RC2) hög.

Programmet ser ut som följer. (Framåt resp Bakåt är inte implementerat.) och varvtalet är inte uträknat. Har satt ett fast rpm för att se om avbrott uppkommer men inget rpm skrivs ut när jag sätter porten hög.

Kod: Markera allt

/****************************************
* 	LCD mainroutine Ver 1.0				*				
*	LCD interface to PIC16F877A			*
*	lcd.c Ver 1.0 is included			*
*	lcd.h is included					*
****************************************/
#include <htc.h>
#include "lcd.h"
#define _XTAL_FREQ 4000000
#define	DelayS(T){unsigned char i;for (i=0; i<T*10; i++)__delay_ms(100);}

//*********** variabler avbrottsrutinen ******************
	unsigned int  	pulse_start;
	unsigned int  	pulse_old;
	unsigned int  	pulse_diff; 

//*********** variabler varvtal ******************	
	int thousands = 0;
	int hundreds = 0;
	int tens = 0;
	int ones = 0;	
	int direction = 1;	
	int	rpm = 0;

void put_rpm(){
	while (rpm >= 1000) {
 	thousands++;
  	rpm -= 1000;
	}
	while (rpm >= 100) {
 	 hundreds++;
  	rpm -= 100;
	}
	while (rpm >= 10) {
 	tens++;
  	rpm -= 10;
	}
	while (rpm >= 1) {
  	ones++;
  	rpm -= 1;
	}
	lcd_putch('0' + thousands);
	lcd_putch('0' + hundreds);
	lcd_putch('0' + tens);
	lcd_putch('0' + ones);
}

void main()
{

//**************** initiera registren *********************	
	TRISC		= 	0b11111111;	1;	// All input
  	CMCON       = 	0b00000111; 	// No comparator inputs
  	CCP1CON     =  	0b00000101; 	// Set CCP1 to capture all rising edges 
  	CCP1IE      =  	1; 	 			// Enable CCP1 interrupts
  	TMR1IF      =  	0; 				// TMR1 not overflowed
  	GIE       	=  	1; 				// Allow global interrupts
  	T1CON       =  	0b00001111; 	// Ställ in för rätt frekvens

//**************** initiera lcd *********************	
	lcd_init();
	lcd_goto(0);	// select first line
	lcd_puts("STYRBORD MASKIN");
	lcd_goto(0x40);	// Select second line 	
	if (direction==0)	
	lcd_puts(" RPM FRAMÅT");	
	if (direction==1)
	lcd_puts(" RPM BAKÅT");
		
	for(;;){	
	}	
}

// avbrottsrutin som tar in pulser till rpm (pinne RC2)

void interrupt isr(void)
{
        pulse_start = TMR1; // the value in CCPR1L and CCPR1H     
       pulse_diff = pulse_start - pulse_old; // Calculate the difference
       pulse_old = pulse_start; // Save for next time	
	TMR1IF = 0; // reset owerflow 
	rpm = 1050; // eg.pulse_diff * lämplig konstant. Men satt värde 1050 bara för test;
	put_rpm(); 
   
}
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Senast redigerad av SeniorLemuren 8 september 2012, 20:19:06, redigerad totalt 2 gånger.
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Problem med avbrottsrutin

Inlägg av Icecap »

Du verkar ha glömt att aktivera PEIE i INTCON.
Användarvisningsbild
SeniorLemuren
Inlägg: 8407
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Problem med avbrottsrutin

Inlägg av SeniorLemuren »

Inte glömt, jag trodde det räckte med GIE=1. Trots ett noggrant studerande av databladet lyckades jag missa att även PEIE måste sättas. Nu blev det avbrott hela tiden. När jag satte CCP1IF = 0 i avbrottsrutinen så verkar det funka. Nu är det bara resten kvar. :)
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Problem med avbrottsrutin

Inlägg av Icecap »

Jupp, du måste cleara interrupt-flaggan - men det står ju i databladet. Kör hårt.
Användarvisningsbild
SeniorLemuren
Inlägg: 8407
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Fler problem med avbrottsrutin

Inlägg av SeniorLemuren »

Så här är läget nu. Jag försöker skriva ut pulse_diff som är ett 16 bitars tal från TMR1. På displyen får jag upp en massa blandade tecken som hela tiden varierar när jag kör borrmaskinen i konstant varvtal framför hallgivaren. Det skall omsider räknas om till ett varvtal men nu vill jag bara skriva ut vad som finns i TMR1 som enligt Hi-Tech är de sammanslagna 8-bitars registren TMR1H:TMR1L. Verkar ha en jäkla otur när jag tänker? :)

Kod: Markera allt

/****************************************
* 	LCD mainroutine Ver 1.0				*				
*	LCD interface to PIC16F877A			*
*	lcd.c Ver 1.0 is included			*
*	lcd.h is included					*
****************************************/
#include <htc.h>
#include "lcd.h"
#define _XTAL_FREQ 4000000
#define	DelayS(T){unsigned char i;for (i=0; i<T*10; i++)__delay_ms(100);}

//*********** variabler avbrottsrutinen ******************
	unsigned int  	pulse_start;
	unsigned int  	pulse_old;
	unsigned int  	pulse_diff;
	unsigned int	rpm = 0;
//	const char 		*sInfo_txt; 

//*********** variabler varvtal ******************	
	int tenthousands = 0;
	int thousands = 0;
	int hundreds = 0;
	int tens = 0;
	int ones = 0;	
	int direction = 1;	
	
	

void put_rpm(){
	while (rpm >= 10000) {
 	tenthousands++;
  	rpm -= 10000;
	}
	while (rpm >= 1000) {
 	thousands++;
  	rpm -= 1000;
	}
	while (rpm >= 100) {
 	 hundreds++;
  	rpm -= 100;
	}
	while (rpm >= 10) {
 	tens++;
  	rpm -= 10;
	}
	while (rpm >= 1) {
  	ones++;
  	rpm -= 1;
	}
	lcd_putch('0' + tenthousands);
	lcd_putch('0' + thousands);
	lcd_putch('0' + hundreds);
	lcd_putch('0' + tens);
	lcd_putch('0' + ones);
}

void main()
{
//**************** initiera registren *********************	
	TRISC		= 	0b11111111;	1;	// All input
  	CMCON       = 	0b00000110; 	// No comparator inputs
  	CCP1CON     =  	0b00000110; 	// Set CCP1 to capture all rising edges 
  	CCP1IE      =  	1; 	 			// Enable CCP1 interrupts
  	TMR1IF      =  	0; 				// TMR1 not overflowed
  	GIE       	=  	1; 				// Allow global interrupts
	PEIE		=	1;
  	T1CON       =  	0b00001111; 	// Ställ in för rätt frekvens

//**************** initiera lcd *********************	
	lcd_init();
	lcd_goto(0);	// select first line
	lcd_puts("STYRBORD MASKIN");
	lcd_goto(0x40);	// Select second line 	
	if (direction==0)	
	lcd_puts(" RPM FRAMÅT");	
	if (direction==1)
	lcd_puts(" RPM BAKÅT");
		
	for(;;){	
	}	
}

// avbrottsrutin som tar in pulser till rpm (pinne RC2)

void interrupt isr(void)
{
	pulse_start = TMR1; // the value in CCPR1L and CCPR1H     
    pulse_diff = pulse_start - pulse_old; // Calculate the difference
    pulse_old = pulse_start; // Save for next time
	CCP1IF = 0; 	
	rpm = pulse_diff;	
	lcd_clear();
	lcd_goto(0);
	put_rpm();	
	pulse_diff = 0 ;
   
}
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Fler problem med avbrottsrutin

Inlägg av Icecap »

Till att börja med skriver du ut direkt från ISR'n - och det är inte bra alls!!!

Kod: Markera allt

#define true  1
#define false 0

#define T1XTAL 8000000
// Här måste du skriva in oscillatorns värde

#define CONSTANT_X (T1XTAL / 480)
// Okänd just nu, jag ser inte vilken frekvens du har på Timer1's oscillatoringång
// Jag antar att du kanske tror att du använder CPU-klockan men i T1CON har du slagit på den extra oscillator som finns.

unsigned long Calc;
unsigned int  pulse_start;
unsigned int  pulse_old;
unsigned int  pulse_diff;
unsigned int  rpm;
unsigned char Flag;

void interrupt isr(void)
  {
  pulse_start = CCPR1L + (CCPR1H << 8); // Read the captured time
  CCP1IF = 0;    
  if(!Flag)
    {
    pulse_diff = pulse_start - pulse_old; // Calculate the difference
    Flag = true;
    }
  pulse_old = pulse_start; // Save for next time
  }

void main()
{
//**************** initiera registren *********************   
  TRISC      =    0b11111111;   1;   // All input
  CMCON       =    0b00000110;    // No comparator inputs
  CCP1CON     =     0b00000110;    // Set CCP1 to capture all rising edges
  CCP1IE      =     1;              // Enable CCP1 interrupts
  TMR1IF      =     0;             // TMR1 not overflowed
  GIE          =     1;             // Allow global interrupts
  PEIE      =   1;
  T1CON       =     0b00001111;    // Ställ in för rätt frekvens, extern oscillator
//**************** initiera lcd *********************   
  lcd_init();
  while(true)
    {
    if(Flag)
      {
      Calc = CONSTANT_X;
      Calc /= (unsigned long)pulse_diff;
      Flag = false;
      rpm = Calc;
      lcd_goto(0);   // select first line
      lcd_puts("STYRBORD MASKIN");
      lcd_goto(0x40);   // Select second line    
      put_rpm();
      if (direction==0)   
      lcd_puts(" RPM FRAMÅT");   
      if (direction==1)
      lcd_puts(" RPM BAKÅT");
     }   
  }
EDIT: skrev bara de delar av källkoden som behövdes ändras.
Skillnaden är att en mätning görs vilket slår på en flagga. Så länge den flagga är på tas nya mätningar men de används inte.

I main-loop avkännas flaggan, RPM räknas ut, flaggan släcks och värdet visas. Då flaggan ju släcks kan en ny mätning ske osv. osv.
Detta kan förfinas mycket med utjämning, mätning över fler varv osv. men för stunden kan det duga.
Användarvisningsbild
SeniorLemuren
Inlägg: 8407
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Fler problem med avbrottsrutin

Inlägg av SeniorLemuren »

Ja det är klart fel. Jag har ingen extern oscillator så jag antar att det skall stå

Kod: Markera allt

T1CON       =     0b00001101;    // Ställ in för rätt frekvens
Jag har ändrat koden till den du kom med men det kommer fortfarande en bunt med asciitecken av varierande slag på displayen som skiftar hela tiden och jag fattar mindre och mindre? :(
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Fler problem med avbrottsrutin

Inlägg av Icecap »

T1CON = 0b00000101; är nog mer rätt...

EDIT: ska fylla i lite mer.

Kod: Markera allt

void put_rpm(void)
  { // Formats to buffer-size digits, leading zero blanking also
  char Buffer[4], Index;
  unsigned int Work;
  for(Index = 0; Index < sizeof(Buffer); Index++) Buffer[Index] = ' '; // Fill with spaces
  Buffer[sizeof(Buffer) - 1] = '0'; // Just to be sure if RPM == 0
  Work = rpm;
  Index = sizeof(Buffer); // Point on last space
  while(Index && Work) // Do while room and value exists
    {
    Buffer[Index] = '0' + Work % 10; // Use the remainder and add '0, making it go '0'-'9'
    Work /= 10; // Used the ones, take it down a decade
    Index--; // Point on next higher position
    }
  for(Index = 0; Index < sizeof(Buffer); Index++) lcd_putch(Buffer[Index]); // Print out the digits
  }
EDIT: Lade till lite kommentarer.

Vilken klockfrekvens kör du på µC'n?
gokartnisse
Inlägg: 120
Blev medlem: 3 mars 2011, 01:58:43

Re: Fler problem med avbrottsrutin

Inlägg av gokartnisse »

Är lite trött nu men har du inte missat att ta hand om vad som händer när timern slår runt?
Dvs vad händer om pulse_start har ett lägre värde än pulse_old? Då får man väl se overflow-flaggan som en 17:e bit i TIMR1?
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Fler problem med avbrottsrutin

Inlägg av Icecap »

Det är såklart en viktig faktor! Tiden man vill mäta får aldrig överstiga en full Timer1-cykel och det kan man ställa med prescalen. Och kjälvklart kan man expandera Capture till att ha t.ex. 32 bit vid att använda Timer1 overflow-interrupt men i nuläget är det nog överkurs.

Det viktiga just nu är att få stabila siffror på LCD-modulen, sedan kan det putsas till.
Användarvisningsbild
SeniorLemuren
Inlägg: 8407
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Fler problem med avbrottsrutin

Inlägg av SeniorLemuren »

Blev sängen tidigt i går. Somnade med en del frågetecken. Ändrade T1CON till 0b00000101 . Kopierade in de nya kodstyckena. Nu blev det i alla fall siffror till slut. Displayen pendlar runt 4 och 5 oberoende hur ort jag kör skruvdragaren (ca 50- 300 rpm), någon enstaka gång dyker 35 upp men aldrig något däremellan.

Den nya rutinen put_rpm har jag inte kollat utan kopierade in den rakt av. Den är mer avancerad än vad jag har lärt in ännu. :) Får sätta mig någon dag och läsa om stränghantering och pekare.

Jag känner att jag har noll kontroll på detta med inställning av CCP1CON och T1CON.Jag har räknat så här:

XTAL_FREQ 4 000 000
T1CON = prescaler 1:8
CCP1CON = capture every 16 rising edge

4 000 000 / 8 = 500 000
500 00 / 16 = 31250 tick / sec

rpm = 300 varv/min = 5 varv / sek. vid en puls per varv från hallgivaren ges 5 pulser / sek.
eller 31250 / 5 = 6250 tick / puls

6250 tick motsvarar då 300 varv / min om jag har fattat rätt?
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Fler problem med avbrottsrutin

Inlägg av Icecap »

Timer1 har Fosc/4 som grundklocka när den kör på intern klocka. Alltså är dens basklocka 4MHz/4 = 1MHz.

Detta ger att den har en maximal mättid på 65,5ms utan prescaler aktiv, detta motsvarar 915,5RPM som lägsta varvtal.
Med prescaler på 1:8 blir det 114,4RPM som lägsta mätmöjlighet utan lite trixande med extra funktioner.

Nu vet jag inte vilka varvtalsområde du avser att mäta men jag skulle tro att detta börjar bli ganska viktigt vid detta läge, samtidig är det mycket viktigt att pulserna är "rena" och att det inte finns t.ex. "dubbelpulser" som kan ställa till det ganska duktigt.

EDIT: Som jag ser det måste du öka på programmet lite så att du dels får en bra struktur för utskrift osv. samt att RPM-mätningen bör expanderas till att tiderna är 32 bit istället för 16 bit. Detta kan rimligt enkelt klaras vid att använda Timer1-interrupten som sedan räknar upp en 16-bitars variabel men jag kan tro att det kan kännas lite skrämmande just i detta läge.
gokartnisse
Inlägg: 120
Blev medlem: 3 mars 2011, 01:58:43

Re: Fler problem med avbrottsrutin

Inlägg av gokartnisse »

Icecap: Det finns väl inget som nollställer TIMR1 när interrupt sker? så man måste hålla koll på BÅDE att tiden man mäter inte överstiger en full cykel på TIMR1 och TIMR1 overflow-flagga (man behöver inte trigga intrerrupt utan bara se flaggan som en 17-bit).

Vad händer annars om man har FFF0 i pulse_old och pulse_start är 000F, då blir ju pulse_diff "negativ" (slår runt och ger totalt fel bit-mönster).
Möjligheten till "Special Event trigger" som nollställer TIMR1 finns bara när man använder compare vad jag kan se i databladet?!
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Fler problem med avbrottsrutin

Inlägg av Icecap »

Vilket värde Timer1 har är faktisk totalt likgiltigt och man ska INTE börja nollställa den! Jag har använd denna funktion på en hel del andra projekt och hela grejen är att det man kalla nollpunkten i tidsmätningen är den tid man mätt förra gången.

Jag hade själv en hel den funderingar på om man nu skulle få negativa tal av uträkningarna men då tiden mäts som enbart positiva tal har det ingen betydelse. Jag kontrollräknade en del olika värden och insåg att det var så.

Det man ska göra är att man sparar "denna" mätning, den är ju med 16 bit från Timer1, variablen ska sparas som "förra gång" så att säga. I variablen man sparar den in ser man till att nolla de övre 16 bit av den 32-bits variablen man sparar i, värdet blir alltså (i hex): 0000 1234 (1234 bara som exempel, kan vara allt mellan 0000 och FFFF).

Sedan rullar allt på och nästa puls kommer, under tiden har alla Timer1 overflow-interrupt bara stegat upp "Tid nu"-variablens översta 16 bit. När sedan nästa puls kommer låsas Timer1-värdet ut, blir avläst och placerat i de låga 16 bit av "tid nu"-variablen. Den kommer då att ha värdet (hex igen) 000F 6789 (6789 bara som exempel, kan vara allt mellan 0000 och FFFF) om vi förutsätter att Timer1 overflow har kommit 15 gångar under mätperioden.

Alltså har man två värden: Förra tiden och nuvarande tiden. Differensen är då "Tid nu" - "Tid då" = 000F6789 - 00001234 = F5555. När detta är uträknat kopierar man de låga 16 bit av "Tid nu" till "Tid då" och nollar de övre 16 bit och allt börjar om igen.

På detta vis mäter man tide med 32 bit och i detta fall, om man tar maximal hastighet på timer1, tider upp till 4294 sekunder med en upplösning på 1µs - och det torde väl räcka. Man kan såklart expandera ytterligare men i detta fall skulle den tid motsvarar så låga värden på RPM att de inte finns, detta betyder alltså att man faktisk kan använda denna Timer1-interrupt uppräkning av de höga 16 bit till att avkänna om det tog så lång tid att motorn inte kör alls.

Och jag skulle inte bli förvånat om man inte ens behöver 16 bit som expansion, 8 bit skulle räcka långt, speciellt om man vill använda prescalern på Timer1.

EDIT: och din specifika fråga: Time_old = FFF0h, Time_new = 000Fh.
000Fh - FFF0h = 15 - 65520 = -65505
-65505 -> hex = 001Fh
Uträknar jag 000Fh - FFF0h på min miniräknare blir resultatet 001Fh. Kusligt eller hur? Just detta negativa tal och att man bara tar de 16 bit man räknar med gör att det hela passar ändå helt enkelt.

Med EDIT: med bara en byte extra som overflow-räknare kan den maximala tiden mellan pulserna uppgå till 16,78 sek vilket motsvarar 3,5 RPM, då med en prescaler på 1:1. Med 1:8 i prescalevärde blir det 0,447 RPM som undre gräns och jag skulle tro att det räcker mycket långt... iaf. i detta fall.
Senast redigerad av Icecap 31 augusti 2012, 13:36:32, redigerad totalt 1 gång.
Användarvisningsbild
SeniorLemuren
Inlägg: 8407
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Fler problem med avbrottsrutin

Inlägg av SeniorLemuren »

Aktuellt varvtal skall ligga mellan 200 - 2400 r/m. Om det finns någon fördel så kan man sätta fler magneter på mätstället. 1 magnet = 1 puls/varv.
Skriv svar