Nano und PWM Steuerung vom Lüfter mit 25 KHz läuft nicht

Hallo ich melde mich wieder mal hier bei euch. ich habe meinen Code soweit fertig nur läuft mein Lüfter nicht mit PWM. Lüfter hängt mit seinem PWM Anschluss Am Arduino. Meine Temperaturregelung läuft nur halt der Lüfter läuft ungeregelt.

Ich schaffe es einfach nicht an dem PWM Pin die 25KHz auszugeben. kann mir bitte jemand helfen beim Code das der PWM Läuft. Habe schon mehrere Lüfter getestet keiner Reagiert auf das PWM- Signal






/**Ver.3 ***/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SHT21.h>

#define SCREEN_WIDTH 128    // OLED display width, in pixels
#define SCREEN_HEIGHT 64    // OLED display height, in pixels
#define OLED_RESET 4

SHT21 sht;                      //SHT on OLED entities
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

float Temp;          //Here we store the temperature and humidity values
//float Humidity;

#include <Fonts/FreeMonoBold12pt7b.h>
//#include "DHT.h"    // Include DHT11 library
//#define DATApin 12  // Defines Arduino pin which is connected to the sensor
//DHT
//#define DHTPIN 12                                            // DHT pin
//#define DHTTYPE DHT22

//dht(DHTPIN, DHTTYPE);            // Creates a DHT object

int fan = 9;       // fan is connect to Arduino Pin D3 (PWM)
int GreenLed = 3;  // Green LED is connect to Arduino Pin
int RedLed = 4;     // Red LED is connect to Arduino Pin D9

//int HumidityMax = 95;
//int HumidityMin = 50;
int tempMin = 26;   // the temperature to start the fan 0%
int tempMax = 30;   // the maximum temperature when fan is at 100%
int fanSpeed = 0;
int fanLCD;
int fanOut = 1;
int fanMin = 0;
unsigned long previousMillis = 0; // speichert den Zeitpunkt an dem zuletzt geschalten wurde
const long intervall = 200; //600000; // Länge der Pause (hier 5 Min.) in Millisekunden 5 * 60 *

const byte OC1A_PIN = 9;
const byte OC1B_PIN = 10;

const word PWM_FREQ_HZ = 25000; //Adjust this value to adjust the frequency (Frequency in HZ!) (Set currently to 25kHZ)
const word TCNT1_TOP = 16000000/(2*PWM_FREQ_HZ);


void setup()
{
  pinMode(OC1A_PIN, OUTPUT);

  // Clear Timer1 control and count registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  // Set Timer1 configuration
  // COM1A(1:0) = 0b10   (Output A clear rising/set falling)
  // COM1B(1:0) = 0b00   (Output B normal operation)
  // WGM(13:10) = 0b1010 (Phase correct PWM)
  // ICNC1      = 0b0    (Input capture noise canceler disabled)
  // ICES1      = 0b0    (Input capture edge select disabled)
  // CS(12:10)  = 0b001  (Input clock select = clock/1)
  
  TCCR1A |= (1 << COM1A1) | (1 << WGM11);
  TCCR1B |= (1 << WGM13) | (1 << CS10);
  ICR1 = TCNT1_TOP;


  //Sets the baud for serial data transmission between Arduino and your computer
  Serial.begin(9600);
  //dht.begin();
  //sht.begin();
  //initialize with the I2C addr 0x3C (128x64)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  delay(10);

  pinMode(fan, OUTPUT);
  pinMode(RedLed, OUTPUT);   // initialize digital pin RedLed as an output.
  pinMode(GreenLed, OUTPUT); // initialize digital pin GreenLed as an output.

  // Print text on display
  display.clearDisplay();
  //display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(25, 0);
  display.println(F("ELECTRODUIN0")); // Print text
  /*
    display.setCursor(10, 20);
    display.println(F("temperature Based"));
    display.setCursor(3, 35);
    display.println(F("fan Speed Controller"));
    display.display();
  */
  delay(2000);
}

