Forum: Mikrocontroller und Digitale Elektronik ATmega328p-pu / TIMER bzw. OCR0A-Register


von Felix (Gast)


Lesenswert?

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

von Sinnlos (Gast)


Lesenswert?

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?

von Felix (Gast)


Lesenswert?

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
}

von Felix (Gast)



Lesenswert?

PS: oder wenn der Wert zu Groß ist, fängt es genauso zu spinnen an, hier 
mal ein Foto von "Spinnen".
Danke,

von Sinnlos (Gast)


Lesenswert?

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.

von Felix Adam (Gast)


Lesenswert?

Um den HW-PWM-Ausgang zu nutzen, ändere wie folgt ab:

TCCR0A = (1<<COM0A1) | (1<<COM0A0) | (1<<WGM00);

von Dieter F. (Gast)


Lesenswert?

?

 TCCR0A |= (1<<CS00) ...

CS00 gibt es da nicht :-)

von spess53 (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Felix (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.