PWM Ausgang PWM Frequenz auf 15kHz // TFT Touchscreen

Moin Leute,

nach einigen Jahren "Arduinopause" fange ich nun wieder zu Programmieren an und möchte die Komponenten von einem Produkt durch einen Arduino steuern. Das Produkt wird bereits von einem extern erworbenen (und teuren) Microkontroller gesteuert, welcher von einer deutschen Elektronikfirma selbst entwickelt wurde. Leider sind nicht alle Funktionen , die wir für die nächste Generation brauchen, enthalten.

Nun wollen wir In-House uns diese Ressource selbst aufbauen und sind auch schon ziemlich fortgeschritten. Der Inhalt des funktionalem Codes ist fertig, jetzt fehlt "nur noch" die Ausgabe des PWM Signals um einen ebmPapst Lüfter mit einer Frequenz von 10-20kHz über PWM ansteuern.

Das Produkt soll einen TFT Touchdisplay bekommen und über eine App steuerbar sein. Deswegen habe ich mich vorerst für den Arduino Nano 33 IoT entschieden. Ob die Pins ausreichen werde ich noch sehen - TFT wurde noch nicht ausgewählt. Habt ihr eine Empfehlung für einen ca. 3 Zoll großen TFT Touchscreen? Besonders wichtig ist die Zuverlässigkeit und Lebensdauer. Auf was muss ich bei dem Touchscreen achten?

Bis vor kurzem habe ich mit dem Arduino Uno gearbeitet, da ich diesen und ein passendes TFT Display Shield noch rumliegen hatte. Dort konnte ich die Core Timings problemlos einstellen, sodass sich auf eine Frequenz von ca. 16kHz kam und der Lüfter Drehzahlgesteuert werden konnte. Ich kriege leider das Timing vom Arduino Nano 33 IoT Core nicht eingestellt, sodass die richtige Frequenz bei rauskommt. Das ist der beste (+ angepasste) Codeschnipsel, den ich zur Verfügung habe:

//************************************** Clock Setting ********************************************************//

// Feed GCLK0 at 48MHz to TCC0 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK0 as a clock source (1 << 14)
GCLK_CLKCTRL_GEN_GCLK0 | // Select GCLK0 at 48MHz (0 << 8)
GCLK_CLKCTRL_ID_TCC0_TCC1; // Route GCLK0 to TCC0 and TCC1 (0x1A << 0)

//************************************** Clock Setting ********************************************************//

//************************************** PORT Enabling ********************************************************//

// Enable the port multiplexer for pins D2
PORT->Group[g_APinDescription[2].ulPort].PINCFG[g_APinDescription[2].ulPin].bit.PMUXEN = 1;

// D2 is on EVEN port pin PB10 and TCC0/WO[4] channel 0 is on peripheral F
PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;

//************************************** PORT Enabling ********************************************************//

//************************************** TIMER Setting ********************************************************//

// Normal (single slope) PWM operation: timer countinuously counts up to PER register value and then is reset to 0
REG_TCC0_WAVE = TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCC0 (2 << 0)
while (TCC1->SYNCBUSY.bit.WAVE); // Wait for synchronization

REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 ; // Set prescaler to 1, 48MHz/1 (0 << 8)

REG_TCC0_PER = 2906; // Set the frequency of the PWM on TCC0 to 33kHz: 48MHz / (1 * 1453 + 1) = 33kHz
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization

REG_TCC0_CC0 = 1452; // TCC1 CC0 - 50% duty cycle
while (TCC0->SYNCBUSY.bit.CC0); // Wait for synchronization

// dead time generation (edit the value to adjust the dead time on Low side (bit: 16) and High side (bit:24))
REG_TCC0_WEXCTRL |= (50 << 16) | (50 << 24) | (1 << 8) | (2 << 0); // (1 << 8): implement dead time at output WO[0] and WO[4]
// (2 << 0): output matrix (all output use CC0

TCC0->CTRLA.bit.ENABLE = 1; // Enable the TCC0 counter
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization

//************************************** TIMER Setting ********************************************************//

//////////////////////////////////////////

GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |           // Enable GCLK0 as a clock source
                      GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK0 at 48MHz
                      GCLK_CLKCTRL_ID_TCC0_TCC1;   // Route GCLK0 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Enable the port multiplexer for pins D7
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;

  // D7 is on EVEN port pin PA06 and TCC1/WO[0] channel 0 is on peripheral E
  PORT->Group[g_APinDescription[7].ulPort].PMUX[g_APinDescription[7].ulPin >> 1].reg |= /*PORT_PMUX_PMUXO_E |*/ PORT_PMUX_PMUXE_E;
  
  // Normal (single slope) PWM operation: timer countinuously counts up to PER register value and then is reset to 0
  TCC1->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;          // Setup single slope PWM on TCC1
  while (TCC1->SYNCBUSY.bit.WAVE);                 // Wait for synchronization

  TCC1->CTRLA.reg = TC_CTRLA_PRESCALER_DIV8 |      // Set prescaler to 8, 48MHz/8 = 6MHz
                    TC_CTRLA_PRESCSYNC_PRESC;      // Set the reset/reload to trigger on prescaler clock
  
  TCC1->PER.reg = 5999;                            // Set the frequency of the PWM on TCC1 to 1kHz: 48MHz / (8 * 5999 + 1) = 1kHz
  while (TCC1->SYNCBUSY.bit.PER);                  // Wait for synchronization
  
  TCC1->CC[0].reg = 3000;                          // TCC1 CC0 - 50% duty cycle on D7
  while (TCC1->SYNCBUSY.bit.CC0);                  // Wait for synchronization
  
  TCC1->CTRLA.bit.ENABLE = 1;                     // Enable the TCC1 counter
  while (TCC1->SYNCBUSY.bit.ENABLE);  

Leider komm ich nun nicht weiter und habe auch leider kein Oszilloskop hier, um das Signal nachzumessen. Fakt ist, der Lüfter dreht mit einer konstanten geringen Drehzahl und über die klassische analogWrite Funktion kann ich diese nicht verändern.

Leider komme ich nicht mehr weiter und benötige eure Hilfe.

Bezüglich einem TFT-Touchdisplay wollte ich auch die Community fragen, ob es eine Empfehlung diesbezüglich gibt. Besonders wichtig ist die Zuverlässigkeit ( also auch die Lebensdauer) und die Kompatibilität mit dem Arduino Nano 33 IoT. Auf was muss ich besonder bei der Auswahl ahcten?

Vielen Dank im Voraus,

Liebe Grüße, Tobias.

Es ist eine gute Idee, Dein Programm zu zeigen, aber leider ist es falsch formatiert und damit unleserlich. Bitte verändere Deinen Beitrag und schließe das Programm in Code-Tags ein.

//************************************** Clock Setting ********************************************************//

// Feed GCLK0 at 48MHz to TCC0 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK0 as a clock source (1 << 14)
GCLK_CLKCTRL_GEN_GCLK0 | // Select GCLK0 at 48MHz (0 << 8)
GCLK_CLKCTRL_ID_TCC0_TCC1; // Route GCLK0 to TCC0 and TCC1 (0x1A << 0)

//************************************** Clock Setting ********************************************************//

Wie Du an der "umfangreichen" Reaktion hier im Forum bemerkst, hast Du Dir einen m. E. schlecht beschriebenen und daher wohl selten genutzten Exoten ausgewählt. Kann mich irren :roll_eyes:

Ich habe mich entschlossen, den ESP32 zu erkunden. Wahrscheinlich werde ich nie damit fertig, aber ein paar Anfangserfolge konnte ich erzielen. So steuere ich beispielsweise mit einer Webseite die Parameter einer Animation für LED-Streifen. Komme ich nicht weiter, habe ich bislang immer eine Antwort im WWW gefunden. Nur so als Anregung, wenn Du nicht weiterkommen solltest :thinking:

Aus meiner Bastelkiste:

const int pwmPin = 12;  // the number of the PWM pin, 12 corresponds to GPIO12
const int pwmChannel = 0;
const int resolution = 8;
int freq = 2500;
int tv = 50;

void setupPWM() {
  server.on("/modified", HTTP_POST, []() {
  if (server.hasArg("Frequenz")) {
    freq = server.arg("Frequenz").toInt();
    if (freq < 1) freq = 1;
    if (freq > 5000) freq = 5000;
  }
  if (server.hasArg("Tastverhältnis")) {
    tv = server.arg("Tastverhältnis").toInt();
    if (tv < 0) tv = 0;
    if (tv > 100) tv = 100;
  }
    if (server.args()) {
      for (auto i = 0; i < server.args(); i++){
        DEBUG_P(server.argName(i)); DEBUG_P("\t"); DEBUG_L(server.arg(i));
      }
    }

    String temp = "{\"Frequenz\":\"";
    temp += freq;
    temp += "\",\"Tastverhältnis\":\"";
    temp += tv;
    temp += "\"}";
    server.send(200, "application/json", temp);
    DEBUG_L(temp);
    
    int dutyCycle = tv * 255 / 100;
    ledcSetup(pwmChannel, freq, resolution);  // configure PWM functionalitites
    ledcAttachPin(pwmPin, pwmChannel);        // attach the channel to the GPIO to be controlled
    ledcWrite(pwmChannel, dutyCycle);  // changing the LED brightness with PWM
    temp = "freq: " + String(freq); anzeigeOLED(temp.c_str()); DEBUG_L(temp);
    temp = "dutyCycle: " + String(dutyCycle); anzeigeOLED(temp.c_str()); DEBUG_L(temp); DEBUG_L("");
  });
}

Hallo,

hast du die App Note schon durchgearbeitet?
Link 1
Link 2
Link 3

Ohne Oszi oder Logic Analyzer ist natürlich blöd. Nach Bauchgefühl zu programmieren ist ganz schlecht. Man muss es prüfen. Nur weil der Lüfter dreht muss das Signal noch nicht das sein was man eigentlich wollte.

Man kann natürlich auch den Prescaler des verwendeten Timers anpassen den analogWrite für den verwendeten Pin nutzt. Müßtest du herausfinden und etwas rechnen ob das im kHz Fenster für den Lüfter liegt. Ob es dadurch Nebenwirklungen gibt kann ich nicht sagen.