void loop() {

  float temp = sht.getTemperature();
  float Humidity = sht.getHumidity();
  //float temp = dht.readTemperature();
  //float Humidity = dht.readHumidity();

  //Print tempareture Value on Serial Monitor Window
  Serial.print("temperature = ");
  Serial.print(temp, 0); // temperature value in Degree Celsius
  Serial.println("°C");

  unsigned long currentMillis = millis();//Jetzige Millisekunden

  if (currentMillis - previousMillis >= intervall) // Falls mehr als 300000 ms vergangen sind
  {
    previousMillis = currentMillis; // Zeitpunkt der letzten Schaltung wird festgehalten

    if (temp < tempMin)    // if temp is lower than tempMin
    {
      fanSpeed = 100;      // fan is not spinning
      analogWrite(fan, fanSpeed);
      fanLCD = 100;
      digitalWrite(fan, 255);      // fan aus
      digitalWrite(GreenLed, HIGH); // turn off Green LED
    }
   
    //if temperature is higher than tempMin and lower than tempMax
    if ((temp >= tempMin) && (temp <= tempMax))
    //if ((Humidity >= HumidityMin) && ( Humidity <= HumidityMax)) 
    {
      fanSpeed = map (temp, tempMin, tempMax, 255,0); // the actual speed of fan//map(temp, tempMin, tempMax, 32, 255);
      //fanSpeed = 1.5 * fanSpeed;
      fanLCD = map (temp, tempMin, tempMax, 100, 0);  // fan speed Show on display
      analogWrite(fan, fanSpeed);   // spin the fan at the fanSpeed speed
      digitalWrite(GreenLed, HIGH);  // turn on Green LED
    } //if temp is higher than tempMax
    if (temp > tempMax)



    
    {
      digitalWrite(RedLed, HIGH);  // turn on Red LED
      digitalWrite(GreenLed, LOW); // turn off Green LED
    }
    else
    {
      digitalWrite(RedLed, LOW); // turn off Red LED
    }  
  
// Wenn der PWM Wert unter den van FanMin fällt, schält der Lüfter ab
  if (fanSpeed < fanMin)
  {
    fanSpeed = 0;
    fanOut = 1;
  }
  
  // Hysterese
  if (fanOut == 1)
  {
    fanSpeed = 0;
  }
  
  if(temp >= 31)
  {
    if(fanOut == 1)
    {
      fanOut = 0;
      analogWrite(fan, 255);
    }
  }
  
  // PWM Wert auf 255 begerenzen  
  if (fanSpeed > 255)
  { 
    fanSpeed = 255;
  }
  
  // Lüftergeschwindigkeit über den Seriellen Monitor ausgeben
  //Serial.print("Lueftergeschwindigkeit: ");          
  //Serial.println(fanSpeed);

  analogWrite(fan, fanSpeed);      // Den Lüfter mit dem PWM Wert ansteuern
  delay(500);  
} 
  
  // Print text on display
  display.clearDisplay();
  //  display.setTextSize(1);
  display.setTextColor(WHITE, BLACK);
  display.setCursor(0, 0);
  display.print("H: "); // Print text
  display.print(Humidity);
  display.print(" %");
  display.setTextSize(2);
  display.setCursor(0, 20);
  // Print temperature
  display.print("Temp:");
  display.setCursor(60, 20);
  display.print(temp, 1); // temperature Value
  display.setCursor(95, 20);
  //display.print((char)247);
  display.setCursor(110, 20);
  display.print("C");
  // Print fan Speen
  display.setCursor(0, 40);
  display.print("FAN:: ");
  display.setCursor(60, 40);
  display.print(fanLCD); // fan Speed Value
  display.setCursor(110, 40);
  display.print("%");
  display.display();
  //delay(2000); // wait two seconds
}

Moin.
Vor einiger Zeit hat man hier im Forum geholfen zum Thema Timer und Frequenz einstellen.

/* https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
 * Timer 1 Erklärung Seite 89 bis 113
 * 
 * Gute Tutorial-Folge, wenn auch anderer Chip (Beim ATMega328p sind die Register etwas anders, aber intuitiv zu finden): 
 * https://maxembedded.wordpress.com/2011/06/22/introduction-to-avr-timers/
 * https://maxembedded.wordpress.com/2011/06/24/avr-timers-timer0-2/
 * https://maxembedded.wordpress.com/2011/06/28/avr-timers-timer1/
 * https://maxembedded.wordpress.com/2011/06/29/avr-timers-timer2/
 * https://maxembedded.wordpress.com/2011/07/14/avr-timers-ctc-mode/
 * https://maxembedded.wordpress.com/2011/08/07/avr-timers-pwm-mode-part-i/
 * https://maxembedded.wordpress.com/2012/01/07/avr-timers-pwm-mode-part-ii/
 * 
 * Andere gute Tutorials die mir geholfen haben
 * https://arduino-projekte.webnode.at/registerprogrammierung/fast-pwm/ (Deutsch)
 * https://wolles-elektronikkiste.de/timer-und-pwm-teil-1 
 * https://wolles-elektronikkiste.de/timer-und-pwm-teil-2-16-bit-timer1
 * https://embedds.com/programming-16-bit-timer-on-atmega328/
 * 
 * Forum-Link
 * https://forum.arduino.cc/t/atmega328p-analogwrite-pwm-frequenz/1042224/8
 * 
 * Arduino Pin | Timer | OCnx Pin | Arduino Standard Frequenz | Arduino Stadard Mode
 *      5      |   0   |   OC0B   |          980Hz            |   Mode 3, Fast PWM, 8-bit
 *      6      |   0   |   OC0A   |          980Hz            |   Mode 3, Fast PWM, 8-bit
 *      9      |   1   |   OC1A   |          460Hz            |   Mode 1, PWM, phase correct, 8-bit
 *      10     |   1   |   OC1B   |          460Hz            |   Mode 1, PWM, phase correct, 8-bit
 *      11     |   2   |   OC2A   |          460Hz            |   Mode 1, PWM, phase correct, 8-bit
 *      3      |   2   |   OC2B   |          460Hz            |   Mode 1, PWM, phase correct, 8-bit
 */


