Atmega 8515 Servoansteuerung

OP #4171770
Lesenswert?

Moin moin,

vorab, da dies mein erster Post ist werde ich mich kurz vorstellen. Ich 
bin Paddy, 22 Jahre alt und komme aus der Nähe von Hamburg. Bin 
gelernter Elektroniker für Betriebstechnik.

Ich bin mir leider nicht sicher ob ich hier im richtigen Bereich bin, 
sonst bitte korrigieren.

Ich habe vor kurzem erst mit der Mikrocontrollertechnik bzw. 
Hochsprachen angefangen, daher habe ich noch nicht so viel Erfahrung.

Ich wollte einen Servomotor mittels eines Atmega8515 ansteuern, nach 
diesem Vorbild: Modellbauservo Ansteuerung
Den Code habe ich etwas abgeändert, auf den 8515 bezogen.
1
#define F_CPU 16000000UL
2

3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6

7
ISR( TIMER1_COMPA_vect )                // Interruptbehandlungsroutine
8
{
9
  OCR1A = 5000-OCR1A;      // Das Servosignal wird aus der Differenz von
10
                // Periodenlänge (5000*0,004ms=20ms) und letztem
11
                // Vergleichswert (OCR1A) gebildet
12
}
13

14

15
int main (void)
16
{
17
  DDRB = 0b11111100;
18
  PORTB = (1<<PB1) | (1<<PB0);          // Pullup für PB0 und PB1
19
  
20
  TCCR1A = (1<<COM1A0);                 // Togglen bei Compare Match
21
  TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);      // CTC-Mode; Prescaler 64
22
  TIMSK  = (1<<OCIE1A);                 // Timer-Compare Interrupt an
23

24
  
25

26
  
27
  OCR1A = 4625;                         // Neutralposition ((5000-4625)*0.004ms)=1,5ms)
28
  
29
  sei();                                // Interrupts global an
30
  
31
  while( 1 ) {
32
    
33
    if ( !(PINB & (1<<PINB0)) ) {       // Impuls-Zeit verlängern
34
      cli();
35
      OCR1A = OCR1A + 3;
36
      sei();
37
      _delay_ms(50);
38
    }
39
    
40
    if ( !(PINB & (1<<PINB1)) ) {      // Inpuls-Zeit verkürzen
41
      cli();
42
      OCR1A = OCR1A - 3;
43
      sei();
44
      _delay_ms(50);
45
    }
46
  }
47

48
  return 0;
49
}

Aber an meinem Servo tut sich leider gar nichts, ich habe mit nem Oszi 
an OC1A gemessen, da ist auch kein Signal. Kann mir jemand einen Tip 
geben woran es liegen kann?

vielen Dank
Gruß Paddy
Gast #4171789
Lesenswert?

"However, note that the Data Direction Register (DDR) bit
corresponding to the OC1A or OC1B pin must be set in order
to enable the output driver."

OC1A liegt auf D5, ich sehe aber im Programm keine Zuweisung an DDRD.
OP #4171951
Lesenswert?

Ah okay vielen Dank. Muss ich den Port also noch als Ausgang beschalten?
1
DDRD |= (1 << PD5)

Ich dachte der wäre mit den folgenden Registern schon aktiviert:
1
TCCR1A = (1<<COM1A0);                 // Togglen bei Compare Match
2
TCCR1B = (1<<WGM12) | (1<<CS11) | (1<<CS10);      // CTC-Mode; Prescaler 64
3
TIMSK  = (1<<OCIE1A);                 // Timer-Compare Interrupt an
Gast #4172016
Lesenswert?

> Muss ich den Port also noch als Ausgang beschalten?
Ja.

>Ich dachte der wäre mit den folgenden Registern schon aktiviert
Nein, deshalb hatte ich das Datenblatt des ATmega8515 zitiert.

> DDRD |= (1 << PD5)
Einfach mal ausprobieren.
OP #4485410
Lesenswert?

Ich hoffe das liest hier noch jemand, da ich jetzt nicht unbedingt ein 
neues Thema aufmachen wollte.

Als nächstes wollte ich 2 Servos ansteuern (ebenfalls mit nem 
Atmega8515). Ich habe auf dieser Seite (Modellbauservo Ansteuerung) 
ganz unten auch den Code für bis zu 8 Servos gefunden, leider nur für 
nen Mega16.

