Hallo, Ich habe ein Problem und hoffe, Ihr könnt mir helfen, Ich habe gerade versucht, einen Timer für den ATmega328p zu programmieren. Ich verwende, (weil ich einen Motor ansteuern will, den PWM-phasenrichtigen Modus) Laut Datenblatt S.108 ist das Mode 1. Wenn ich aber das OCR0A-Register beschreibe, dass ja ständig mit dem aktuellen Zählwert (TCNT0-Register) verglichen wird, habe ich in manchen Fällen ein Problem. Einerseits, wenn der Wert des OCR0A-Registers zu klein wird (also in die nähe von Null kommt) oder, wenn er zu groß wird (also in die nähe von 255 kommt). Wenn mein OCR0A Register einen so einen kritischen Wert erhält, spielt bei mir alles verrückt. Es verändert sich die Frequenz (wird halb so langsam) und das Puls-Pausenverhältnis hüpft auf rund 50 %. Weiß jemand von euch, warum man dem OCR0A-Register keine besonders hohen oder niedrigen Werte geben darf ? Danke schon mal im Voraus, LG Felix
Felix schrieb: > Weiß jemand von euch, warum man dem OCR0A-Register keine besonders hohen > oder niedrigen Werte geben darf ? Klar darf man. Warum hast du dein Programm nicht angehangen?
Ok, hier is mein Programm..ich lese von einem Potentiometer den Wert über einen AD-Wandler ein, und will darüber das Puls-Pausenverhältnis regeln. Aber wenn der Wert zu gering ist (Der Wert wird dann insr OCR0A-Register übernommen) fängt meine Pulsweite zu spinnen an.. Hier mal mein Code:
1 | //ATmega328p-pu
|
2 | #define F_CPU 8000000UL
|
3 | |
4 | #include <avr/io.h> |
5 | #include <avr/interrupt.h> |
6 | #include <util/delay.h> |
7 | |
8 | void adc_init(); |
9 | void timer_init(); |
10 | |
11 | int main(void) |
12 | {
|
13 | |
14 | DDRB |= (1<<PINB0); //Steuerausgang für PWM |
15 | adc_init(); |
16 | timer_init(); |
17 | |
18 | while (1); |
19 | return 0; |
20 | }
|
21 | |
22 | ISR(ADC_vect) |
23 | {
|
24 | cli(); |
25 | OCR0A = (ADC/4); //Weil ADC-Max-Value 1024 (10 bit) und TIMER-Max-Value 256 (8-bit) ist |
26 | ADCSRA |= (1<<ADSC); //Jede Konvertierung wird nur durch dieses Bit gestartet ("Single Conversion Modus") |
27 | sei(); |
28 | }
|
29 | |
30 | ISR(TIMER0_COMPA_vect) |
31 | {
|
32 | cli(); |
33 | PORTB ^= (1<<PINB0); //Toggeln |
34 | sei(); |
35 | }
|
36 | |
37 | void timer_init() |
38 | {
|
39 | //Beim Initialisieren sollte OCR0A einen Wert haben
|
40 | //Bei ORRA <= 1 gibt es aber Probleme!
|
41 | OCR0A = 2; |
42 | |
43 | //Start Timer0 (PWM->Phasenrichtig) mit TOP von 0xFF
|
44 | //im nichtinvertierenden phasenrichtigen Modus
|
45 | TCCR0A |= (1<<CS00)|(1<<WGM00); |
46 | |
47 | //CPU-Clock und Prescaller von 0
|
48 | TCCR0B |= (1<<CS00); |
49 | |
50 | //Enable Timer Compare Match-Interrupt
|
51 | TIMSK0 |= (1<<OCIE0A); |
52 | |
53 | //Set the I-bit in SREG -> start Interrupts
|
54 | sei(); |
55 | }
|
56 | |
57 | void adc_init() |
58 | {
|
59 | ADMUX |= (1<<REFS0)|(1<<MUX2)|(1<<MUX0); |
60 | // Referenzspannung-> AVCC intern, ADC5 als AD-Wandler
|
61 | |
62 | ADCSRA |= (1<<ADEN)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADIE); |
63 | //AD_enable, und Prescaller auf 64 für f=125KHz, AD-Interrupt-enable
|
64 | |
65 | sei(); |
66 | //schaltet Interrupts ein
|
67 | |
68 | ADCSRA |= (1<<ADSC); |
69 | //Startet die ERSTE Konvertierung... (Im "Single-Conversion-Mode" muss das Bit
|
70 | //ADSC jedesmal manuell beschrieben werden, um die Konvertierung zu starten
|
71 | }
|
PS: oder wenn der Wert zu Groß ist, fängt es genauso zu spinnen an, hier mal ein Foto von "Spinnen". Danke,
Ich würde versuchen einen HW-Ausgang des Timers zu nutzen. Also zB PD6 (OC0A) oder so. Toggeln in der ISR verbraucht Zeit um in die ISR und zurück zu springen. Da könnte der TCNT0 schon einen Wert haben, der nicht mehr zum bisherigen Ablauf bei sehr kleinen OCR0A paßt. Die cli, sei in den ISRs sind witzlos, da sowieso keine anderen ISR parallel laufen können.
Um den HW-PWM-Ausgang zu nutzen, ändere wie folgt ab: TCCR0A = (1<<COM0A1) | (1<<COM0A0) | (1<<WGM00);
Hi Die CLI/SEI sind mehr als flüssig-> überflüssig. Das Beschreiben des OCR0A-Registers sollte im entsprechenden Interrupt erfolgen. Zeit dazu hast du. Die Timerfrequenz kann niedriger gewählt werden. Die Angaben des 'Oszis' in Bezug auf Dutycycle, Periode und Frequenz sind mehr als merkwürdig. MfG Spess
Hi Nachtrag: Zum Synchronisieren von ADC und Timer ist das Autotrigger-Register ADCSRB geeignet. OCR0A = (ADC/4) lässt sich durch Setzen von ADLAR einsparen. MfG Spess
Ja, Ihr seid genial! Danke an Euch alle. hab jetzt insofern abgeändert, dass ich gleich den TimerPin PIND6 als Output konfiguriert habe, und den, statt des Ursprünglichen PINB0 verwende, die cli(); und sei(); hab ich auch weggelassen und das TCCR0A Register hab ich entsprechend für den PIND6 konfiguriert. Hier noch mein funktionierender Code:
1 | //ATmega328p-pu
|
2 | #define F_CPU 8000000UL
|
3 | |
4 | #include <avr/io.h> |
5 | #include <avr/interrupt.h> |
6 | #include <util/delay.h> |
7 | |
8 | void adc_init(); |
9 | void timer_init(); |
10 | |
11 | int main(void) |
12 | {
|
13 | |
14 | //DDRB |= (1<<PINB0); //Steuerausgang für PWM
|
15 | DDRD |= (1<<PIND6); |
16 | adc_init(); |
17 | timer_init(); |
18 | |
19 | while (1); |
20 | return 0; |
21 | }
|
22 | |
23 | ISR(ADC_vect) |
24 | {
|
25 | //cli();
|
26 | OCR0A = (ADC/4); //Weil ADC-Max-Value 1023 (10 bit) und TIMER-Max-Value 255 (8-bit) ist |
27 | ADCSRA |= (1<<ADSC); //Jede Konvertierung wird nur durch dieses Bit gestartet ("Single Conversion Modus") |
28 | //sei();
|
29 | }
|
30 | |
31 | ISR(TIMER0_COMPA_vect) |
32 | {
|
33 | //cli();
|
34 | //PORTB ^= (1<<PINB0); //Toggeln
|
35 | //sei();
|
36 | }
|
37 | |
38 | void timer_init() |
39 | {
|
40 | //Beim Initialisieren sollte OCR0A einen Wert haben
|
41 | //Bei ORRA <= 1 gibt es aber Probleme!
|
42 | OCR0A = 2; |
43 | |
44 | //Start Timer0 (PWM->Phasenrichtig) mit TOP von 0xFF
|
45 | //im nichtinvertierenden phasenrichtigen Modus
|
46 | TCCR0A |= (1<<WGM00)|(1<<COM0A1)|(1<<COM0A0); |
47 | |
48 | //CPU-Clock und Prescaller von 0
|
49 | TCCR0B |= (1<<CS00); |
50 | |
51 | //Enable Timer Compare Match-Interrupt
|
52 | TIMSK0 |= (1<<OCIE0A); |
53 | |
54 | //Set the I-bit in SREG -> start Interrupts
|
55 | sei(); |
56 | }
|
57 | |
58 | void adc_init() |
59 | {
|
60 | ADMUX |= (1<<REFS0)|(1<<MUX2)|(1<<MUX0); |
61 | // Referenzspannung-> AVCC intern, ADC5 als AD-Wandler
|
62 | |
63 | ADCSRA |= (1<<ADEN)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADIE); |
64 | //AD_enable, und Prescaller auf 64 für f=125KHz, AD-Interrupt-enable
|
65 | |
66 | sei(); |
67 | //schaltet Interrupts ein
|
68 | |
69 | ADCSRA |= (1<<ADSC); |
70 | //Startet die ERSTE Konvertierung... (Im "Single-Conversion-Mode" muss das Bit
|
71 | //ADSC jedesmal manuell beschrieben werden, um die Konvertierung zu starten
|
72 | }
|
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.