struct Pin{
  uint8_t pwmPin;
};

const byte normalPWMpin {5};                        // Pin zum testen für normales analogWrite
const Pin  modifizierterPWMpin {9};                 // Pin zum Überladen der analogWrite Funktion

void analogWrite(const Pin &meinPin, uint8_t wert){
  if(meinPin.pwmPin == 9) OCR1A = map(wert,0,255,0,ICR1);
}

void timerEinstellen(){
  TCNT1=0;                                          // Counter zurücksetzen
  TCCR1A = 0; TCCR1B = 0;                           // Setzt TCCR1 Register auf 0 
  ICR1  = 10000;                                     // Setzt Obergrenze des Zählers, wo dann wieder runter gezählt wird
  TCCR1B |= (1 << WGM13); TCCR1A |= (1 << WGM11);   // Aktiviert Mode 10, PWM phase correct, ICR1 at TOP //Seite 109 Tabelle 15-5
  TCCR1A |= (1 << COM1A1);                          // Modus Setzt OC1A bei Übereinstimmung beim Hochzählen auf LOW. Setzt OC1A bei Übereinstimmung beim Runterzählen auf HIGH. //Seite 109 Tabelle 15-4
  TCCR1B |= (1 << CS11);                            // Vorteiler 8 //Seite 110 Tabelle 15-6
  // ICR1 = 16.000.000/8/2/100 = 10000
  // TOP = ProzessorHz/Vorteiler/hoch-runterzählen/100Hz
}

void setup() {
  pinMode(normalPWMpin, OUTPUT);
  pinMode(modifizierterPWMpin.pwmPin, OUTPUT);
  timerEinstellen();
}

void loop() {
  analogWrite(normalPWMpin,122);                    // 50% at 980Hz
  analogWrite(modifizierterPWMpin,122);             // 50% at 100Hz
  delay(5000);
  analogWrite(normalPWMpin,255);                    // 100% at 980Hz
  analogWrite(modifizierterPWMpin,255);             // 100% at 100Hz
  delay(5000);
  analogWrite(normalPWMpin,63);                     // 25% at 980Hz
  analogWrite(modifizierterPWMpin,63);              // 25% at 100Hz
  delay(5000);
  analogWrite(normalPWMpin,191);                    // 75% at 980Hz
  analogWrite(modifizierterPWMpin,191);             // 75% at 100Hz
  delay(5000);
}

Mir ging es damals um 100Hz. Mit einem kleinen billigen Digital-Oszilloskop konnte ich dies prüfen, und es funktioniert einwandfrei.

Um auf deine 25kHz zu kommen, bräuchte man nur den Vorteiler auf 0 1 setzen und ICR1 auf 319 320 stellen. Daraus ergibt sich folgender Testcode für deinen Lüfter.

struct Pin{
  uint8_t pwmPin;
};

const byte normalPWMpin {5};                        // Pin zum testen für normales analogWrite
const Pin  modifizierterPWMpin {9};                 // Pin zum Überladen der analogWrite Funktion

void analogWrite(const Pin &meinPin, uint8_t wert){
  if(meinPin.pwmPin == 9) OCR1A = map(wert,0,255,0,ICR1);
}

void timerEinstellen(){
  TCNT1=0;                                          // Counter zurücksetzen
  TCCR1A = 0; TCCR1B = 0;                           // Setzt TCCR1 Register auf 0 
  ICR1  = 320;                                     // Setzt Obergrenze des Zählers, wo dann wieder runter gezählt wird
  TCCR1B |= (1 << WGM13); TCCR1A |= (1 << WGM11);   // Aktiviert Mode 10, PWM phase correct, ICR1 at TOP //Seite 109 Tabelle 15-5
  TCCR1A |= (1 << COM1A1);                          // Modus Setzt OC1A bei Übereinstimmung beim Hochzählen auf LOW. Setzt OC1A bei Übereinstimmung beim Runterzählen auf HIGH. //Seite 109 Tabelle 15-4
  TCCR1B |= (1 << CS10);                            // Vorteiler 1 //Seite 110 Tabelle 15-6
  // ICR1 = 16.000.000/1/2/25000 = 320
  // TOP = ProzessorHz/Vorteiler/hoch-runterzählen/25000Hz
}

void setup() {
  pinMode(normalPWMpin, OUTPUT);
  pinMode(modifizierterPWMpin.pwmPin, OUTPUT);
  timerEinstellen();
}

void loop() {
  analogWrite(normalPWMpin,122);                    // 50% at 980Hz
  analogWrite(modifizierterPWMpin,122);             // 50% at 25000Hz
  delay(5000);
  analogWrite(normalPWMpin,255);                    // 100% at 980Hz
  analogWrite(modifizierterPWMpin,255);             // 100% at 25000Hz
  delay(5000);
  analogWrite(normalPWMpin,63);                     // 25% at 980Hz
  analogWrite(modifizierterPWMpin,63);              // 25% at 25000Hz
  delay(5000);
  analogWrite(normalPWMpin,191);                    // 75% at 980Hz
  analogWrite(modifizierterPWMpin,191);             // 75% at 25000Hz
  delay(5000);
}

Wenn dies mit deinem Lüfter läuft, dann liegt es an deinem Code. Wenn dies nicht mit deinem Lüfter läuft, braucht dein Lüfter ein anderes Steuersignal, oder ist falsch angeschlossen.

Ok werde ich heute nachmittag mal testen. Tue mich bei einigen dingen noch schwer, was das programmieren angeht.

Hallo,

hier sehe ich mich gezwungen zu korrigieren. Ich weiß gar nicht wie oft im Forum schon geschrieben wurde das die Timer bei Arduino für analogWrite, millis usw. vorkonfiguriert sind. Deswegen sollte, eigentlich muss, man sie zuerst stoppen, dann konfigurieren und danach erst starten. Niemand steigt in ein fahrendes Auto ein und aus. Das heißt zuerst Prescaler nullen und erst ganz zum Schluss setzen zum starten des Timers. Sonst läuft er mitten in der Konfiguration los. Das mag vielleicht erstmal nicht auffallen, wie bei vielen kleinen Lapsus, aber irgendwann ...

Mit Vorteiler 0 steht der Timer. Division 0 in der Formel funktioniert auch nicht. Du meinst Vorteiler 1. TOP Wert 319 ist nah dran, laut Formel müssen es jedoch 320 sein. -1 gibt es in der Formel für den Timermode nicht.

In deinem Bsp. dann bitte

void timerEinstellen() {
  TCCR1B = 0;                           // Setzt TCCR1 Register auf 0 
  TCCR1A = 0;
  TCNT1=0;      
  OC1A=0;
  ICR1  = 320;                                      // TOP,  16.000.000/1/2/25000=320
  TCCR1B |= (1 << WGM13); TCCR1A |= (1 << WGM11);   // Aktiviert Mode 10, PWM phase correct, ICR1 at TOP //Seite 109 Tabelle 15-5
  TCCR1A |= (1 << COM1A1);                          // Modus Setzt OC1A bei Übereinstimmung beim Hochzählen auf LOW. Setzt OC1A bei Übereinstimmung beim Runterzählen auf HIGH. //Seite 109 Tabelle 15-4
  TCCR1B |= (1 << CS10);                            // Vorteiler 1 //Seite 110 Tabelle 15-6
}

Eine andere Art der Konfiguration mit fertigen Funktionen. Noch etwas steif für Timer 1 zugeschnitten, als Grundlage jedoch ausreichend. Die Pulsweite wird hier mit changeT1Duty() eingestellt unter Ausnutzung der vollen Auflösung. Jetzt könnte man ein Poti nehmen, den eingelesen Wert zwischen 0% und 100% mappen und changeT1Duty() übergeben.

Sketch
/*
  IDE 1.8.19
  Arduino Mega2560 & UNO (wobei UNO nicht getestet)
  https://forum.arduino.cc/t/nano-und-pwm-steuerung-vom-lufter-mit-25-khz-lauft-nicht/1178142/4
  Arduino Default:
  TCCR1A 1010.0001  // COM1A1, COM1B1, WGM10 (Clear on Compare, 8Bit PWM Phase Correct))
  TCCR1B 11         // CS11, CS10 (Prescaler 64)
  TCCR1C 0
  ICR1   0
  OCR1A  0
  OCR1B  0
*/

#include <Streaming.h>  // https://github.com/janelia-arduino/Streaming
Stream &out {Serial};

const byte pinFan {9};

void setup(void)
{
  Serial.begin(9600);
  Serial.println(F("\nuC Reset ####"));
  resetTimer1();
  initPwmPin(pinFan);     
  initTimer1To25kHz();
  changeT1Duty(pinFan, 33); // Pin, Duty [%]
}

void loop(void)
{

}

void initPwmPin (const uint8_t pin)
{  
  switch(pin)
  {
    #if defined(__AVR_ATmega328P__)
      case  9: TCCR1A |= _BV(COM1A1); pinMode(pin, OUTPUT); break;  // UNO Pin  9
      case 10: TCCR1A |= _BV(COM1B1); pinMode(pin, OUTPUT); break;  // UNO Pin 10
    #endif
    #if defined(__AVR_ATmega2560__) 
      case 11: TCCR1A |= _BV(COM1A1); pinMode(pin, OUTPUT); break;  // Mega2560 Pin 11
      case 12: TCCR1A |= _BV(COM1B1); pinMode(pin, OUTPUT); break;  // Mega2560 Pin 12
    #endif 
    default: break;
  }
}

void changeT1Duty (const uint8_t pin, uint16_t duty)
{
  //constexpr uint16_t TOP {320}; // macht mit Prescaler 1 -> 25kHz
  if (duty > 100) duty = 100;
  // duty = TOP*duty/100;
  duty = ICR1*duty/100;
  
  switch(pin)
  { 
    #if defined(__AVR_ATmega328P__)
      case  9: OCR1A = duty; break;   // UNO Pin  9
      case 10: OCR1B = duty; break;   // UNO Pin 10
    #endif
    #if defined(__AVR_ATmega2560__) 
      case 11: OCR1A = duty; break;   // Mega2560 Pin 11
      case 12: OCR1B = duty; break;   // Mega2560 Pin 12
    #endif 
    default: break;
  }
}

