Arduino ATtiny45 servoproblem

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Arduino ATtiny45 servoproblem

Inlägg av Magnus_K »

Hej!

Mina små ATtiny45:or loggar snart flygtid.
När jag valde MCU så missade jag helt att dessa ATtiny45:or inte har 16-bitars timer, utan bara 8. Detta gör således att jag inte kan använda biblioteket som kommer med Arduino IDE:t.
Efter lite googling så hittar jag något som kallas för "SoftwareServo" och "Servo8bit", men får fasicken ingen av dessa exempelkoder att generera något på utgången.

Jag har ändrat klockan från default 1MHz till 8Mhz och bekräftat att utgången "fungerar" och att klockan går rätt genom att vifta med pinnen.

Fick ett förslag att använda interrupt och en PWM-rutin i denna men här tar det stopp. Är rent usel på Atmels datablad och "språk".

Har ni något förslag på hur jag ska bära mig åt? Någon som kanske har en färdig kodsnutt för ett servo som nyttjar interrupt?

EDIT: Nedan är ett av dom färdigskrivna exemplen för Servo8bit. Denna kompileras utan varningar men inget händer på utgången.
Det är också "pinne 0" jag viftat med och bekräftat fungerar.

Kod: Markera allt

#include "Servo8Bit.h"
 
int main()
{
    Servo8Bit myServo;  //create a servo object.
                        //a maximum of five servo objects can be created 
 
    myServo.attach(0);  //attach the servo to pin PB1
 
    //sweep the servo
    while(1)
    {
        for(int pos = 0; pos <90; pos++)  // goes from 0 degrees to 180 degrees
        {                                   // in steps of 1 degree
            myServo.write(pos);             // tell servo to go to position in variable 'pos'
            delay(15);                      // waits 15ms for the servo to reach the position
        }
 
        for(int pos = 90; pos > 1; pos--)  // goes from 180 degrees to 0 degrees
        {
            myServo.write(pos);             // tell servo to go to position in variable 'pos'
            delay(15);                      // waits 15ms for the servo to reach the position
        }
    }
}
Användarvisningsbild
adent
Inlägg: 4094
Blev medlem: 27 november 2008, 22:56:23
Ort: Utanför Jönköping
Kontakt:

Re: Arduino ATtiny45 servoproblem

Inlägg av adent »

Hur många servon vill du driva med varje ATtiny?

MVH: Mikael
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino ATtiny45 servoproblem

Inlägg av Magnus_K »

Bara ett servo. Fick precis en 50Hz-puls på utgången genom att ladda ner och köra Adafruits bibliotek.
Känns väl som att det borde gå att göra ganska enkelt utan bibliotek?

EDIT: Deras exempelkod som jag modiferat lite:

Kod: Markera allt

#include <Adafruit_SoftServo.h>  // SoftwareServo (works on non PWM pins)

// We demonstrate two servos!
#define SERVO1PIN 0   // Servo control line (orange) on Trinket Pin #0

#define POTPIN   2   // Potentiometer sweep (center) on Trinket Pin #2 (Analog 1)

Adafruit_SoftServo myServo1;
   
void setup() {
  // Set up the interrupt that will refresh the servo for us automagically
  OCR0A = 0xAF;            // any number is OK
  TIMSK |= _BV(OCIE0A);    // Turn on the compare interrupt (below!)
  
  myServo1.attach(SERVO1PIN);   // Attach the servo to pin 0 on Trinket
  myServo1.write(90);           // Tell servo to go to position per quirk
  delay(15);                    // Wait 15ms for the servo to reach the position
}

void loop()  {
  int potValue;  // variable to read potentiometer
  int servoPos;  // variable to convert voltage on pot to servo position
  potValue=analogRead(POTPIN);                // Read voltage on potentiometer
  servoPos = map(potValue, 0, 1023, 0, 89);  // scale it to use it with the servo (value between 0 and 180) 
  myServo1.write(servoPos);                    // tell servo to go to position

  delay(15);                              // waits 15ms for the servo to reach the position
}

// We'll take advantage of the built in millis() timer that goes off
// to keep track of time, and refresh the servo every 20 milliseconds
volatile uint8_t counter = 0;
SIGNAL(TIMER0_COMPA_vect) {
  // this gets called every 2 milliseconds
  counter += 2;
  // every 20 milliseconds, refresh the servos!
  if (counter >= 20) {
    counter = 0;
    myServo1.refresh();
  }
}
Användarvisningsbild
adent
Inlägg: 4094
Blev medlem: 27 november 2008, 22:56:23
Ort: Utanför Jönköping
Kontakt:

Re: Arduino ATtiny45 servoproblem

Inlägg av adent »

Hmmmmm...

Min hjärna har nog stängt av för idag :( Jag hade nog satt 8-bitars-timern på att räkna 0-255 på ungefär 2ms (skalning och klocka).
Sedan hade jag haft overflow-interruptet på och när det slagit 9ggr (ca 18ms) så hade jag satt servopinnen hög, satt ett capture/compare-interrupt på någonstans mellan 127 och 255 (ca 1-2ms). När det interruptet slår, sätt servopinnen låg och disable capture/compare-interruptet och åter väntat på 9ggr overflowinterrupt och gjort om...

Men det kan finns ännu bättre sätt. Brukar gå att knyta en pinne till capture/compare. Fast du hittar nog snart något färdigt som funkar känns det som :)

MVH: Mikael
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino ATtiny45 servoproblem

Inlägg av Magnus_K »

Hehe, nja, det blir nog inget färdigt för den här funktionen ser det ut som. Trodde verkligen att jag skulle hitta en färdig kodsnutt för ATtiny45/85 utan inblandning av bibliotek. Eller ja, något färdigt som fungerar rakt av i alla fall.

Ska göra ett försök med det du säger ikväll. Har hittat en sida där man aktiverar interrupt på en 85a. Återkommer!
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino ATtiny45 servoproblem

Inlägg av Magnus_K »

Sitter och klurar på vad du skriver adent. Menar du så här?:
  • - Ett timer-interrupt som triggas varje period, dvs var 20ms (50Hz)
    - När denna rutin körs så sätter man servopinnen hög samt startar ytterligare en timer-interrupt
    - Den andra timern ställs in på 1-2ms beroende på önskad position av servot och sätter pinnen låg
Skulle man kunna ha en enbart en timer-interrupt som istället triggas väldigt ofta och som istället ökar en räknare.
Beroende på värdet på räknaren så sätts pinnen hög/låg.
Nackdelen är väl att om man vill ha jättebra upplösning på servot så kommer interrupten flaggas jätteofta.

EDIT: Skrivit och lånat lite kod och nu genererar jag en låg->hög var 20ms i alla fall. Härifrån borde det gå att jobba för att sätta pinnen hög->låg med lite mer upplösning. Dock står det helt still hur man kan göra.

Kod: Markera allt

#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>

void setup() {
  pinMode(0, OUTPUT);
}
 
void loop() {
  
}

static inline void initTimer1(void)
{
  TCCR1 |= (1 << CTC1);  // clear timer on compare match
  TCCR1 |= (1 << CS13) | (1 << CS11) | (1 << CS10); //clock prescaler
  OCR1C = 158; // compare match value 
  TIMSK |= (1 << OCIE1A); // enable compare match interrupt
}

ISR(TIMER1_COMPA_vect)
{
  digitalWrite(0, !digitalRead(0));  // toggle state
}

int main(void)
{
  // initializations 
  DDRB = 0x01;         // enable PB0 as output
  initTimer1();        // initialize timer registers
  sei();               // enable interrupts
  
  while(1)
  {
  }
  
  return 0;
}
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Såhär kan man göra :) Lite luddigt men funkar, ServoPosition är där man lagrar servots läge, man bör lägga till någonting som håller servot inom sitt rörelse-område.

Kod: Markera allt

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

#define SERVO_PORT PORTB
#define SERVO_PIN PB2
#define SERVO_FREQ 9 //Högre siffra ger lägre frekvens

uint8_t Counter = 0;
uint8_t ServoPosition = 150;

void setup(){
	DDRB = (1<<SERVO_PIN);
	TCCR0B = (0<<CS02)|(1<<CS01)|(1<<CS00);
	TIMSK = (1<<TOIE0);
	asm("sei");
}

ISR(TIMER0_OVF_vect){
	Counter++;
	if(Counter == SERVO_FREQ){
		SERVO_PORT |= (1<<SERVO_PIN);
		TCNT0 = 255 - ServoPosition;
	}
	if(Counter > SERVO_FREQ){
		SERVO_PORT &= ~(1<<SERVO_PIN);
		Counter = 0;
	}
}

int main(){
	
}
Edit: Kör du det i ArduinoIDE(jag ser att du använder pinMode)? I så fall verkar inte timer0 funka rakt av och sen kanske prescale blir annorlunda, jag tänkte på 8MHz klockfrekvens när jag skrev koden.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino ATtiny45 servoproblem

