logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[Xmega128A4U] - Efektywne przesyłanie liczb int przez UART bez bibliotek

prm_ex 20 Maj 2014 21:50 1638 14
REKLAMA
  • #1 13620697
    prm_ex
    Poziom 9  
    Witam serdecznie
    Moja xmega ma docelowo odbierać liczby po uart z zewnętrznego urządzenia przy 9600bps. Cyfra po cyfrze liczba z zakresu 0..99999. Na końcu standardowo \r\n.
    Liczby przychodzą 20x /s. Czyli w max wersji 140 bajtów 8-bitowych na sekundę.

    Po konwersji jednostek (stopy na metry czyli zakres 0..30000), do tych liczb chcę dodawać (nie algebraicznie) inne liczby (m.in. stan timera RTC) czyli powiedzmy aktualny czas przyjścia danej liczby i może jeszcze inną/e liczby wyliczone na podstawie tamtych.
    Te nowe liczby int odsyłam do jeszcze innego urządzenia zewn (soft na PC) po drugim uart'ie z jak najmniejszym baudrat'em jak się da. Jeśli nie zdążę 9600 to może 19200. Ale to wyjdzie w praniu.

    Nie chcę używać ASF'a, funkcji itoa, atoi itp z zewnętrznych bibliotek.
    I założyłem sobie, że zamiast cyfra po cyfrze liczby int chcę podzielić na dwa bajty 8-bitowe (LO i HI). Pięciocyfrową liczbę wyślę wówczas dwoma bajtami, odpadnie konieczność dodatkowych znaczników (znaków oddzielających liczby) i/lub pętlenia w kodzie aby dodać zera wiodące. Mam nadzieję się wyrobić w czasie. (Odbiór mam w przerwaniach).

    Ale do rzeczy. Utknąłem przy próbie zmiany odebranego int'a na LO i HI i wysłaniu ich.

    Niezredukowany jeszcze do 2-3 linii sposób wygląda tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Niby hula, ale....
    Małe liczby przechodzą ok.
    Większe liczby przechodzą o 1 mniejsze a jeszcze większe przechodzą o 2-3 mniejsze.
    Dla przykładu 16384 powinno podzielić się na bajty HI=64 i LO=0 ale w terminalu odbieram o 3 mniejszą. Podane wyżej bajty HI i LO odbieram dopiero kiedy wyślę 16387 ! A przecież wówczas powinienem odebrać 64 i 3.
    No i oglądam to z lewej i z prawej i nie mogę znaleźć "knota".
    Może ktoś coś widzi co tu bruździ lub spotkał się z podobnym zachowaniem?
    pozdrawiam
    Przemo
  • REKLAMA
  • #2 13620755
    tmf
    VIP Zasłużony dla elektroda
    Problemem jest mnożenie stopy*3 i samo przypisanie wartości tej zmiennej. int na aVR ma 16 bitów i nijak nie da się mu przypisać wartości 76384 bo jest spoza zakresu. Potem to mnożysz, tym bardziej przekraczając zakres i gubisz bity znaczące. Sama konwersja 16-bitowej wartości na dwie 8-bitowe jest poprawna.
  • #3 13620761
    BlueDraco
    Specjalista - Mikrokontrolery
    Typ uint16_t ma zakres 0..65535, więc niekoniecznie uda Ci się to, co planujesz.
    Z kolei typ int na 8-bitowcach ma zakres -32768..32767, więc jeszcze gorzej. A to wyrażenie:
    int metry = (int)(stopy * 3 / 10);
    Poprawnie policzy się tylko dla liczb z zakresu 0..10922.

    Inne efekty, o których piszesz brzmią dość magicznie, więc podejrzewam, że źródło problemu leży 40 cm przed monitorem, a nie w samym oprogramowaniu.
  • REKLAMA
  • #4 13620931
    prm_ex
    Poziom 9  
    BlueDraco napisał:
    ... podejrzewam, że źródło problemu leży 40 cm przed monitorem, a nie w samym oprogramowaniu.

    hehe no jak zwykle ;)

    Tak zdaję sobie sprawę że uint16 to max 65535.
    Czy konwersja na uint16_t nie odbywa się dla wartości całego nawiasu po jego obliczeniu?! :idea:
    maksymalnie 99999stóp * 0.3 to maksymalnie 30000 metrów
    Czyli co najpierw obliczenia konwersji jednostek i w nowej linii konwersja typu?

    aaaa już widzę odebranego stringa nie mogę przypisać do int'a ponad pewną wartość :D , a co najwyżej do innego typu. Dopiero po mnożeniu przez 0.3 zmieści się w int'cie.
    Tyle, że ten mój przykład wartości błądził o 3. Już wiemy czemu m.in.
    Natomiast wartości rzędu 200-300 "łapią" się na int'a, a mimo to różnią się np. o 1 po wysłaniu. :cry:
  • #5 13620954
    BlueDraco
    Specjalista - Mikrokontrolery
    Zgodnie ze standardem C, jeśli typy argumentów operacji są nie dłuższe niż int/unsigned int, to obliczenia są prowadzone na typie int/unsigned int.

    Możesz napisać tak
    metry = (uint32_t) stopy * 3048/10000

    Będzie wolniej, ale za to poprawnie.

    Ten "błąd o 3" to czysta magia. Nie ma takiej możliwości, chyba, że masz błąd w kodzie, którego nie pokazałeś (np. po stronie PC).
  • REKLAMA
  • #6 13621434
    prm_ex
    Poziom 9  
    Witam
    dziękuję za odpowiedzi.
    Oczywiście przypisanie wartości stopy do int'a było moim bezmyślnym babolem.
    Przelecę nią przez "dłuższy" typ do metrów w int'cie i zrobię zaraz testy.
    - Soft odbiera poprawnie, ale do testów na szybko używam terminala.
    - Aż takiej dokładności konwersji jednostek nie potrzebuję. Wystarczy przybliżenie *0.3 ;)
    - No magia. :D Że tylko o 3 skoro zmienna aż taka wystawała poza typ. Ale magia również dlatego, że nawet jeśli zmienna nie wystaje to różni się o 2 lub 1 lub nic. Ale Kopperfield'a bym tu nie mieszał. Jak już ktoś w tym wątku napisał ;) to nie kod się myli tylko piszący. Oczywiście aby doświadczyć magii, dla chętnego mogę podać kompletny kod. Kod w którym oprócz tego co pokazałem za dużo nie ma. Ot normalna inicjalizacja usartów, zegarów i przerwanie. RTC jeszcze nie dodałem bo utknąłem na tym dzieleniu inta na dwa bajty. Kompletny kod w załączniku.
    Aha... jak tablicę char wysyłam przed zmianą na zmienną stopy, cyfra po cyfrze, to oczywiście jest OK.

    Tak więc w załączniku "magiczny" kod, a ja zaraz będę testował z 32-bitowcem, jak tylko obowiązki pozwolą i popodłączam się kablami.
    pozdrawiam
  • #7 13621840
    prm_ex
    Poziom 9  
    Zmienną z zakresu 0..99999 dałem do uinta32_t i błędu o 3 już nie ma.
    Ale jeszcze walczę bo czasem się jeszcze o 1 różni. Dziwne bo nawet jak wynik mnożenia przez 0,3 jest całkowity to daje również wtedy o 1 mniej mimo, że powinien zabrać do uinta własnie tylko całkowitą wartość ze zmiennej.
    Może przygotowanie zmiennej coś kuleje.
    Podam może całość operacji wysyłania począwszy od tablicy charów z odebraną liczbą receivedString[] oraz przypisanie do tej tablicy przychodzących znaków w przerwaniu:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    (Dla funkcji pow mam dołączony math.h)

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Dodano po 1 [godziny] 16 [minuty]:

    Witam ponownie
    Melduję, że znalazłem wredniaka (którego oczywiście to ja wpisałem).
    Pierwszy babol to jak koledzy zauważyli - dziękuję - przypisywałem za dużą zmienną do inta.

    Drugi babol znalazłem przed chwilą. pow() na intach zmyśla sobie wyniki bo musi działać na doublach. Pokombinowałem i zupełnie pozbyłem się pow'a a co za tym idzie biblioteki math.h.
    Odebraną liczbę z tablicy charów wrzucam do uint32_t tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    I wszystko hula jak należy oraz pozbyłem się jednej biblioteki ;)
    dziękuję za podpowiedzi i inspirację.
    pozdrawiam
  • #8 13622436
    BlueDraco
    Specjalista - Mikrokontrolery
    A wystarczyłoby:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    ... i tablica potęg dziesiątki zbędna, a obliczenia sporo szybsze.
  • #9 13622963
    prm_ex
    Poziom 9  
    Prostsze, szybsze itd - sprytne. Warto zapamiętać ;)
    Musiałem tylko dopasować do swoich oznaczeń
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    dziękuję Wam bardzo, pozdrawiam
    (dobrze, że nie zamknąłem jeszcze tematu ) :)
  • #10 13624890
    prm_ex
    Poziom 9  
    Szanowni forumowicze, mam jeszcze jeden dylemat.
    Zdecydowałem się na wysyłanie liczb w postaci "bajtvalue" bo po cyfrze (znaku) potrzeba wiele razy więcej bajtów wysłać, a mogę nie mieć czasu na transmisję.
    Nie muszę również używać znaków separacyjnych lub zer wiodących w liczbach.
    Ale potrzebowałbym jednak w jakiś sposób zaznaczyć że po np. 3ech liczbach czyli 6ciu bajtach, seria danych się kończy. Ale przecież LF też ma wartość, którą może przyjmować część wysyłanej danej.

    Teoretycznie wszystkie moje serie będą mieć te 6 bajtów więc niby nie potrzebowałbym tego zaznaczać, ale sęk w tym że po drodze wysyłane dane będą lecieć "wireless", a mając taki końcowy znacznik miałbym weryfikację w odbiorze że doszła cała seria danych jeśli siódmy bajt byłby charakterystyczny.
    Ale nie ma takiego przecież nie wymyślę bajtu o wartości 256 ;)

    Może ktoś miałby dla mnie jakąś podpowiedź jak to sobie zorganizować?
    pozdrawiam
    Przemo
  • #11 13624921
    tmf
    VIP Zasłużony dla elektroda
    A wysyłanie ramki 9-bitowej, zamiast 8-bitowej? W 9-bicie mógłbyś umieścić dodatkowe informacje, np. znacznik końca ramki.
  • #12 13625012
    prm_ex
    Poziom 9  
    Obawiam się że z moim modemem uart-uart (HM-TPR) jestem zdany na 8N1. :cry:
    (Podobno też 7E1 ale to i tak dla mnie tym bardziej nieinteresujące.)

    edit> praktycznie ostatnia liczba (trzecia) nigdy nie będzie większa niż 255 więc piątym bajtem mogłaby polecieć. Miałbym wówczas szósty bajt do dyspozycji ale to i tak nic nie zmienia bo jakakolwiek wartość 0..255 wygląda jak dane i nie ma jak zainicjować sprawdzenia czy to szósty bajt odebrany. Zresztą tak samo jakbym dodał sobie dowolny 7-my bajt. Nie ma jak go rozpoznać.

    edit2> jedyne co mi przychodzi do głowy to zabronić xmedze wysyłania serii (opuszczać ją) jeśli którykolwiek z 5-ciu bajtów ma wartość 255. 6-ty bajt wysyłać 255 i to traktować jako znacznik.
  • #13 13625062
    tmf
    VIP Zasłużony dla elektroda
    To oprzyj rozpoznawanie o timeouty. Jeśli pomiędzy kolejnymi bajtami upływa co najmniej określony czas, to traktujesz to jako nową transmisję.
  • #14 13625118
    prm_ex
    Poziom 9  
    No tak ale po wznowieniu transmisji (np po zgubieniu zasięgu, bo tego się obawiam) nadal nie mam pewności, że wznowiona została od pierwszego bajtu serii.
    Poza tym modemy między sobą możliwe że mogą po błędzie wysłać ponownie bajt i wówczas też się zrobi przerwa. Ciężkie to byłoby do opanowania.

    No nic. Modemy mają jeszcze ECC czyli error correction coś tam i możliwe że nie będę gubił bajtów. Więc spróbuję najpierw na żywca bez znacznika z włączonym ECC.
    Cytat:

    The ECC parameter makes a big difference to the data rate you can
    support at a given AIR_SPEED. If you have ECC set to zero, then no
    error correcting information is sent, and the radio uses a simple 16
    bit CRC to detect transmission errors. In that case your radio will be
    able to support data transfers in one direction of around 90% of the
    AIR_SPEED.

    If you enable ECC (which is highly recommended), then the data rate
    you can support is halved. The ECC system doubles the size of the data
    sent by the radios. It is worth it however, as the bit error rate will
    drop dramatically, and you are likely to get a much more reliable link
    at longer ranges.


    Jeśli się coś zgubi mimo to i po czkawce dalej polecą śmieci (pomieszane liczby) to wyłączę ECC i wykluczę z transmisji serie z 255. Chyba nic innego nie wymyślę.

    P.S. Dane ze źródła przychodzą 20Hz więc nawet jakbym połowę odrzucił lub zgubił w powietrzu to 10 serii na sek nadal w zupełności mi wystarczą. ;)
  • REKLAMA
  • #15 13625261
    tehaceole

    Poziom 28  
    prm_ex mógłbyś do wydzielania bajtów użyć unii:
    - definiujesz sobie nowy typ danych:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    - teraz definiujesz sobie zmienne których będziesz używać:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    - i już masz piękny dostęp do poszczególnych elementów zmiennej:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    - do tego wszystkiego możesz jeszcze dorzucić do unii pola bitowe jeżeli chciałbyś mieć dostęp do pojedynczych bitów w tych zmiennych.


    Tu masz praktyczny przykład użycia takiego "potworka":
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    O jego zastosowaniu możesz poczytać tutaj.
REKLAMA