Hi,
ich habe ein merkwürdiges Problem mit dem ATMega32@16Mhz. Dieser soll
zur Ansteuerung von 4 Servomotoren verwendet werden. Die Generierung der
PWM-Signale klappt soweit sehr gut. Ich verwende alle vier möglichen
PWM-Kanäle (Timer0, Timer1 (OC1A, OC1B) und Timer2).
Das Modul zur PWM-Erzeugung enthält eine Initialisierungsfunktion, die
die Timer intialisiert und vier weitere mit denen die Pulsbreite zur
Laufzeit festgelegt wird:
1
...
2
3
voidinitPWM()
4
{
5
// Note: _BV(x) => 1 << x
6
// (converts a bit number into a byte value)
7
8
//-- Timer 0 (for pwm_8_1 alias OC0 alias PB3, Low Resolution)
// Set PD7 in the Data Direction Register as output pin
44
DDRD|=_BV(PD7);
45
}
46
47
// Sets a pulswidth between 0 and 20000 µs (0% and 100% of 20 ms) on PWM Channel 1 (OC1A/PD5)
48
voidsetPwm16_1(uint16pulsWidth)
49
{
50
if(pulsWidth>=0&&pulsWidth<=TIMER1_TOP)
51
{
52
// as closer as we reach the TOP Value as smaller the pulse becomes
53
// since we use the inverted Mode
54
// Output Compare Register 1A = TOP Value - pulsWidth
55
OCR1A=TIMER1_TOP-pulsWidth;
56
}
57
}
58
59
// Sets a pulswidth between 0 and 20000 µs (0% and 100% of 20 ms) on PWM Channel 2 (OC1B/PD4)
60
voidsetPwm16_2(uint16pulsWidth)
61
{
62
if(pulsWidth>=0&&pulsWidth<=TIMER1_TOP)
63
{
64
// as closer as we reach the TOP Value as smaller becomes the pulse (inverted mode)
65
// Output Compare Register 1B = TOP Value - pulsWidth
66
OCR1B=TIMER1_TOP-pulsWidth;
67
}
68
}
69
70
// Sets a pulswidth between 0 and 256 (0% and 100% of 1/(61,03 Hz) = 15,87 ms) on PWM Channel 3 (OC0/PB3)
71
voidsetPwm8_1(uint8pulsWidth)
72
{
73
OCR0=pulsWidth;
74
}
75
76
// Sets a pulswidth between 0 and 256 (0% and 100% of 1/(61,03 Hz) = 15,87 ms ) on PWM Channel 4 (OC2/PD7)
77
voidsetPwm8_2(uint8pulsWidth)
78
{
79
OCR2=pulsWidth;
80
}
Wie erwähnt klappt das im Prinzip ganz gut. Jetzt geht es daraum die
Servoparameter über zwei Taster zu verändern, da ich die viele
Pulsweiten für die Anwendung durch experimentieren ermitteln muss. Dazu
nutze ich den Timer2 Overflow Interrupt um zyklisch die Tasterwerte
abzufragen. Soweit ich das Datenblatt verstanden habe ist es möglich
sowohl den Timer2 zur PWM-Erzeugung (Fast PWM Mode) als auch den Timer 2
Overflow Interrupt zu nutzen, oder?
Bevor ich die Interrupt-Routine vorstelle will ich nochmal auf ein für
diese Experimente entscheidendes Modul eingehen, dass die Datenstruktur
definiert mit dem die vier PWM-Parameter verändert werden können:
1
// Represents a pwm parameter
2
structpwmParameter
3
{
4
// name of the pwm parameter
5
charname[10];
6
// value of the pwm parameter
7
uint16value;
8
// max value of the pwm parameter value (255 for 8-bit pwm, 20000 for 16-bit pwm)
9
uint16maxValue;
10
// pointer to the next parameter (lsingle-linked-list)
11
structpwmParameter*next;
12
// Pointer to a function which increases the pwm parameter value by one ( + 1)
13
void(*increase)(structpwmParameter*);
14
// // Pointer to a function which decreases the pwm parameter value by one (- 1)
15
void(*decrease)(structpwmParameter*);
16
// Updates the pwm parameter. Not needed to be called after increase/decrease! Its called within increase/decrease functions
17
void(*update)(structpwmParameter*);
18
};
19
20
typedefstructpwmParameter*psPwmParameter;
21
22
// Initializes the pwm parameters (Should be called before getFirstParameter() is called!!!)
23
voidinitPwmParameters(void);
24
// Gets the first parameter of the of the linked-list of parameters
// initialize function (bind together the linked-list
41
voidinitPwmParameters()
42
{
43
pwm16_1.next=&pwm16_2;
44
pwm16_2.next=&pwm8_1;
45
pwm8_1.next=&pwm8_2;
46
pwm8_2.next=&pwm16_1;
47
}
48
49
// Gets the first parameter of a single linked list of pwm parameters
50
psPwmParametergetFirstParameter(void)
51
{
52
return&pwm16_1;
53
}
54
55
voidincrease(psPwmParameterparameter)
56
{
57
if(parameter->value<parameter->maxValue)
58
parameter->value=(parameter->value)++;
59
60
parameter->update(parameter);
61
}
62
63
voiddecrease(psPwmParameterparameter)
64
{
65
if(parameter->value>0)
66
parameter->value=(parameter->value)--;
67
68
parameter->update(parameter);
69
}
70
71
voidupdatePwm16_1(psPwmParameterpwm16_1)
72
{
73
setPwm16_1(pwm16_1->value);
74
}
75
76
voidupdatePwm16_2(psPwmParameterpwm16_2)
77
{
78
setPwm16_2((pwm16_2->value));
79
}
80
81
voidupdatePwm8_1(psPwmParameterpwm8_1)
82
{
83
setPwm8_1((uint8)(pwm8_1->value));
84
}
85
86
voidupdatePwm8_2(psPwmParameterpwm8_2)
87
{
88
setPwm8_2((uint8)(pwm8_2->value));
89
}
Das Modul in dem die Interrupt-Routine definiert wird und die mit dem
ersten pwm Parameter (element der verketteten Liste) initialisiert wird
sieht wie folgt aus:
1
volatileuint8SW_PrescalerCounter;
2
// Value of the software prescaler. The value affects the effective interrupt frequency.
3
// Higher values lead to slower up-/downcounting of the current parameter.
4
constuint8SW_PRESCALER=2;
5
// counter used to measure how long _both_ buttons are pressed.
6
volatileuint8changeModeCounter;
7
// The Threshold value used to changed the mode.
8
constuint8CHANGEMODETHRESHOLD=30;
9
10
// Displays the current parameter
11
voiddisplayCurrentParameter(void);
12
13
// pointer to the (current) struct pwmParameter. Will be initialized when InitPwmParameterConfigurationControl(...) is called.
// ISR-Routine is called each time the timer 2 overflows (61,04 times per second)
22
// An additional software prescaler reduces the frequency to 61,04 / 2 = 30,52 Hz
23
ISR(TIMER2_OVF_vect)
24
{
25
// Additional SW Prescaler
26
if((SW_PrescalerCounter++%SW_PRESCALER)!=0)
27
{
28
return;
29
}
30
// button s1 pressed?
31
if(!(PIND&_BV(PD3)))
32
{
33
current->decrease(current);
34
}
35
// button s2 pressed?
36
if(!(PIND&_BV(PD1)))
37
{
38
current->increase(current);
39
}
40
// both pressed (means that the user wants to change the pwm parameter to configure)?
41
if(!(PIND&_BV(PD3))&!(PIND&_BV(PD1)))
42
{
43
changeModeCounter++;
44
45
// pressed _both_ buttons long enough?
46
if(changeModeCounter==CHANGEMODETHRESHOLD)
47
{
48
changeModeCounter=0;
49
// change the current pwm param to configure
50
current=current->next;
51
// sleep 1 ms.
52
sleep_ms(1);
53
// clearDisplay();
54
}
55
}
56
// print the current parameter on the display
57
displayCurrentParameter();
58
}
59
60
voiddisplayCurrentParameter()
61
{
62
writeToDisplay_s(0,1,current->name);
63
writeToDisplay_uint16(8,1,current->value);
64
}
Jedenfalls funktioniert das Verändern des aktuelle PWM-Parameter
prinzipiell gut über die beiden Taster und es klappt auch der Wechsel
des aktuellen parameters zum nächsten. Die vier PWN-Signale werden auch
generiert ABER nur wenn ich den Timer 2 Overflow Interrupt NICHT
aktiviert habe, d.h. wenn ich am Anfang des Programs einmal alle vier
set_pwm_xx-Funktionen mit hardcodierten Werten aufrufe und dann nichts
mehr (while(1);). Wenn ich die Werte mit dem obigen Interrupt Routine
ändere funktioniert zwar das verändern des aktuellen parameters (sehe
ich an ausgaben am Display) aber es werden KEINE PWM-Signale (weder
Timer2 noch Timer1 und Timer0) mehr erzeugt.
Liegt es vielleicht am sleep_ms(1) in der ISR?
Meint ihr die Interrupt-Routine ist zu lang?? Da ich mit einem
Prescaler von 1024 arbeite müssten doch genügend Takte zur Verfügung
stehen?
Es muss sich um einen systematischen Fehler handeln, da beide Teile
einzeln (PWM-signalerzeugung selbst als auch der Teil zur Veränderung
der Parameter zur laufzeit) funktionieren.
Bin mal gespannt was ihr dazu meint, Laut den Antworten im Forum die ich
bisher gelesen habe müsste es eigentlich funktionieren. Vielen Dank
schonmal!
Sascha N. schrieb:> Hmm.. kann mir wirklich keiner helfen?
Code komplett als Anhang posten
Frage bzw. Problem kurz und prägnant formulieren.
Dann klappst auch mit der Hilfe. Durch den Wust in deiner Frage arbeitet
sich freiwillig keiner durch.
Oliver
Sascha N. schrieb:> Liegt es vielleicht am sleep_ms(1) in der ISR?>> Meint ihr die Interrupt-Routine ist zu lang?? Da ich mit einem> Prescaler von 1024 arbeite müssten doch genügend Takte zur Verfügung> stehen?
Möglich, das sleep in der ISR ist jedenfalls keine gute Idee. Das
"clearDisplay();" (OK auskommentiert) und das
"displayCurrentParameter();" sind ebenfalls daneben.
Setz einfach ein Flag und mach die Ausgaben in main.
In Deinem Fall scheint das zwar nicht allzu kritisch zu sein (zumindest
wenn in main eine leere Schleife steht), Du verlierst eventuell ein paar
Interrupts und Deine Tasten reagieren etwas träge, schön ist das aber
trotzdem nicht.
Ansonsten fehlt ein großer Teil Deines Programms ==> Fehler in Zeile 42
Eventuell geht Dir einfach der Ram aus.
PS:
Das hier ist auch ziemlich daneben:
1
parameter->value=(parameter->value)++;
Eine Variable sich selbst zuzuweisen und sie anschließend zu erhöhen
macht irgendwie keinen Sinn.
Also entweder so: