Moin!
Ich habe hier schon sehr viel im Forum gesucht und leider keine passende
Lösung gefunden. Folgende Aufgabenstellung:
Ich muss mit einem Controller 8 PWM-Signale erzeugen um 8 Motoren
anzutreiben. Dafür sind ja auch viele Beispiele vorhanden. Die
Besonderheit bei mir ist aber: ich muss für JEDEN Kanal einzeln
FREQUENZ, TASTVERHÄLTNIS und ob das Signal mit Low- oder High-Pegel
anfängt festlegen können. Und zudem sollen die Motoren beliebig
gestartet und gestoppt werden können.
Ich habe folgenden Code programmiert (bin erst seit kurzem dabei, von
daher nicht der sauberste :-)
Meine Überlegung: ich nutze den Timerinterrupt als globalen Taktgeber,
jeder Kanal hat seinen eigenen Zähler, dessen Grenzen einzeln festgelegt
werden können. So wie der Code unten steht erzeugt der Zähler jede ms
einen Interrupt, und ruft dann für jeden Motor die Funktion MotorControl
auf, die entsprechend der Werte des Motors die PWM erzeugt. Im Beispiel
unten habe ich zwei Motoren, einen mit der Periodendauer 20ms (50Hz) und
einem Tastverhältnis von 0.5 (duty=10ms) und einen Motor mit
Periodendauer 30ms (33,3Hz) und Tastverhältnis 1/3 (duty=10ms).
Das ganze basiert auf dem Soft-PWM-Artikel-Beispiel.
Habt ihr Ideen, wie man das besser lösen kann? Vor allem macht mir
sorgen, dass die motorControl ja innerhalb des Interrupts achtmal
aufgerufen wird, was ja wohl ziemlich ungünstig ist.
Und dann fehlt noch die komplette Logik falls ich nicht mit einem
High-Pegel starten will, sondern mit einem Low-Pegel.. Das Signal soll
dann nicht invertiert werden, sondern genau um den Low-Pegel verschoben
sein (also einfach einmal den LowPegel vorm High-Pegel durchlaufen
lassen, danach nicht wieder). Ich dachte, das wäre ganz einfach, isses
aber wohl doch nicht :-)
1 | #define F_CPU 8000000L // Systemtakt in Hz
|
2 | #define F_PWM 1000 // PWM-Frequenz in Hz
|
3 | #define PWM_STEPS 1 // PWM-Schritte pro Zyklus(1..256)
|
4 | #define T_PWM (F_CPU/(F_PWM*PWM_STEPS)) // Systemtakte pro PWM-Takt
|
5 |
|
6 | struct motordata{ //Motordaten für jeden Motor einzeln
|
7 | uint8_t nbr; //Nr. d. Motors, wird später der Pin am uC
|
8 | uint8_t period; //Zählergrenze für Periodendauer
|
9 | uint8_t duty; //Zählergrenze für High-Pegel
|
10 | //uint8_t low; //soll mit Low-Pegel begonnen werden? (noch nicht impl.)
|
11 | uint8_t stop; //soll Motor aus sein; 1=aus
|
12 | } motorOne = {1,20,10,0}, motorThree = {3,30,10,0};
|
13 |
|
14 |
|
15 | unsigned int motorControl(struct motordata mtd) {
|
16 | static uint8_t cnt[8] = {1,1,1,1,1,1,1,1}; //kann wegen static nicht ins
|
17 | //struct, also hier array dafür (geht das besser?)
|
18 | uint8_t tmp=0;
|
19 | if(mtd.stop != 1) //Motor an?
|
20 | {
|
21 | if(cnt[mtd.nbr]<mtd.period) //Periodendauer testen
|
22 | {
|
23 | if(cnt[mtd.nbr]<=(mtd.duty)) //Dutycycle testen
|
24 | {
|
25 | tmp |= (1<<mtd.nbr);
|
26 | }
|
27 | else
|
28 | {
|
29 | tmp &= ~(1<<mtd.nbr);
|
30 | }
|
31 | }
|
32 | else
|
33 | {
|
34 | cnt[mtd.nbr] = 0;
|
35 | }
|
36 |
|
37 | cnt[mtd.nbr]++; //unsaubere Lösung, nach Nullsetzung gleich wieder um
|
38 | //eins erhöhen, ging aber so auf den ersten Blick am einfachsten
|
39 | }
|
40 | return tmp;
|
41 | }
|
42 |
|
43 |
|
44 | ISR(TIMER1_COMPA_vect) {
|
45 | uint8_t tmp=0;
|
46 |
|
47 | OCR1A += (uint16_t)T_PWM;
|
48 | //ab hier jetzt für jeden Motor einmal motorControl aufrufen
|
49 | tmp |= motorControl(motorOne);
|
50 | tmp |= motorControl(motorThree);
|
51 |
|
52 | PWM_PORT = tmp; // PWMs aktualisieren
|
53 |
|
54 | }
|
oh, jetzt ist das ganz schön viel Text geworden, ich hoffe, es findet
sich jemand, der Lust hat, das alles durchzulesen :-)
ps: die Frequenzen sind später nicht sehr hoch, mit denen die Motoren
angesteuert werden, die liegen im Bereich 1-5Hz (statt PWM könnte man
vllt auch besser periodisches An/Ausschalten sagen, das PWM-Signal wird
nicht dazu genutzt, die Drehzahl zu regulieren)
pps: gleichzeitig kommuniziert der Controller noch per UART mit einem
SteuerungsPC, so dass dafür auch noch Leistungsreserven übrig sein
müssen