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

Jak przesłac wartość 16bit z PC do uC poprzez UART i odwrotn

Bladex 26 Sie 2007 18:26 2313 13
  • #1 4218037
    Bladex
    Poziom 11  
    Posty: 61
    Ocena: 8
    Tak jak w temacie, być moze kiedys ktos juz próbował i wymyślił jak to zrobic, osobiscie niewiem ponieważ jesli ramka jest 8-bit to daje tylko zakres od 0 do 255 , a gdy chciałbym przesłac jakąś wiekszą wartosć np 2000 ,to jest problem , gdzies czytałem że da się to zrobić poprzez łączenie pojedynczych bajtów,tak samo po stronei Mikrokontrolera jak i po stronie PC w Delphi czy VC++, do programownai mikrokntrolerów używam C (cavr). bardzo bym prosił o pomoc jesli ktos wie. Pozdrawiam!
  • #2 4218071
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    No to przesyłasz albo jako dwa bajty MSB, LSB i potem składasz albo zamieniasz liczbę na ASCII i jako ciąg znaków ze znakiem start i stop...
    Osobiście uważam drugą metodę za wygodniejszą...
    Jeśli chodzi o rozbijanie na bajty...
    
    unsigned char s_bajt, m_bajt;
    unsigned int liczba;
    ...
    m_bajt=liczba&0xFF;
    s_bajt=liczba/0x100; // s_bajt = liczba >> 8;
    ...
    
  • #3 4218090
    p@wel
    Poziom 13  
    Posty: 130
    Pomógł: 2
    Ocena: 10
    Zdecydowanie przekonwertować liczbe na ciąg znaków ASCII i przesłać po RS-ie. Jest to najbardziej uniwersalne i bezpieczne rozwiązanie, ponieważ są pewne znaki specjane stosowane w standardzie RS232 i wysłanie np liczby 10 spowoduje, że komputer odbierze to jak znak końca linii. Z kolei wysłanie tej liczby jako "1","0" (0x31,0x30,0x0A) nie powoduje żadnych problemów.
  • #4 4218175
    Bladex
    Poziom 11  
    Posty: 61
    Ocena: 8
    Dziekuje za odpowiedz ! No więc tak , sposób z "rozbiciem" liczby na dwa bajty ,starszy i młodszy jest raczej prosty bo rzeczywiscie wysyłamy 2 bajty przez port Com ,jednak według Kolegów:) prostszy jest sposbób zamioaną liczby na ciąg znaków czyli po stronie PC , jest to dosc proste ,jednak wtedy przy liczbie np 123 wysyłam 3 bajty itd.. , więc po jednej i drugiej stornie trzeba znów odpowiednio oprogramować bugof odbioru i potem znów konversja na wartość 16 bit... , Niewiem czy dobze to zrozumiałem ,prosze o wyrozumiałosc programuje jestem w tym temacie raczej srednio zaawansowany..., byćmoże któryś z Kolegów dysponuje jakims przykładem , tzn.kodem zaróno po stronie uC i PC .. Pozdrawiam i jeszcze raz dziekuje za odpowiedz i zainteresowanie :)
  • #5 4218389
    bobbyAIR
    Poziom 20  
    Posty: 267
    Pomógł: 41
    Ocena: 6
    p(małpa)wel napisał:
    wysłanie np liczby 10 spowoduje, że komputer odbierze to jak znak końca linii

    Komputer odbierze liczbę 10 i nic innego nie może odbierać. To dopiero teminal interpretuje to jako znak końca linii. Jeśli jednak masz własny program który obsługuje łącze szeregowe to ty decydujesz jak reagujesz na jakie liczby. Możesz np na liczbe 32 ( w ascii byłaby to spacja ) wyświetlac sobie napis "kotek"
  • #6 4218496
    gufiak
    Poziom 21  
    Posty: 444
    Pomógł: 15
    Ocena: 2
    pubus, ten kod jest tragiczny. Jest jeszcze dla Ciebie nadzieja, że nie rzutowałeś tych liczb na double przed podzieleniem (widziałem bibliotekę graficzną, gdzie w konstruktorze w pewnej klasie był czyszczony obrazek operując na liczbach double, co można było zrobić na liczbach stałoprzecinkowych, po poprawieniu kodu, konstruktor był wykonywany w ciągu pojedyńczych ms, a nie w ciągu ~800ms...). Jednak powinieneś zapamiętać że do operacji bitowych (a taką jest rozbijanie liczb na bajty) używa się funkcji logicznych, a nie arytmetycznych. Fakt, że na komputerze PC po jednokrotnym wykonaniu takiego dzielenia nie zauważy się różnicy, ale gdyby to było w pętli, a jeszcze tym bardziej wykonywane na jakimś uC, gdzie nie ma dzielenia sprzętowego, to taki sposób rozbijania liczby byłby dużym błędem. Prawidłowo kod powinien tak wyglądać:
    unsigned char s_bajt, m_bajt;
    unsigned int liczba;
    ...
    m_bajt = (unsigned char)(liczba & 0xFF);
    s_bajt = (unsigned char)(liczba >> 8);
    ... 
    Czyli to co miałeś w komentarzu było dużo lepsze, niż to co było przed nim. Można także zrobić to za pomocą unii połączonej ze strukturą, ale nie będę motać (choć wtedy dzielenie na bajty jest jeszcze łatwiejsze).

    Bladex, jak przekonwertujesz liczbę na liczbę szesnastkową, to do 255 będziesz miał dwa, a nie trzy znaki (0xFF). Ale to tak na marginesie, bo bobbyAIR ma rację.
  • #7 4218523
    aster11
    Poziom 19  
    Posty: 211
    Pomógł: 36
    Ocena: 1
    Też polecam metodę z przedstawieniem liczby w postaci znakowej i przesłania jej w takiej postaci. Wymieniłbym tu następujące uwagi:

    1. Rzeczywiście, jak zauważył bobbyAIR, żadna z wartości zaraz po odbiorze przez uP lub PC nie ma jeszcze jakiejś szczególnej interpretacji, o ile takiej nie narzucisz. Jednak używając postaci binarnej, każdy przesyłany bajt informacyjny będzie mógł mieć dowolną (0-255) wartość i nie będziesz mógł opracować wartości specjalnych, mających interpretację sterującą, np. początku ramki transmisyjnej. Jeżeli zastosujesz kodowanie znakowe, właściwe bajty informacyjne będą należeć tylko do zbioru kodującego znaki użytych cyfr. Pozostałym wartościom możesz nadać dowolne znaczenia specjalne.

    2. Zatem:
    a) przy kodowaniu binarnym nie będziesz mógł użyć znaków specjalnych, sygnalizujących początek (ew. także koniec) zbioru bajtów stanowiących ramkę informacyjną. Nie pozostanie nic innego jak odróżniać ramki za pomocą odpowiedniej szczeliny czasowej.
    b) przy kodowaniu znakowym możesz zastosować znak specjalny, symbolizujący początek ramki. Detekcja programowa początku ramki będzie wtedy bardzo prosta.
    Jestem przekonany, że rozwiązanie b) będzie prostsze do implementacji, zwłaszcza po stronie PC - chyba, że jesteś dobry w timerach, szczegółach WinAPI dla RS232 i wątkach. Różnica pomiędzy tymi dwoma trybami jest taka jak pomiędzy odmianami protokołu MODBUS-RTU i MODBUS-ASCII. Ten pierwszy używa szczelin czasowych do oddzielania ramek i nagimnastykowałem się trochę, tworząc kiedyś prosty program korzystający z tego protokołu.

    3. Zastosuj kodowanie znakowe, ale reprezentacji tekstowej heksadecymalnej liczby, a nie dziesiętnej! Jest to w zasadzie "standard", poza tym zaoszczędzisz na liczbie znaków, bo będzie ich zawsze 4 dla liczby 16-bitowej.

    4. Zawsze przesyłaj pełną 4-znakową reprezentację liczby, niezależnie od ilości nieznaczących zer. Rozwiązanie takie będzie prostsze do oprogramowania i bardziej eleganckie.
  • #8 4218693
    gufiak
    Poziom 21  
    Posty: 444
    Pomógł: 15
    Ocena: 2
    aster, jeżeli się używa konkretnego protokołu to naprawdę nie jest potrzebne wysyłanie reprezentacji liczby w ASCII. Można na przykład wysyłać takie ramki:
    [komenda:8][długość danych:8][dane:x][suma kontrolna:8]
    Ale jeżeli zawsze mają to być dwa bajty wysyłane, to wystarczy wysyłać je w takiej postaci w jakiej są. Oczywiście wtedy może pojawić się problem, gdy któraś ze stron zgubi jeden bajt. Wszystko zależy od tego z jaką częstotliwością będą dane wysyłane. Bo jeśli z niezbyt wielką, to można ustawić timeouty i odrzucać bajt jeśli nie pojawi się drugi w odpowiednim czasie. Jeśli jest większa częstotliwość to lepiej zastosować taką ramkę jak podałem niż zamieniać na ASCII, bo taka ramka ma większe możliwości, nie trzeba konwertować ASCII na liczbę po stronie uC (a tu warto by było sprawdzić czy nie ma w odebranych znakach wartości spoza przyjętego zakresu, więc jest więcej roboty), a do tego dochodzi suma kontrolna, więc transmisja jest pewniejsza. Zawsze można zastosować jakiś handshake, potwierdzanie przyjęcia danych oraz zgłaszanie błędnych pakietów i retransmisję, jeśli koniecznie każdy pakiet musi dotrzeć do odbiorcy (wtedy warto jeszcze dodać numer sekwencji, ale to się już robi coraz bardziej skomplikowane).
  • #9 4218825
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    gufiak pytanie...
    Co daje taki zapis...?
    Na mój rozum jeżeli wynik podstawiam do zmiennej określonego typu to jest on automatycznie do takiego konwertowany...
    No chyba, że o czymś nie wiem...
    Co do sposobu i protokołu transmisji...
    Jeżeli wysyłamy znak start + liczba w ASCII + stop, to łatwo się połapać, że jest błąd...
    Prawdopodobieństwo, że dany bajt zostanie przekłamany na inny z zakresu kodów 0-9 (cyfry) jest baaardzo małe...
    W większości przypadków wystarczy w zupełności a bajtów jest mniej w ramce...
    Oczywiście twój sposób jest pewniejszy ale może być przerostem formy nad treścią...
  • #10 4219035
    gufiak
    Poziom 21  
    Posty: 444
    Pomógł: 15
    Ocena: 2
    Cytat:
    gufiak pytanie...
    Co daje taki zapis...?
    Na mój rozum jeżeli wynik podstawiam do zmiennej określonego typu to jest on automatycznie do takiego konwertowany...
    Tu akurat dopuszczalne jest rzutowanie niejawne. Tyle, że po paru latach programowania w C wyrobiłem u siebie nawyk rzutowania jawnego gdzie się da, a przynajmniej tam, gdzie wykonuję operacje bitowe na różnych typach danych. Z prostej przyczyny - niejednokrotnie miałem problemy ze znalezieniem błędu w obliczeniach, bo po prostu były różne typy danych, które się gryzły. Ale oczywiście, jak już pisałem, w takim przypadku możesz śmiało korzystać z rzutowania niejawnego.

    Cytat:
    Co do sposobu i protokołu transmisji...
    Jeżeli wysyłamy znak start + liczba w ASCII + stop, to łatwo się połapać, że jest błąd...
    Prawdopodobieństwo, że dany bajt zostanie przekłamany na inny z zakresu kodów 0-9 (cyfry) jest baaardzo małe...
    Nie jest znowu takie bardzo małe. Reprezentacja cyfr 0-9 w kodzie ASCII ma wartości 0x30 - 0x39. Daje to nam 9 kombinacji na każdy bajt, w których może być błąd, a błąd może pojawić się na czterech bitach i nie być wykryty. Jeżeli wysyłamy jeden bajt, to musimy wysłać dwa bajty w kodzie ASCII, czyli już mamy 8 bitów na których mogą wystąpić błędy, co jest równe 1 bajtowi. Poza tym przy zapisie w ASCII robi się więcej danych do wysłania gdy wysyłamy dane większe niż 8-bitowe.
    Np.:
    Liczba 16-bitowa: w zaproponowanym przeze mnie formacie mamy 5 bajtów: bajt komendy, bajt długości danych, 2 bajty danych oraz bajt sumy kontrolnej. W zaproponowanym przez Ciebie formacie mamy już 6 bajtów: bajt startu, 4 bajty danych oraz bajt stopu, i jeszcze słabszą wykrywalność błędów. Jakby dodać bajt sumy kontrolnej, to w przypadku ASCII mamy oczywiście większe bezpieczeństwo bo ograniczyliśmy alfabet dla danych, lecz już jest 7 bajtów do wysłania.
    Liczba 32-bitowa: u mnie 7 bajtów, u Ciebie 10 bajtów.
    W przypadku wysyłania łańcuchów znakowych byłoby jeszcze gorzej...
    Cytat:
    W większości przypadków wystarczy w zupełności a bajtów jest mniej w ramce...
    Dla liczby 8-bitowej jest mniej, ale dla liczby 8-bitowej nie widzę w większości przypadków sensu tworzenia jakiejkolwiek ramki.
    Cytat:
    Oczywiście twój sposób jest pewniejszy ale może być przerostem formy nad treścią...
    To właśnie wysyłanie w danych w formacie ASCII jest zdecydowanym przerostem formy nad treścią.
  • #11 4219166
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Dobra tak to się zgadza...
    Myślałem, że też chcesz jako ASCII wysyłać...
  • #12 11467615
    micrograf
    Poziom 11  
    Posty: 40
    Pomógł: 1
    witam,

    Odświeżę trochę temat. Wysyłam UARTem liczby w postaci binarnej po 2 bajty na liczbę. stosuję podział liczby na MSB i LSB podobnie jak w tym temacie. Liczby dodatnie są OK, problem pojawia się w momencie przesłania liczby ujemnej, po podziale na MSB i LSB otrzymuje złe wartości. Proszę o naprowadzenie na rozwiązanie
  • #13 11467678
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Nie zamieściłeś kodu ale zakładam, że masz skopane rzutowanie typów...
    Gdzieś rzutujesz na unsigned po czym traktujesz jak signed... albo na odwrót...
    Sprawdź obie strony PC i uC...
  • #14 11472315
    superduo
    Poziom 13  
    Posty: 181
    Pomógł: 3
    Ocena: 49
    Nie czytałem wszystkich postów ale:

    Przyjętym standardem to transmisji nietypowych danych jest słanie ich jako znaki ascii z wartościami zakodowanymi jako HEXy (od 0 do F).
    W ten sposób transmituje się bajty np w telefonii komórkowej (PDU).

