Tak Vás týmto prosím o pomoc. Či som náhodou niekde niečo neprehliadol, stať sa môže všetko
Problem riadenie serva
Moderátor: Moderátoři
Problem riadenie serva
Tak Vás týmto prosím o pomoc. Či som náhodou niekde niečo neprehliadol, stať sa môže všetko
- Přílohy
-
serva.txt- serva
- (2.63 KiB) Staženo 199 x
Ale chcel by som sa opýtať na Váš názor. Či mi zvládne procesor ešte niekoľko operácií, ako nejaké AD prevody + odosielanie všetkých dát do PC, bez ovplyvnenia chodu serv? Alebo by bolo idálnejšie ísť s frekvenciou ešte vyššie pre istotu? Teraz tam mam 8MHz
mna by len zaujimalo, ci sa da nejak spocitat, ako dlho trva to jedno prerusenie. respektive ci by to niekto vedel. mam taky pocit ze by sa to malo dat cez pocet instrukcii ktore sa vykonavaju nie?
else if(!strcmp(slovo1,dozae)
v rutime smer()
se slovo 1 otestuje, a než se stačí provést kód na podmínce závislý, tak se může stát, že rutina
ISR(USART_RXC_vect)
to slovo1 přepíše:
slovo1[x]=recchar();
Takže rutina smer() okracuje s hlediska podmínky s vadnými daty.
Obdobně pro data pohyb. Navíc, proč smer() vrací hodnotu pohyb, když tuto hodnotu rovnou nastavuje?
Při předděliči 1024 trvá jeden krok čítače 128 us.
Pro OCR2 = 8 vychází impuls 8*128 = 1024 us.
Pro OCR2 = 16 vychází impuls 16*128 = 2048 us.
Takže můžeme nastavit jenom 8 nebo 9 poloh serva.
Hodnota OCR2 = 20 v kódu je už mimo rozsah serva.
Nejlépe je použít osvědčený režim Fast pwm a čítač1.
Mimo jiné pak nepotřebujeme přerušení.
Kód: Vybrat vše
// servo2.c
// Řízení dvou serv, připojených k PB1, PB2
// Atmega8, F_CPU = 8 MHz
// Avr-gcc
#include <avr/io.h>
#include <util/delay.h>
//---------------------------------------------------------------------
int main (void)
{
// fast pwm, mode 14 , TOP=ICR1
TCCR1A = (1<<WGM11);
TCCR1B = (1<<WGM12) | (1<<WGM13);
TCCR1B |= (1<<CS11); //prescale 8, step = 1 us
// clear OC1A, OC1B on compare
TCCR1A |= (1<<COM1A1) | (1<<COM1B1);
// periode = 20 ms (50 Hz)
ICR1 = 20000;
OCR1A=1500;// 1.5 ms (center)
OCR1B=1500;// 1.5 ms
DDRB |= (1<<PB1) | (1<<PB2); // OC1A, OC1B output
for(;;) //test servo1
{
OCR1A = 1000; // 1000 us - left
_delay_ms(1500);
OCR1A = 1500; // 1500 us - center
_delay_ms(1500);
OCR1A = 2000; // 2000 us - right
_delay_ms(1500);
}//for
}//main
//---------------------------------------------------------------------
Takže ak som pochopil dobre, je lepšie hodnotu premennej slovo1 najprv zapísať vo funkcii smer(); do nejakej premenej, napr. temp, definovanej v tejto funkcii a potom túto porovnávať v if?
A to, že vracia funkcia smer(); pohyb je už opravené.
AB1:
Preddeličku mám ale nastavenú na 32. Tak jedno prerušenie pri hodnote OCR=250 je presne 1ms.
V tomto prípade sa strednej hodnote serva rovná OCR 125, a u krajných hodnôt je to 0 a 250. Tam je podľa môjho názoru clekom dostačujúci krok.
Tvoj príklad ma tiež napadol, no vylučuje použitie ďalších serv. No na riadenie auta je určite výhodnejší.
pokrivnik píše:mtajovsky:
Takže ak som pochopil dobre, je lepšie hodnotu premennej slovo1 najprv zapísať vo funkcii smer(); do nejakej premenej, napr. temp, definovanej v tejto funkcii a potom túto porovnávať v if?
To by mohlo být lepší. Musíte ale předpokládat, že přerušení může přijít kdykoliv. Ten problém je obecnější a chce to trochu teorie. V podstatě jde o to, že pokud existuje více úseků programu, které pracují s stejnými proměnnými a mohou být vykonávány asynchronně (z hlediska programu), třeba na základě asynchronního přerušení, tak je potřeba přístup k těm proměnným nějak ošetřit - serializovat. V principu byste měl na dobu, co trvá jak otestování proměnné, tak vykonávání kódu, který je na této proměnné závislý, zakázat přerušení té rutině, která by chtěla proměnnu měnit. Jelikož je nanejvýš žádoucí zakazovat přerušení na nejkratší možnou dobu, je celý program třeba navrhovat s ohledem na tuto potřebu. Vazeb mezi rutinu přerušení a kódem na popředí by mělo být co nejméně, například rutina příjmu znaku by mohla někde uložit znak nebo řetězec a jen signalizovat událost příchodu dat pro běh na popředí, který by měl data co nejrychleji někam převzít a po tuto dobu zakázat přerušení, aby se mu data nezměnila pod rukama. Složitější možnost je jen signalizovat, že si přebírám data, ale potom musí být driver schopen odložit rozpracovanou IO operaci do fronty a ve spolupráci s během na popředí se k ní vrátit po uvolnění dat. Tahle technika byla poprvé uvedena na OS RSX11 firmou DEC a dočtete se o ní na
http://en.wikipedia.org/wiki/Talk%3ARSX-11
jako o "fork queue" nebo i jinde při hledání hesla "RSX11 fork".
Sežeňte si něco o multiprogramování, o threadech a jejich synchronizaci, pokud tomu chcete porozumět.
Pardon, špatně jsem přečetl předdělič.
Několik poznámek:
1. Proměnné, které jsou použity v hlavním programu i v přerušení musí být deklarovány jako volatile.
2. Kód přerušení má být co nejkratší, mimo jiné v něm nemá být volání funkcí.
3. V přerušení COMP nemusíš dorovnávat čas impulsů do 2 ms. Servu je jedno, jestli je mezera mezi impulsy 18, 20 nebo 22 ms.
4. Jestli dobře rozumím, tak z PC chceš jenom testovat pohyb serva doleva nebo doprava.
Potom je lepší posílat servu jednobajtové příkazy, ne stringy které pak musíš pracně dekódovat.
Vyzkoušel jsem úspěšně následující kód
Kód: Vybrat vše
//Atmega8 8 MHz
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "uart.h"
#define SERVO1_ON PORTB |= (1<<PB0);
#define SERVO1_OFF PORTB &= ~(1<<PB0);
#define SERVO2_ON PORTB |= (1<<PB1);
#define SERVO2_OFF PORTB &= ~(1<<PB1);
volatile uint8_t servo1_puls, servo2_puls; // 1 - 250 pro puls 1 - 2 ms
//----- main ---------------------------------------------
int main()
{
uart_init();
DDRB = (1<<0) | (1<<1);
TCCR2 = (1<<WGM21); //CTC
TCCR2 |= (1<<CS21)|(1<<CS20); //presc=32, step 4 us
TIMSK = (1<<OCIE2); //enable interrupt COMPARE A
UCSRB |= (1<<RXCIE); //enable interrupt RX
sei();
servo1_puls = 125; //center position
servo2_puls = 125;
while(1)
{
//odkomentuj pro test bez uart
/*
UCSR0B &= ~(1<<RXCIE0); //disable int RX
servo1_puls = 1;
servo2_puls = 1;
_delay_ms(1500);
servo1_puls = 125; //center
servo2_puls = 125;
_delay_ms(1500);
servo1_puls = 250; //right
servo2_puls = 250;
_delay_ms(1500);
*/
}
}
//----- end main -----------------------------------------
ISR(TIMER2_COMP_vect)
{
static uint8_t count;
count++;
switch (count)
{
case 1:
{
SERVO1_ON;
OCR2 = 250;
break;
}
case 2:
{
OCR2 = servo1_puls;
break;
}
case 3:
{
SERVO1_OFF;
SERVO2_ON;
OCR2 = 250;
break;
}
case 4:
{
OCR2 = servo2_puls;
break;
}
case 5:
{
SERVO2_OFF;
OCR2 = 250;
break;
}
case 22:
{
count = 0;
break;
}
}//switch
}
//---------------------------------------------------------------------------
/*
Klávesami A a S posunujeme servo1 na jednu nebo druhou stranu.
Klávesami Q a W řídíme podobně druhé servo.
*/
ISR(USART_RXC_vect)
{
uint8_t recv_byte;
recv_byte = UDR;
if((recv_byte == 'A') || (recv_byte == 'a'))
servo1_puls = servo1_puls + 5;
if((recv_byte == 'S') || (recv_byte == 's'))
servo1_puls = servo1_puls - 5;
if((recv_byte == 'Q') || (recv_byte == 'q'))
servo2_puls = servo2_puls + 5;
if((recv_byte == 'W') || (recv_byte == 'w'))
servo2_puls = servo2_puls - 5;
}