PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Mathematik: 100kHz ISR



Merlin321
26.01.2010, 15:16
Hallo zusammen,
Ich verzweifle daran, eine 100kHz ISR an einem mit 16 MHz getakteten µC hinzubekommen (Servoansteuerung). Könnt ihr mir auf die Sprünge helfen, was das Prescalen etc. angeht, oder was ich auch für Werte gut verwenden kann?

lg
Merlin

XBert
26.01.2010, 16:28
Wenn ich dich richtig verstanden habe möchtest du alle 160 Zyklen eine ISR auslösen. (16MHz/100kHz)

Das kannst du am besten mithilfe des Datenblatts lösen (Timer).
Allerdings ist stark darauf zu achten das innerhalb der ISR nicht viel gemacht wird da du nur 160 Taktzyklen Zeit hast! (Außer du verwendest innerhalb der ISR cli() und sei(), was allerdings bei servoansteuerungen nicht günstig ist)

Nur mal so als Frage: wozu brauchst du dafür 100kHz speed??

MFG

Merlin321
26.01.2010, 16:32
Hey XBert,
Die 100kHz hätte ich gern, um für 1 ms eine Auflösung von 100 zu haben, um die Dinger möglichst genau ansteuern zu können.
Das mit dem Datenblatt werde ich mir mal anschauen, aber ist das nicht bei allen ATmega32ern gleich mit den Prescalern? Mein Punkt ist dass ich das Controllerboard erst in ca. ner Woche bekomme und bis dahin schonmal ein wenig in die Programmierung reinmöchte :)

Merlin

XBert
26.01.2010, 16:45
Ich habs bei meiner Servo-Ansteuerung so gemacht das ich den timer nur da auslöse wenn ich ihn brauche -> Tabellen mit Zeiten und servo-nummer der größe nach sortieren und immer nach der differenz zwischen 2 servos den timer auslösen und nach dem letzten warten bis die insgesamt 20ms vorbei sind.

z.B.:

servo1: 1,5ms
servo2: 2ms
servo3: 1ms

timer (beim ersten mal) = 1ms;
timer (beim zweiten mal) = 0,5ms;
...
timer (am ende) = 20ms-letzerServo

Ich hoffe du verstehst was ich meine :)

EDIT: ich hab vergessen zu sagen das alle servo-pins am anfang auf high gesetzt werden und dann nach ablauf des timers die servos deren Zeit "abgelaufen" ist auf low gesetzt werden.

LG

Merlin321
26.01.2010, 16:53
Zugegeben verstehe ich zwar das Prinzip, aber die Umsetzung erschließt sich mir nicht ganz, kannst du mir da noch nen Ansatz geben? :)

Merlin

XBert
26.01.2010, 17:19
ich werds versuchen:

Pseudocode:

#define SERVO1_LOW PORTA&=~(1<<0)
...
#define SERVO8_LOW PORTA&=~(1<<7)

servozeiten[anzahlServos];
servozahl[anzahlServos];
servo_count = 9;

init(){
for(/*alle servos*/){
servozeiten[i] = 1,5ms; //natürlich musst du das in taktzyklen umrechnen
servozahl[i] = i;
}
}

main(){
init();
for(;;){/*tu was (zb servos setzen)*/}
}

ISR(){
switch(servo_count){
case 0:
SERVO1_LOW;break;
case 1:
SERVO2_LOW;break;
....
case 9:
//sortieren von servozeiten und servozahl (dafür hast du 2^16 zyklen zeit, da der timer max 16bit hat)
//differenz zwischen servozeiten brechnen
//warten bis 20ms vorbei sind (bitte nicht mit delay_ms() sondern den timer so oft setzen und nichts tun bis die 20ms vorbei sind)
//Pins auf high setzen
}
//timer auf nächsten IR setzen
}


Ich hoffe ich hab nix vergessen

LG

wkrug
26.01.2010, 17:23
@Merlin321
So eine Servoansteuerung passiert eigentlich immer mit einem Puls von 1...2ms gefolgt von einer Pause mit ca. 20ms.

Ich bau für meine Servoansteuerungen immer eine ISR im Comparematch Interrupt auf.

Dazu verwendest Du einen 16Bit Timer (Timer 1) und lässt diesen mit Prescaler 8 laufen.
Dabei entspricht dann ein Count 0,5µs, also 1ms einem Zählerwert von 2000.

Der Trick dabei ist nun die Servoimpulse, falls man mehrere Servos verwendet, nacheinander zu erzeugen und nach dem letzten Impuls eine Pause einzufügen, die die gewünschten 20ms voll macht.

Beispiel:
Du hast 4 Servos mit 1,5ms = 6ms.
Die Pause wäre somit 20ms-6ms=14ms = Counterwert 28000.
Die Servoimpulse werden dann, gesteuert von einer Variablen, nacheinander an verschiedenen Ausgängen ausgegeben.
Dazu wird der Zählerstand des TCNT1 Registers ausgelesen, der Wert der gewünschten Servoimpulslänge dazugezählt und dieser ins OCR1A Register für den nächsten Comparematch Interrupt reingeschrieben.
Nun folgt die Pause, wo kein Ausgang aktiv ist und somit kein Servo angesteuert wird.
Mit dieser Methode kann man bis zu 8 Servos ansteuern und der Controller langweilt sich dabei sogar noch.

