Sari la conținut
ELFORUM - Forumul electronistilor

Tahometru/RPM


Postări Recomandate

  • Răspunsuri 35
  • Creat
  • Ultimul Răspuns

Top autori în acest subiect

Nu e din cauza aceasta.

 

Merge si cum spui tu doar ca sunt cateva operatii in plus care se fac in intrerupere lucru pe care trebuei sa il eviti cat poti.

Functia rulata de intrerupere trebuie sa fie cat mai scurta.

Dar repet, merge si cum spui tu. Doar ca lipseste ceva ....

 

Ruleaza programul de mai jos si incearca sa vezi unde sta diferenta :)

/*
   Compiler     = mikroC 7.1.0
   uC type      = 16F876A
   uC freq      = 4MHz
   uC Datasheet = http://ww1.microchip.com/downloads/en/DeviceDoc/39582b.pdf
   Author       = mars01, on elforum.ro
*/

sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;

sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;

volatile unsigned int period = 0;
volatile unsigned char display_flag = 0;
unsigned char text[6];  // enough space for 5 characters

// those are declared as volatile otherwise are optimized out by the compiler
volatile unsigned int start = 0;
volatile unsigned int stop = 0;

// function prototype - declaration
unsigned int period_func (unsigned int, unsigned int);
void hw_init();

// function definition
unsigned int period_func (unsigned int start_value, unsigned int stop_value){
   return (stop_value - start_value);
}

   
void Interrupt(){
   static unsigned int cnt = 0;
   static unsigned char flag = 0;

   if (CCP1IF_bit && CCP1IE_bit){
      CCP1IF_bit = 0;       // Reset CCP1 Interrupt period

      if (flag){
         stop = (((unsigned int)CCPR1H)<< 8) | ((unsigned int)(CCPR1L));

         // actual function usage (call)
         period = period_func(start, stop);
         flag = 0;

         // Reset Timer1
         TMR1L = 0x00;
         TMR1H = 0x00;
      }
      else{
         flag = 1;
         start = (((unsigned int)CCPR1H)<< 8) | ((unsigned int)(CCPR1L));
      }
   }

   if (TMR0IF_bit && TMR0IE_bit){
      // reset Timer0
      TMR0IF_bit = 0;
      TMR0 = 0x06;

      // frequency will be displayed once every 500ms - twice per second
      if (cnt >= 500){
         display_flag = 1;
         cnt = 0;
      }
      else{
         cnt++;
      }
   }
}


// definitation of the hw_init() function which manage the HW initialization
void hw_init(){

   // Blink a LED to serve as a heart beat
   CMCON = 0x07;     // comparators are OFF
   TRISA0_bit = 0;   // RA0 is output;
   ADCON1 = 0xFF;    // all pins are DIGITAL
   RA0_bit = 0;      // RA0 start as OFF;
   
// Configure CCP1
   CCP1CON = 0b00000101;      // capture mode every rising edge
   CCP1IF_bit = 0;            // CCP1 interrupt flag - needs to be cleared in sw

   // Configure Timer1
   T1CKPS1_bit = 0;           // Timer1 prescaler set to 1:1; increment each 1us
   T1CKPS0_bit = 0;
   T1OSCEN_bit = 0;           // LP oscillator is OFF
   TMR1CS_bit = 0;            // TMR1 is incremented on internal clock
   TMR1ON_bit = 1;            // enable Timer1

   // Configure Timer0: Prescaler 1:4 and TMR0 preload with 0x06
   PS2_bit = 0;               // Prescaler configured at 1:4
   PS1_bit = 0;
   PS0_bit = 1;
   NOT_RBPU_bit = 1;          // pull-ups on Port B are disabled
   T0CS_bit = 0;              // use internal instruction cycle clock
   PSA_bit = 0;               // prescaler assigned to Timer0
   TMR0 = 0x06;               // preload the TMR0 register, combined with the prescaler value will give a 1ms interrupt

   // Initialize working registers
   TMR1L = 0x00;              // Set Timer1 register to zero
   TMR1H = 0x00;
   CCPR1L = 0x00;             // Initialize CCP register to 0
   CCPR1H = 0x00;

   CCP1IE_bit = 1;            // enable CCP1 interrupt
   TMR1IE_bit = 0;            // disable TMR1 interrupt
   TMR0IE_bit = 1;            // enable TMR0 interrupt

   // General enable of the interrupts
   GIE_bit = 1;               // enable all interrupts
   PEIE_bit = 1;              // enable peripheral interrupts

   LCD_init();                // initialize LCD
   Lcd_Cmd(_LCD_CLEAR);       // Clear display
   Lcd_Cmd(_LCD_CURSOR_OFF);  // Cursor off
}
   

