CPU z kwarcem do transmisji szeregowej a odliczanie 1ms

Witam!

Tak sie zastanawiam teoretycznie:

Mam CPU który popędzam kwarcem który źle się dzieli aby uzyskać całkowiete odstępy czasu (wielokrotności 1ms). Na przykład kwarcem

11,059MHz. W dodatku timer generuje przerwanie co 256 zliczeń. W ogóle można powiedziec, że 1ms nie jest całkowitą wielokrotnością okresu pojawiania się przerwania.

W jaki sposób obliczyc co które przerwanie wywołać moją funckję, która powinna być wywołana co 1ms. Zastanawiam się nad tym i ewidentnie nie mogę wywoływac co z góry założoną ilość przerwań timera - musze wewnatrz przerwania timera to wyliczyć. Zakładam, że nie mogę korzystać z dzielenia/mnożenia. Dobrze by było również nie korzystać z dużych liczb (procesor ma inne rzeczy na głowie niż arytmetyka 4-bajtowa).

Czy istnieje jakaś wygodna sztuczka do tego ? na razie mam to zrobione tak, że jest duży licznik 4 bajty, ale on mi się nie podoba (za dużo czasu zajmuje liczenie, to jest 8-bitowy AVR). Niestety brakuje mi już mocy obliczeniowej ...

PS. Zakładam że nie chce wywoływać dokładnie co 1ms, ale statystycznie powinno wypaśc 100 wywołań na 100ms. Zakładam równiez że znam wartośc kwarcu w momencie kompilacji (program w C).

Reply to
Sebastian Bialy
Loading thread data ...
Reply to
Tomasz Bednarz

Nie, stabilnośc transmisji szeregowej ma większe znaczenie niż stabilonośc okresów między przerwaniami.

Nawet gdybym mógł, to niestety podział czestotliwości zegara przez ilośc spodziewanych wywołań funkcji jest niecałkowity. Z resztą timer jest prostacki, generuje overflow przy przekroczeniu pojemności, której nie mozna regulowac.

Oczywiście, tylko wszystkie obliczenia albo wymagają dzielenia/monożenia, albo dużych liczników (u mnie na razie sprawnie działa na 4 bajtach, ale wymaga on jednego dodawania, jednego porównania i sporadycznie jednego odjęcia - co jest w sumie pracochłonne w jednym cyklu)./

owszem, tylko ja szukam drogi do kombinowania w sposób optymalnie najszybszy :)

Reply to
Sebastian Bialy

Sebastian Bialy snipped-for-privacy@poczta.onet.pl> napisał(a):

Ja bym zrobił tak:

11059200/64(preskaler)/256(pełen obrót licznika)=675 , a to już tylko 2 bajty do obróbki ;-)

Piotr

Reply to
Piotrek Sz.

Piotrek Sz. snipped-for-privacy@NOSPAM.gazeta.pl> napisał(a):

Upssss....zapędziłem się i wyszła z tego sekunda :(

Piotr

Reply to
Piotrek Sz.

Może mi się uda naprawić poprzednią wpadkę ;-)

#include <mega8.h>

register unsigned char licznik5=0; register unsigned char licznik43=43; register unsigned char licznik100=0; // Timer 0 overflow interrupt service routine interrupt [TIM0_OVF] void timer0_ovf_isr(void) { // Place your code here licznik43--; if(licznik43==0) { licznik43=43; licznik5++; //to sie wykonuje co ~1ms licznik100++; if(licznik5==5) { licznik43++; licznik5=0; } if(licznik100==100) {licznik100=0;} //to sie wykonuje co 100ms }

} ..

Piotr

Reply to
Piotrek Sz.

Bushi wrote: > [ciach]

jest tak:

istnieje zegar popędzany kwarcem 11059000MHz który steruje licznikiem jednobajtowym. Licznik ten przepełnia się po 256 zliczeniach generując przerwanie.

Czyli pojawia się przerwanie z częstotliwością 43199.21875Hz. A ja chce, żeby pojawiało się z częstotliwością 1000Hz.

Może to zrobić albo zmianiając kwarc na taki, który da "ładny podział" przez 256 (ale tego zrobic nie mogę bo musi być tak a nie inny -> transmisja szeregowa).

Mogę też wywoływac przerwanie z częstotliwością ~43199Hz i raz na jakiś czas wywołać moją procedurę. Cała zabawa polega na znalezieniu metody obliczania co które przerwanie wywołać moją.

Reply to
Sebastian Bialy

Świetnie, ale ten akurat kwarc to był przykład, a ja pytam dla DOWOLNEJ wartości kwarcu :) I to w dodatku wyliczalne na poziomie kompilacji. Akurat 11059200 nie jest jeszcze taki upierdliwy :)

Konkretnie piszę procedurę która musi być wywaołana co 1ms bez względu na to jakim kwarcem poganiam procesor. Decyzja co do kwarcu podejmowana jest na etapie kompilacji za pomocą #define.

Zrobiłem algorytm który robi to bardzo ładnie i w dodatku jest prostacki, jednak korzysta z operacji dodawania 4 bajtowej liczby i porównywania. To jest troche sporo pracy jak na przerwanie timera. Szukam jakiegoś genialnie prostego sposobu.

Reply to
Sebastian Bialy

A rozkład na ułamki, jaka dokładność potrzebna?

f=11059000;c=f/256/1e3,;C=round(c);cx=(c-C); for i=2:4;C(i)=round(1/cx);cx=cx-1/C(i);endfor;printf('%d\n',C);

c = 43.199

43 5

-1280

-90425216047595840 Czyli cykle po l=43 takty, co piąty l=44, ... czyli dwa liczniki jednobajtowe, z których tylko jeden jest dekrementowany każdym impulsem zegara.

Chyba starczy, jak zależy ci na prędkości, jak nie, to co 1280 l=43-1+1=43 (bo 1280 dzieli się przez pięć), co 90425216047595840, ech to chyba już nie będę liczył ;)

Reply to
pisz_na.mirek

A czemu licznik musi dzielic przez 256? Przeciez mozna go przeladowywac podczas kazdego przerwania (to jeden sposob) Inny sposob bedzie taki: Znajac czestotliwosc przerwan z licznika, musisz obliczyc ile przerwan musi zaistniec dla otrzymania okraglej liczby czasu - np. podwielokrotnosci 1ms. Im wieksza czestotliwosc przerwan, tym wieksza bedzie dokladnosc, ale wiecej impulsow musisz zliczyc (wiecej bajtow zajetych). Nastepnie robisz kaskade licznikow - kazdy o odp. podziale czestotliwosci - tak zeby na wyjsciu ostatniego byla czestotliwosc odp. 1ms (1000Hz). A pozniej wystarczy ustawiac flage sygnalizacyjna dla procedur w petli glownej - tlko uwaga - jesli flaga jest typu toggle - to dzieli ona przez dwa, wiec trzeba to uwzglednic podczas obliczen dzielnikow i dac np. 0,5ms na wyjsciu ostatniego licznika. Wszystkie te operacje wymagaja tylko instrukcji inc (zwieksz o 1) i porownania (np. xor i spr. zera) - zadnego mnozenia czy dzielenia, i minimalnie obciazaja procesor.

Reply to
Jack Houseman

Sztuczka jest imho taka ze liczymy najmniejsza wspolna wielokrotnosc [NWW, LCM] kwarcu i pozadanej czestotliwosci - tu 1000Hz. Tu wychodzi nam 55296000. Teraz bedziemy liczyc w jednostce j=1/55296000 sekundy.

Co kazde przerwanie dodajemy do licznika ilosc jednostek odpowiednia dla okresu - czyli u nas 5*256=1280. Jak licznik przekroczy 55296

- minela 1 ms. Obie liczby mozemy jeszcze zredukowac o czynnik wspolny, ktory tu wynosi az 256. Czyli co przerwanie dodajemy do licznika 5, a jak przekroczy 216 - odejmujemy tylez i wolamy procedure

1ms.

Na dobra sprawe to niepotrzebnie bralismy czestotliwosc kwarcu. Mozna bylo od razu liczyc dla 11059200/256=43200 i 1000. Tyle ze w przypadkach szczegolnych moga tu wychodzic niecalkowite liczby.

No to w makrach C mozesz miec problem z LCM/GDC :-)

Mozna od razu liczyc w ps. Niby bedzie jakis blad .. ale dla typowej dokladnosci kwarcu to 16 bit dokladnosci powinno wystarczyc.

Albo od razu przyjmij jako jednostke pewna niewielka wielokrotnosc kwarcu. np 8. Wtedy co przerwanie dodajemy 8*256, a zliczyc musimy do 8*11059200/1000=88437.6~=88438. Tym sposobem licznik jest tylko 3 bajtowy, dodawanie banalnie proste, porownywanie troche gorzej, a dokladnosc na poziomie 10ppm i tak lepsza niz kwarcu. Przy wielokrotnosci mniejszej - 2,3,4 - dokladnosc niewiele mniejsza, a kwarce do 16M powinny zapewnic licznik tylko 2 bajty ..

No i jakbys sie nie upieral przerwan robic co 256 taktow, to byc moze by sie znalazl kwarc dobry do precyzyjnego taktowania i seriala i ms - np 15.360MHz.

J.

Reply to
J.F.

Hmmm jesli skróce czas liczenia to niestety cały algorytm będzie zajmował więcej czasu cpu. Zalezy mi na tym, żeby nie trzeba było nic wyliczć ręcznie w kodzie, tylko podać #define CRYSTAL=11.59200 w kodzie i miec automatycznie wyliczone. Co jesli wyliczy mi 10 cykli :) ?

Hmmm jesli kaskada liczników - to prawie ocieram się o rozwiązanie które już mam na 4 bajtach. Dalej jest problem jak wyliczyć tą kaskadę (i ilośc stopni) mając do czynienia wyłacznie z preprocesorem w C.

Zastanwoei się w ogóle, czy rozwiązanie z 4-bajtowym licznikiem nie jest jednak optymalne.

Reply to
Sebastian Bialy

A jak to zrobic preprocesorem C :) ? Bo proces wyznaczania ma być automagiczny, programista docelowy zmienia CRYSTAL=20000000 i powinno mu podczas kompilacji przeliczyć nowe współczynniki.

Reply to
Sebastian Bialy

Hmmm mam coś podobnego w tej chwili, tylko nie licze wspólnej wielokrotności tylko na zywca dodaje tak:

#define CRYSTAL=11059200 #define FREQ=1000

// 256 cykli na przerwanie licznika i preskaler przez 8 #define COUNTER_VALUE_ADD ((unsigned long)FREQ*256*8)

SIGNAL(SIG_OVERFLOW0) { static unsigned long counter=0; counter+=COUNTER_VALUE_ADD; if(counter>CRYSTAL) { counter-=CRYSTAL; /* tu mam co 1ms */ } }

I bardzo ładnie i stabilnie dostaje 1ms odstepu.

Jednak: szukam czegoś szybszego albo metody na zgadnięcie podczas kompilacji, czy licznik jest unsigned long czy unsigned short.

Założenie jest takie, że kod do tego licznika nie powinien podlegac ręcznej poprawce, jedyne co może docelowy programista to zmienić CRYSTAL i na podstawie tego preprocesor ustali wszystkie parametry (i fajnie jak by ustalił również szerokość zmiennej).

To niezłe ćwiczenie by było pod warunkiem że GCC pozwalałby na jakąś arytmetykę i porównania :D

Inne wielkości licznika niż 2/4 bajty odpadaja ponieważ nie mam szans na optymalizację kodu z użyciem C. Nie mam dostępu do bitu carry i musiałbym testować wynik dodawania co będzie trwało więcej jak moja prosta procedurka na 4 bajtach.

Za szybko :) Biedactwo 90S2313 nie da rady ...

Reply to
Sebastian Bialy

To nie jest kwarc 11059000MHz tylko 11059200MHz i dzieli się równo przez wszystko do 2^14

11059200/256 -> 43200

1ms z tego nie dostaniesz równo, najwyrzej 10ms

sword

Reply to
Adam Jurkiewicz

W życiu nie uzyskasz czegoś takiego, kwarc dobiera się do konkretnego zastosowania, a nie układ do kwarcu. Nigdy nie dostaniesz dobrego podziału dla obliczeń czasu i UARTA przy dowolnym kwarcu.

Zawsze pisze tak program aby na podstawie zdefiniwanego kwarcu program się odpowiednio skompilował ale są granice takiego doboru.

Jak chcesz mieć poprawnie wyznaczany czas to nie dobieraj kwarcu pod kątem UARTA bo odchyłki do kilku % nic w tej transmisji i tak nie zmienią.

pozdrawiam, sword

Reply to
Adam Jurkiewicz

Masz rację, ale ja specjalnie podałem przykład który się _nie dzieli_ żeby zobrazowac problem. Oczywiście 11059200 jest ok, ale dla dowolnego kwarcu już nie.

Reply to
Sebastian Bialy

Statystycznie zawsze dostacje w granicach błędu samego kwarcu - mi to pasuje. Nie jestkrytyczny odstęp "1ms" a jedynie statystyczna ilośc takich przerwań na jednostkę czasu. Innymi słolwy mi taki podział wystarcza.

Oczywiście ze tak, w pełni uniwersalnie można by napisać, jednak wymagało by to wsparcia obliczeniowego samego CPU co w moim przypadku odpada.

Hmmm nie wiadomo, mam przesłać blok danych ponad 100 bajtów i z moich doświadczeń wynika, że marnie się to robi z kwarcami nie będącymi UART-komaptybilne. Po prostu ilośc błędów rośnie poza granicę akceptacji.

Reply to
Sebastian Bialy

Podpiąć octavę lub whatever(TM) do makefile?

Właściwe narzędzie do danego celu - powiedział magik wbijając wkręty młotkiem ;)

Reply to
pisz_na.mirek

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.