Das funktioniert natürlich nur bei hohen Teilerfaktoren also niedrigen Frequenzen.Code:// Enuberation für 1 bis 10000 enum incs { ones = 1, tens = 10, hundrets = 100, thousends = 1000, tenthousends = 10000 }; // Globale Variable für das aktuelle PER-Register static volatile uint16_t current_per = 240; uint8_t pwm_set_frequency(uint32_t freq_hz) { // Sicherheitsgrenzen prüfen if (freq_hz < 1 || freq_hz > 100000) { return 0; // Fehler: Frequenz außerhalb des erlaubten Bereichs } uint32_t ticks = 0; uint8_t clk_sel = 0; // Timer stoppen für glitchfreies Umschalten uint8_t ctrla_backup = TCA0.SINGLE.CTRLA; TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_ENABLE_bm; // Dynamische Berechnung des optimalen Prescalers bei 24 MHz if (freq_hz >= 367) { ticks = F_CPU / freq_hz; clk_sel = TCA_SINGLE_CLKSEL_DIV1_gc; } else if (freq_hz >= 92) { ticks = (F_CPU / 4) / freq_hz; clk_sel = TCA_SINGLE_CLKSEL_DIV4_gc; } else if (freq_hz >= 46) { ticks = (F_CPU / 8) / freq_hz; clk_sel = TCA_SINGLE_CLKSEL_DIV8_gc; } else if (freq_hz >= 23) { ticks = (F_CPU / 16) / freq_hz; clk_sel = TCA_SINGLE_CLKSEL_DIV16_gc; } else if (freq_hz >= 6) { ticks = (F_CPU / 64) / freq_hz; clk_sel = TCA_SINGLE_CLKSEL_DIV64_gc; } else if (freq_hz >= 2) { ticks = (F_CPU / 256) / freq_hz; clk_sel = TCA_SINGLE_CLKSEL_DIV256_gc; } else { ticks = (F_CPU / 1024) / freq_hz; clk_sel = TCA_SINGLE_CLKSEL_DIV1024_gc; } // Das Periodenregister schreiben current_per = (uint16_t)(ticks - 1); TCA0.SINGLE.PER = current_per; // Neuen Prescaler in CTRLA schreiben TCA0.SINGLE.CTRLA = (TCA0.SINGLE.CTRLA & ~TCA_SINGLE_CLKSEL_gm) | clk_sel; // Timer wieder starten, falls er vorher lief TCA0.SINGLE.CTRLA |= (ctrla_backup & TCA_SINGLE_ENABLE_bm); return 1; // Erfolg } void pwm_set_duty_cycle_percent(float percent) { if (percent < 0.0) percent = 0.0; if (percent > 100.0) percent = 100.0; // Berechnung des Registerwertes basierend auf der aktuellen Periode uint16_t cmp_value = (uint16_t)((percent / 100.0) * current_per); // Vergleichsregister für Kanal 0 beschreiben (Gibt das Signal nun auf PA0 aus) if(percent >= 100)cmp_value++; TCA0.SINGLE.CMP0 = cmp_value; } void pwm_init(void) { // 1. PORTMUX konfigurieren: TCA0-Ausgang WO0 von PORTA auf PORTC umleiten // Das Bitfeld TCA0_bm in PORTMUX.TCAROUTEA steuert das Routing für TCA0 //PORTMUX.TCAROUTEA = PORTMUX_TCA0_PORTC_gc; // 2. Pin PC0 (WO0) als Ausgang definieren PORTA.DIRSET = PIN0_bm; // 3. TCA0 im Single-Slope PWM Modus konfigurieren (WGMODE = SINGLESLOPE) TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_SINGLESLOPE_gc | TCA_SINGLE_CMP0EN_bm; // 4. Standardfrequenz beim Start setzen (z. B. 1 kHz) pwm_set_frequency(1000); pwm_set_duty_cycle_percent(50.0); // 50% Tastverhältnis starten // 5. Timer aktivieren (Der Prescaler wird in pwm_set_frequency gesetzt) TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm; }
Beim kleinsten Teilerfaktor = 1 sind dann schon 425Hz Sprünge drin.
Das geht im Prinzip auch nicht genauer, weil der Controller natürlich nur Ganze Zahlen für das MAX ( PER ) Register verwenden kann.
Das ist also ein Fehler von 0,4%.
Bei größeren Teilfaktoren wird natürlich die Frequenzauflösung besser bis hinunter in den Hz Bereich.
120kHz wären bei Duty Cycle mit 0,5% auch noch möglich. Aber auch der L293D schafft das nicht.
Es läuft der interne 24MHz Generator. An Peripherie ist nur der Treiber, das Display, der Drehencoder und das Poti. Betrieben wird das Ganze vorerst mit nem Labornetzteil.







Zitieren

Lesezeichen