Inlägg av Magnus_K »

Intressant, och tack för koden! :tumupp:

Ska kika närmare på den imorron och testa lite.
Har insett nu att jag också måste lägga in en rutin för att sluta sända pulser då jag nått position.
Har väldiga problem med jitter...

Japp, använder Arduino IDEt. Ska se hur din kod går att kompilera.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Hmmm...det har nog smugit sig in nått fel i min kod för det funkar lite skumt faktiskt. Och ArduinoIDE verkar bråka när man försöker använda Timer0 så om du ska koda där behöver du nog använda en annan timer.
Jag ska också kolla lite mer på detta imorn.

Edit: Aha, jag vet nu varför det blir lite konstigt när man gör som jag gjorde. I databladet:
Bits 7:0 – TCNT0[7:0]: TC0 Counter Value
The Timer/Counter Register gives direct access, both for read and write operations, to the Timer/Counter
unit 8-bit counter. Writing to the TCNT0 Register blocks (removes) the Compare Match on the following
timer clock. Modifying the counter (TCNT0) while the counter is running, introduces a risk of missing a
Compare Match between TCNT0 and the OCR0x Registers.


Det kan vara så att timern alltid snurrar ett varv extra vilket gör pulsen ett varv längre än vad man laddar med. Detta går ju iofs att räkna lite på när det gäller ett servo eftersom det inte ska ha en för kort puls, det går att vinna lite upplösning på det ^^
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino ATtiny45 servoproblem

Inlägg av Magnus_K »

Gjorde en liten hybrid mellan ditt förslag och min kod. Mest för att använda Timer1 istället.
Nedan kod genererar en 50Hz-signal med 1700µs pulsbredd.
Det är ungefär 3-4µs jitter på pulsen men då servot har ett "dead-band" på 8µs så borde det inte vara ett problem.

Tyvärr händer knappt ett skit med servot. Lite surr och ryck.
Får se vad som händer om jag matar från separat agg istället för mitt kort.

Kod: Markera allt

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

#define F_CPU 8000000UL
#define SERVO_PORT PORTB
#define SERVO_PIN PB0
#define SERVO_FREQ 11 //Högre siffra ger lägre frekvens


uint8_t Counter = 0;
uint8_t ServoPosition = 150;

static inline void initTimer1(void)
{
  TCCR1 |= (1 << CTC1);  // clear timer on compare match
  TCCR1 |= (1 << CS13); // | (1 << CS11) | (1 << CS10); //clock prescaler
  OCR1C = 255; // compare match value 
  TIMSK |= (1 << OCIE1A); // enable compare match interrupt
}

ISR(TIMER1_COMPA_vect)
{ 
        Counter++;
        if(Counter == SERVO_FREQ){
            SERVO_PORT |= (1<<SERVO_PIN);
            OCR1C = 255 - ServoPosition;
        }
        if(Counter > SERVO_FREQ){
            SERVO_PORT &= ~(1<<SERVO_PIN);
            Counter = 0;
        }
 
}

int main(void)
{
  // initializations 
  DDRB = 0x01;         // enable PB0 as output
  initTimer1();        // initialize timer registers
  sei();               // enable interrupts
  
  while(1)
  {
  }
  
  return 0;
}
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino ATtiny45 servoproblem

Inlägg av Magnus_K »

I felsökningssyfte tänkte jag försöka svepa servot med nuvarande kod.. Hur bär man sig åt egentligen?
Vill svepa mellan 1500µs-1900µs. Det är mellan 135-161 med variabeln som kallas ServoPosition.

Programmet verkar strunta helt i min switch() utan låser sig på värdet som jag deklarerar i början.

Kod: Markera allt

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

#define F_CPU 8000000UL
#define SERVO_PORT PORTB
#define SERVO_PIN PB0
#define SERVO_FREQ 11 //Högre siffra ger lägre frekvens


uint8_t Counter = 0;
uint16_t SweepCounter = 0;
uint8_t ServoPosition = 150; // 161 = 1500us, 135 = 1900us 

static inline void initTimer1(void)
{
  TCCR1 |= (1 << CTC1);  // clear timer on compare match
  TCCR1 |= (1 << CS13); // | (1 << CS11) | (1 << CS10); //clock prescaler
  OCR1C = 255; // compare match value 
  TIMSK |= (1 << OCIE1A); // enable compare match interrupt
}

ISR(TIMER1_COMPA_vect)
{ 
        Counter++;
        SweepCounter++;
        if(Counter == SERVO_FREQ){
            SERVO_PORT |= (1<<SERVO_PIN);
            OCR1C = 255 - ServoPosition;
        }
        if(Counter > SERVO_FREQ){
            SERVO_PORT &= ~(1<<SERVO_PIN);
            Counter = 0;
        }
 
}

int main(void)
{
  // initializations 
  DDRB = 0x01;         // enable PB0 as output
  initTimer1();        // initialize timer registers
  sei();               // enable interrupts


while(1){

    int range = map(SweepCounter, 0, 1000, 0, 3);

switch (SweepCounter) {
  case 0:
    ServoPosition = 135;
    break;
  case 1:
    ServoPosition = 145;
    break;
  case 2:
    ServoPosition = 154;
    break;
  case 3:
    ServoPosition = 161;
    SweepCounter = 0;
    break;
  }
}
}
Användarvisningsbild
adent
Inlägg: 4094
Blev medlem: 27 november 2008, 22:56:23
Ort: Utanför Jönköping
Kontakt:

Re: Arduino ATtiny45 servoproblem

Inlägg av adent »

Hej!

Edit: Ändrat tusen småsaker och indentering och grejjer. Börjar närma sig nu :)

Prova detta!

Lade till raden: OCR1C = 255; // Maybe another value?
och skippade lite variabler och förenklade testkoden i main, den kan vara enkel for now :)

Kod: Markera allt

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

#define F_CPU 8000000UL
#define SERVO_PORT PORTB
#define SERVO_PIN PB0
#define SERVO_FREQ 11 //Högre siffra ger lägre frekvens


uint8_t Counter = 0;
uint8_t ServoPosition = 150; // 161 = 1500us, 135 = 1900us 

static inline void initTimer1(void)
{
  TCCR1 |= (1 << CTC1);  // clear timer on compare match
  TCCR1 |= (1 << CS13); // | (1 << CS11) | (1 << CS10); //clock prescaler
  OCR1C = 255; // compare match value 
  TIMSK |= (1 << OCIE1A); // enable compare match interrupt
}

ISR(TIMER1_COMPA_vect)
{ 
        Counter++;        
        if(Counter == SERVO_FREQ){
            SERVO_PORT |= (1<<SERVO_PIN);
            OCR1C = 255 - ServoPosition;
        }
        if(Counter > SERVO_FREQ){
            SERVO_PORT &= ~(1<<SERVO_PIN);
            Counter = 0;
            OCR1C = 255;  // Maybe another value?
        }
 
}

int main(void)
{
  // initializations 
  DDRB = 0x01;         // enable PB0 as output
  initTimer1();        // initialize timer registers
  sei();               // enable interrupts


  while(1)
  {
    _delay_ms (1000);
    ServoPosition = 135;
    _delay_ms (1000);
    ServoPosition = 145;
    _delay_ms (1000);
    ServoPosition = 154;
    _delay_ms (1000);
    ServoPosition = 161;
  }
}
Användarvisningsbild
ffredrik
Inlägg: 340
Blev medlem: 20 oktober 2009, 17:52:18
Ort: Göinge

Re: Arduino ATtiny45 servoproblem

Inlägg av ffredrik »

Globala variabler som accessas i ISR bör deklareras volatile, t ex:
volatile uint8_t Counter = 0;
så att kompilatorn behandlar variabeln korrekt.
Vet dock inte om det är avgörande här.
Användarvisningsbild
adent
Inlägg: 4094
Blev medlem: 27 november 2008, 22:56:23
Ort: Utanför Jönköping
Kontakt:

Re: Arduino ATtiny45 servoproblem

Inlägg av adent »

Helt rätt!

Så länge man kör utan optimering kan det gå bra ändå dock.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino ATtiny45 servoproblem

Inlägg av Magnus_K »

Underbart, tack!
Ändrade OCR1C = 255; till OCR1C = 105;, vilket la frekvensen exakt på 50Hz.

Koden tycks fungera exakt som tänkt men upptäckte att min egna lilla buck-omvandlare inte tycks fungera.
Matade servot från labbagget och då fungerar allt perfekt. Matar jag från mitt kort så ser jag djupa dippar (6V->4V) på matningen som följer PWM-frekvensen. Den hinner antagligen inte att reglera och jag kan kasta det här förbannade projekt i soporna. Usch, switchat skit. Borde förbjudas :wink:

Tack än en gång för programmeringshjälpen! :tumupp:
Skriv svar