Das schnellste, was ich mir in Software vorstellen kann:
pwm-interrupt:
in savereg, SREG ;SREG retten
ser pwm_set ;jeden PWM-Ausgang setzen
cseq counter, pwm0 ;wenn gleich ...
cbr toggle, (1 << 0) ;... dieses Bit doch nicht setzen
cseq counter, pwm1 ;wenn gleich ...
cbr toggle, (1 << 1) ;... dieses Bit doch nicht setzen
... usw. ...
or pwm_out, pwm_set ;alle zu setzenden setzen
out PORTx, pwm_out
inc counter
bne return
ldi pwm_out, 0 ;Bei Counterüberlauf alle auf null
return:
out savereg, SREG ;SREG zurück
reti
(Die unübersichtliche Anordnung mit Überlauf-Zurücksetzen nach dem
out ist nötig, um eine Dauer-1 zu ermöglichen.)
12 Takte inkl. Rücksprung plus 2 pro Kanal plus x für den
Interrupteinsprung. Bei 8 Kanälen also ca. 32 Takte. Damit ist
softwaremäßig die theoretische Grenze bei 122 Hz pro MHz Taktfrequenz.
Das reicht sicherlich für viele Anwendungen, aber wenn es nicht hörbar
sein darf, natürlich nicht.
Wenn weniger Auflösung reicht, wird pro Aufruf ein Takt mehr benötigt,
um ein ORI counter, Bitmaske auszuführen. Mit einem 16 MHz AVR und 32
Stufen wäre man also immerhin bei 15 kHz für 8 Kanäle.
Insgesamt aast diese Implementierung natürlich gräßlich mit Registern,
aber es liegt auf der Hand, daß pwm-Werte im RAM die Sache erheblich
ausbremsen würden.
Wenn Geschwindigkeit und hohe Auflösung verlangt werden, wäre noch
eine Lösung mit Sigma-Delta-Modulation denkbar, die natürlich
prinzipbedingt bei Werten nahe Null oder nahe Vollaussteuerung auch
wieder hörbar sein wird (sogar verschärft, weil die Interruptroutine
langsamer wird). Wenn man mehr mit Teillast arbeitet, aber wohl eine
Erwägung wert.