[Gelöst] Leonardo FAN RPM Messung

Hallo,

ich habe ein 4-Pin PWM PC-Lüfter mit Hilfe eines Grove MOSFET (http://www.seeedstudio.com/wiki/Grove_-_MOSFET) zum laufen gebracht. Was mir jetzt noch fehlt ist die Drehzahlmessung über ein Pin Change Interrupt (http://www.geertlangereis.nl/Electronics/Pin_Change_Interrupts/PinChange_en.html). Das Problem dabei ist nur, dass ich keinerlei Ausgabe auf dem seriellen Monitor bekomme, wenn ich folgenden Sketch auf mein Arduino Leonardo hochlade:

#include <TimerOne.h>

#define FAN1_POWER_PIN 8
#define FAN1_CTRL_PIN 9
// Possible Leonardo Pin Change Interrupt Pins:
// PB0 = RXLED = PCINT0, PB1 = SCK = PCINT1, PB2 = MOSI = PCINT2, PB3 = MISO = PCINT3
// PB4 = 8 = PCINT4, PB5 = 9 = PCINT5, PB6 = 10 = PCINT6, PB7 = 13 = PCINT7
#define FAN1_RPM_SIG 13
// see Possible Pin Change Interrupt Pins
#define FAN1_RPM_SIG_NAME PCINT7
// see Possible Pin Change Interrupt Pins PCINT*_vect
#define FAN1_RPM_SIG_INTERRUPT PCINT7_vect

volatile int x;

void setup()
{
  // switch interrupts off while messing with their settings 
  cli(); 
  // Pin Change Interrupt Control Register
  PCICR = (1 << PCIE0);
  //PCICR = 0x01;
  // Pin Change Mask Register
  PCMSK0 = (1 << FAN1_RPM_SIG_NAME);
  // turn interrupts back on
  sei();
  Serial.begin(9600);
  while(!Serial)
  {
    ;
  }
  // initialize timer1, and set a 25kHz frequenze
  Timer1.initialize(40);
  // setup pwm on pin 9, 50% duty cycle
  Timer1.pwm(FAN1_CTRL_PIN, 512);
  pinMode(FAN1_POWER_PIN, OUTPUT);
  digitalWrite(FAN1_POWER_PIN, HIGH);
  Serial.println("ende setup");
  
}
 
void loop()
{
  int i;
  for(i = 0; i < 1000; ++i)
  {
    Timer1.pwm(FAN1_CTRL_PIN, i);
    Serial.print("i = ");
    Serial.println(i);
    delay(10);
  } 
 delay(1000); 
 for(i = 1000; i > 1; --i)
  {
    Timer1.pwm(FAN1_CTRL_PIN, i);
    Serial.print("i = ");
    Serial.println(i);
    delay(10);
  } 
 delay(1000); 
 Serial.print("x ");
 Serial.println(x);
}

// FAN - Pin Change Interrupt
ISR(FAN1_RPM_SIG_INTERRUPT)
{
  if (digitalRead(FAN1_RPM_SIG) == 1) ++x;
}

Es funktioniert alles, bis auf der Teil, der den Interrupt betrifft für die RPM Messung. Bei der TimerOne Library muss man für das Leonardo allerdings eine andere Version als die Offizielle benutzen, da die Pinbelegung für das Leonardo eine andere ist als beim UNO https://code.google.com/p/arduino-timerone/issues/detail?id=13

Pinbelegung am Leonardo (http://arduino.cc/en/Hacking/PinMapping32u4):
Pin 8 - Schaltsignal des MOSFET
Pin 9 - PWM Signal für FAN
Pin 13 - RPM Signal des Lüfters

Gruß surfhai

Wie hast du denn das angeschlossen?
Normalerweise brauchst du keinen FET um einen 4-Pin Lüfter zu steuern. Der hat den schon eingebaut.

Arduino Pin 8      - MOSFET SIG
Arduino Pin 9      - FAN PWM
Arduino Pin 13     - FAN RPM SIG
Arduino 5V         - MOSFET VCC(5V)
Arduino GND        - MOSFET GND
Arduino Vin(12V)   - Netzteil Vout(12V)
Arduino GND        - Netzteil GND(12V)
Netzteil Vout(12V) - MOSFET Vin(12V)
Netzteil GND(12V)  - MOSFET GND(Vin 12V)
FAN Vin(12V)       - MOSFET Vout(12V)
FAN GND(12V)       - MOSFET GND(Vout 12V)

Wie gesagt, bei einem 4-Pin Lüfter brauchst du keinen Transistor. Dafür ist der 4. Pin da:
http://www.elektronik-kompendium.de/sites/com/1503111.htm

Und bei einem 3-Pin Lüfter der mit einem N-Kanal angesteuert wird kannst du glaube ich nicht das Signal messen, weil du dem Lüfter durch den Transistor ständig die Masse wegnimmst. Dafür brauchst du einen P-Kanal.

Aber der MOSFET stört auch nicht, oder seh ich das faslch? Denn wenn der Leonardo nicht laufen würde, würde der Lüfter auf volle Leistung laufen. Mit dem MOSFET dazwischen ist er aus, wenn der Leonardo auch nicht läuft.

Das Problem ist, dass man nicht sehen kann, wie die Mosfet-platine beschaltet ist.
Anscheinend stört der Mosfet doch.

Mach mal das:
Netzteil Vout(12V) => FAN Vin(12V)
Netzteil GND(12V) => FAN GND => Arduino GND
Arduino Pin 9 => FAN PWM
Arduino Pin 13 = > FAN RPM SIG

Das sollte reichen.

Ich werde es Morgen mal ausprobieren, aber ich erwarte mir keine Besserung. Weil das Leonardo sich scheinbar aufhängt, oder die serielle Schnittstelle außer Kraft gesetzt wird.

Das hier ist auch der einzige Teil im Sketch für den MOSFET. Die Pins sind unterschiedliche für POWER und PWM SIG, deswegen seh ich hier auch kein Problem.

  pinMode(FAN1_POWER_PIN, OUTPUT);
  digitalWrite(FAN1_POWER_PIN, HIGH);

Oder es ist ein Problem mit der TimerOne Library, deswegen werde ich auch mal nur den Interrupt Teil mal einzeln Testen.

Zu dem MOSFET, ich hatte den Link zu dem Ding gepostet, dort kann man auch die Eagle-files runterladen.
http://www.seeedstudio.com/wiki/Grove_-_MOSFET

Das KANN so nicht funktionieren.

Und ich rede jetzt nicht vom Code, sondern von der Hardware.

Du hast den Mosfet in die Versorgungsleitung des FANs geschalten.
Im FAN ist aber eine Elektronik, die selbstständig die Regelung der Drehzahl übernimmt, gesteuert von dem PWM Signal .
Was du machst, ist mittels FET die Elektronik im FAN ständig aus und ein zu schalten!
Wie schon gesagt, das kann nicht funktionieren.

Nimm entweder den FET raus, oder nimm einen "dummen" 2-Pin FAN.

Ich glaube du hast den Sketch nicht richtig angeschaut, ich schalte den MOSFET genau einmal an und nicht an und aus um die Spannung zu ändern. Denn wie gesagt, der Lüfter läuft ohne den Interrupt Teil und macht auch die gewünschten Geschwindigkeitsänderungen, wie ich sie im unteren Teil beschreibe.

Setup:

  1. Interrupt auf Pin 13 einschalten (RPM Signal)
  2. serielle Schnittstelle initialisieren und warten bis der serielle Monitor am Rechner geöffnet wird (USB)
  3. PWM Signal initialisieren Pin 9
  4. PWM Duty cycle auf 50% setzen Pin 9
  5. Leonardo Pin 8 als Ausgang definieren (MOSFET)
  6. MOSFET einschalten und FAN mit 12V versorgen Pin 8
  7. serielle Schnittstelle "ende setup" ausgeben

Loop:

  1. PWM Signal Pin 9 von 0 - 97% duty cycle laufen lassen und aktuelle Einstellung auf der seriellen Schnittstelle ausgeben mit 10ms delay
  2. eine sekunde warten
  3. PWM Signal Pin 9 von 97 - 0% duty cycle laufen lassen und aktuelle Einstellung auf der seriellen Schnittstelle ausgeben mit 10ms delay
  4. eine sekunde warten
  5. Ausgabe Variable x die in der Interrupt Routine hochgezählt wird

Interrupt an Pin 13:

  1. Variable x hochzählen, wenn an Pin 13 ein high Pegel gemessen wird.

Ich bin ziemlich sicher, dass du den Interrupt Vektor falsch benannt hast. Bei Pin Change Interrupts teilen sich immer 8 Pins einen Vektor. Das ist ja der Grund weshalb man danach abfragen muss welcher Pin tatsächlich ausgelöst hat wenn man mehr also einen aktiviert hat!

Der Atmega32u4 hat genau einen Pin Change Interrupt Vektor:
PCINT0

Und nicht:

PCINT7_vect

Siehe Datenblatt Seite 61

Das steht doch auch so bei der Beschreibung des PCICR Registers:

The corresponding interrupt of Pin Change Interrupt Request is executed from the PCI0 Interrupt Vector

Das hier kannst du dir sparen wenn du nur einen PC Interrupt hast:

if (digitalRead(FAN1_RPM_SIG) == 1)

Wenn du nur PCINT7 im Masken Register aktiviert hast, weißt du dass dieser ausgelöst hat. Bei mehreren PC Interrupts wäre dies allerdings nötig, da die im Gegensatz zu den normalen externen Interrupts nur einen Vektor haben.

Wieso verwendest du nicht einfach die externen Interrupts? Davon hast du 5 Stück und die kannst du einfach mit attachInterrupt() ansprechen

Das scheint es gewesen zu sein. Mit PCINT0_vect als interrupt vector und

if (digitalRead(FAN1_RPM_SIG) == 0) ++x;

statt

if (digitalRead(FAN1_RPM_SIG) == 1) ++x;

kommt genau das am seriellen Monitor an, was ich erwarte.

Danke