Mega16, ADC, PWM, Serwo

To forum jest dla wszystkich pasjonatów mikrokontrolerów AVR Atmela. Wymiana doświadczeń i pomoc dla początkujących w pisaniu programów zarówno w C, Asemblerze jak i BASCOM. Zapraszam znawców tematu, aby pomogli wszystkim początkującym!
ODPOWIEDZ
Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Mega16, ADC, PWM, Serwo

Post autor: Dawid2379 » 28 maja 2013, 22:56

Jak pisałem niedawno chciałem napisac kod dla sterowanie serwami za pomocą uC. Narazie napisałem kod dla jednego serwa.
Wykorzystałem Atmege16. FCPU 8MHz.
PWM dla serwa
Użyłem licznika 16 bitowego. Preskaler 8. ICR20000 i mamy 50Hz.

Najcieżej było pogodzić 10bitowa rozdzielczość ADC z rejestrem OCR1A. Wiadomo ADC ma wartość maksymalna 1024, potrzebny jest impuls szer. od ok 0,5 do ok 2,5 ms. dlatego potrzebujemy do OCR1A wisac wartości ok 500-2500 aby uzyskac impulsy do sterowania serwem.
Ja to zrobiłem tak że wartość ADC pomnożyłem przez trzy i podstawiałem pod OCR1A. Niżej są pomierzone voltomierzem i analizatorem stanów logicznyc wartości dla minimalnego i maksymalnego wychylenie orczyka serwa:
min:
0,48V - 0,600ms. - ADC=200 - OCR1A=630
max:
2,2V - 2,600ms. - ADC=900 - OCR1A=2700

f= 52.00 Hz
Miałem tez problem z przekroczeniem jak i za niską wartością OCR1A przy kręceniu potencjometrem (kiedy to potencjometr był w skrajnych pozycjach), co powodowało że impuls był za waski bądż za szeroki dla serwa, dokladnie to widać na filmie, to też rozwiązalem programowo. Mały minus że steruje serwem w pewnym zakresie potencjometru, ale i tak wiecej go wykorzystuje.

Kod: Zaznacz cały

if (ADC<210)
		   { OCR1A = 630;}         
			  
  else if (ADC>900)
		   { OCR1A = 2700;} 
Filmik
http://www.youtube.com/watch?v=iqof8xaW ... e=youtu.be

I cały kod

Kod: Zaznacz cały

#define F_CPU 8000000UL
#include <stdio.h> 
#include <avr/io.h> 
#include <util/delay.h>
     
#define LED1 PD7
#define ADCIN PA3
  

int main(void)  
{ 
  DDRD  |= (1<<LED1); //LED Pins as Out
  DDRA &=~ (1<<ADCIN); 
  ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS0);   
  ADMUX  |= (1<<MUX1)|(1<<MUX0)|(1<<REFS1)|(1<<REFS0);
  DDRB = 0xFF;
  PORTB = 0xFF;
  DDRC = 0xFF;
  PORTC = 0xFF;
  DDRD = 0xFF;
  PORTD = 0xFF;	
  TCCR1A|=(1<<COM1A1)|(1<<COM1B1)|(1<<WGM11);		 	
  TCCR1B|=(1<<WGM13)|(1<<WGM12)|(1<<CS11);
  ICR1 = 20000;
   while(1) 
    {          
     uint16_t x;                
     
    { 
     ADCSRA |= (1<<ADSC); 
        while(ADCSRA & (1<<ADSC)); 
		  x = ADC*3;
 
 
  if (ADC<210)
		   { OCR1A = 630;}         
			  
  else if (ADC>900)
		   { OCR1A = 2700;} 
  else		           
	          { OCR1A = x;} 		  

  if(ADC<500) 
  {  
    PORTD  &=~ (1<<LED1);    
  } 
   else   
  {  
    PORTD  |= (1<<LED1);    
  }  		     
} 	    
} 
}
Jeśli można coś poprawić/zmienić proszę pisać.
Załączniki
Bez tytułu.png
Bez tytułu.png (5.89 KiB) Przejrzano 7014 razy
Ostatnio zmieniony 29 maja 2013, 18:53 przez Dawid2379, łącznie zmieniany 2 razy.

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 29 maja 2013, 18:49

Mam jeszcze pytanie odnośnie sterowanie drugim serwem, z tym mam mały problem. Oczywiście drugie wyjście na serwo to OCR1B, to jasne. Co jednak z ADC?? Teraz używam jednego wejścia czyli pinu 3 ADCIN3, a chce użyć kolejnego kanału np ADCIN4, tylko jak mam odczytać z nich wartości osobno bo teraz to wygląda tak:

Kod: Zaznacz cały

ADCSRA |= (1<<ADSC);
        while(ADCSRA & (1<<ADSC));
          x = ADC*3; 
Jak to zrobić aby za pomocą pierwszego potencjometru sterować pierwszym serwem, a za pomocą drugiego drugim serwem???

Awatar użytkownika
kayron
Użytkownik
Posty: 2088
Rejestracja: 21 wrz 2008, 12:53
Lokalizacja: Poland
Kontakt:

Post autor: kayron » 29 maja 2013, 19:31

Przejrzyj sobie ten temat:
https://forum.elportal.pl/viewtopic.php?t=10442
Co prawda BASCOM, ale idea, kierowania wieloma serwami może ci się przydać :).

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 31 maja 2013, 16:38

Myślę ciągle o kanałach ADC, mam tylko dwa serwa wiec chciałbym uzyć koniecznie OCR1A oraz OCR1B do ich sterowania, podpowie mi ktoś jak za pomocą multipleksera programowo wykonac pomiar na dóch dowolnych kanałach ADC i zapisać w jakiejś zmiennej aby następnie ją wykorzystać ?? proszę o jakąś podpowiedź.

Nie można po prostu włączać i wyłączać w trakcie programu odpowiednich bitów MUX?
np.

Kod: Zaznacz cały

if (...)
    ............jakiś kod
    ADMUX &=~ (1<<MUX0);
   ADMUX |= (1<<MUX3);
    jakiś kod.........

Awatar użytkownika
kayron
Użytkownik
Posty: 2088
Rejestracja: 21 wrz 2008, 12:53
Lokalizacja: Poland
Kontakt:

Post autor: kayron » 31 maja 2013, 17:42

Można. Przełączasz sobie po prostu odpowiednie kanały, tylko trzeba pamiętać, że nie można tego robić podczas trwającej konwersji, a i samo przełączenie chwilę trwa, jak i wydłuża nieco cykl pomiaru.
Ogólnie jeżeli wystarczy ci 100 pomiarów/sek na kanał, to spokojnie.

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 31 maja 2013, 18:05

No i cos mi nie wychodzi. Kombinuję tak:

Kod: Zaznacz cały

if (m==1)  
    { 
	ADMUX &=~ |(1<< MUX0);
	ADMUX |= (1<< MUX1);
     ADCSRA |= (1<<ADSC); 
        while(ADCSRA & (1<<ADSC)); 
          OCR1A = ADC*3; 
else if (m==2)

    }
	{        	;
	ADMUX |= (1<< MUX1)|(1<< MUX0);
     ADCSRA |= (1<<ADSC); 
        while(ADCSRA & (1<<ADSC)); 
          OCR1B ADC*3;
     }

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 31 maja 2013, 18:28

Działa:) Okazalo się że kod nie musi być wcale skomplikowany dla obsługi dwóch serw i diody led

Kod: Zaznacz cały

#include <avr/io.h> 
#include <util/delay.h> 
      
#define LED1 PD7 //dioda LED na Pd7
 
  

int main(void)  
{ 
  DDRD  |= (1<<LED1); // dioda LED   
  ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS0); //uruchominie przetwornika, preskaler 32   
  ADMUX  |= (1<<REFS1)|(1<<REFS0); //wewnętrzne źródło 2,56V
  DDRB = 0xFF; 
  PORTB = 0xFF; 
  DDRC = 0xFF;   // nieuzywane porty jako wyjscia
  PORTC = 0xFF; 
  DDRD = 0xFF; 
  PORTD = 0xFF;    
  TCCR1A|=(1<<COM1A1)|(1<<COM1B1)|(1<<WGM11);  // Fast PWM mode (14), prescaler 8           
  TCCR1B|=(1<<WGM13)|(1<<WGM12)|(1<<CS11); 
  ICR1 = 20000;       // 50 HZ dla serwa, 20ms 
   while(1) 
    {          
     uint16_t x;   // zmienna dla kanału OCR1A
	 uint16_t y;   // zmienna dla kanału OCR1B            
      
   for(;;){{
    ADMUX &=~ (1<<MUX0);  // skasuj
    ADMUX |= (1<<MUX1);  // ustaw kanal ADC2 PA2
     ADCSRA |= (1<<ADSC); //start pomiaru
        while(ADCSRA & (1<<ADSC)); 
          x = ADC*3; 
 
    if (ADC<200) 
           { OCR1A = 600;}  //minimalne wychylenie orczyka serwa,         
            
    else if (ADC>930) 
           { OCR1A = 2790;} //maksymalne wychylenie orczyka serwa,
    else                    
              { OCR1A = x;}  // jesli nie przekraczaja podanych wartosci OCR1A = x (ADC*3)
    } 
    {            ; 
    ADMUX |= (1<< MUX1)|(1<< MUX0);   // ustaw kanal ADC2 PA2
     ADCSRA |= (1<<ADSC);   //start pomiaru
        while(ADCSRA & (1<<ADSC)); 
          y = ADC*3;
		 
		 if (ADC<200) 
           { OCR1B = 600;}          
              
        else if (ADC>930) 
           { OCR1B = 2790;} 
        else                    
              { OCR1B = y;}   
     }
           

  if(ADC<500) 
  {  
    PORTD  &=~ (1<<LED1);  // jeśli ADc mniej niż 500 gasi diode
  } 
   else    
  {  
    PORTD  |= (1<<LED1);   //jesli inaczej zaswieca 
  }                
}         
}}

ODPOWIEDZ