Codeschnipsel für 2 Servos:

// Timer 1 Comparematch A interrupt service Routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
unsigned int ui_buffer=0;
ui_buffer=TCNT1;

if(uc_servocount==0)
{
SERVO1=0;
SERVO2=0;
ui_buffer+=ui_pause;
OCR1A=ui_buffer;
}

if(uc_servocount==1)
{
SERVO1=1;
SERVO2=0;
ui_buffer+=ui_servo1;
OCR1A=ui_buffer;
}

if (uc_servocount==2)
{
SERVO1=0;
SERVO2=1;
ui_buffer+=ui_servo2;
OCR1A=ui_buffer;
}

uc_servocount++;
if(uc_servocount>2){uc_servocount=0;}
}

Ceos
26.01.2010, 17:44
XBert, deine methiode hat allerdings das problem, wenn da sehr viele servos denselben wert haben, werden die servos immer ein wenig aus der spur gehen, je mehr servos denselben wert haben

ich hätte da ne idee die etwas speicherintensiv ist, aber locker mit 8/16/32 servos klarkommt

man nehme ein char array, das der auflösung für die servosteuerung entspricht, also 1mS(vollausschlag links/rechts) / x Steps, wobei x der anzahl an timerüberläufen entspricht, die in 1mS möglich sind ...

jetzt richtet man sich also den timer ein und das array von der größe x

man legt sich ausserdem eine indexvariable und eine countervariable an

bei counter == 0 schaltet man alle pins auf HIGH und setzt die indexvariable auf 0

in der ISR lässt man jetzt 1ms nichts passieren (mindeststellzeit)

dann geht man mit der indexvariable bei jedem step ein stück vorran durch das array ... jedes byte in dem array steht für 8 servos (je bit ein servo) bei der ersten gefundenen 1(also das bit nicht der wert) wird die leitung für den jeweiligen servo auf LOW gezogen

wenn also bei position 0 ein wert von 4 steht (3tes bit 1) wird der impus des 3ten servo bei 1ms abgeschaltetn (linker anschlag) und wenn bei position x eine 3 steht (1tes und 2tes bit) werden die servos 1 und 2 gestoppt (rechter anschlag)

abschliessend setzt man den index wieder auf 0 und wartet dann 18mS und macht wieder garnichts (man sollte eventuell alle impulsleitungen auf LOW setzen bevor man das tut, kann ja passieren man hat ne 1 übersehen hat ;) )

um die position der servos zu ändern, wartet man zunächst mit
while ( index != 0 ) ;
dass der timer in die pausephase geht, löscht das bit der alten position und schreibt die neue position ... die position für jeden servo sollte man sich immer in einer variable speichern

alles zusammen braucht man dafür x + 10-12 bytes arbeitsspeicher, aber das sollte so ganz ordentlich funktionieren

XBert
26.01.2010, 18:03
XBert, deine methiode hat allerdings das problem, wenn da sehr viele servos denselben wert haben, werden die servos immer ein wenig aus der spur gehen, je mehr servos denselben wert haben


Der meinung bin ich nicht, da es zb bei HS-645MG analog servos eine "dead bandwidth" von 8µs gibt. in diesen 8µs sollte es einem µC (bei 16MHz -> 8µs ~ 128 zyklen) wohl möglich sein 20 oder mehr pins auf low zu setzen;

Dirk
26.01.2010, 18:48
Hallo Merlin321,

zu Deiner Frage am Anfang dieses Posts:
Hier die Initialisierung für eine 100kHz ISR mit Timer1 (M32):

#define F_TIMER1 100000 // Timer 1 frequency (100kHz)

cli();
// Timer 1: Normal port operation, mode 4 (CTC), clk/8
TCCR1A = (0 << COM1A1)
| (0 << COM1A0)
| (0 << COM1B1)
| (0 << COM1B0)
| (0 << FOC1A)
| (0 << FOC1B)
| (0 << WGM11)
| (0 << WGM10);
TCCR1B = (0 << ICNC1)
| (0 << ICES1)
| (0 << WGM13)
| (1 << WGM12)
| (0 << CS12)
| (1 << CS11)
| (0 << CS10);
OCR1A = ((F_CPU/8/F_TIMER1)-1); // 19 at 100kHz

Als ISR nimmst du den TIMER1_COMPA_vect.

Gruß Dirk

uwegw
26.01.2010, 19:11
Für solche Problemstellungen gibt es haufenweise kleine Tools, die dir die Einstellungen berechnen. Mein Favorit:
http://www.b9.com/elect/avr/kavrcalc/
Gewünschte ISR-Frequenz und Quarzfrequenz eingeben, und dann kann man sich für verschiedene Prescaler anzeigen lassen, welche Einstellungen sich ergeben.

Merlin321
27.01.2010, 19:12
Ich danke euch allen, ganz besonders Dirk, genau das habe ich gesucht! :) Langsam aber sicher steige ich zumindest in der Theorie hier durch, inwieweit dass dann in der Praxis klappt sehen wir dann :) Vielen dank aber erst einmal!

Merlin