void main(){
   volatile unsigned long temp = 0; // volatile because otherwise it gets optimized out by the compiler

   // actual usage of our function that manage the hardware initialization
   hw_init();
   
   Lcd_Out(1,1,"Frecventa:      "); // display some static text, it will not change, on line 1 of the LCD
   
   while (1){

      if (display_flag){            // if the time to display values has come (500ms passed)
         display_flag = 0;          // make sure that you clear the flag so it is HIGH only when decided in the interrupt
         
         // LED heart beat
         RA0_bit = !RA0_bit;
         
         temp = 1000000 / period;   // convert period (measured in microseconds) in frequency
         temp = temp * 60;          // we get RPM value (rotation per minute) out of the frequency (rotations per second)
         WordToStr(temp, text);     // convert RPM value to text string
         Lcd_Out(2,1, text);        // display the text on LCD on line 2
      }
   }
}
Editat de mars01
Link spre comentariu

Te-ai prins. :) Nu se activase intreruperea pentru Timer0.

 

In modul in care acum este setat prescaler-ul la TMR1 (adica este 1:1) viteza minima care se poate masura este de cca 920RPM.

Pentru ca registrii pe 16bit pot numara pana la 65535. Cu raportul de 1:1 la prescaler ai practic 1us pe tick adica poate determina o durata maxima intre pulsuri de 65535us = 65.535ms.

Convertim in frecventa si rezulta 1/65.535ms = 0.01525 rotatii/ms = 15.25rotatii/sec = 915RPM

 

Daca folosesti un prescaler de 1:4 pt Timer1 atunci viteza minima care se poate masura este de 915RPM /4 deci vreo 230 RPM. S.a.m.d

Dar in caz ca umbli la prescaler trebuie sa corectezi si cand faci conversia din durata in frecventa.

Editat de mars01
Link spre comentariu

Ok...se pare ca acum merge....eu am ventilatorul de mai jos, la care ii citesc bpm-ul ca sa zic asa...momentan imi apar 7500 rpm pe display....dunctioneaza si LED-ul insa nu prea imi pare rotatia corecta...adica 5000 cred ca ar trebui sa arate...acum nu stiu, ati zic ca citeste doar 920

 

https://baraholka.onliner.by/viewtopic.php?t=18742259

 

Aici trebuie sa verifici cu osciloscopul ca sa ai pulsuri clare si bine definite. Este posibil sa ai si zgomot. Poti sa pui un mic filtru trece jos pe pinul uC-ului (CCP1).

 

PS: In ce este mai avantajos sa programezi?in mikroC sau in MPlab....?adica ce este mai bine?

 

La nivelul tau e bine sa ramai cu mikroC. Macar ai libraries-urile de functii oferite gata facute.

In mplab cam trebuie sa le faci tu (sau te bazezi pe cele autogenerate de CCS dar sunt cam greu de inteles).

Link spre comentariu

Am inteles....azi mai invatai cum sa fac headerre, cu sa le implementez in program...ca sa mai usurez munca...o sa vad ce fac si cu filtrul, poate chiar o sa-i pun un optocuplor, ideea era sa vad cum functioneaza...la mine nu imi sta in zero afisajul pentru ca rezistenta este legata la +5V, deci pinul de citire sta tot timpul in 1 logic, doar cand se invarte el pulseaza intre 0-5V...

 

Am vazut ca exista si filtre soft, asa zicea colegul meu ca filtreaza o tensiune...etc...cred ca si o citire precisa se face tot cu timer nu?

 

mai face si o mediere...ce-o mai fi si aia....

Editat de catalin004
Link spre comentariu

In acest caz ar trebui sa configurezi CCP1 ca sa numere pe "falling edge" si nu pe rising edge asa cum este setat acum, adica sa faci:

CCP1CON = 0x04;

sau altfel scris:

CCP1CON = 0b00000100;      // capture mode every falling edge 

Citeste si tu datasheet-ul ala ...

 

Poti sa faci si o mediere daca vrei, este un fel de filtru trece jos software.

Medierea aici este cand aduni sa zicem 10 sample-uri succesive si apoi suma o imparti la 10.

Editat de mars01
Link spre comentariu

A...ok...oricum tot o sa-i pu opto...cred ca e mai ok....multumesc...gata acum trecem la alte jucarii....ma gandeam la un program care cu 2 butoane sa "chem " un anume procent al pwm-ului, adica sa apas pe increment pe display din 10 in 10% si motorul sa isi schimbe turatia, apoi invers....dar..de maine

Link spre comentariu

Asa se face medierea?Am gasit intr-o postarea a dvs mai de mult....sau este mai complicat?

Am inteles ca ar trebui eliminate capetele....adica prima si ultima citire....sau cate 2 din fiecare capete....

unsigned int i, rezultat;

rezultat = ADC_read();
rezultat = 0;

for (i = 0; i < 10; i++){
  rezultat = rezultat + ADC_read();
}
rezultat = rezultat / 10;
Editat de catalin004
Link spre comentariu
/*
   Compiler     = mikroC 7.1.0
   uC type      = 16F876A
   uC freq      = 4MHz
   uC Datasheet = http://ww1.microchip.com/downloads/en/DeviceDoc/39582b.pdf
   Author       = mars01, on elforum.ro
*/

sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;

sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;

unsigned int period_avg = 0;
volatile unsigned int period = 0;
volatile unsigned char reading_count = 0;
volatile unsigned char display_flag = 0;
unsigned char text[6];  // enough space for 5 characters

// those are declared as volatile otherwise are optimized out by the compiler
volatile unsigned int start = 0;
volatile unsigned int stop = 0;

// function prototype - declaration
unsigned int period_func (unsigned int, unsigned int);
void hw_init();

// function definition
unsigned int period_func (unsigned int start_value, unsigned int stop_value){
   return (stop_value - start_value);
}

   
void Interrupt(){
   static unsigned int cnt = 0;
   static unsigned char flag = 0;

   if (CCP1IF_bit && CCP1IE_bit){
      CCP1IF_bit = 0;       // Reset CCP1 Interrupt period

      if (flag){
         stop = (((unsigned int)CCPR1H)<< 8) | ((unsigned int)(CCPR1L));
		 
         // actual function usage (call)
         period += period_func(start, stop);
		 reading_count++;
         flag = 0;
		 
         // Reset Timer1
         TMR1L = 0x00;
         TMR1H = 0x00;
      }
      else{
         flag = 1;
         start = (((unsigned int)CCPR1H)<< 8) | ((unsigned int)(CCPR1L));
      }
   }

   if (TMR0IF_bit && TMR0IE_bit){
      // reset Timer0
      TMR0IF_bit = 0;
      TMR0 = 0x06;

      // frequency will be displayed once every 500ms - twice per second
      if (cnt >= 500){
         display_flag = 1;
         cnt = 0;
      }
      else{
         cnt++;
      }
   }
}


// definitation of the hw_init() function which manage the HW initialization
void hw_init(){

   // Blink a LED to serve as a heart beat
   CMCON = 0x07;     // comparators are OFF
   TRISA0_bit = 0;   // RA0 is output;
   ADCON1 = 0xFF;    // all pins are DIGITAL
   RA0_bit = 0;      // RA0 start as OFF;
   
// Configure CCP1
   CCP1CON = 0b00000101;      // capture mode every rising edge
   CCP1IF_bit = 0;            // CCP1 interrupt flag - needs to be cleared in sw

   // Configure Timer1
   T1CKPS1_bit = 0;           // Timer1 prescaler set to 1:1; increment each 1us
   T1CKPS0_bit = 0;
   T1OSCEN_bit = 0;           // LP oscillator is OFF
   TMR1CS_bit = 0;            // TMR1 is incremented on internal clock
   TMR1ON_bit = 1;            // enable Timer1

   // Configure Timer0: Prescaler 1:4 and TMR0 preload with 0x06
   PS2_bit = 0;               // Prescaler configured at 1:4
   PS1_bit = 0;
   PS0_bit = 1;
   NOT_RBPU_bit = 1;          // pull-ups on Port B are disabled
   T0CS_bit = 0;              // use internal instruction cycle clock
   PSA_bit = 0;               // prescaler assigned to Timer0
   TMR0 = 0x06;               // preload the TMR0 register, combined with the prescaler value will give a 1ms interrupt

   // Initialize working registers
   TMR1L = 0x00;              // Set Timer1 register to zero
   TMR1H = 0x00;
   CCPR1L = 0x00;             // Initialize CCP register to 0
   CCPR1H = 0x00;

   CCP1IE_bit = 1;            // enable CCP1 interrupt
   TMR1IE_bit = 0;            // disable TMR1 interrupt
   TMR0IE_bit = 1;            // enable TMR0 interrupt

   // General enable of the interrupts
   GIE_bit = 1;               // enable all interrupts
   PEIE_bit = 1;              // enable peripheral interrupts

   LCD_init();                // initialize LCD
   Lcd_Cmd(_LCD_CLEAR);       // Clear display
   Lcd_Cmd(_LCD_CURSOR_OFF);  // Cursor off
}
   

void main(){
   volatile unsigned long temp = 0; // volatile because otherwise it gets optimized out by the compiler

   // actual usage of our function that manage the hardware initialization
   hw_init();
   
   Lcd_Out(1,1,"Frecventa:      "); // display some static text, it will not change, on line 1 of the LCD
   
   while (1){
   
	if (reading_count >= 10) {
	  period_avg = period / reading_count;
	  period = 0;
	  reading_count = 0;
	}

	if (display_flag){            // if the time to display values has come (500ms passed)
	 display_flag = 0;          // make sure that you clear the flag so it is HIGH only when decided in the interrupt
	 
	 // LED heart beat
	 RA0_bit = !RA0_bit;
	 
	 temp = 1000000 / period_avg;   // convert period (measured in microseconds) in frequency
	 temp = temp * 60;          // we get RPM value (rotation per minute) out of the frequency (rotations per second)
	 WordToStr(temp, text);     // convert RPM value to text string
	 Lcd_Out(2,1, text);        // display the text on LCD on line 2
	}
   }
}

Cam asa se face media pt variabila period. Treaba cu prima citire e valabila doar pentru citiri ADC (citirea unui tensiuni analogice), prima citire este ignorata si este folosita pt stabilizarea modulului de citire ADC, ultima citire nu imi dau seama de ce ar trebuii ignorata cel putin in cazul unui citiri ADC.

Editat de Bandi Szasz
Link spre comentariu

Creează un cont sau autentifică-te pentru a adăuga comentariu

Trebuie să fi un membru pentru a putea lăsa un comentariu.

Creează un cont

Înregistrează-te pentru un nou cont în comunitatea nostră. Este simplu!

Înregistrează un nou cont

Autentificare

Ai deja un cont? Autentifică-te aici.

Autentifică-te acum



×
×
  • Creează nouă...

Informații Importante

Am plasat cookie-uri pe dispozitivul tău pentru a îmbunătății navigarea pe acest site. Poți modifica setările cookie, altfel considerăm că ești de acord să continui.Termeni de Utilizare si Ghidări