Arduino ATtiny45 servoproblem

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Skrev du detta...

Kod: Markera allt

        if(freqCounter == SERVO_FREQ){
            SERVO_PORT |= (1<<SERVO_CMD_PIN);
            OCR1C = 255 - ActiveServoPositionPulse;
        }
...som en variant av detta som jag skrev...

Kod: Markera allt

    if(Counter == SERVO_FREQ){
        SERVO_PORT |= (1<<SERVO_PIN);
        TCNT0 = 255 - ServoPosition;
    }
Bara så att du är observant på att det inte funkar helt likadant. I mitt exempel blir pulsens längd "ServoPosition"(högre värde, längre puls) medan i din kod blir pulsens längd "255 - ActiveServoPositionPulse"(högre värde, kortare puls).

Sen borde du använda...

Kod: Markera allt

TCCR1 |= (1 << WGM12);
...istället för...

Kod: Markera allt

TCCR1 |= (1 << CTC1);]
...fast det har förmodligen inte med felet att göra utan funkar nog ändå

Edit: Förlåt, CTC1 är rätt
Senast redigerad av Glattnos 4 mars 2018, 19:50:30, redigerad totalt 1 gång.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Sen ska det väl vara OCR1A överallt där du har OCR1C?

Edit: Förlåt igen, jag gräver ju i fel datablad.
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 »

Ska ta en kik lite senare på det du skrivit Glattnos. Dock fungerar själv pulsningen kanon just nu så vill inte röra något timer-relaterat förrän jag löst det nuvarande problemet.

Är det alltid ok att göra så här?

uint16_t = uint8_t;

dvs flytta värdet från en mindre variabel till en större? Blir värdet alltid det samma i dom båda då?

EDIT: Blä. Provade map() funktionen men det fungerar inte heller. Måste vara något med ADC:n och att analogRead() inte fungerar i mitt program.
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 »

Din kod här är helt korrekt:

ServoPositionOpenCal = analogRead(SERVO_OPEN_PIN);
ServoPositionOpenCal = (ServoPositionOpenCal >> 6) & 0xF;


Förmodligen får du för lågt värde från ADn så resultatet blir 0.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Jag petade lite i koden men kan inte testa den själv.

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_CMD_PIN PB0
#define SERVO_CLOSE_PIN PB3
#define SERVO_OPEN_PIN PB4
#define RADIO_OPEN_PIN PB1
#define RADIO_CLOSE_PIN PB2
#define SERVO_FREQ 11 //Högre siffra ger lägre frekvens


volatile uint8_t freqCounter = 0;
volatile uint16_t calCounter = 0;
volatile uint8_t calState = 1;
volatile uint8_t ServoPositionClose = 150;
uint8_t ServoPositionCloseCal = 0; // 8-bit value
volatile uint8_t ServoPositionOpen = 146;
uint16_t ServoPositionOpenCal = 0;
uint8_t ActiveServoPositionPulse = 148; // 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
	
	ADMUX |= (1 << ADLAR) | 0b10; //ADC Left adjusted, ADC-pin PB4
	ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADPS2);
}

ISR(TIMER1_COMPA_vect)
{
	freqCounter++;
	
	if(calState){
		calCounter++;
		
		if(calCounter >= 0xFFFF){
			calState = 0;
		}
	}
	if(freqCounter == SERVO_FREQ){
		SERVO_PORT |= (1<<SERVO_CMD_PIN);
		OCR1C = 255 - ActiveServoPositionPulse;
	}
	if(freqCounter > SERVO_FREQ){
		SERVO_PORT &= ~(1<<SERVO_CMD_PIN);
		freqCounter = 0;
		OCR1C = 105;
	}
	
}

int main(void)
{
	// initializations
	pinMode(SERVO_CMD_PIN, OUTPUT);
	pinMode(SERVO_CLOSE_PIN, INPUT);
	pinMode(SERVO_OPEN_PIN, INPUT);
	pinMode(RADIO_OPEN_PIN, INPUT);
	pinMode(RADIO_CLOSE_PIN, INPUT);
	
	initTimer1();        // initialize timer registers
	sei();               // enable interrupts
	
	
	while(1)
	{
		if(calState){
			
			ServoPositionOpenCal = ADCH; //Read ADCH
			ServoPositionOpenCal = (ServoPositionOpenCal >> 4); //Shift 4 steps
			
		}
		
		_delay_ms (1000);
		ActiveServoPositionPulse = ServoPositionOpen + ServoPositionOpenCal;
		
	}
}
Jag är inte helt säker på att man kan använda Arduino-kommandon, jag har provat att byta ut det som har med ADC:n att göra men det finns risk att det krockar med Arduinos "dolda kommandon".
Senast redigerad av Glattnos 4 mars 2018, 20:40:12, redigerad totalt 2 gånger.
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 »