void initTimer1To25kHz (void)
{
  changeTimer1Mode(10);
  ICR1 = 320;     // macht mit Prescaler 1 -> 25kHz
  startTimer1(1); // Prescaler 1
}

void stopTimer1 (void)
{
  // delete Clock Select Bits, Timer is stopped
  TCCR1B &= ~( _BV(CS12) | _BV(CS11) | _BV(CS10) );
}

void startTimer1 (const unsigned int prescaler)
{
  // set new Prescaler Clock Select Bits
  switch (prescaler) {
    case    1 : TCCR1B |= _BV(CS10);              break;
    case    8 : TCCR1B |= _BV(CS11);              break;
    case   64 : TCCR1B |= _BV(CS11) | _BV(CS10);  break;
    case  256 : TCCR1B |= _BV(CS12);              break;
    case 1024 : TCCR1B |= _BV(CS12) | _BV(CS10);  break;
    default :   break;  // otherwise timer remains stopped
  }
}

void changeTimer1Mode (const uint8_t mode)
{
  if ((mode <=15) && (mode != 13)) {
    stopTimer1();
    deleteTimer1Mode();
    switch (mode) {
      case  0 :                                                                     ; break;  // Normal
      case  1 :                                    TCCR1A |=              _BV(WGM10); break;  // PWM, Phase Correct  8-Bit 
      case  2 :                                    TCCR1A |= _BV(WGM11)             ; break;  // PWM, Phase Correct  9-Bit 
      case  3 :                                    TCCR1A |= _BV(WGM11) | _BV(WGM10); break;  // PWM, Phase Correct 10-Bit 
      case  4 : TCCR1B |=              _BV(WGM12);                                  ; break;  // CTC, OCRnA
      case  5 : TCCR1B |=              _BV(WGM12); TCCR1A |=              _BV(WGM10); break;  // Fast PWM  8 Bit
      case  6 : TCCR1B |=              _BV(WGM12); TCCR1A |= _BV(WGM11)             ; break;  // Fast PWM  9 Bit
      case  7 : TCCR1B |=              _BV(WGM12); TCCR1A |= _BV(WGM11) | _BV(WGM10); break;  // Fast PWM 10 Bit
      case  8 : TCCR1B |= _BV(WGM13)             ;                                  ; break;  // PWM, Phase & Frequency Correct, ICRn
      case  9 : TCCR1B |= _BV(WGM13)             ; TCCR1A |=              _BV(WGM10); break;  // PWM, Phase & Frequency Correct, OCRnA
      case 10 : TCCR1B |= _BV(WGM13)             ; TCCR1A |= _BV(WGM11)             ; break;  // PWM, Phase Correct, ICRn
      case 11 : TCCR1B |= _BV(WGM13)             ; TCCR1A |= _BV(WGM11) | _BV(WGM10); break;  // PWM, Phase Correct, OCRnA
      case 12 : TCCR1B |= _BV(WGM13) | _BV(WGM12);                                  ; break;  // CTC, ICRn
      case 13 : break;                                                                        // Reserved
      case 14 : TCCR1B |= _BV(WGM13) | _BV(WGM12); TCCR1A |= _BV(WGM11)             ; break;  // Fast PWM, ICRn
      case 15 : TCCR1B |= _BV(WGM13) | _BV(WGM12); TCCR1A |= _BV(WGM11) | _BV(WGM10); break;  // Fast PWM, OCRnA                                                        
      default : break; // otherwise no change of the configuration
    }
  }  
}

void deleteTimer1Mode (void)
{
  // delete all WGM Bits
  TCCR1A &= ~( _BV(WGM11) | _BV(WGM10) );
  TCCR1B &= ~( _BV(WGM13) | _BV(WGM12) );
}

void resetTimer1 (void)
{
  // delete all
  TCCR1B = 0;
  TCCR1A = 0;
  TCCR1C = 0;
  TIMSK1 = 0;
  ICR1  = 0;
  OCR1A = 0;
  OCR1B = 0;
}

void readT1Register (Stream &out)
{
  out << "TCCR1A " << _BIN(TCCR1A) << endl;
  out << "TCCR1B " << _BIN(TCCR1B) << endl;
  out << "ICR1   " << ICR1  << endl;
  out << "OCR1A  " << OCR1A << endl;
  out << "OCR1B  " << OCR1B << endl;
  out.println();
}
1 Like

Ja, da muss eine 1 hin. Hab ich bei der schnelle des Kommentar ändern geschusselt. Ich setze ihn ja auf 1 mit.TCCR1B |= (1 << CS10);

Da der Timer bei 0 losläuft sind mit 319 doch 320 erreicht. Zumindest war in einem der verlinkten Tutorials es so geschildert. Vielleicht hat jemand mal Lust und Zeit das zu prüfen mit einem Osziloskop. Den Lüfter wird diese kleine Differenz wohl nicht stören.