Meine Überlegung war nun einfach die Register für Prescaler und CTC Mode 
anpassen. Da ich statt dem Register OCR2 OCR1B verwendet habe habe ich 
ebanfalls in der Interrupt routine
1
ISR (TIMER1_COMPB_vect)
 verwendet.

Also Servo angeschlossen und siehe da es tut sich nichts, auf keinem der 
Pins :/
Hat vielleicht jemand eine Idee was ich falsch gemacht habe? Habe die 
Register schon mehrmals überprüft...
Hier mein code:
1
#define F_CPU 16000000UL
2

3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6

7
//
8
// Der Prescaler muss so gewählt werden, dass der Ausdruck
9
// für MILLISEC_BASE einen Wert kleiner als 128 ergibt
10
// MILLISEC_BASE ist der Timerwert, der 1 Millisekunde Zeitdauer ergeben
11
// soll.
12
//
13
#define PRESCALER      128
14
#define PRESCALER_BITS (1<<CS12) | ( 1 << CS10)
15

16
#define MILLISEC_BASE  ( F_CPU / PRESCALER / 1000 )
17
#define CENTER         ( MILLISEC_BASE / 2 )
18

19
//
20
// Konfiguration der Servoleitungen
21
//
22
#define NR_SERVOS      8
23
#define SERVO_DDR      DDRD
24
#define SERVO_PORT     PORTD
25
uint8_t ServoPuls[NR_SERVOS] = { 1<<PD0, 1<<PD1, 1<<PD2, 1<<PD3,
26
1<<PD4, 1<<PD5, 1<<PD6, 1<<PD7 };
27
//
28
// Werte für die Servoposition
29
// Gültige Werte laufen von 0 bis 2 * CENTER
30
// 0           ... ganz links
31
// CENTER      ... Mittelstellung
32
// 2 * CENTER  ... ganz rechts
33
//
34
volatile uint8_t ServoValue[NR_SERVOS];
35

36
ISR (TIMER1_COMPB_vect)
37
{
38
  static uint8_t ServoId = 0;
39

40
  //
41
  // den Puls des aktuellen Servos beenden
42
  //
43
  SERVO_PORT &= ~ServoPuls[ServoId];
44

45
  //
46
  // welches ist das nächste aktuelle Servo?
47
  //
48
  if( ++ServoId >= NR_SERVOS )
49
  ServoId = 0;
50

51
  //
52
  // die Ausgangsleitung fuer dieses Servo auf 1; den Puls beginnen
53
  //
54
  SERVO_PORT |= ServoPuls[ServoId];
55

56
  //
57
  // den Timer so einstellen, dass bei Pulsende, die ISR erneut aufgerufen wird
58
  //
59
  OCR1B = MILLISEC_BASE + ServoValue[ServoId];
60
}
61

62
void InitServo()
63
{
64
  uint8_t i;
65

66
  //
67
  // Die Servoleitungen auf Ausgang stellen
68
  //
69
  SERVO_DDR = ServoPuls[0] | ServoPuls[1] | ServoPuls[2] | ServoPuls[3] |
70
  ServoPuls[4] | ServoPuls[5] | ServoPuls[6] | ServoPuls[7];
71

72
  //
73
  // Alle Servos in Mittelstellung
74
  //
75
  for( i = 0; i < NR_SERVOS; ++i )
76
  ServoValue[i] = CENTER;
77

78
  //
79
  // Timer auf CTC Modus konfigurieren
80
  //
81
  OCR1B = MILLISEC_BASE + ServoValue[0];
82
  TIMSK |= (1<<OCIE1A)|(1<<OCIE1B);
83
  TCCR1B = (1<<WGM12) | PRESCALER_BITS;  // CTC mode
84
}
85

86
int main(void)
87
{
88
  InitServo();
89

90
  sei();
91

92
  _delay_ms( 1000 );
93

94
  //
95
  // testweise einfach alle 8 Servos ansteuern
96
  // jedes Servo soll sich unterschiedlich schnell bewegen
97
  //
98
  while( 1 ) {
99

100

101
    ServoValue[0] += 2;
102
    if( ServoValue[0] > 2*CENTER )
103
    ServoValue[0] -= 2*CENTER;
104

105
    ServoValue[1] += 1;
106
    if( ServoValue[1] > 2*CENTER )
107
    ServoValue[1] -= 2*CENTER;
108

109
    ServoValue[2] += 2;
110
    if( ServoValue[2] > 2*CENTER )
111
    ServoValue[2] -= 2*CENTER;
112

113
    ServoValue[3] += 3;
114
    if( ServoValue[3] > 2*CENTER )
115
    ServoValue[3] -= 2*CENTER;
116

117
    ServoValue[4] += 1;
118
    if( ServoValue[4] > 2*CENTER )
119
    ServoValue[4] -= 2*CENTER;
120

121
    ServoValue[5] += 3;
122
    if( ServoValue[5] > 2*CENTER )
123
    ServoValue[5] -= 2*CENTER;
124

125
    ServoValue[6] += 2;
126
    if( ServoValue[6] > 2*CENTER )
127
    ServoValue[6] -= 2*CENTER;
128

129
    ServoValue[7] += 1;
130
    if( ServoValue[7] > 2*CENTER )
131
    ServoValue[7] -= 2*CENTER;
132

133
    _delay_ms( 40 );
134
  }
135
}
Gast #4485623
Lesenswert?

> TIMSK |= (1<<OCIE1A)|(1<<OCIE1B);

Du hast hier zwei Interrupts aktiviert, aber dein Programm enthält nur 
eine Interruptroutine. Das würde ich als erste mal in Ordnung bringen.

Außerdem würde ich der Hauptschleife nicht so viel an den 
Servopositionen herum fummeln. Lass doch einfach mal jedes Servo an eine 
bestimmte feste Position fahren und messe nach, ob die erzeugten Signale 
auch dem entsprechen.
OP #4514649
Lesenswert?

Moin nochmal,

ich brauche noch einmal Hilfe :P
Ich habe mir jetzt einen Atmega 16 besorgt und den Originalen Code 
verwendet. Jetzt bekomme ich ein Signal, dieses ist allerdings zu lang. 
Wenn das Signal zum beispiel 2ms sein soll ist es 20ms (habe ich mit dem 
Oszi gemessen). Den Prescaler habe ich schon einige male überprüft, kann 
aber keinen Fehler feststellen. Die Taktfrequenz wird in dem Code ja mit 
11,056Mhz angegeben, da ich im Datenblatte aber 16Mhz gelesen habe, habe 
ich dieses geändert. Es hat sich allerdings nichts an der Funktion 
geändert.

Hier noch einmal der originale Code:
1
//
2
// Programm fuer einen ATmega16
3
//
4
#define F_CPU 11056000UL
5

6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8
#include <util/delay.h>
9

10
//
11
// Der Prescaler muss so gewählt werden, dass der Ausdruck
12
// für MILLISEC_BASE einen Wert kleiner als 128 ergibt
13
// MILLISEC_BASE ist der Timerwert, der 1 Millisekunde Zeitdauer ergeben
14
// soll.
15
//
16
#define PRESCALER      128
17
#define PRESCALER_BITS (1<<CS22) | ( 1 << CS20 )
18

19
#define MILLISEC_BASE  ( F_CPU / PRESCALER / 1000 )
20
#define CENTER         ( MILLISEC_BASE / 2 )
21

22
//
23
// Konfiguration der Servoleitungen
24
//
25
#define NR_SERVOS      8
26
#define SERVO_DDR      DDRD
27
#define SERVO_PORT     PORTD
28
uint8_t ServoPuls[NR_SERVOS] = { 1<<PD0, 1<<PD1, 1<<PD2, 1<<PD3,
29
1<<PD4, 1<<PD5, 1<<PD6, 1<<PD7 };
30
//
31
// Werte für die Servoposition
32
// Gültige Werte laufen von 0 bis 2 * CENTER
33
// 0           ... ganz links
34
// CENTER      ... Mittelstellung
35
// 2 * CENTER  ... ganz rechts
36
//
37
volatile uint8_t ServoValue[NR_SERVOS];
38

