Servoansteuerung Attiny

Funktionen:

Das Programm erhält über die I2C-Schnittstelle die Pulsweiten für die Ansteuerung von zwei Servos. Die PWM-Frequenz von 50 Hz (= Periodendauer 20 ms), mit der die Impulse an die Servos ausgegeben werden, werden durch einen Timer-Interrupt erzeugt. Wie ein Servo angesteuert wird, habe ich hier bereits gezeigt: Servosteuerung.

Wie ein Timer-Interrupt erzeugt wird, zeige ich hier für einen Uno: Timer-Interrupt.

Beim Attiny unterscheiden sich allerdings die Register etwas, so dass die Beschreibung nicht eins zu eins übernommen werden kann. Hier habe ich bereits die Ansteuerung eines Servos mit einem Attiny gezeigt: Servotest Attiny

Die LED leuchtet als Funktionskontrolle beim Start des Attiny für 1 Sekunde und anschließend bei jedem eintreffenden eines Wertes über die I2C-Übertragung für 0,1 ms auf.


Verwendete Library:

Für die Kommunikation mit dem Uno über die I2C-Schnittstelle (der Attiny ist der "Slave") benötigt man die Library TinyWireS, einen Link zur Library findet ihr hier: Fremd-Libraries


Flashen des Attiny:

Ich betreibe den Attiny45 mit einer Taktfrequenz von 8 MHz. Bevor das Programm auf den Attiny aufgespielt wird, muss der entsprechende Bootlader auf den Attiny übertragen werden. Wie das mit Hilfe eines Uno geht und wie der Attiny mit Hilfe eines Uno geflasht werden kann, zeige ich hier: Attiny programmieren

Programm zur Servoansteuerung von zwei Servos:

//Servo Testprogramm
//Funk-Fernsteuerung_Ansteuerung_Servo_Attiny201.ino
//Code fuer Attiny45 / 8 MHz
//Author Retian
//Version 1.2


#include <TinyWireS.h>
#define attinyI2CAdd 0x07 //I2C-Adresse des ersten Attiny45

#define servoPin1 3 //Servo auf Pin PB3
#define servoPin2 4 //Servo auf Pin PB4
#define ledPin 1 //LED auf Pin 1


volatile int pwmTime1;
volatile int pwmTime2;
volatile bool rxEvent = false; //Merker für I2C-receiveEvent


void setup() {
  pwmTime1 = 1500; //Servo1 in Mittenstellung
  pwmTime2 = 1500; //Servo2 in Mittenstellung
  pinMode(servoPin1, OUTPUT);
  digitalWrite(servoPin1, 0);
  pinMode(servoPin2, OUTPUT);
  digitalWrite(servoPin2, 0);
  pinMode(ledPin, OUTPUT);
  //LED leuchtet beim Start für 1 Sekunde (Funktionskontrolle)
  digitalWrite(ledPin, 1);
  delay(1000);
  digitalWrite(ledPin, 0);


  //Setzen der Register für 20 ms Timerinterrupt
  cli(); // Loesche globales Interruptflag
  TCNT1 = 0; //Loesche Timer Counter 1
  TCCR1 = 0; //Loesche Timer Counter Controll Register
  OCR1C = 155; //Setze Output Compare Register C
  // Setze CS10, CS11 und CS13 - Clock Select Bit 10,11,13 (Prescaler 1024)
  TCCR1 |= (1 << CS10) | (1 << CS11) | (1 << CS13);
  //CTC-Mode ein
  TCCR1 |= (1 << CTC1); // CTC-Mode (Clear Timer and Compare)
  //Timer/Counter Interrupt Mask Register
  TIMSK |= (1 << OCIE1A); //Output Compare A Match Interrupt Enable
  sei(); //Setze globales Interruptflag

  //Initialisiere TinyWireS-Library
  TinyWireS.begin(attinyI2CAdd);
  TinyWireS.onReceive(receiveEvent);
}


void loop() {
  TinyWireS_stop_check();
  if (rxEvent)
  {

    cli();
    //Bei jedem empfangenem Telegramm blinkt die LED für 0,05 ms auf
    digitalWrite(ledPin, 1);
    delayMicroseconds(50);
    digitalWrite(ledPin, 0);

    sei();
    rxEvent = false;
  }
}


ISR(TIMER1_COMPA_vect) //Interrupt-Serviceroutine
{

  //Setzen des Servopins durch Registermanipulation (siehe: Ein-Ausgangsports)

  PORTB |= (1 << PORTB3);
  delayMicroseconds(pwmTime1);
  PORTB &= ~(1 << PORTB3);

  PORTB |= (1 << PORTB4);
  delayMicroseconds(pwmTime2);
  PORTB &= ~(1 << PORTB4);

}


void receiveEvent(uint8_t x)
{
  byte i2cRead[3];
  byte servoNr;
  byte i = 0;
  rxEvent = true;
  while (TinyWireS.available())
  {
    i2cRead[i] = TinyWireS.receive();
    i++;
  }
  //Abfrage, ob Empfaenger Servo1 oder Servo2 ist und jeweilige PWM-Impulszeit
  //aus zwei gesendeten Bytes zu einem Integerwert zusammenbauen
  servoNr = i2cRead[0];
  if (servoNr == 1) pwmTime1 = ((int)i2cRead[1] << 8) + i2cRead[2];
  else if (servoNr == 2) pwmTime2 = ((int)i2cRead[1] << 8) + i2cRead[2];
}