Podsumowanie tematu

✨ Dyskusja dotyczy przesyłania wartości 16-bitowych między komputerem PC a mikrokontrolerem (uC) za pomocą interfejsu UART, gdzie standardowa ramka ma 8 bitów, co ogranicza zakres do 0-255. Proponowane rozwiązania obejmują: rozbicie liczby 16-bitowej na dwa bajty (MSB i LSB) i przesłanie ich kolejno, a następnie złożenie po stronie odbiorcy; lub konwersję liczby na ciąg znaków ASCII i przesłanie jako tekst, co ułatwia interpretację i synchronizację ramki, ale zwiększa ilość przesyłanych danych. Wskazano, że operacje bitowe powinny być wykonywane za pomocą operatorów logicznych (AND, przesunięcia bitowe), a nie arytmetycznych dzielenia. Omówiono także kwestie protokołu transmisji, takie jak stosowanie znaków start/stop, sum kontrolnych, timeoutów i ewentualnego handshake, aby zapewnić poprawność i synchronizację danych. W przypadku przesyłania liczb ujemnych problemem jest prawidłowe rzutowanie typów (signed/unsigned) po obu stronach transmisji. Zwrócono uwagę, że standardem w transmisji nietypowych danych jest kodowanie ich jako znaki ASCII w formacie HEX, co jest powszechne np. w telefonii komórkowej (PDU). Podsumowując, wybór metody zależy od wymagań dotyczących szybkości, niezawodności i prostoty implementacji po stronie PC (Delphi, VC++) i mikrokontrolera (C dla AVR).
Wygenerowane przez model językowy.
REKLAMA