Skocz do zawartości

[C] [STM32F411RE] Odmierzanie czasu przerwaniami wewnątrz funkcji - ruch silnika krokowego


przemulala

Pomocna odpowiedź

Cześć,

Bawię się silnikiem krokowym 28BYJ-48 na STM32F411RE. Algorytm realizacji pół-kroku zawiera 8 kroków, w których zasilane są odpowiednie cewki silnika. Oczywiście żeby to wszystko ruszyło, trzeba wprowadzić pewne opóźnienia pomiędzy poszczególne kroki, około 1,5 ms. I tutaj pojawia się mój problem.

Mam skonfigurowany timer TIM2, który wywołuje przerwanie co 1 ms (to mogę elastycznie zmienić w prosty sposób). Nie wiem jednak jak mógłbym odmierzać nim czas pomiędzy kolejnymi krokami wewnątrz funkcji sterującej ruchem silnika... Innymi słowy, chciałbym w oparciu o przerwania timera zbudować funkcję void delay(float ms), którą mógłbym wołać pomiędzy kolejnymi krokami silnika. Zamieszczam kod ruchu silnika - ruch wywoływany tylko na żądanie użytkownika:

void Do_N_Half_Steps_Clockwise_StepperMotorNo1(int n)
{
unsigned short i = 0;
while(n > i)
{
//8 step-algorithm for half-stepping

	//step 1 - blue on
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
               //TUTAJ POWINNO BYC OPOZNIENIE O X MS
	//step 2 - blue & pink on
	GPIO_SetBits(GPIOB, GPIO_Pin_13);
	//step 3 - pink on
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
	//step 4 - pink & yellow on
	GPIO_SetBits(GPIOB, GPIO_Pin_14);
	//step 5 - yellow on
	GPIO_ResetBits(GPIOB, GPIO_Pin_13);
	//step 6 - yellow & orange on
	GPIO_SetBits(GPIOB, GPIO_Pin_15);
	//step 7 - orange on
	GPIO_ResetBits(GPIOB, GPIO_Pin_14);
	//step 8 - blue & orange on
	GPIO_SetBits(GPIOB, GPIO_Pin_12);

	//all pins to logic 0
	GPIO_ResetBits(GPIOB, GPIO_Pin_12 | GPIO_Pin_15);

	++i;
}
}

Będę wdzięczny za pomoc!

Link do komentarza
Share on other sites

Jeśli zrobisz to w ten sposób, algorytm ruchu silnika będzie zużywał 100% czasu procesora. Musisz to zrobić odwrotnie: gdy jakiś proces nadrzędny chce ruszyć silnikiem, wyznacza parametry tego ruchu (przyśpieszenie od startu, prędkość maksymalną, opóźnienie hamowania i liczbę kroków - albo sumaryczną albo w poszczególnych trzech fazach) i wpisuje je do pewnej struktury danych. Do tego powinien jeszcze ustawić początkowy stan ruchu (status = przyśpieszanie, aktualna prędkość = 0, numer kroku = 1) i sztucznie odpalić przerwanie od timera. W obsłudze jego przerwania powinna siedzieć funkcja której działanie jest sterowane właśnie tą strukturą danych. Na podstawie aktualnego stanu oraz żądanych parametrów ruchu powinna dokonać komutacji faz silnika, wyznaczyć czas jaki powinien upłynąć do następnego kroku, uaktualnić stan w statycznej strukturze danych i odpowiednio zaprogramować timer. Dzięki temu nit nie musi na nic czekać a wszystko dzieje się "samo". Kilka takich samych silników jadących z różnymi prędkościami i będących w różnych fazach ruchu - proszę bardzo, wystarczy tylko do każdego podłączyć inny timer. Nawet funkcja wykonująca ruch i obliczająca okres kroków może być ta sama, tylko musi dostawać wskaźnik na inną strukturę danych. Proces nadrzędny może podczytywać status danego silnika i z tego wiedzieć co się z nim dzieje i czy już ruch został zakończony lub może też dostawać callback od funkcji z przerwania, gdy ta dojdzie do ostatniej fazy. To nie musi być zakończenie i stop, przecież czasem chcesz zmienić prędkość lub inne parametry "w locie". Nigdy nie zakładaj, że pisana akurat funkcja będzie jedynym procesem wykonywanym przez procesor - bo nigdy nie będziesz tworzył dobrych programów na kontrolery. Najlepiej gdy każda rzecz zużywa tak mało czasu procesora jak to jest możliwe. Żadnych opóźnień wstawianych w kod, bo tym zajedziesz nawet najpotężniejszy procesor.

Najlepiej gdybyś o sterowaniu silnikami krokowymi coś poczytał. Przecież na pewno nawet ST ma coś w swoich notach aplikacyjnych. Szukałeś? Nikt nie robi tego na opóźnieniach.

Link do komentarza
Share on other sites

Dzięki za odpowiedź, ale nie do końca zrozumiałeś moje pytanie 🙂 Oczywiście wiem, że nie należy robić tego na opóźnieniach i nie chcę tak tego rozwiązywać - zastanawiam się, w jaki sposób wykorzystać timer działający w przerwaniach aby wyznaczać w wygodny sposób, w dowolnym miejscu kodu, opóźnienie o zadanym czasie.

Tymczasem jednak rozwiązałem sprawę inaczej. Mam zmienne globalne, określające ilość kroków do wykonania oraz aktualny krok algorytmu (komutacji fazy silnika). Żądanie zgłoszone przez użytkownika ustawia wartości tych zmiennych, a timer, działający w przerwaniu update, wykonuje odpowiednią robotę, tj. dokonuje komutacji odpowiednich faz silnika o zadanym czasie trwania, a po przejściu 8 kroków "algorytmu", zmniejsza ilość kroków silnika do wykonania o jeden - i tak aż do zrealizowania wszystkich żądanych kroków.

Nie zajmuję czasu procesora, dla mojej aplikacji chyba jest to całkiem optymalne rozwiązanie, no i działa - pokombinuję jeszcze jak to ulepszyć.

Link do komentarza
Share on other sites

Trochę nie rozumiem dlaczego w algorytmie ruchu odróżniasz mikrokroki od prawdziwych kroków silnika. Decydując się na sterowanie drobniejszymi krokami odtąd to one stają się podstawowymi jednostkami ruchu. Jeżeli do tej pory miałeś np. 200 kroków na obrót to teraz masz 400, 800 albo jeszcze więcej. Jeśli do tej pory silnik miał zrobić 150 kroków to teraz ma zrobić ich 1200 (dla 1/8). Inne liczby, ale zasada nie powinna się zmieniać. Mikrokroki widać jedynie na najniższym poziomie sterowania: tam, gdzie komutujesz jakieś sygnały sterujące mostkiem. Wszędzie wyżej to już są zwykłe, tylko drobniejsze kroki. Między każdym takim "nowym krokiem" musisz wołać funkcję wyznaczającą czas w zależności od fazy (przyspieszanie, stała prędkość, hamowanie) i odpowiednio do wyniku programować timer. Z tego co zrozumiałem, Ty obliczenia czasu będziesz wykonywał tylko co 8 kroków a podczas wykonywania 8 kolejnych silnik będzie pracował ze stałą prędkością. Jaki to ma sens? W sterowaniu mikrokrokowym musisz się proporcjonalnie więcej naliczyć, ale dostajesz w zamian gładszą pracę wirnika. Nie jest nigdzie powiedziane, że silnik powinien stawać na swoich pełnych krokach. Jeżeli z obliczeń ruchu wynika, że ma zrobić 35 "nowych kroków" to ma tyle zrobić. W programie który pokazałeś nie będzie to możliwe, bo reszta kodu pracuje na starych, normalnych pełnych krokach. Przecież wystarczy zrobić gdzieś stałą określającą aktualną rozdzielczość silnika i modyfikować ją, plus odpowiednio napisać samą funkcję sterującą mostkiem tak, by zmiana trybu z pełno- na pół- lub bardziej drobno-krokowy była tylko formalnością.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Dzięki za uzupełnienie. Rzeczywiście, z jakichś powodów uznałem (bezsensownie), że muszę wykonywać pełne kroki, stąd w moim kodzie jeden krok to 8 "podkroków". Właśnie dziwiło mnie, czemu pełny obrót osiągam przy około 500 kroków - zgodnie ze specyfiką silnika powinno to być 64^2 kroków (na silniku jest jeszcze przełożenie 64:1). Teraz już wiem, skąd wynika ta różnica.

Generalnie to, o co mi chodziło, to uzyskanie pewnego czasu płynięcia prądu przez silnik, żeby on w ogóle ruszył - stąd dopytywania o opóźnienie. Ostatecznie zrealizowałem to wewnątrz timera i wywołuję ustawianiem pewnej zmiennej sterującej - wszystko działa tak jak powinno. Nie mam potrzeby modyfikacji parametrów silnika, o których piszesz: zależy mi na maksymalnym momencie (z wyników doświadczeń, którymi się zainteresowałem, wynika, że występuje on dla zasilania przez 3 ms, w związku z czym prędkość obrotowa jest u mnie stała), a sterowanie będzie na bieżąco realizowane przez człowieka.

Reasumując, moja realizacja w obecnym kształcie zaspokaja wymagania projektu. Dziękuję jednak za uwagi, trochę mi rozjaśniły i pokazały dodatkowe możliwości, pewnie skorzystam z tego w przyszłości. Temat do zamknięcia.

Link do komentarza
Share on other sites

Skoro do zamknięcia to pewnie już tu nie zajrzysz, ale gdyby jednak to chcę jeszcze dodać, że właśnie z powodu zbytniego uproszczenia algorytmu ruchu musiałeś pójść na kompromis i przez to nie wykorzystujesz możliwości silników. A to oznacza, że są przewymiarowane (masa, wielkość, koszt, prąd) w stosunku do potrzeb. Praca ze stałą prędkością (bez faz rozpędzania i hamowania) zmusiła Cię do takiego dobrania (stałego) czasu komutacji, by silnik pokonał bezwładność swoją i obciążenia tylko w jednym, pierwszym kroku. Jeśli w czasie pierwszych 3ms nie rozpędzi się do prędkości "marszowej", zgubi ten krok i następne a napęd będzie tylko drgał. Gdybyś zaprogramował rampy startu i stopu, pierwszy krok mógłby trwać np. 50ms a następne coraz krócej (zgodnie z krzywą stałego przyśpieszenia kątowego). Taki tryb pozwala na użycie znacznie słabszych silników a przy tych samych - na osiągnięcie znacznie większych prędkości.

Sterowanie ręczne jeszcze bardziej uwypukla zaletę tego typu dynamiki, bo na początku silniki kręcą wolniej i można bardziej precyzyjnie ustawić położenie. Ponieważ przyspieszenie mógłbyś dobrać doświadczalnie (a procesor może pobierać czasy kolejnych kroków z predefiniowanej tablicy lub wyliczać na podstawie żądanego przyspieszenia), mógłbyś zrobić to optymalnie do możliwości swoich napędów i do wymagań związanych z precyzją ręcznego ustawiania położenia.

Spróbuj do tego podejść choćby dla eksperymentu. Myślę, że skoro teraz silniki są w stanie ruszyć z kopyta na 330Hz (3ms) to docelowo, gdybyś umiał je rozpędzać mógłbyś osiągnąć w tym samym układzie dobrze ponad 1000Hz.

  • Lubię! 1
Link do komentarza
Share on other sites

Dzięki za kolejne uwagi. Wezmę pod uwagę Twoje sugestie, jeśli miałbym problem z pracą silnika. Obecnie uzyskany efekt jest wystarczający dla mojej aplikacji, a to jest najważniejsze. Byłbym jednak wdzięczny, gdybyś podlinkował jakieś artykuły, na które warto spojrzeć w kontekście Twoich wcześniejszych wypowiedzi 🙂 A tymczasem stawiam piwo za przekazanie dużej ilości ciekawych informacji 🙂

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.