@ffredrik:Har trott något sånt också men då blink-programmet fungerade så klockrent och delay(adc-värde) stämde så precis så uteslöt jag det.

Efter blod, svett och tårar (mest tårar) har jag fått igång det nu. Tyvärr har jag inte en aning om varför det blir så här.
Tyckte mig komma fram till det verkligen är något tok med analogRead() i mitt program så sökte runt på nätet efter kod som kunde hjälpa mig att konfigurera ADC:n manuellt och göra en manuell utläsning (eller vad man ska kalla det).

Hittade lite kod och testade och visst, fungerade hur bra som helst på första försöket.
Det jag alltså inte har en aning om är varför analogRead() inte fungerar i mitt program. Är det för att man stället om timers och annat?

Koden nedan fungerar alltså precis som tänkt. Obs. Här läser jag bara ut 8-bitar så skiftar enbart bort 4 LSBs.

EDIT: Hehe, du tänkte precis rätt Glattnos :D Ska jämföra programmen lite och se skillnaderna. Ditt program är mycket renare än mitt ihopklistrade klabb.

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_CMD_PIN PB0
#define SERVO_CLOSE_PIN PB3
#define SERVO_OPEN_PIN PB4
#define RADIO_OPEN_PIN PB1
#define RADIO_CLOSE_PIN PB2
#define SERVO_FREQ 11 //Högre siffra ger lägre frekvens
 
 
volatile uint8_t freqCounter = 0;
volatile uint16_t calCounter = 0;
volatile uint8_t calState = 1;
static uint8_t ServoPositionClose = 150;
volatile uint16_t ServoPositionCloseCal = 0;
static uint8_t ServoPositionOpen = 146;
volatile uint16_t ServoPositionOpenCal = 0;
volatile uint8_t ActiveServoPositionPulse = 148; // 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)
{
        freqCounter++;
        
        if(calState){
          calCounter++;
    
          if(calCounter >= 0xFFFF){
              calState = 0;
          }
        }        
        if(freqCounter == SERVO_FREQ){
            SERVO_PORT |= (1<<SERVO_CMD_PIN);
            OCR1C = 255 - ActiveServoPositionPulse;
        }
        if(freqCounter > SERVO_FREQ){
            SERVO_PORT &= ~(1<<SERVO_CMD_PIN);
            freqCounter = 0;
            OCR1C = 105;
        }
 
}
 
int main(void)
{
  // initializations
  pinMode(SERVO_CMD_PIN, OUTPUT);
  pinMode(SERVO_CLOSE_PIN, INPUT);
  pinMode(SERVO_OPEN_PIN, INPUT);
  pinMode(RADIO_OPEN_PIN, INPUT);
  pinMode(RADIO_CLOSE_PIN, INPUT);

  initTimer1();        // initialize timer registers
  sei();               // enable interrupts
  initADC();
 
 
  while(1)
  {
    if(calState){
     
      ADCSRA |= (1 << ADSC);         // start ADC measurement
      while (ADCSRA & (1 << ADSC) ); // wait till conversion complete
      ServoPositionOpenCal = ADCH >> 4;
      
    }
          
    _delay_ms (1000);
    ActiveServoPositionPulse = ServoPositionOpen + ServoPositionOpenCal;

  }

  return 0;
  
}

void initADC()
{ 
  ADMUX =
            (1 << ADLAR) |     // left shift result
            (0 << REFS1) |     // Sets ref. voltage to VCC, bit 1
            (0 << REFS0) |     // Sets ref. voltage to VCC, bit 0
            (0 << MUX3)  |     // use ADC2 for input (PB4), MUX bit 3
            (0 << MUX2)  |     // use ADC2 for input (PB4), MUX bit 2
            (1 << MUX1)  |     // use ADC2 for input (PB4), MUX bit 1
            (0 << MUX0);       // use ADC2 for input (PB4), MUX bit 0

  ADCSRA = 
            (1 << ADEN)  |     // Enable ADC 
            (1 << ADPS2) |     // set prescaler to 64, bit 2 
            (1 << ADPS1) |     // set prescaler to 64, bit 1 
            (0 << ADPS0);      // set prescaler to 64, bit 0  
}
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Då satt vi och gjorde precis samma sak. Jag tror inte att DigitalRead() funkar på en ATiny45. Bra att du fått det att funka :)
Du behöver inte starta ADC convertion och vänta varje gång du ska läsa, ADCn går i FreeRunning mode om man inte sätter någon av ADTS-bitarna. Kolla hur jag lagt in det i initTimer1 rutinen, nu vet jag inte om det funkar eftersom jag inte har en ATiny att testa med.
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 »

Du är ju fantastisk Glattnos! Satt och läste om freerunning-läget precis och du svarade på exakt vad jag sökte :)
Kommer finlira lite nu med hjälp av din kod men onekligen var det detta som felade.

Då jag nyttjar två pottar, en för att kalibrera öppningsvinkel, och en för stängnigsvinkeln, så blir det lite kul utmaning att få till det här i koden. Men nu har jag förutsättningarna i alla fall.
Tack så hemskt mycket för all tid du/ni lagt ner :tumupp:
Lägger självklart upp det färdiga programmet sen. Kanske alltid hjälper någon.
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 »

Nä det vill inte. Tänk vad tid man lägger ner på att harva bland dessa kodsnuttar och det blir aldrig som man vill. Suck och stön.

Det är så konstigt. Vill bara byta kanal i MUX:en så jag får värdet från ADC3 istället för ADC2.
Utläsningen och allt som har med ADC2 fungerar klockrent men 3:an vill sig inte.
Som jag förstår från databladet så ändrar man bara i MUX-registret till rätt kanal, ger ADC:n tid för en ny konvertering, och sen är det bara att läsa ur värdet. Men icke...

Vet ni om man ska ändra något mer när jag byter kanal? Tycker inte jag hittar något om det i databladet så borde gå bra...

Kod: Markera allt

while(1)
  {
    if(calState){

      ADCSRA |= (1 << ADSC);                                        // Start the ADC conversion
      while (ADCSRA & (1 << ADSC) );                                // Wait until conversion complete
      
      ServoPositionOpenCal = ADCH;                                  // Write ADC-value
      ServoPositionOpenCal = (ServoPositionOpenCal >> 4) & 0xF;     // Decrease resolution  
      ADMUX = (1 << MUX0)  |  (1 << MUX1);                          // Change to MUX to ADC3 

      ADCSRA |= (1 << ADSC);                                        // Restart the ADC conversion
      while (ADCSRA & (1 << ADSC) );                                // Wait until conversion complete

      ServoPositionCloseCal = ADCH;                                 // Write ADC-value
      ServoPositionCloseCal = (ServoPositionCloseCal >> 4) & 0xF;   // Decrease resolution
      ADMUX = (0 << MUX0)  |  (1 << MUX1);                          // Change to MUX to ADC2
      
    }
          
    _delay_ms (1000);
    ActiveServoPositionPulse = ServoPositionOpen - ServoPositionOpenCal;
    _delay_ms (1000);
    ActiveServoPositionPulse = ServoPositionClose + ServoPositionCloseCal;

  }
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 »

Vilken slamkrypare... Då jag byter kanal i MUX:en så nollas tydligen ADLAR vilket gör att ADC-värdet blir högerjusterat istället (säger man så?).
Oavsett så löstes problemet genom att lägga till | (1 << ADLAR) när jag byter kanal. Pust...

EDIT: Eller ja, sättet jag manipulerar ADMUX-registret skriver även över (nollar) ADLAR-biten. Värt att tänka på... Tur att dom satt Vcc som Vref 00 i mitt fall :)
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Du kan köra en ADC-avbrottsrutin istället när du ska ta in värdet från flera pinnar, då kan du i avbrottsrutinen rotera mellan de pinnar du vill läsa och spara respektive värde i dedikerade variabler som du sedan använder i ditt huvudprogram. Då kan man läsa alla tillgängliga ADC-pinnar utan att det tar någon extra tid i huvudprogrammet :)

Och helt rätt, istället för..

Kod: Markera allt

ADMUX = (1 << ADLAR) | (0 << MUX0) | (1 << MUX1);
...så kan du ju...

Kod: Markera allt

ADMUX |= (1 << MUX1);  //Set bit
ADMUX &= ~(1 << MUX0);  //Clear bit
Det är bra att ta för vana att bara ändra de bitar som man faktiskt ämnar ändra för att hindra konstiga fel som inträffar när man råkar ändra viktiga bitar av misstag.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Detta funkar hyffsat bra. Du kan lägga in kod under ADC-avbrottsrutinen som byter ADC-pinne och sparar i en egen variabel.
Dock har jag skrivit koden för en ATtiny13 som var det närmaste jag hade hemma, men koden funkar i alla fall bra på den och jag tror inte att det är stor skillnad mot en ATtiny45.
Denna kod ger nästan full 8-bitars upplösning på servo-positionen. Det är lite olika på olika servon så man kan ju få justera vissa värden. Ett Hitec HS-422 som jag hade liggande funkar uppenbarligen bra i spannet 700-2400us.

Kod: Markera allt

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

#define SERVO_PORT PORTB
#define SERVO_DDR DDRB
#define SERVO_PIN PB3
#define SERVO_FREQ 20 //Frequency-period in ms
#define TIMER0_MS 150 //150 for 9.6 MHz or 125 for 8 MHz gives 1ms
#define SHORTEST_SERVO_PULSE 90 //90 for 9.6 MHz gives 600us

volatile uint8_t Counter = 0;
volatile uint8_t ServoPosition;
volatile uint8_t ADC_Value;

ISR(ADC_vect){
	ADC_Value = ADCH; //Read and store the ADC value
	ADCSRA |= (1 << ADSC); //Start new conversion
}

ISR(TIM0_COMPA_vect){
	Counter++; //Increase Counter by 1	
	if(Counter == SERVO_FREQ){ //If Counter equals to SERVO_FREQ...
		SERVO_PORT |= (1<<SERVO_PIN); //...then set the servopin...
		OCR0A += SHORTEST_SERVO_PULSE;  //...and make next compare match in 600us
	}	
	else if(Counter == SERVO_FREQ + 1){ //If this was the ShortestServoPulse..
		OCR0A += ServoPosition; //...then make next compare match in "ServoPosition"
	}
	else if(Counter >= SERVO_FREQ + 2){  //If this is the end of the pulse...
		SERVO_PORT &= ~(1<<SERVO_PIN);  //...then clear the servopin...
		OCR0A += TIMER0_MS;  //...and make next compare match in 1ms
		Counter = 0; //Restart by clearing Counter
	}
	else OCR0A += TIMER0_MS;  //Else make next compare match in 1ms
}

void setup(){
	//Setup I/O
	SERVO_DDR = (1<<SERVO_PIN);
	//Setup Timer0
	TCCR0B = (0<<CS02)|(1<<CS01)|(1<<CS00); //Precsale clk/64
	OCR0A = TIMER0_MS; //Make a compare match in 1ms
	TIMSK0 = (1<<OCIE0A); //Enable compare match Interrupt
	//Setup ADC
	ADMUX = (1 << ADLAR) | (1<<MUX1); //ADC Left adjusted, ADC-pin PB4
	ADCSRA = (1 << ADEN) | (1<<ADIE) | (1 << ADSC) | (1 << ADPS2); //Enable ADC, Enable ADC Interrupt, StartConversion, Set prescaler
	//Enable global interrupts
	asm("sei");
}

int main(){
	setup();
	
	while(1){
		if(ADC_Value > 5 && ADC_Value < 250) ServoPosition = ADC_Value; //Limit the range
		}
}
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 »

Tack (igen) Glattnos! :D
Ska sätta mig med det här ikväll igen och försöka modifiera din kod.

Kommer klura under dagen men om man är en jäkel på det här programmering, borde det inte gå att få till så man i början sätter pulslängd för max moturs och pulslängd för max medurs.
Baserat på dessa värden så justeras klockan så man får max antal bitar på det spannet?
Alltså väldigt "dynamiskt" och anpassningsbar kod beroende på vilket servo du har.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino ATtiny45 servoproblem

Inlägg av Glattnos »

Det är en sån lösning jag skulle vilja få till men samtidigt vill jag inte belasta processorn med onödigt mycket konverteringar av värden. Men jag är bara hobby-kodare :D
Vad menar du med "så justeras klockan"? Går 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 »

Du gör väldigt bra ifrån dig för att "bara" vara hobby-kodare!

Klockan var nog fel, menade kanske mer timer/prescaler men det blir för rörigt i min skalle när man måste hålla ~50Hz samtidigt.
Det är bra som det är :D
Skriv svar