Anmelden

Archiv verlassen und diese Seite im Standarddesign anzeigen : PWM Generator bis 100kHz



wkrug
14.07.2026, 07:20
Ich arbeite zur Zeit an einem PWM Generator, der Frequenzen von 1Hz bis 100kHz mit einer Pulsweiten Auflösung von 0,5% erzeugen kann.
Als Basis dient ein AVR32DB28 Controller und als Treiber hab Ich einen L293D geplant. Als Display hab Ich ein 128x64 OLED Display mit SSD1306 Controller auf I2C.
Als Stromversorgung werde Ich wohl ein einstellbares Steckernetzteil nehmen.

Die Frequenzeinstellung hab Ich per Drehencoder mit Drucktaste gemacht.
Die Drucktaste verstellt die Dezimalstelle, der Drehencoder inkrementiert oder dekrementiert dann die ausgewählte Stelle.

Für die Pulsweite habe Ich ein Spindelpoti mit 5k Ohm Widerstand benutzt ( 10k sollte auch funktionieren ).

Es läuft eigentlich schon alles. Die Frequenz stimmt noch nicht ganz genau - Ich denk mal, das liegt an der Benutzung der internen 24MHz Quelle des AVR32DB.
Die Pulsweiteneinstellung bei den Prozentwerten suggeriert eine Genauigkeit, die so nicht ganz stimmt. Die Zeitwerte in us kommen aber gut hin.
Der L293D verarbeitet keine Impulse mehr die kürzer als 1us sind, aber damit kann Ich leben.
Der Ausgangspegel geht bis knapp unter die Betriebsspannung, die maximal 30V beträgt ( 36V lt. Datenblatt ).
Beim Spannungsregler für die 5V Seite muss man aufpassen, das der Spannungsregler auch die 36V am Eingang verträgt (TSR 1-2450 ?).
Die Schaltung zieht zur Zeit 60mA ohne Last.


(https://www.roboternetz.de/community/attachment.php?attachmentid=36183&stc=1) https://www.roboternetz.de/community/attachment.php?attachmentid=36183&stc=1 (https://www.roboternetz.de/community/attachment.php?attachmentid=36183&stc=1)
10kHz mit 50% Duty Cycle
(https://www.roboternetz.de/community/attachment.php?attachmentid=36184&stc=1) https://www.roboternetz.de/community/attachment.php?attachmentid=36184&stc=1 (https://www.roboternetz.de/community/attachment.php?attachmentid=36184&stc=1)
Aktueller Versuchsaufbau

Searcher
14.07.2026, 09:07
Hallo,



Die Frequenzeinstellung hab Ich per Drehencoder mit Drucktaste gemacht.
Die Drucktaste verstellt die Dezimalstelle, der Drehencoder inkrementiert oder dekrementiert dann die ausgewählte Stelle.


Hört sich an, als wenn die Frequenz in 1 Hz Schrittweiten einstellbar wäre. Wie bekommst Du das hin und mit welcher Peripherie des AVR (https://www.rn-wissen.de/index.php/AVR-Einstieg_leicht_gemacht)32DB28?

Gruß
Searcher

wkrug
14.07.2026, 09:30
// 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;
}
Das funktioniert natürlich nur bei hohen Teilerfaktoren also niedrigen Frequenzen.
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.