Forum: Mikrocontroller und Digitale Elektronik Servo mit internen PWM (atmega328P) ansteuern - Verständnisfragen


von Manuel (Gast)


Lesenswert?

Guten Morgen liebes Forum!

Zuerst einmal wollte ich mich für das hilfreiche Programmier-"Ebook" 
bedanken, da mir dieses wirklich schon oft den Hintern gerettet hat.
Leider konnte ich meine folgende Frage nicht aus dieser Zusammenschrift 
entnehmen, somit wende ich mich nun an euch!

Die Aufgabenstellung, welche ich zu bewältigen habe, lautet : Steuere 
einen Servo mittels Timer und Poti an, so. Ansteuern möchte ich das 
Ganze mit dem FAST PWM. Ich soll bei einem Takt von 1,6MHz eine Frequenz 
von 50Hz bekommen,aber der Timer sollte nicht höher als 20ms "laufen". 
Meine Idee: Prescaler von 8 verwenden -> 4000 Schritte und diese dann 
bei der Einlesung von ICRA1 mit 5 multiplizieren - ist dies möglich? Den 
doppelten ADC wert hätte ich dann anschließend bei dem "Startwert" 
(OCCR1A) einfach subtrahiert?

Es tut mir leid, falls diese Frage schon x-tausend Mal gestellt worden 
war, dennoch bin ich gerade am verzweifeln!

Liebe Grüße,

Manuel :)

: Verschoben durch Moderator
von Manuel (Gast)


Lesenswert?

Prinzipiell sieht der Code so aus, wobei sich der Servo nicht vom Fleck 
rührt:
1
/*
2
 * Fastpwm_Servo.c
3
 *
4
 * Created: 10.03.2018 13:01:41
5
 * Author : Maenii
6
 */ 
7
8
#include <avr/io.h>
9
#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
10
#define FLIPBIT(ADDRESS,BIT) (ADDRESS ^= (1<<BIT))
11
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
12
#define POTI PORTC0
13
#define servo PORTB1
14
int val_adc;
15
double faktor;
16
17
18
//ADC aktivieren
19
//-------------------------------------------------------------------------------------------------------------------------
20
int adcIn(int channel)
21
{
22
  ADCSRA  =  (1<<ADEN)  | (1<<ADPS1) | (1<<ADPS0);//ADEN: ADC Enable
23
  ADMUX   =  channel;
24
  
25
  //ADMUX  |=  (1<<REFS1) | (1<<REFS0);           // interne 1,1V Referenzspannung
26
  ADMUX  |=   (1<<REFS0);              // +Ub als Referenzspannung
27
  ADCSRA |=  (1<<ADSC);              // alles fertig eingestellt - jetzt start conversion
28
  
29
  while      (ADCSRA    & (1<<ADSC));        // warten bis Konversion zu ende
30
  
31
  ADCSRA &= ~(1<<ADEN);              // weitere Konversionen verhindern und ADC ausschalten (enable - > 0)
32
  return ADCW;                  // Wert retour liefern
33
}
34
//--------------------------------------------------------------------------------------------------------------------------
35
int main(void)
36
{
37
  
38
  // Eingänge und Ausgänge festlegen
39
  SETBIT(DDRB,servo);
40
  CLEARBIT(DDRC,POTI);
41
  
42
  // Fast-PWM mode 14 aktivieren -> WGM11,WGM12,WGM13 = set
43
  TCCR1A |= (1<<WGM11) | (1<<COM1A0);
44
  TCCR1B |= (1<<WGM12) | (1<< WGM13);
45
  
46
  //Prescale 8 - CS11 = set
47
  SETBIT(TCCR1B, CS11);
48
  
49
  //Max-Wert für fast PWM festlegen - ICR1 
50
  ICR1 = 39999;
51
  
52
  
53
    /* Replace with your application code */
54
    while (1) 
55
    {
56
    val_adc = adcIn(POTI);
57
    
58
    faktor = 2*val_adc;
59
    
60
    SETBIT(PORTB,servo);
61
    
62
    OCR1A = ICR1 - (faktor + 2000); 
63
   }
64
}

von Stefan F. (Gast)


Lesenswert?

Wie sieht denn das aktuelle Ausgangssignal aus? Das kannst du mit einem 
Oszilloskop oder Logik-Analysator visualisieren. Kauf Dir ggf. einen, 
die kosten keine 20 Euro.

Zur Fehlersuche könnte es hilfreich sein, erstmal den ADC raus zu lassen 
und ein Signal mit mittlerer Pulsbreite (1,5ms) zu erzeugen.

von Stefan F. (Gast)


Lesenswert?

Lass uns das mal reverse analysieren:
1
PB1 = OC1A
2
COM1A0 = non inverting mode für OC1A
3
WGM11+WGM12+WGM13 = Mode 14 fast PWM (TOP=ICR1)
4
CS11 = Prescaler CLK/8
5
ICR1 = 3999
6
CLK = 1,6Mhz (wirklich? das ist ungewöhnlich)
7
1,6Mhz / 8 / 4000 = 50 Hz
Bis hierhin passt alles.

Ein Zählertakt dauert 1/(1,6Mhz/8) = 5µs

Für 1ms Pulsbreite brauchst du 200 Takte
Für 2ms Pulsbreite brauchst du 400 Takte

Du musst also dafür sorgen, dass dein OC1A auf Werte zwischen 200 und 
400 gesetzt wird. Ich nehme an, dass ist Dir bereits soweit klar.

2*val_adc ergibt Werte von 0 bis 2046.
ICR1 - (faktor + 2000) ergibt Werte zwischen 1999 und -47.

Merkst du was?
Warum subtrahierst du überhaupt von ICR1?

von Peter King (Gast)


Lesenswert?

Ein Servo sollte man immer langsam anfahren. D.h. die ersten Schritte 
langsam ausführen - besonders wenn noch Last dran hängt, die bewegt 
werden muss. Sonst dreht sich das Magnetfeld im Servo aber der Motor 
nicht. ;-)

Bin das erste Mal auch drauf reingefallen.

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.