FAQ | Points | Add... | Recent posts | Search | Register | Log in


Atmega16 generowanie przebiegów pwm na 16 serw


Post new topic  Reply to topic      Main Page -> Forum Index -> Microcontrollers Generally -> AVR Microcontrollers -> Atmega16 generowanie przebiegów pwm na 16 serw
Author
Message
Majtek
Poziom 12
Poziom 12


Joined: 28 Jan 2006
Posts: 93
Location: kalisz

Post#1 Post from the author of the topic 20 Apr 2011 20:21   

Atmega16 generowanie przebiegów pwm na 16 serw


Witam.
Tworzę sobie programik do sterowania 16 serwami. wykorzystuję 2 timery
timer2 generuje przez przerwanie odstępy 2.2ms. każdy impuls to obsługa 2 serwów, więc aby odsłużyć wszystkie mamy 8 impulsów(18.4ms) więc impulsy na serwo powtarzają się częściej niż te 20ms. więc tu jest dobrze.

Timer 2 steruje timerem1, blok A obsługuje pierwszą 8 serw, a blok B drugą 8 serw.
Mam taki kod i on dział dobrze.
Code C - [expand]
 
void SerwaInit()
{
i=0;
//ustawienie portów gdzie są podłączone serwa jako wyjscia
DDRSERWOA=0xFF;
#ifdef SERWA16
DDRSERWOA=0xFF;
#endif
//inicjacja poszczególnych serw
int i;
for( i=0;i<8;i++)
{
serwa[i].enable=0;
serwa[i].nrPort=i;
serwa[i].skok=2;
serwa[i].pos=(1.0f/667.0f)/(64.0f/(float)F_CPU); //1.5ms 667hz
serwa[i].max=(1.0f/455.0f)/(64.0f/(float)F_CPU); //2.2ms 455hz
serwa[i].min=(1.0f/1250.0f)/(64.0f/(float)F_CPU); //0.8ms 1250hz
#ifdef SERWA16
serwa[i+8].enable=0;
serwa[i+8].nrPort=i;
serwa[i+8].skok=2;
serwa[i+8].pos=(1.0f/667.0f)/(64.0f/(float)F_CPU); //1.5ms 667hz
serwa[i+8].max=(1.0f/455.0f)/(64.0f/(float)F_CPU); //2.2ms 455hz
serwa[i+8].min=(1.0f/1250.0f)/(64.0f/(float)F_CPU); //0.8ms 1250hz
#endif
}
//timer1
//Tryb CTC timera
 
TCCR1B|=(1<<WGM12) | (1<<CS10)|(1 << CS11);//preskaler 64 i tryb CTC
OCR1A=(1.0f/667.0f)/(64.0f/(float)F_CPU); //375-1.5ms 200-0.8ms 549-2.2ms dla zegara 16mhz //ustaw 1.5ms(neutrum serwa) 667hz
#ifdef SERWA16
OCR1B=(1.0f/667.0f)/(64.0f/(float)F_CPU); //375-1.5ms 200-0.8ms 549-2.2ms dla zegara 16mhz //ustaw 1.5ms(neutrum serwa) 667hz
#endif
 
OCR2=(1.0f/417.0f)/(256.0f/(float)F_CPU);// coś około 2.6ms 385hz
// tryb OCT | preskaler 256
TCCR2=_BV(WGM21) | _BV(CS22) | _BV(CS21);
TIMSK|=(1<<OCIE2);//włączenie przerwania compare
}
 
 
//przerwanie timera2 compare
// po koleji wszystkie serwa przechodzi, każde przerwanie to kolejna para serw
// przetwarza 2 naraz 0 i 8 ,1 i 9, 2 i 10 itd.
ISR(TIMER2_COMP_vect)
{
PORTSERWOA^=(1<<5);
 
////obsługa serw 2 naraz z timera1A i timera1B
//pierwsza 8 serw na timer1A
if(serwa[i].enable==1)//jak Serwo włączone to generuj mu impuls
{
//pierwsza 8 serw
TCNT1=0;//licznik timera1, zerujemy
OCR1A=serwa[i].pos;
TIFR|=(0<<OCF1A);//wyzerowanie flagi CTC
PORTSERWOA|=(1<<serwa[i].nrPort);//ustaw 1
while(bit_is_clear(TIFR,OCF1A))
{//i tu jest pies pogrzebany
}
PORTSERWOA&=~(1<<serwa[i].nrPort);//ustaw 0
}
//druga 8 serw na timer1B do poprawy tak jak 1 8 ma wygladac
//wyrzucone
 
if(++i>7) i=0;//nastepne serwa, jak koniec to do poczatku
}
 


o co chodzi? a no o ten kawałek
Code:

while(bit_is_clear(TIFR,OCF1A))
      {//i tu jest pies pogrzebany
      }

on zabiera cały czas procesora, było by ok gdyby było tylko generowanie przebiegów dla serw, ale ten procek jeszcze coś innego musi robić
więc wymyśliłem taki kod
Code C - [expand]
 
 
//inicjacja taka sama
 
//przerwanie timera2 compare
// po koleji wszystkie serwa przechodzi, każde przerwanie to kolejna para serw
// przetwarza 2 naraz 0 i 8 ,1 i 9, 2 i 10 itd.
ISR(TIMER2_COMP_vect)
{
PORTSERWOA^=(1<<5);
 
////obsługa serw 2 naraz z timera1A i timera1B
//pierwsza 8 serw na timer1A
if(serwa[i].enable==1)//jak Serwo włączone to generuj mu impuls
{
//pierwsza 8 serw
TCNT1=0;//licznik timera1, zerujemy
OCR1A=serwa[i].pos;
//to jest inne
TIMSK |= (1 << OCIE1A); // Zezwolenie na przerwania dla CTC
PORTSERWOA&=~(1<<serwa[i].nrPort);//ustaw 1
 
}
//druga 8 serw na timer1B do poprawy tak jak 1 8 ma wygladac
/////
 
if(++i>7) i=0;//nastepne serwa, jak koniec to do poczatku
}
 
//timer1a
ISR(TIMER1_COMPA_vect)
{
PORTSERWOA|=(1<<serwa[i].nrPort);//ustaw 1
 
//TIMSK &= ~(1 << OCIE1A);/
 
}
 
//timer1b
//////
 


i co jest, niby generuje przebiegi ale ma o innym wypełnieniu do tego niektóre się zmieniają jak inna ilość serw ma generowany sygnał.
Wie ktoś jak rozwiązać ten problem, bo mnie zaczyna przerastać.
Chcę po prostu pozbyć się tej pętli i robić to w przerwaniu.
Wtedy zostaje w głównej pętli więcej czasu aby coś tam zrobić zależności czasowe dla 1 kodu
Atmega16 generowanie przebiegów pwm na 16 serw
zależności czasowe dla 2 kodu żółty jakoś 200us
Atmega16 generowanie przebiegów pwm na 16 serw
Back to top
   
Szumlus
Poziom 11
Poziom 11


Joined: 23 Jan 2006
Posts: 69
Location: Tarnów

Post#2 20 Apr 2011 22:58helpful post - solution   

Re: atmega16 generowanie przebiegów pwm na 16 serw


Witam,

Pomysł dobry ale sypie się najprawdopodobniej z powodu zbyt dużej ilości przerwań.

Proponuje projekt sterownika "równoległego", taki wykorzystałem w mojej pracy inżynierskiej. Wprawdzie korzystałem tylko z 8 serw ale 16 też obsłuży po drobnej modyfikacji.

Program zabiera większość mocy procesora do obsługi przerwań ale tylko przez czas 2ms potem procek jest całkowicie "wolny". Może realizować inne zadania.

Kod wymaga niewielkich modyfikacji ale myślę że dasz radę.
[code]
Code C - [expand]
 
 
 
 
 
/*
************************************************************************
Nazwa pliku - servos.h
 
Kompilator - AVR Studio 4.17 + WinAVR 2010.01.10
 
Autor - Marcin Szumlański
 
Data - 10-03-2010
 
Procesor - ATmega16/32
 
Opis :
 
************************************************************************
*/

#ifndef _SERVOS_H_
#define _SERVOS_H_
 
 
#define cbi(sfr,b) (sfr &=~(1<<b))
#define sbi(sfr,b) (sfr |=(1<<b))
 
//port Servo
#define ServoPort PORTA
#define ServoDir DDRA
 
//kanały Servo
#define ServoChannel0 0
#define ServoChannel1 1
#define ServoChannel2 2
#define ServoChannel3 3
#define ServoChannel4 4
#define ServoChannel5 5
#define ServoChannel6 6
#define ServoChannel7 7
 
//min liczba przerwań T0 do odliczenia aby długośc imp nie trwała mniej niż 0,76ms
#define ServoInterruptMin 76
//max liczba przerwań To do odliczenia aby długośc imp nie trwała dłużej niż 2,06ms
#define ServoInterruptMax 130+ServoInterruptMin
 
//pozycje domyślne (inicjalizacyjne) serv w stopniach
#define ServoDefault0 0+ServoInterruptMin
#define ServoDefault1 0+ServoInterruptMin
#define ServoDefault2 0+ServoInterruptMin
#define ServoDefault3 0+ServoInterruptMin
#define ServoDefault4 0+ServoInterruptMin
#define ServoDefault5 0+ServoInterruptMin
#define ServoDefault6 0+ServoInterruptMin
#define ServoDefault7 0+ServoInterruptMin
 
//inicjalizacja sterownika Servo (konfig T0,T1 i I/O)
void ServosInit(void);
 
//ustawia/pobiera pozycję serwa w określonym kanale format wyskalowany w stopniach (0-130)
void ServoSetPosition(unsigned char Channel, unsigned char Position);
unsigned char ServoGetPosition(unsigned char Channel);
 
#endif
 


Code C - [expand]
 
 
 
#include <avr/io.h>
#include <avr/interrupt.h>
#include "servos.h"
 
volatile unsigned char ServoInterruptCounter = 0;
volatile unsigned char ServoPerioid[8];
 
void ServosInit(void)
{
ServoDir = 0xFF; //Port PWM=Output
ServoPort = 0x00; //Zerowanie wyść
 
//Timer1
OCR1A = 0x04E2; //1250
//Odmaskowanie przerwania CTC
sbi(TIMSK,OCIE1A);
//Tryb CTC,presk.=256(przerwanie co 20ms dla F_CPU 16Mhz)
TCCR1B = _BV(WGM12)|_BV(CS12);
 
//Timer0
OCR0 = 20;
//Tryb CTC,presk.=8(przerwanie co 0,01ms dla F_CPU 16Mhz)
sbi(TCCR0,WGM01);
//Odmaskowanie przerwania CTC
sbi(TIMSK,OCIE0);
}
 
//ustawia pozycję serwa w określonym kanale format wyskalowany
void ServoSetPosition(unsigned char Channel, unsigned char Position)
{
ServoPerioid[Channel] = Position+ServoInterruptMin;
}
 
//pobiera pozycję serwa w określonym kanale format wyskalowany w stopniach
unsigned char ServoGetPosition(unsigned char Channel)
{
return(ServoPerioid[Channel]-ServoInterruptMin);
}
 
 
//OBSŁUGA PRZERWAŃ
ISR(TIMER1_COMPA_vect)
{
//wyjścia PWM w stan wysoki
ServoPort = 0xFF;
//zerowanie T0
TCNT0 = 0x00;
//start T0
sbi(TCCR0,CS01);
}
 
ISR(TIMER0_COMP_vect)
{
//jeśli liczba przerwań odpowiada wypełnieniu kanału zeruj odpowiedni pin I/O
if(ServoInterruptCounter == ServoPerioid[ServoChannel0])
{
cbi(ServoPort,ServoChannel0);
}
if(ServoInterruptCounter == ServoPerioid[ServoChannel1])
{
cbi(ServoPort,ServoChannel1);
}
if(ServoInterruptCounter == ServoPerioid[ServoChannel2])
{
cbi(ServoPort,ServoChannel2);
}
if(ServoInterruptCounter == ServoPerioid[ServoChannel3])
{
cbi(ServoPort,ServoChannel3);
}
if(ServoInterruptCounter == ServoPerioid[ServoChannel4])
{
cbi(ServoPort,ServoChannel4);
}
if(ServoInterruptCounter == ServoPerioid[ServoChannel5])
{
cbi(ServoPort,ServoChannel5);
}
if(ServoInterruptCounter == ServoPerioid[ServoChannel6])
{
cbi(ServoPort,ServoChannel6);
}
if(ServoInterruptCounter == ServoPerioid[ServoChannel7])
{
cbi(ServoPort,ServoChannel7);
}
//inkrementujemy licznik przerwań T0
ServoInterruptCounter++;
//jeśli liczba przerwan > max wyłącz T0 i zeruj licznik
if(ServoInterruptCounter > ServoInterruptMax)
{
cbi(TCCR0,CS01);
ServoInterruptCounter = 0;
}
}
 



Wyznacznikiem maksymalnej liczby serw jest czas wykonywania przerwania Timer0, musi się zakończyć zanim T0 zdąży się przepełnić i wywołać kolejne przerwanie. Oryginalnie to część programu sterująca modelem ramienia, przystosowana do czasów serw Tower Pro 955. Jak kolega ma inne możliwe że trzeba będzie troszkę pozmieniać ustawienia programu. Jeśli program nie zdążył by się wyrobić można zoptymalizować procedurę obsługi przerwania w asemblerze.
:D. U mnie procesor oprócz obsługi serw komunikował się z PC przez RS232 na 19200 bod. protokół miał 12 bajtową ramkę wysyłaną z częstotliwością 50Hz i nie było żadnych problemów.

Co do kodu kolegi....jak coś ma być szybko zrobione unikamy jakichkolwiek pętli iteracyjnych!!! Wpisujemy wszystko na "sztywno" tzn żadnych pętli po tablicy tylko wypisujemy w słupku Tablica[0]=...Tablica[1]=....więcej flash zajmie ale prędkość wykonania błyskawiczna. Polecam podglądać kod dissasemblera można dużo się dowiedzieć i obliczyć czas wykonywania danej funkcji a w takich "napiętych" aplikacjach to niezastąpione.
W razie czego śmiało pytać.

Pozdrawiam,
Marcin
Back to top
   
Google

Google Adsense


Post# 20 Apr 2011 22:58helpful post - solution   





Back to top
   
Majtek
Poziom 12
Poziom 12


Joined: 28 Jan 2006
Posts: 93
Location: kalisz

Post#3 Post from the author of the topic 21 Apr 2011 06:39   

Re: atmega16 generowanie przebiegów pwm na 16 serw


fajny patent :)
wypróbuje go wieczorkiem. Na taki sposób sterowanie serwami nie wpadłem, do tego bardzo dużą ilość serw może obsłużyć, kosztem ilości kroków serwa.
Back to top
   
Google

Google Adsense


Post# Post from the author of the topic 21 Apr 2011 06:39   





Back to top
   
Szumlus
Poziom 11
Poziom 11


Joined: 23 Jan 2006
Posts: 69
Location: Tarnów

Post#4 23 Apr 2011 21:37   

Re: atmega16 generowanie przebiegów pwm na 16 serw


Wersja programu którą wrzuciłem obsługiwała rozdzielczość do 1 stopnia.
Klasyczne serwa ze średniej półki cenowej i tak większej dokładności nie osiągną więc nie ma co przesadzać z precyzją w sofcie.
Back to top
   
Google

Google Adsense


Post# 23 Apr 2011 21:37   





Back to top
   
Majtek
Poziom 12
Poziom 12


Joined: 28 Jan 2006
Posts: 93
Location: kalisz

Post#5 Post from the author of the topic 03 May 2011 08:56   

Re: Atmega16 generowanie przebiegów pwm na 16 serw


Mam pytanko. Jaki jest minimalny czas odmierzany przez timer 8-bitowy?
bo mi minimum na zegarze 8mhz wychodzi 0.016ms, nawet jak preskaler dam 1 i OCR0 =1. A 16 bitowym mogę mniejsze odmierzyć. według mojej wiedzy powinny minimalny czas mieć taki sam (1/czestotliwość zegara głównego)
edit:
No i jest problem atmega z zegarem 16mhz jest za wolna. Gdy generuje przebiegi dla 5 serw, to procek jest tak zajęty, że reszty nie wyrabia obsłużyć, głównie to wysyłanie danych po rs do drugiego procka i odbieranie i wysyłanie danych poprzez nadajnik rfm12 (magistrala SPI). Zostaje chyba tylko następny procek do obsługi tylko serw i komunikaty po rs dostaje.
Albo przesiąść się na lepsze procki ARM (STM32 np.):)
Back to top
   
Szumlus
Poziom 11
Poziom 11


Joined: 23 Jan 2006
Posts: 69
Location: Tarnów

Post#6 10 Jun 2011 11:48   

Re: Atmega16 generowanie przebiegów pwm na 16 serw


A te pozostałe peryferia śmigają na przerwaniach??
Minimalny czas przerwania powinien być tak dobrany aby zanim przepełni się Timer zdążyło się wykonać jego przerwanie.
U mnie tylko działał UART podczas pracy z serwami więc żadnych problemów nie było.
Back to top
   
albertb
Poziom 22
Poziom 22


Joined: 04 May 2004
Posts: 2074
Location: Nowy Targ

Post#7 10 Jun 2011 14:27   

Re: Atmega16 generowanie przebiegów pwm na 16 serw


Temat często powraca, a rozwiązania ... takie sobie.
Skoro czas wykonania przerwania jest wyznacznikiem ilości obsługiwanych serw to go skróćmy.
We wszystkich opisanych rozwiązaniach w przerwaniu wykonuje się lwia część logiki, a występujące rzadko przestawienia wartości to funkcje zawierające jedną instrukcję. Bez sensu.
Proponuję zmienić format danych o czasach włączenia serw na tablicę której indeksami są kolejne czasy, a wartościami liczby całkowite, w których każdy bit określa, które serwo należy wyłączyć:

Zalety: przerwanie to jedna inkrementacja indeksu i wyzerowanie odpowiednich wyjść bez jakichkolwiek sprawdzań - czas pomijalnie mały nawet dla kilkudziesięciu serw.

Wady:
- Bardziej skomplikowane funkcje do zmiany położeń (ale tylko troszeczkę, każdy powinien sobie poradzić)
- przy dużej rozdzielczości wzrost zapotrzebowania na RAM (ale to także można wyeliminować tworząc odpowiednią strukturę danych)

Albert
Back to top
   
dj_max84
Poziom 13
Poziom 13


Joined: 24 Feb 2004
Posts: 127
Location: Farnham

Post#8 14 Jun 2011 10:47   

Re: Atmega16 generowanie przebiegów pwm na 16 serw


na stronie AMTELa jest opis dla 8 kanalowego software'owego PWM'a z mozliwoscia rozszerzenia do 24 kanalow. wartosci PWM dla kazdego kanalu sa przesylane sa po RS232. przyklad 8 kanalowy jest oparty na ATTINY2313. kiedys tez widzialem gdzies kod w C ale nie moge go teraz znalezc..

http://www.atmel.com/dyn/resources/prod_documents/doc8020.pdf
Back to top
   
Google

Google Adsense


Post# 14 Jun 2011 10:47   





Back to top
   
janbernat
Poziom 23
Poziom 23


Joined: 22 Oct 2008
Posts: 3918
Location: Warszawa

Post#9 14 Jun 2011 11:32   

Re: Atmega16 generowanie przebiegów pwm na 16 serw


Kod jest tu:
http://www.atmel.com/dyn/general/advanced_search_results.asp?&checkAll=1&checkAllReference=1&target=AVR136
Obok PDF.
Po pewnych poprawkach działa.
Back to top
   
Post new topic  Reply to topic      Main Page -> Forum Index -> Microcontrollers Generally -> AVR Microcontrollers -> Atmega16 generowanie przebiegów pwm na 16 serw
Page 1 of 1
Similar topics
[atmega16][bascom]generowanie nośnej 36kHz (26)
[stm32] Generowanie PWM 27MHz (7)
[LPC21xx][C] Generowanie sygnałów PWM (12)
PWM dla dwóch serw sterowanych przyciskami (10)
[PWM 3.3V] - generowanie 12V (5)
generowanie dźwięku na ATmega16 (19)
[Bascom][Atmega16] Po implementacji serw ,szaleje (4)
[ATMega16] Sterowanie wieksza iloscia serw (35)
PWM (dioda RGB ). Timer 1, atmega16, zrobienie 2 PWM-ów. (10)
Generowanie sygnałów/przebiegów prostokątnego i trójkątnego (13)

Page generation time: 0.178 seconds


FAQ || Administrator || Moderators || Widgets and banners || Contact
elektroda.pl topic RSS feed