Hallo
Hier ein Versuch mit Timer1 und 18 Servos. Da steigt dann mein RP6 aus, vermutlich ein Zeitüberlauf in der ISR:
Code:
// 18 Servos ansteuern mit Mega32 und 16-Bit Timer1 24.3.2008 mic
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#define systemtakt 1 // 1 bei 8MHz, 2 bei 16MHz-Prozessortakt
#define grundimpuls 47 // grundimpuls + 125 sollte Servomitte sein
// Servoausgänge 1-8
#define servoinit {DDRB |= (1<<PB7); PORTB &= ~(1<<PB7); DDRC |= 0b01110000; PORTC &= ~0b01110000;}
#define servo1on PORTC |= (1<<PC4)
#define servo1off PORTC &= ~(1<<PC4)
#define servo2on PORTC |= (1<<PC5)
#define servo2off PORTC &= ~(1<<PC5)
#define servo3on PORTC |= (1<<PC6)
#define servo3off PORTC &= ~(1<<PC6)
#define servo4on PORTB |= (1<<PB7)
#define servo4off PORTB &= ~(1<<PB7)
#define servo5on PORTB |= (1<<PB0) // Dummyservos 4-9 an SL6
#define servo5off PORTB &= ~(1<<PB0)
#define servo6on PORTB |= (1<<PB0)
#define servo6off PORTB &= ~(1<<PB0)
#define servo7on PORTB |= (1<<PB0)
#define servo7off PORTB &= ~(1<<PB0)
#define servo8on PORTB |= (1<<PB0)
#define servo8off PORTB &= ~(1<<PB0)
#define servo9on PORTB |= (1<<PB0)
#define servo9off PORTB &= ~(1<<PB0)
volatile uint8_t p; // 20ms-Timer
uint16_t servo1, servo2, servo3, servo4, servo5, servo6, servo7, servo8, servo9;
/************************* Ausgabe an Terminal ********************************/
void writeChar(char ch) {while (!(UCSRA & (1<<UDRE))); UDR = (uint8_t)ch;}
void writeString(char *string) {while(*string) writeChar(*string++);}
void writeInteger(int16_t number, uint8_t base)
{char buffer[17]; itoa(number, &buffer[0], base); writeString(&buffer[0]);}
/******************************************************************************/
int main(void)
{
/************************ UART-Setup für RP6 *******************************/
#define BAUD_LOW 38400 //Low speed - 38.4 kBaud
#define UBRR_BAUD_LOW ((F_CPU/(16*BAUD_LOW))-1)
UBRRH = UBRR_BAUD_LOW >> 8; // Baudrate is Low Speed
UBRRL = (uint8_t) UBRR_BAUD_LOW;
UCSRA = 0x00;
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
UCSRB = (1 << TXEN) | (1 << RXEN) | (1 << RXCIE);
/***************************************************************************/
//Timer1 Initialisierung
TCCR1A = 0;
TCCR1B = (0<<CS12) | (1<<CS11) | (1<<CS10); // Prescaler /64
TCCR1B|= (1<<WGM12); // CTC-Mode
OCR1A=100; // 100 Takte bis zum ersten Interrupt
OCR1B=100;
TIMSK |= (1 << OCIE1A); // Das klappt so leider nur mit Timer1A?
//TIMSK |= (1 << OCIE1B);
servo1=60; // Drehbereich ist ca. 10-245!
servo2=125;
servo3=190;
servo4=245;
servo5=125;
servo6=125;
servo7=125;
servo8=125;
servo9=125;
//servo1=servo2=servo3=servo4=servo5=servo6=servo7=servo8=servo9=60; // Test
servoinit; // Datenrichtung der Servopins einstellen
sei(); // ... und los!
while(1) // Hauptschleife
{
servo4=125; // ab hier funktioniert es mit meinen RP6 nicht mehr!
p=50; while(p); // Das sollte ungefähr 50*20ms=1 Sekunde verzögern
servo4=125;
p=50; while(p);
}
return(0);
}
ISR (TIMER1_COMPB_vect)
{
uint16_t temp=grundimpuls;
static uint8_t servo_nr=1;
static uint16_t impulspause=3000;
if(servo_nr==1) {temp+=servo1; servo1on; if(p) p--;}
if(servo_nr==2) {temp+=servo2; servo1off; servo2on;}
if(servo_nr==3) {temp+=servo3; servo2off; servo3on;}
if(servo_nr==4) {temp+=servo4; servo3off; servo4on;}
if(servo_nr==5) {temp+=servo5; servo4off; servo5on;}
if(servo_nr==6) {temp+=servo6; servo5off; servo6on;}
if(servo_nr==7) {temp+=servo7; servo6off; servo7on;}
if(servo_nr==8) {temp+=servo8; servo7off; servo8on;}
if(servo_nr==9) {temp+=servo9; servo8off; servo9on;}
if(servo_nr >9) {temp =impulspause; servo9off; servo_nr=0;}
OCR1B=temp*systemtakt;
if(servo_nr) impulspause-=temp; else impulspause=3000;
servo_nr++;
}
ISR (TIMER1_COMPA_vect)
{
uint16_t temp=grundimpuls;
static uint8_t servo_nr=1;
static uint16_t impulspause=3000;
switch(servo_nr)
{
case 1: temp+=servo1; servo1on; if(p) p--; break;
case 2: temp+=servo2; servo1off; servo2on; break;
case 3: temp+=servo3; servo2off; servo3on; break;
case 4: temp+=servo4; servo3off; servo4on; break;
case 5: temp+=servo5; servo4off; servo5on; break;
case 6: temp+=servo6; servo5off; servo6on; break;
case 7: temp+=servo7; servo6off; servo7on; break;
case 8: temp+=servo8; servo7off; servo8on; break;
case 9: temp+=servo9; servo8off; servo9on; break;
default:temp =impulspause; servo9off; servo_nr=0; break;
}
OCR1A=temp*systemtakt;
if(servo_nr) impulspause-=temp; else impulspause=3000;
servo_nr++;
}
Mit systemtakt kann man zwischen 8 oder 16MHz wählen. Das ist einfach nur ein Multiplikator der im 16MHz-Modus alle Timerwerte mit 2 multipliziert. Ob das wie gedacht funktioniert kann ich nicht testen weil ich keinen 16MHz-Mega32 habe.
Grundsätzlich funktionieren so immerhin 9 Servos an Timer1A. Allerdings wird bei mir dann die Hauptschleife nicht mehr ausgeführt. Eigentlich sollten die minimalen (64*grundimpuls)-Takte zur Ausführung der ISR ausreichen, ich weiß noch nicht, warum mein RP6 bockt.
Wenn ich (nur) die ISR für COMPB aktiviere funktioniert nichts (nur der Impuls für das erste Servo wird gestartet aber nie beendet) An den unterschiedlichen Varianten der ISRs liegts anscheinend nicht denn wenn man die über kreuz umbenennt bleibt das Verhalten gleich. Scheinbar ist da ein Unterschied zwischen TIMER1A und TIMER1B dessen Beschreibung ich im Datenblatt noch nicht gefunden habe. Oder ich mache einen blöden Denk- oder Anfängerfehler den ich nicht erkenne :(
Gruß
mic
Lesezeichen