39
ISR (TIMER2_COMP_vect)
40
{
41
  static uint8_t ServoId = 0;
42
  
43
  //
44
  // den Puls des aktuellen Servos beenden
45
  //
46
  SERVO_PORT &= ~ServoPuls[ServoId];
47
  
48
  //
49
  // welches ist das nächste aktuelle Servo?
50
  //
51
  if( ++ServoId >= NR_SERVOS )
52
  ServoId = 0;
53
  
54
  //
55
  // die Ausgangsleitung fuer dieses Servo auf 1; den Puls beginnen
56
  //
57
  SERVO_PORT |= ServoPuls[ServoId];
58
  
59
  //
60
  // den Timer so einstellen, dass bei Pulsende, die ISR erneut aufgerufen wird
61
  //
62
  OCR2 = MILLISEC_BASE + ServoValue[ServoId];
63
}
64

65
void InitServo()
66
{
67
  uint8_t i;
68
  
69
  //
70
  // Die Servoleitungen auf Ausgang stellen
71
  //
72
  SERVO_DDR = ServoPuls[0] | ServoPuls[1] | ServoPuls[2] | ServoPuls[3] |
73
  ServoPuls[4] | ServoPuls[5] | ServoPuls[6] | ServoPuls[7];
74
  
75
  //
76
  // Alle Servos in Mittelstellung
77
  //
78
  for( i = 0; i < NR_SERVOS; ++i )
79
  ServoValue[i] = CENTER;
80
  
81
  //
82
  // Timer auf CTC Modus konfigurieren
83
  //
84
  OCR2 = MILLISEC_BASE + ServoValue[0];
85
  TIMSK |= (1<<OCIE2);
86
  TCCR2 = (1<<WGM21) | PRESCALER_BITS;  // CTC mode
87
}
88

89
int main(void)
90
{
91
  InitServo();
92
  
93
  sei();
94
  
95
  _delay_ms( 1000 );
96
  
97
  //
98
  // testweise einfach alle 8 Servos ansteuern
99
  // jedes Servo soll sich unterschiedlich schnell bewegen
100
  //
101
  while( 1 ) {
102
    
103
    
104
    ServoValue[0] += 2;
105
    if( ServoValue[0] > 2*CENTER )
106
    ServoValue[0] -= 2*CENTER;
107
    
108
    ServoValue[1] += 1;
109
    if( ServoValue[1] > 2*CENTER )
110
    ServoValue[1] -= 2*CENTER;
111
    
112
    ServoValue[2] += 2;
113
    if( ServoValue[2] > 2*CENTER )
114
    ServoValue[2] -= 2*CENTER;
115
    
116
    ServoValue[3] += 3;
117
    if( ServoValue[3] > 2*CENTER )
118
    ServoValue[3] -= 2*CENTER;
119
    
120
    ServoValue[4] += 1;
121
    if( ServoValue[4] > 2*CENTER )
122
    ServoValue[4] -= 2*CENTER;
123
    
124
    ServoValue[5] += 3;
125
    if( ServoValue[5] > 2*CENTER )
126
    ServoValue[5] -= 2*CENTER;
127
    
128
    ServoValue[6] += 2;
129
    if( ServoValue[6] > 2*CENTER )
130
    ServoValue[6] -= 2*CENTER;
131
    
132
    ServoValue[7] += 1;
133
    if( ServoValue[7] > 2*CENTER )
134
    ServoValue[7] -= 2*CENTER;
135
    
136
    _delay_ms( 40 );
137
  }
138
}

vielleicht weiß ja jemand wo der Fehler liegen könnte.
Vielen dank
Paddy
Gast #4514730
Lesenswert?

Mit F_CPU stellst du nicht die gewünschte Taktfrequenz ein!

F_CPU muss der tatsächlichen Taktfrequenz entsprechen. Die Taktquelle 
und der Vorteiler dazu wird durch Fuses festgelegt. Die wiederum müssen 
entsprechend der Beschaltung eingestellt sein.

Welche Taktfrequenz und Quelle hat deine Schaltung tatsächlich?
Gast #4515087
Lesenswert?

mega16 und CKDIV8? Kenne ich nur von den Tiy-µCs

Beim mega16 sind CKSEL, CKOPT und SUT die zuständigen Fuses.
Die stehen bei fabrikfrischen µCs aber auch so, dass etwa
1 MHz intern wirksam sind.

"The device is shipped with CKSEL = “0001” and SUT = “10”.
 The default clock source setting is therefore the 1 MHz
 Internal RC Oscillator with longest startup time. This
 default setting ensures that all users can make their
 desired clock source setting using an In-System or
 Parallel Programmer."

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren