Pages: [1] 2 3   Go Down
Author Topic: reading rpm signal from cpu fan  (Read 10119 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 25
I Love YaBB 2!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hello
I am using arduino to make an RPM meter to read the pulses off an ordinary intel cpu fan with 3 leads (+,-, rpm signal)
I tried using digitalread() and pulsein() but I get fuzzy results.
anyone knows how to read those signals reliably?

code examples:




void loop()
 {
  
  scanvalue=digitalRead(7);
  if (scanvalue!=previous_scan) {pulsecount++; previous_scan=scanvalue;}
    
   if (millis() - previousMillis > interval)
  {
   interval=((millis() - previousMillis)/pulsecount)*50
   previousMillis = millis();    
   pulsecount=0;

   //code blinking code here
   }
}
Logged

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello Tomas,

That the code you are looking for :

Code:
/***************************************/
// FAN SPEED Arduino
// Benoît ROUSSEAU juillet 2006
// - Mesure de la vitesse de rotation d'un
// ventillateur de CPU sous interruption.
/***************************************/

// variables et définitions
#define INT_0_PIN  2
#define EXTINT_LEVEL_LOW      0x00      // trigger on low level
#define EXTINT_EDGE_ANY            0x01      // trigger on any edge
#define EXTINT_EDGE_FALLING      0x02      // trigger on falling edge
#define EXTINT_EDGE_RISING      0x03      // trigger on rising edge
#define sbi(PORT,BIT) PORT|=_BV(BIT)   // macro pratique pour mettre un bit x à 1 (Set Bit In)

unsigned int NbTopsFan;
unsigned int MeasuredTopsFan;

/***************************************/
// Fonction appelée à chaque
// déclenchement de l'interruption 0
/***************************************/
SIGNAL(SIG_INTERRUPT0)
{
  NbTopsFan++;
}

/***************************************/
// iniInt0
/***************************************/
// Init. interrupt 0
/****************************************/
void initInt0 (byte config)
{
  sbi(GICR, INT0); //
  sbi(GIFR, INT0); //

  MCUCR &= ~((1<<ISC01) | (1<<ISC00)); // raz
  MCUCR |= config; //
}

/***************************************/
void setup()
{
  pinMode(INT_0_PIN, INPUT);
  Serial.begin(9600);
  initInt0 (EXTINT_EDGE_RISING);
};

void loop ()
{
    NbTopsFan = 0;
    delay (1000);
    MeasuredTopsFan = NbTopsFan;
    Serial.print (MeasuredTopsFan * 60, DEC);
    Serial.print (" rpm");
    Serial.print (13, BYTE);
};

It was test and it work !

You have to connect the "sens output" of the cpu fan to the digital pin #2 with a pull-up resitor >= 10Ko to +5V of the Ardiuno board. This output is a open collector ouput. I don'y test the activatation of the internals pull-ups of the ATmega. I don't remerber if they was activated by default but it work with a externel resistor... Take care of that you can't use another pin, interrupt 0 sense only on the #2 pin.

Remenber to connect the ground power of the fan and the ground power of the Arduino card.

Finally, if every thing is connect, open HyperTerminal serial monitor to see every second the fan speed in rpm.
Logged

Cordialement,
Benoît ROUSSEAU

0
Offline Offline
Newbie
*
Karma: 0
Posts: 25
I Love YaBB 2!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

thanks very much friend , merci mon ami
when the project is done I'll post the how-to
« Last Edit: July 23, 2006, 06:22:54 am by tomas » Logged

Enschede - The Netherlands
Offline Offline
Jr. Member
**
Karma: 0
Posts: 75
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Do you have a link were the source code is explained ? I'm trying to understand the code. It seems I can't find all the info in the arduino reference. Are there any other resources we're I can find this information ?

Especially the void initInt0() function is hard to understand.

hope you can give me some hints.
Logged

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

  Yes I will do it... If you understand french there's explanations here : http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1147164546.

   Else,

   Download the full datasheet of ATmega8 here http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf take a look at the #66 page "External interrupts".

void initInt0 (byte config)
{
// set bit 6 "External Interrupt Request 0 Enable" in the General Interrupt Control
// Register – GICR

  sbi(GICR, INT0);

// set bit 6 "External Interrupt Flag 0" in the General Interrupt Flag
// Register – GIFR

  sbi(GIFR, INT0);

// set ISC01, ISC00: Interrupt Sense Control 0 Bit 1 and Bit 0
// in the MCU Control Register – MCUCR

  MCUCR &= ~((1<<ISC01) | (1<<ISC00)); // raz
  MCUCR |= config; //
}

Extracts of datasheet

The MCU Control Register contains control bits for interrupt sense control and general
MCU functions.
• Bit 3, 2 – ISC11, ISC10: Interrupt Sense Control 1 Bit 1 and Bit 0
The External Interrupt 1 is activated by the external pin INT1 if the SREG I-bit and the
corresponding interrupt mask in the GICR are set. The level and edges on the external
INT1 pin that activate the interrupt are defined in Table 31. The value on the INT1 pin is
sampled before detecting edges. If edge or toggle interrupt is selected, pulses that last
longer than one clock period will generate an interrupt. Shorter pulses are not guaranteed
to generate an interrupt. If low level interrupt is selected, the low level must be held
until the completion of the currently executing instruction to generate an interrupt.

• Bit 1, 0 – ISC01, ISC00: Interrupt Sense Control 0 Bit 1 and Bit 0
The External Interrupt 0 is activated by the external pin INT0 if the SREG I-flag and the
corresponding interrupt mask are set. The level and edges on the external INT0 pin that
activate the interrupt are defined in Table 32. The value on the INT0 pin is sampled
before detecting edges. If edge or toggle interrupt is selected, pulses that last longer
than one clock period will generate an interrupt. Shorter pulses are not guaranteed to
generate an interrupt. If low level interrupt is selected, the low level must be held until
the completion of the currently executing instruction to generate an interrupt.
General Interrupt Control
Register – GICR
• Bit 6 – INT0: External Interrupt Request 0 Enable
When the INT0 bit is set (one) and the I-bit in the Status Register (SREG) is set (one),
the external pin interrupt is enabled. The Interrupt Sense Control0 bits 1/0 (ISC01 and
ISC00) in the MCU general Control Register (MCUCR) define whether the external
interrupt is activated on rising and/or falling edge of the INT0 pin or level sensed. Activity
on the pin will cause an interrupt request even if INT0 is configured as an output. The
corresponding interrupt of External Interrupt Request 0 is executed from the INT0 Interrupt
Vector.
Table 32. Interrupt 0 Sense Control
ISC01 ISC00 Description
0 0 The low level of INT0 generates an interrupt request.
0 1 Any logical change on INT0 generates an interrupt request.
1 0 The falling edge of INT0 generates an interrupt request.
1 1 The rising edge of INT0 generates an interrupt request.

« Last Edit: August 08, 2006, 11:57:24 pm by Benoit » Logged

Cordialement,
Benoît ROUSSEAU

Enschede - The Netherlands
Offline Offline
Jr. Member
**
Karma: 0
Posts: 75
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you for the explanation. I can understand a little bit French, but I have viewed the link in French thru Babelfish. Do I understand it right that you use the assembly code from the Atmel processor ? Is this because of speed issues or is this the only way ( I'm a beginner ) to do this in Aduino ?

Why do you define so much variables ( especially in the "French post" ), it seems to my that you don't use all those variables.

Do you have some good resources (links, books) we're you can learn programming the Atmel processor ? Or is reading the whole datasheet the best option ?

Logged

Poitiers (France)
Offline Offline
Full Member
***
Karma: 0
Posts: 136
Ca va j'vais le faire !
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello Kapser,

  Long life to BabelFish !

  Do I understand it right that you use the assembly code from the Atmel processor ?

   - there no assembly code in this program, just use of some internal function of the ATmega8. The MCUCR, GIFR, ... are just internals registers of the microcontroler and are delaclared in the C library.

   Is this because of speed issues or is this the only way ( I'm a beginner ) to do this in Aduino ?

  - nothing about speed, just the only way.

  Do you have some good resources (links, books) we're you can learn programming the Atmel processor ? Or is reading the whole datasheet the best option ?

   - Oh ! The bible is the whole datasheet ! Don't read it at once. Look at the index table and just read what is supose to interest you. Take a piece of paper near you to take some notes and simulate by hand on the paper.

   - read the .c and .h of the arduino library is another way of understand and take some ideas.


Logged

Cordialement,
Benoît ROUSSEAU

Enschede - The Netherlands
Offline Offline
Jr. Member
**
Karma: 0
Posts: 75
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you for your usefull tips! Sometimes its hard to find a good starting point to learn something. Your example posts and explanation posts are excellent. Very inspirational to see some examples that go deeper into possibility's of Arduino. A blinking led is nice, but sometimes you want more of course. Nice to see that this is actually possible.

Well, I'll have to start reading then smiley

Kasper



Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 36
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've tried your code Benoit on 0009 and it get the error:

Quote
In function 'void initInt0(byte)':
error: 'GICR' was not declared in this scop

Has the way interrupts changed since this code was written?
Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 12
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You should be able to use a simple call to attachInterrupt (see: http://www.arduino.cc/en/Reference/AttachInterrupt), instead of dealing with the registers directly.  In this case, the problem is that the code was written for the ATmega8 and you're probably compiling it for the ATmega168 on which the register names have changed.  This is exactly the sort of reason we created the attachInterrupt() function - so you're not reliant on the low-level details of the particular microcontroller.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 36
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok I still can't get this to work on a Diecimila with 0009.  Am I calling the interrupt correctly?
Quote
volatile int NbTopsFan = 0;
int hallsensor = 3;

void rpm()
{
  ++NbTopsFan;
 }


/***************************************/
void setup()
{
  pinMode(hallsensor, INPUT);
  Serial.begin(9600);
  attachInterrupt(1, rpm, CHANGE);
};

void loop ()
{
    NbTopsFan = 0;
    
    delay (1000);
    NbTopsFan = NbTopsFan / 2;
    Serial.print ("     ");
    Serial.print (NbTopsFan, DEC);
    Serial.print (" rpm");
    Serial.print (13, BYTE);
    
};

Just to add: I have an external 12V powering the PC fan with the Yellow lead (hall effect sensor) going to pin 3.  When I open up the serial monitor I get 60 or 61 rpm, even when the fan is full out going (real rpm is somewhere around 7200) or when it's stopped.

Thanks

**exit: had the wrong pin in my wiring explanation
« Last Edit: August 20, 2007, 01:00:24 pm by boardboy » Logged

0
Offline Offline
Full Member
***
Karma: 1
Posts: 223
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I wrote this a while ago:

http://www.arduino.cc/playground/Main/ReadingRPM

Hope it helps,
-Z-
« Last Edit: August 20, 2007, 04:54:30 pm by zitron » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 36
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks I finally got it working  ;D

I was missing the 10k resistor and I used the following as code:

Quote
volatile byte NbTopsFan;
int hallsensor = 2;

void rpm()
{
  NbTopsFan++;
 }


/***************************************/
void setup()
{
 // pinMode(hallsensor, INPUT);
  Serial.begin(9600);
  attachInterrupt(0, rpm, RISING);
};

void loop ()
{
    NbTopsFan = 0;
    
    delay (1000);
    NbTopsFan = NbTopsFan * 30;
    Serial.print ("     ");
    Serial.print (NbTopsFan, DEC);
    Serial.print (" rpm");
 
    
};

It was a mix of yours and a mix of Benoit's, at least it works, now to get my character LCD to work.  On a side note I couldn't get yours to work, in the terminal I would get random numbers regardless if my fan was running or not.
Logged

Netherlands
Offline Offline
Sr. Member
****
Karma: 0
Posts: 414
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If I am correct, these fans are brushless but using only 2 wires (+5v and ground). Would reading rpm also be possible with a 3 wire brushless motor?
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 36
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

These pc fans have 3 wires, red (12V+), black(GND) and yellow or green (sensor wire).  We're taking advantage of the third wire to read the rotations (2 signals per rotation on the yellow wire).

Take a look at the picture that zitron has on the link he posted, it has a 3 wire fan wired up to the arduino.

I don't know of a way to measure the rpm of a 2 wire fan, I don't think you could do it with just the two wires as they do not give any info on rotation or anything else.
Logged

Pages: [1] 2 3   Go Up
Jump to: