PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem beim Konfigurieren von TIMER1 beim M32-Board



dirty_bird_981
09.01.2012, 21:13
Hallo an Alle!

Ich benötige eine ISR die alle 1µs aufgerufen wird. Ich habe versucht mir dafür den ungenutzten TIMER1 des M32-Boards zu konfigurieren, aber da ich auf diesem Gebiet Null Durchblick habe, ist es mir bislang nicht gelungen.
Kann mir bitte jemand den Code geben mit dem TIMER1 dementsprechend konfiguriert wird und wie die ISR dann aussehen muss?

Danke schonmal im Vorraus!

Magelan1979
10.01.2012, 20:53
Moin, vielleicht hilft Dir ja das weiter

https://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169

dirty_bird_981
10.01.2012, 20:59
Das habe ich auch schon gefunden, nur leider habe ich keinen Schimmer wie ich das was da rauskommt in C schreiben muss.

radbruch
10.01.2012, 21:18
Hallo

1µs ist aber wenig. Das m32 taktet mit 16MHz, ein Takt dauert 1s/16000000Hz = 0,0000000625 Sekunde. Eine Mikrosekunde dauert 0,000001s oder 0,000001s/0,0000000625s = 16Takte! (verblüffend;)

Was willst du denn mit der 1µs anstellen?

Gruß

mic

dirty_bird_981
10.01.2012, 21:23
Ich möchte damit meine Servos steuern, dies habe ich bislang über den schon vorhandenen Timer0 gemacht, wo ich meine Routine für die Steuerung mit hinzugeschrieben habe:

if(servo_timer>servo1_pos_d) PORTD &= ~IO_PD6; else PORTD |= IO_PD6;
if(servo_timer>servo2_pos_d) PORTC &= ~IO_PC4; else PORTC |= IO_PC4;
if(servo_timer>servo3_pos_d) PORTC &= ~IO_PC6; else PORTC |= IO_PC6;

if(servo_timer<200) servo_timer++; else servo_timer=0;


Allerdings ist die Auflösung bei 100µs recht bescheiden (ca. 17 mögliche Servo-Positionen).

Beim näheren darüber nach denken muss ich aber feststellen das 1µs wirklich etwas wenig sind, 10µs wären (mit dann ca. 170 möglichen Positionen) ja fast schon auf 1° genau. Also 10µs reichen!

radbruch
10.01.2012, 22:21
Ach, es geht um Servoimpulse, alles klar.

Vielleicht wäre das eine Basis:

// Servoansteuerung mit 16Bit-Timer1 (mit arexx cat16) mic 18.1.2011

// Die 8 Servos werden an Port B und C angeschlossen:
// Servo0 - PB0
// Servo1 - PB1
// Servo2 - PC2
// Servo3 - PC3
// Servo4 - PC4
// Servo5 - PC5
// Servo6 - PC6
// Servo7 - PC7

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t count_20ms;
volatile uint16_t servo[9] ={1700, 1700, 1700, 1700, 1700, 1700, 1111, 1111, 40000}; // Servopositionen

void sleep(uint8_t pause); // 1/50 Sekunde blockierende Pause

int main(void)
{
cli();

// für 16MHz-ATMega16
TCCR1A = (0<<WGM11)|(0<<WGM10); // CTC-Mode 4
TCCR1B = (0<<WGM13)|(1<<WGM12);
TCCR1B |= (0<<CS12)|(1<<CS11)|(0<<CS10); // prescaler /8
TIMSK = (1<<OCIE1A); // MatchCompare-Interrupt erlauben

DDRB = 0b00000011; // Datenrichtung Servoausgänge setzen
DDRC = 0b11111100;

sei();
sleep(100);

while(1) // Demo
{
cli();
servo[0]=2500;
servo[1]=servo[2]=1600;
sei();
sleep(50);

cli();
servo[0]=servo[1]=servo[2]=1700;
sei();
sleep(50);
}
return(0);
}

ISR(TIMER1_COMPA_vect)
{
static uint8_t nr=8; // Nummer des aktuellen Servos

PORTB &= ~0b00000011; // alle Servoimpulse low
PORTC &= ~0b11111100;

if(nr < 3) // Impuls für Servo oder Pause? (8 Servos)
{
if(nr<2) PORTB |= (1<<nr); // Impulsleitung des aktuellen Servos
else PORTC |= (1<<nr); // auf High setzen und
OCR1A = servo[nr]; // Impulslänge in OCR1A laden
servo[8] -= servo[nr]; // Impulslänge von der Pause abziehen
nr++; // nächstes Servo
}
else
{
OCR1A = servo[8]; // servo[8] ist die Impulspause
servo[8] = 40000; // Startwert 20ms laden
nr = 0; // beim nächsten ISR-Aufruf Impuls
// für Servo 0 erzeugen
if(count_20ms) count_20ms--; // blockierende Pause aktiv?
}
}
void sleep(uint8_t pause) // 1/50 Sekunde blockierende Pause
{
count_20ms=pause+1;
while(count_20ms);
}
(Aus https://www.roboternetz.de/community/threads/54583-Code-Optimierung-f%C3%BCr-Interrupt-m%C3%B6glich?p=522751&viewfull=1#post522751)

Das Programm wurde für einen 16MHz-Mega16 geschrieben, sollte aber auch auf dem Mega32 laufen. Der Timer1 läuft im CTC-Mode mit Prescaler/8 von 0 bis 40000 in 20ms. Während eines Zyklus werden nacheinander jeweils die Impulse für bis zu 8 Servos erzeugt. Die Servopositionen sind in servo[0] bis servo[7] gespeichert, 2000 sind eine Millisekunde (0,001s). In servo[8] ist der Wert für das Zyklusende gespeichert, ein kleinerer Wert (muss aber größer als die Summe der Servos sein, z.B TCNT1+=10) erhöht die Wiederholfrequenz. Mit if(nr < 3) kann man die Anzahl der verwendeten Servos ändern.

Gruß

mic

dirty_bird_981
10.01.2012, 22:25
Wird denn in dem Beispiel der Timer1 (ISR(TIMER1_COMPA_vect)) alle 10µs ausgeführt? Und was muss ich wie ändern um das auf dem Mega32 mit 10µs auszuführen?

radbruch
10.01.2012, 22:50
Warum bestehst du nun auf den 10µs? Diese Variante ruft die ISR in einem 20ms-Zyklus genau Servos+1 mal auf. Viel Resourcenschonender gehts nimmer und das auch noch quasi fertig zur Verwendung. Du brauchst nur noch die Pins eintragen:


// Servoansteuerung mit 16Bit-Timer1 mic 10.1.2012

// Drei Servos an PD6,PC4 und PC6
// https://www.roboternetz.de/community/threads/56242-Problem-beim-Konfigurieren-von-TIMER1-beim-M32-Board

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t count_20ms;
volatile uint16_t servo[9] ={1700, 1700, 1700, 1700, 1700, 1700, 1111, 1111, 40000}; // Servopositionen

void sleep(uint8_t pause); // 1/50 Sekunde blockierende Pause

int main(void)
{
cli();

// für 16MHz-ATMega16
TCCR1A = (0<<WGM11)|(0<<WGM10); // CTC-Mode 4
TCCR1B = (0<<WGM13)|(1<<WGM12);
TCCR1B |= (0<<CS12)|(1<<CS11)|(0<<CS10); // prescaler /8
TIMSK = (1<<OCIE1A); // MatchCompare-Interrupt erlauben

//DDRB = 0b00000011; // Datenrichtung Servoausgänge setzen
//DDRC = 0b11111100;
DDRC |= (1<<PC6)|(1<<PC4);
DDRD |= (1<<PD6);

sei();
sleep(100);

while(1) // Demo
{
cli();
servo[0]=2500;
servo[1]=servo[2]=1600;
sei();
sleep(50);

cli();
servo[0]=servo[1]=servo[2]=1700;
sei();
sleep(50);
}
return(0);
}

ISR(TIMER1_COMPA_vect)
{
static uint8_t nr=8; // Nummer des aktuellen Servos

//PORTB &= ~0b00000011; // alle Servoimpulse low
//PORTC &= ~0b11111100;
PORTC &= ~((1<<PC6)|(1<<PC4));
PORTD &= ~(1<<PD6);

if(nr < 3) // Impuls für Servo oder Pause? (8 Servos)
{
//if(nr<2) PORTB |= (1<<nr); // Impulsleitung des aktuellen Servos
//else PORTC |= (1<<nr); // auf High setzen und
if(nr==0) PORTD |= (1<<PD6);
else if(nr==1) PORTC |= (1<<PC4);
else if(nr==2) PORTC |= (1<<PC6);
OCR1A = servo[nr]; // Impulslänge in OCR1A laden
servo[8] -= servo[nr]; // Impulslänge von der Pause abziehen
nr++; // nächstes Servo
}
else
{
OCR1A = servo[8]; // servo[8] ist die Impulspause
servo[8] = 40000; // Startwert 20ms laden
nr = 0; // beim nächsten ISR-Aufruf Impuls
// für Servo 0 erzeugen
if(count_20ms) count_20ms--; // blockierende Pause aktiv?
}
}
void sleep(uint8_t pause) // 1/50 Sekunde blockierende Pause
{
count_20ms=pause+1;
while(count_20ms);
}

dirty_bird_981
10.01.2012, 22:52
Na ja, eigentlich wollte ich gerne meine eigene Methode verwenden, weil ich gerne möglichst viel von dem was da passiert verstehen möchte. Und das tut man am besten wenn man es sich selber erarbeitet hat.

Ich habe das o.g. Beispiel mal kopiert und ausprobiert. Die Servos gehen beim Programmstart auf die fest eingestellten Positionen, aber wenn man in der Hauptschleife andere Werte einstellt, tut sich gar nichts.