Wenn OC1A LOW ist und beim erreichen von ICR1 OC1A wieder LOW gesetzt wird, dann fehlt dir der erste Takt. Eigentlich wäre dann folgendes richtig.

  OC1A=1;
  ICR1  = 320;

Hallo,

Die Formel im Manual berechnet den Wert für das Register. Damit ist alles berücksichtigt. Nicht anfangen irgendwas reinzuinterpretieren was nicht da steht. Die üblichen Internet Tutorials sind zwar für den Anfang ganz gut, schreiben aber alle voneinander ab. Jeder Fehler wird übernommen, weil das meistens auch Anfänger sind. Später mit mehr Wissen wird das leider nicht korrigiert. Allein was zählt ist das Manual. Vollkommen egal was irgendwelche Internet Tutorials schreiben. Zudem muss man immer den Timer Mode beachten. Die Formeln unterscheiden sind in Details.

Sehe ich nicht als Anfänger, aber du wirst schon recht haben.
Die Formel ist dort nicht zu finden.

Hallo,

Wolle ist zwar Quelle vieler Ideen usw. und ich möchte ihm auch nicht zu Nahe treten, aber wie man sieht stimmt da auch nicht alles. Er beschreibt Normal, CTC und Fast PWM Mode. Bei Phase Correct fehlt die Formel. Wo hast du die Formel dafür her? Von Fast PWM passt hier nicht. Auch wie er die Timer setzt ist wie geschrieben nicht "optimal". Vielleicht weiß er es heute besser aber korrigiert seine Webseite nicht. Wie das eben leider meistens so ist. Du brauchst nur das Manual aufschlagen und dir die Formeln in den Beschreibungen der Timer Modi anschauen. Ich kann dir nur meine Erkenntnisse nach besten Wissen und Gewissen mitteilen.

Ich habe es mal mit dem DSO138mini Oszilloskop "nachgemessen". Und du scheinst recht zu haben. (Wie auch vermutet.)

Wenn ich mich nicht täusche, und man die Frequenz berechnen will, sollte man die Formel von
TOP = CPU_Speed / Vorteiler / 2 / Freq
zu
Freq = CPU_Speed / Vorteiler / 2 / TOP
umstellen können.

Beziehnungsweise:
TOP = ( CPU_Speed / Vorteiler / 2 / Freq ) - 1
zu
Freq = CPU_Speed / Vorteiler / 2 / ( TOP + 1 )

Bei Vorteiler 1024 und Top 16 sollten diese beiden Frequenzen möglich sein.

16.000.000 / 1024 / 2 / 16 = 488,28125 Hz
bzw.
16.000.000 / 1024 / 2 / (16 + 1) = 459,5588235 Hz

Das wäre ein Durchlauf aller 2,048 Milliskunden bzw. 2,176 Millisekunden.

Das DSO138mini Oszilloskop zeigt eher 2,048 Millisekunden. (Frequenz spuckt es als Info leider nicht aus, da muss man selber Kästchen zählen)

Danke für das Aufklären

Da ist einiges Durcheinander. An welchem Pin sitzt nun der Fan?

Welche NANO Version hast Du? einen mit einem ATmega328P ?

Grüße Uwe

Wenn ich das wüsste. Ich habe jetzt alle im Sketch verlinkten Seiten durchsucht.
Das eine Tutorial war nicht mehr auf der verlinkten Seiten zu finden. Hat ne Weile gedauert es wiederzufinden:

Aber nein, ich finde es nicht. Da du ja Recht hast, schiebe ich den Fehler zu mir. Weiß der Geier was mich damals dazu brachte dort ein -1 zu nutzen. Jetzt ist es ja richtig gestellt.

Danke

Hi also an Pin 9 Sitzt der PWM Anschluss vom Lüfter, Das was hier mit grüner LED Beschrieben wird ist ein NPN Mosfet der mir den Lüfter bei der Temperatur anschaltet also GND durchschaltet. habe den Sketch mal getestet er regelt das PWM nicht nach der Temperatur.

Also der Test Code von Plumps läuft mit meinem Arduino. Ich habe einen Nano.

Hallo,

du musst nicht so weit suchen. Wie gesagt, lese das ATmega328P Manual und es gibt keine Zweifel. Gehe auf Seite 134, 135 zum Kapitel Phase Correct PWM.

Natürlich. Nur Datenblatt lesen ist das eine. Verstehen was dort steht ist das andere. Zum besseren Verständnis helfen dann Tutorials. Leider wird oft etwas falsch interpretiert. Zum Glück gibt es euch, die dann Fehlerhaftes wieder gerade rücken!

Versuch mal folgenden Code. Ich habe versucht nicht viel zu ändern. Ich musste zum Kompilieren, alles was mit Display zu tun hat, bei mir auskommentieren.

/**Ver.3 ***/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SHT21.h>

#define SCREEN_WIDTH 128    // OLED display width, in pixels
#define SCREEN_HEIGHT 64    // OLED display height, in pixels
#define OLED_RESET 4

