ATmega, jak zaprogramować narastanie wartości sygn

Oct 08, 2012 10 Replies

Witam,



Używam mikrokontrolera ATmega128. W moim programie użytkownik ustawia wartość prądu (liczba całkowita od 0 do 1000) oraz czas (od 1 do 40 sekund). Prąd ma narosnąć od 0 do wartości ustawionej w ustawionym czasie. Funkcja służąca do obliczania aktualnej wartości prądu podczas jego narastania jest wywoływana z pewną częstotliwością (od 10 do 60Hz, zależnie od ustawienia w parametrach programu). Zatem narastanie prądu odbywa się w liczbie kroków wyrażonej wzorem: K = czas[s] * częstotliwość[Hz]. Żeby uniknąć obliczeń zmiennoprzecinkowych, zastosowałem algorytm Bresenhama do kreślenia odcinków. Zapowiada się, że będzie to działać bardzo szybko, gdy K >= wartość prądu. Gdy K < wartość prądu, to algorytm Bresenhama normalnie kreśli odcinek w pętli "idąc po zmiennej Y". Tymczasem ja działam w osi odciętych, czyli X (u mnie jest to liczba kroków, w których prąd ma narosnąć). Bez większych problemów przerobiłem ten algorytm tak, aby dla K < wartość prądu, "szedł on po zmiennej X". Przy czym po tej przeróbce uzyskałem podpętlę (while), która niekiedy będzie wykonywać się wiele razy (dla pojedynczego kroku w osi X), co spowolni algorytm. Inną metodą, zamiast tej podpętli, byłoby zastosowanie dzielenia, co też nie jest rozwiązaniem, gdyż dzielenie realizowane jest przez kompilator także w pętli. Poniżej przedstawiłem algorytm, który stosuję -- na razie to jest wersja kreśląca grafikę (tak łatwiej mi obserwować działanie tego algorytmu), a nie funkcja wywoływana z pewną częstotliwością i sterująca prądem. Algorytm działa prawidłowo. Przy czym może działać niekiedy wolno (pętla while może wykonywać się wiele razy) dla K < wartość prądu -- no i to jest mój problem. Mam pytanie do Was, jak przyspieszyć ten algorytm? A jeśli się nie da, to byłbym wdzięczny za naprowadzenie na jakiś inny sposób zaprogramowania opisanego problemu (narastania wartości w pewnej liczbie kroków).



Ilustracja graficzna działania poniżej zaprezentowanego algorytmu:

formatting link
K = czas * czestotliwosc;



if (K >= wartoscPradu) { for (t = 0; t <= K; t++) { drawPixel(px, py); y += wartoscPradu + 1; if (y >= K + 1) y -= K + 1, py++; px++; } } else { for (t = 0; t <= K; t++) { while (1) { x += K + 1; if (x >= wartoscPradu + 1) { drawPixel(px, py); x -= wartoscPradu + 1, px++; py++; break; } py++; } } }


Pozdrawiam i z góry dziękuję za pomoc, Robbo


cyt:

"Algorytm może działać zarówno na liczbach zmiennoprzecinkowych jak i całkowitych, ale ze względów wydajnościowych wykorzystuje się najczęściej realizacje całkowitoliczbowe. Zatem jeżeli początkowa wartość zmiennej decyzyjnej d lub jej przyrosty wychodzą nam ułamkowe, to mnożymy zarówno zmienną decyzyjną d jak i jej przyrosty tak by uzyskać liczby całkowite. Możemy tak zrobić gdyż do decyzji bierzemy pod uwagę jedynie znak zmiennej decyzyjnej d."

źródło:

formatting link

Albo czegoś nie zrozumiałem, albo to jest zwykła proporcja: I(s)=I(S0)+(I(Se)-I(S0))*((s-S0)/(Se-S0)) I(s)=I(S0)+(I(Se)-I(S0))/(Se-S0) * (s-S0) gdzie s - czas, S0 - czas początkowy, Se - czas końcowy

Przekształcasz to sobie na: I(t)=I(T0)+t*dI I(t)=I(t-1)+dI

Raz wyliczasz iloraz różnicowy dI, potem masz w każdym cyklu jedno dodawanie i jeden shift.

Użytkownik "Robbo" snipped-for-privacy@gmail.com napisał w wiadomości news:5073652e$0$26682$ snipped-for-privacy@news.neostrada.pl...

Jestem lepszy w EMC niż algorytmach ale zaproponuję to, co mi się nasunęło.

Może w przypadku KI, a potem pętla już jak dla przypadku K>I, tylko py++ zastąpić py+=n. "Schodki" wyjdą większe niż z twojej części else, ale jeśli dzielenie przed pętlą nie byłoby problemem to może być n=2,3,4,5,.... i wtedy to chyba już wyjdzie prawie to samo. P.G.

Użytkownik "Piotr Gałka" snipped-for-privacy@CUTTHISmicromade.pl napisał w wiadomości news:5073db09$ snipped-for-privacy@news.home.net.pl...

Poprawka. Jednak nie prawie to samo bo będą stałe przyrosty a czasem brak przyrostu. Przyrosty zmienne są jednak bliższe ideału. P.G.

Witam,

Dziękuję za odpowiedź, która mam nadzieję naprowadzi mnie na właściwe tory.

Tak. Prąd ma przyrastać w K krokach o wartość maxPrąd/K w każdym kroku.

Czas będzie zawsze liczony od zera. Prąd początkowy także będzie narastać od zera. Więc jeśli I(S0) = 0, S0 = 0. Zatem powyższe formuły można uprościć do: I(s) = I(Se) * (s/Se) I(s) = I(Se)/Se*s

Tu bym prosił o wyjaśnienie. Mało spałem i chyba nie do końca myślę, dlatego proszę o wybaczenie.

Czy dI będzie wartością zmiennoprzecinkową? W jaki sposób będzie użyty shift?

Czy chodzi o to, że przy wyliczaniu dI robimy shift w lewo, aby nie tracić precyzji, a działać na liczbach całkowitych. Potem działamy na dużych wartościach dI, a tuż przed użyciem wartości I robimy shift w prawo?

Robbo

Dokładnie. Ale tak długo jak zmiany są liniowe to wystarcza raz policzyć iloraz różnicowy.

Stałoprzecinkową, np. o 16 bitach części całkowitej i 16 po przecinku:

uint16_t I=0; uint32_t I_accu=0; uint32_t dI; time_t last_time=0;

void new_setting(/**/){ dI=(I_target<<16)/time_slices; } void calc(time_t t){ I_accu+=dI*(t-last_time); I=I_accu>>16; last_time=t; }

Serdeczne Bóg zapłać. Wszystko ładnie działa.

Robbo

Użytkownik Robbo napisał:

Jakie bóg zapłać? Chłopaki piwa by się napili ;)

Nie ma problemu. Chętnie wyślę pocztą dobre piwo z małego browaru, a nie sikacz z koncernu.

R.

Łączę zainteresowanie elektroniką i piwem:

formatting link

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required