SHT21 sht;                      //SHT on OLED entities
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

float Temp;          //Here we store the temperature and humidity values
//float Humidity;

#include <Fonts/FreeMonoBold12pt7b.h>
//#include "DHT.h"    // Include DHT11 library
//#define DATApin 12  // Defines Arduino pin which is connected to the sensor
//DHT
//#define DHTPIN 12                                            // DHT pin
//#define DHTTYPE DHT22

//dht(DHTPIN, DHTTYPE);            // Creates a DHT object

struct Pin {
  uint8_t pwmPin;
};
const Pin fan {9};       // fan is connect to Arduino Pin (PWM)

int GreenLed = 3;  // Green LED is connect to Arduino Pin
int RedLed = 4;     // Red LED is connect to Arduino Pin

//int HumidityMax = 95;
//int HumidityMin = 50;
float tempMin = 26.0;   // the temperature to start the fan 0%
float tempMax = 30.0;   // the maximum temperature when fan is at 100%
byte fanSpeed = 0;
unsigned long previousMillis = 0; // speichert den Zeitpunkt an dem zuletzt geschalten wurde
const long intervall = 200; //600000; // Länge der Pause (hier 5 Min.) in Millisekunden 5 * 60 *

void analogWrite(const Pin &meinPin, uint8_t prozent) {
  if (meinPin.pwmPin == 9) OCR1A = map(prozent, 0, 100, 0, ICR1);
}

void setup()
{
  pinMode(fan.pwmPin, OUTPUT);
  TCNT1 = 0;                                        // Counter zurücksetzen
  TCCR1A = 0; TCCR1B = 0;                           // Setzt TCCR1 Register auf 0
  ICR1  = 320;                                      // Setzt Obergrenze des Zählers, wo dann wieder runter gezählt wird
  TCCR1B |= (1 << WGM13); TCCR1A |= (1 << WGM11);   // Aktiviert Mode 10, PWM phase correct, ICR1 at TOP //Seite 109 Tabelle 15-5
  TCCR1A |= (1 << COM1A1);                          // Modus Setzt OC1A bei Übereinstimmung beim Hochzählen auf LOW. Setzt OC1A bei Übereinstimmung beim Runterzählen auf HIGH. //Seite 109 Tabelle 15-4
  TCCR1B |= (1 << CS10);                            // Vorteiler 1 //Seite 110 Tabelle 15-6
  // ICR1 = 16.000.000/1/2/25000 = 320
  // TOP = ProzessorHz/Vorteiler/hoch-runterzählen/25000Hz

  //Sets the baud for serial data transmission between Arduino and your computer
  Serial.begin(9600);
  //dht.begin();
  sht.begin();
  //initialize with the I2C addr 0x3C (128x64)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  delay(10);

  pinMode(RedLed, OUTPUT);   // initialize digital pin RedLed as an output.
  pinMode(GreenLed, OUTPUT); // initialize digital pin GreenLed as an output.

  // Print text on display
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(25, 0);
  display.println(F("ELECTRODUIN0")); // Print text
  /*
    display.setCursor(10, 20);
    display.println(F("temperature Based"));
    display.setCursor(3, 35);
    display.println(F("fan Speed Controller"));
    display.display();
  */
  delay(2000);
}

void loop() {

  float temp = sht.getTemperature();
  float Humidity = sht.getHumidity();
  //float temp = dht.readTemperature();
  //float Humidity = dht.readHumidity();

  //Print tempareture Value on Serial Monitor Window
  Serial.print("temperature = ");
  Serial.print(temp, 0); // temperature value in Degree Celsius
  Serial.println("°C");

  unsigned long currentMillis = millis();//Jetzige Millisekunden

  if (currentMillis - previousMillis >= intervall) // Falls mehr als 300000 ms vergangen sind
  {
    previousMillis = currentMillis; // Zeitpunkt der letzten Schaltung wird festgehalten

    //if temp is lower than tempMin
    if (temp < tempMin)    // if temp is lower than tempMin
    {
      fanSpeed = 0;        // fan is not spinning
      digitalWrite(GreenLed, HIGH); // turn off Green LED
      digitalWrite(RedLed, LOW);    // turn on Red LED
    }

    //if temperature is higher than tempMin and lower than tempMax
    if ((temp >= tempMin) && (temp <= tempMax))
    {
      fanSpeed = map (temp, tempMin, tempMax, 0, 100); // the actual speed of fan//map(temp, tempMin, tempMax, 32, 255);
      //fanSpeed = 1.5 * fanSpeed;
      //fanLCD = map (temp, tempMin, tempMax, 0, 100);  // fan speed Show on display
      digitalWrite(GreenLed, HIGH);  // turn on Green LED
      digitalWrite(RedLed, LOW);     // turn on Red LED
    } 
    
    //if temp is higher than tempMax
    if (temp > tempMax)
    {
      fanSpeed = 100;
      digitalWrite(RedLed, HIGH);  // turn on Red LED
      digitalWrite(GreenLed, LOW); // turn off Green LED
    }
    if (fanSpeed > 100)
    {
      fanSpeed = 100;
    }

    // Lüftergeschwindigkeit über den Seriellen Monitor ausgeben
    //Serial.print("Lueftergeschwindigkeit: ");
    //Serial.println(fanSpeed);

    analogWrite(fan, fanSpeed);      // Den Lüfter mit dem PWM Wert ansteuern
    delay(500);
  }

  // Print text on display
  display.clearDisplay();
  //  display.setTextSize(1);
  display.setTextColor(WHITE, BLACK);
  display.setCursor(0, 0);
  display.print("H: "); // Print text
  display.print(Humidity);
  display.print(" %");
  display.setTextSize(2);
  display.setCursor(0, 20);
  // Print temperature
  display.print("Temp:");
  display.setCursor(60, 20);
  display.print(temp, 1); // temperature Value
  display.setCursor(95, 20);
  //display.print((char)247);
  display.setCursor(110, 20);
  display.print("C");
  // Print fan Speen
  display.setCursor(0, 40);
  display.print("FAN:: ");
  display.setCursor(60, 40);
  display.print(fanSpeed); // fan Speed Value
  display.setCursor(110, 40);
  display.print("%");
  display.display();
  //delay(2000); // wait two seconds
}

Also PWM Läuft super schaue nach dem Kaffee mal was ich falsch gemacht habe. bin gerade dabei gewesen.

Hallo,

  TCNT1 = 0;                                 
  TCCR1A = 0; TCCR1B = 0;                          
  ICR1  = 320;                                    
  TCCR1B |= (1 << WGM13); TCCR1A |= (1 << WGM11);   
  TCCR1A |= (1 << COM1A1);   
  TCCR1B |= (1 << CS10);

Ich nochmal. Ich glaube du bist dir der Tragweite noch nicht bewusst. Auf die Gefahr hin das es sehr sehr sehr pingelig erscheinen mag, muss ich nochmal darauf eingehen. Erst Timer stoppen! Weil die Prescaler Bits im Register TCCR1B sind demzufolge erst dieses Register reseten. Vorher hat ein Nullen des Counters auch keinen Sinn. Wenn zuerst TCCR1A resetet wird, sind nur Timer Mode Bits und Compare Enable Bits gelöscht, der Timer läuft aber weiter.
Dann hat man 2 Probleme. Der Timer läuft wenn auch nur kurz in einem für den User völlig undefinierten Modus. Der Timer kann in dem kurzen Moment immer noch einen Interrupt auslösen wenn der vorher enabled war. Ich gehe immer von Worst Case aus. Was passiert wenn ich mitten im Programmablauf den Timer umkonfiguriere ...

Das hier wäre immer sicher.

  TCCR1B = 0; 
// Ab hier bis zum setzen der Prescaler Bits kann man konfigurieren wie man will.                              
  TCCR1A = 0;  
  TIMSK1 = 0;
  TCNT1 = 0;     
  OCR1A = 0;
  OCR1B = 0;                      
  ICR1  = 320;     
  TCCR1A |= _BV(WGM11);                                 
  TCCR1B |= _BV(WGM13);  
  TCCR1A |= _BV(COM1A1);   
// An dem Punkt muss die Konfiguration komplett sein.
  TCCR1B |= _BV(CS10);

Klar kann man die Neukonfiguration auch auf die Arduino Vorkonfiguration speziell zuschneiden und muss nicht alles reseten, aber das wissen sowieso die Wenigsten. Von daher erstmal Nummer sicher und bei Bedarf abspecken wenn man weiß warum und weshalb.

@Doc_Arduino
Jetzt hab ich es geschnallt. CS10, CS11, CS12 auf 0 stoppt den Timer! Deswegen TCCR1B = 0; zuerst.

Den Timer jedes mal stoppen wenn man TOP oder Compare ändern will? Das würde ja bei jedem ändern, und sogar setzen, des DutyCycles oder der Freqenz ein Stoppen des Timers benötigen. Und das bei jeden Aufruf einer Funktion die da etwas ändert, wie im Beispiel die überladene analogWrite Funktion. Ist das dann der richtige Weg?

Hallo,

nein, man muss nicht jedesmal den Timer stoppen. Es drehte sich die ganze Zeit um die Neukonfiguration des Timers, sprich Änderung des kompletten Betriebsmodi. Du willst doch nicht jedesmal den Timer komplett neu konfigurieren, macht ja keinen Sinn.
Den "Compare" kann man jederzeit ändern. ICR1 kann man wiederum nicht einfach so ändern, weil der ist nicht gepuffert. Den Wert von ICR1 vergrößern ggf. ja. Verkleinern hat Risiken wenn Compare nicht dazu passt oder der Counter schon größer dem neuen kleineren TOP ist. Dann läuft der Timer einmal komplett durch, was zu kurzen Fehlverhalten führt. Aber Compare darf sowieso nicht größer TOP sein. Man muss sich einmal in Ruhe mit der Funktionsweise des Timers beschäftigen.

1 Like