Skocz do zawartości

Elvis

Użytkownicy
  • Zawartość

    2506
  • Rejestracja

  • Ostatnio

  • Wygrane dni

    177

Posty napisane przez Elvis


  1. Błąd był celowy, gdy wprowadzano GPS do użycia, ale później wprowadzany błąd bardzo zredukowano.

    Na bazie różnic między pomiarem dokładnym, a uzyskanym z GPS działa DGPS (https://pl.wikipedia.org/wiki/Różnicowy_GPS).

    Niestety w przypadku tanich odbiorników nie liczyłbym na jakąkolwiek użyteczność rozwiązania opisywanego w artykule. Profesjonalne odbiorniki GPS to zupełnie inna półka - proponuję sprawdzić ich ceny, gdyby można było w takich zastosowaniach użyć popularnych odbiorników używanych np. w telefonach, czy nawigacjach samochodowych, już dawno firmy sprzedające urządzenia za dziesiątki tysięcy złotych musiałyby zmienić asortyment...

    Tanie czytniki zwracają potworną wręcz ilość błędnych odczytów - nie jest to jedna i ta sama wartość, ale seria mocno losowych odczytów. To że wyniki wyświetlane w telefonie wyglądają sensownie, zawdzięczamy tylko odpowiednim algorytmom filtrującym oraz wykorzystaniem danych z wielu źródeł.

    Ale jako ciekawostkę zachęcam do podłączenia taniego odbiornika GPS i pobrania danych NMEA - z tego co wiem, takie surowe dane nie przydadzą się właściwie do niczego.

    • Lubię! 1

  2. W obsłudze przerwania masz wywołanie TIM_OC1Init, po pierwsze nie jestem pewien, czy zmienna channel jest poprawnie ustawiona, ale co ważniejsze Init służy do inicjalizacji - a z tego co rozumiem chciałeś tylko zmienić wypełnienie PWM. Do tego lepiej użyć TIM_SetCompare4.

    • Lubię! 1

  3. @aldenham Bardzo dobrze, że pytałeś i bardzo się cieszę że sprawa się wyjaśniła 🙂 Po prostu skoro już wiemy, że problem nie dotyczył DMA można pytania wydzielić z tego tematu - to nic złego i mam nadzieję że admin bez problemu to załatwi.

    Jeśli będziesz miał kolejne pytania odnośnie tablic, wskaźników i printf-ów, to też pytaj - na pewno pomożemy, w końcu po to jest Forbot.

    Wracając do printf-ów, nie wiem jakie ostrzeżenia generował kompilator, mogło chodzić o użycie typów bez znaku i wtedy zamiast %d można zastosować %u. Ale możliwe, że ostrzeżenia dotyczyły użycia wskaźników w miejscu gdzie powinny być liczby - więc jeśli ten problem pojawi się przy poprawnym użyciu tablic, napisz co wyświetla kompilator i jaki dokładnie masz kod.

    • Lubię! 1

  4. Widzę, że kolega @miszczu18 wyprzedził mnie w tłumaczeniu o co chodziło w programie 🙂

    Proponuję więc zostawienie DMA na chwilę w spokoju i powtórkę z działania tablic oraz wskaźników w języku C - bez tego raczej ciężko będzie napisać właściwie jakikolwiek program.

    Natomiast od administratora mam prośbę o wydzielenie tej dyskusji do oddzielnego tematu, dotyczy ona podstaw C i nie ma nic wspólnego z DMA, ani kursem STM32.


  5. Przyznam, że nic nie zrozumiałem - bufory mają i powinny mieć inne adresy, to co wstawiasz wygląda właśnie na adres bufora źródłowego, bo 536873560 czyli 0x20000a58 to adres w pamięci SRAM. Co więcej 536873592 - 536873560 = 32, a tyle wynosi wielkość bufora źródłowego, więc możemy się domyślać że wyświetliłeś tutaj adresy buforów, a nie ich zawartość.

    Funkcje copy_cpu/dma nie zmieniają adresów buforów, a jedynie kopiują dane. Więc adresy powinny być różne, ale ich zawartość identyczna.

    • Lubię! 1

  6. @hantercv spróbuj podłączyć sam konwerter usb-uart do komputera i sprawdź, czy Windows poprawnie go wykrywa. Jeśli nie to nie ma sensu podłączać malinki, najpierw musisz mieć wirtualny port COM, a na zdjęciu wygląda na problem ze sterownikami.

    Jeśli windows nie wykrywa konwertera, to najlepiej spróbować z innym komputerem - możliwe że jest to uszkodzony układ, ale w 99% przypadków problem jest po stronie oprogramowania.


  7. Komunikaty o błędach wskazują na zbyt małą ilość pamięci RAM dla sterty (heap) maszyny wirtualnej javy. To dość dziwne, ale na wszelki wypadek proponuję sprawdzić ustawienia JVM.

    Google powinien podpowiedzieć jak to zrobić, przykładowe linki:

    http://www.messiahpsychoanalyst.org/wikihow/index.php/How_to_Increase_Java_Memory_in_Windows

    https://stackoverflow.com/questions/17369522/set-default-heap-size-in-windows

    • Lubię! 1

  8. Specjalnie jeszcze raz przetestowałem - i u mnie też nie widać różnicy. Od 3,5V które deklaruje producent diody świecą z taką samą jasnością. Wcześniej wykonałem serię pomiarów i moim zdaniem od napięcia zależy prąd pobierany przez ws2812b kiedy są wyłączone, czyli im wyższe napięcie tym więcej prądu się marnuje. Natomiast różnica między "ciemnym" prądem, a pobieranym podczas świecenia jest niezależna od napięcia. Nie mam jak tego zmierzyć, ale moim zdaniem ta różnica to prąd faktycznie płynący przez diody świecące, więc ws2812b ma wyjścia prądowe, chociaż na wejściu pobiera prąd zależny od napięcia.

    ws2812b_pomiary10.thumb.png.c5330e6787539ff10b9ff0d12b197566.png

    W tych pomiarach, kolumna PWM=0 określa prąd (w miliamperach) jaki płynie gdy diody są wyłączone. Przy PWM=64 wypełnienie wynosi 25%, ale ciekawa jest ostatnia kolumna, czyli różnica między tymi prądami. Jak widać jest ona (w granicach błędu pomiaru) stała.

    • Lubię! 1

  9. Widzę, że pisząc cokolwiek w internecie trzeba niesamowicie uważać na słówka - faktycznie źle się wyraziłem, na pewno z Arduino i atmegii 328p da się jeszcze niejedno wycisnąć. Co do obsługi przerwań to nie byłbym pewien, czy to dobry kierunek - standardowe przerwanie od timera zajmuje ponad 6us, a transfer jednego bitu - 1,25us. Więc nawet mając 3 bity do przesłania (np. używając UART), zaczyna brakować czasu. Tak jak napisałem wcześniej - ws2812b są o wiele bardziej tolerancyjne na przerwy w komunikacji niż podaje specyfikacja, więc użycie przerwań i SPI, PWM lub UART pewnie zadziała, tyle że to nie będzie sterowanie zgodne z dokumentacją i to miałem na myśli pisząc o zmianie platformy.

    W tym co napisałem chciałem pokazać że za pomocą Arduino UNO można sterować nawet dość długim łańcuchem diod ws2812b. Nie jest to jedyne możliwe rozwiązanie i postaram się jeszcze opisać inne możliwości - dlatego wspomniałem o innych platformach, chociaż może niezbyt dobrze dobrałem słowa.


  10. Choinki jeszcze nie kupiłem, więc są ostatnie chwile na przygotowania 😉 Natomiast czy wrażenia artystyczne są takie ważne nie jestem pewien - w każdym razie oświetlenie choinki to dla mnie dobry pretekst do zajęcia się ws2812b, których trochę nazbierałem a jakoś ciągle brakowało czasu.


  11. Pisanie w języku C lub C++ programów, które mają działać z dokładnością do cykli procesora jest w sumie bez sensu, ale postanowiłem chociaż spróbować. Używając analizatora oraz dobierając mocno empirycznie wartości udało mi się przygotować taki kod na wysyłanie bitu zerowego:

    static inline void send_bit_zero(void)
    { 
      PORTD |= _BV(6);
      _NOP();
      PORTD &= ~_BV(6);
      for (uint8_t i = 0; i < 4; i++)
        _NOP();
    }

    Najważniejsze jest małe opóźnienie między ustawieniem stanu wysokiego na wyjściu, a powrotem do stanu niskiego - to raptem jeden _NOP().

    Aby wysłać bit o wartości "1" trzeba dodać większe opóźnienie:

    static inline void send_bit_one(void)
    { 
      PORTD |= _BV(6);
      for (uint8_t i = 0; i < 8;i++)
        _NOP();
      PORTD &= ~_BV(6);
    }

    Teraz już można napisać cały program. Jak wspominałem bity są wysyłane zaczynając od najbardziej znaczącego, kolejność kolorów to GRB

    #define LED_PIN         6 
    
    void setup()
    {
      DDRD |= _BV(6);
    }
    
    static inline void send_bit_zero(void)
    { 
      PORTD |= _BV(6);
      _NOP();
      PORTD &= ~_BV(6);
      for (uint8_t i = 0; i < 4; i++)
        _NOP();
    }
    
    static inline void send_bit_one(void)
    { 
      PORTD |= _BV(6);
      for (uint8_t i = 0; i < 8;i++)
        _NOP();
      PORTD &= ~_BV(6);
    }
    
    static inline void send(uint8_t value)
    {
      for (uint8_t i = 0; i < 8; i++ ) {
        if (value & 0x80)
          send_bit_one();
        else
          send_bit_zero();
        value <<= 1;
      }
    }
    
    void loop()
    {
      cli();
      for (int i = 0; i < 24; i++) {
        send(0x01);
        send(0);
        send(0);
      }
      sei();
    
      delay(10);
    }

    Gdy podłączymy analizator zobaczymy, że efekt jest dużo gorszy niż biblioteka Adafruit_Neopixel, ale co ważne działa poprawnie - testowałem nawet z 4 x 144 diodami.

    Tutaj wynik dla 24 diod:

    ws2812b_20.thumb.png.25788b15a9a8f5328e6c14f0392c4ad1.png

    Problemy pojawiają się po transmisji bajtu - powroty z funkcji, obsługa pętli itd zajmuje czas 😞 To niestety widać podczas transmisji:

    ws2812b_20b.thumb.png.307a42b79aada1c7ced35f0adab07471.pngws2812b_20c.thumb.png.6bfdc8bf75f2558f98ef0ca5832ab87b.png

    Jak widać napisanie dobrego programu do sterowania ws2812b nie jest łatwe - ten który przygotowałem działa, ale nie jestem z niego dumny. Chciałem natomiast przetestować moje rozumienie komunikacji z ws2812 i pokazać że to nie czarna magia. Po prostu trzeba dbać o dokładne czasy generowanych przebiegów. Co więcej sprzęt potrafi nam wybaczyć nawet więcej niż gwarantuje dokumentacja.

    • Lubię! 2

  12. Żeby lepiej zrozumieć sterowanie ws2812b postanowiłem spróbować napisać własną obsługę tych diod. Od razu chciałbym wyjaśnić, że nie mam aspiracji do napisania lepszej biblioteki niż Adafruit_Neopixel - kod tej biblioteki został wielokrotnie przetestowany, napisany w asemblerze i świetnie zoptymalizowany. Jest więc pod prawie każdym względem lepszy - tym czego mu brakuje to czytelność, ale nie można mieć o to chyba pretensji. Ja chciałbym tylko napisać prosty kod prototypowy, który pozwoli mi zrozumieć sterowanie ws2812b.

    Na początek samo sterowanie liniami GPIO - ws2812b wymaga raczej szybkiego sterowania, więc zanim zabiorę się do pracy trzeba upewnić się, że jest to wykonalne.

    Każdy, kto miał do czynienia z Arduino wie jak proste jest używanie funkcji digitalWrite() i sterowanie pinami. Zacznę więc od wręcz banalnego programu:

    #define LED_PIN         6 
    
    void setup()
    {
      pinMode(LED_PIN, OUTPUT);
    }
    
    void loop() {
      digitalWrite(LED_PIN, HIGH);
      digitalWrite(LED_PIN, LOW);
    }

    Podłaczam analizator i mogę zobaczyć co udało mi się uzyskać:

    ws2812b_18.thumb.png.d2621004697185c50e3fbd144c5a6f63.png

    Szerokość stanu wysokiego to 3,8 us, a do sterowania ws2812b konieczne są ~0,4us - więc digitalWrite() jest miłe i sympatyczne, ale zupełnie się nie nadaje.

    Na szczęście pod Arduino są dostępne też inne biblioteki. Spróbuję użyć FastGPIO (https://github.com/pololu/fastgpio-arduino)

    Nie znam tej biblioteki za bardzo, więc jeśli program jest niepoprawny to proszę śmiało pisać. W każdym razie udało mi się przygotować coś takiego:

    
    #include <FastGPIO.h>
    
    #define LED_PIN         6 
    
    void setup()
    {
      FastGPIO::Pin<LED_PIN>::setOutput(1);
    }
    
    void loop() {
      FastGPIO::Pin<LED_PIN>::setOutput(1);
      FastGPIO::Pin<LED_PIN>::setOutput(0);
      FastGPIO::Pin<LED_PIN>::setOutput(1);
      FastGPIO::Pin<LED_PIN>::setOutput(0);
    }

     

    Efekt działania programu wygląda następująco:

    ws2812b_18b.thumb.png.54b71f8194ac377d37b1a055725b93ef.png

    Jak widać impuls ma teraz szerokość 0,252us - to 15 razy lepiej niż przy użyciu digitalWrite !

    Poprzednio sterowanie wyjściem było tak powolne, że czas spędzony poza pętlą loop() nie miał praktycznie znaczenia. Używając szybszej biblioteki możemy jednak zobaczyć, że dwa impulsy są generowane z odstępem równym ich szerokości, ale kolejna przerwa jest trochę większa (ma 0,5us). Wywołanie funkcji loop() oraz pętli głównej trochę zajmuje, więc mierząc czasy trzeba o tym pamiętać.

    Na koniec spróbujmy zupełnie pominąć biblioteki Arduino i bezpośrednio odwołać się do rejestrów mikrokontrolera

    #define LED_PIN         6 
    
    void setup()
    {
      DDRD |= _BV(6);
    }
    
    void loop()
    {
      PORTD |= _BV(6);
      PORTD &= ~_BV(6);
      PORTD |= _BV(6);
      PORTD &= ~_BV(6);
    }

    Teraz wyniki są już prawie takie jak powinny:

    ws2812b_18c.thumb.png.5dbfa3c767294763b7cd026c9bc11ee4.png

    Arduino UNO jest taktowane zegarem o częstotliwości 16MHz, co oznacza że czas wykonywania instrukcji to 62,5ns (albo więcej). Skoro uzyskaliśmy szerokość impulsu 126ns to znaczy że zmiana stanu wyjścia wymaga dwóch cykli maszynowych. Nie jest źle, ale w asemblerze biblioteki Adafruit_Neopixel pewnie mogą osiągnąć więcej.

    W każdym razie ja postanowiłem zostać przy tej wersji - nadal jest to język C, ale jak widać wydajność starej szkoły bezpośredniego dostępu do rejestrów jest trudna do pobicia nawet przez C++.

    Przy okazji mała dygresja odnośnie przerwań - jak wspominałem wcześniej biblioteka Neopixel wyłącza obsługę przerwań na czas całej komunikacji z ws2812b. Mając prosty program testowy można ładnie zobaczyć dlaczego:

    ws2812b_19.thumb.png.ff935ba0d10ac30175f98ec776752639.png

    Jak widzimy obsługa przerwania zajmuje prawie 6,5us i wówczas program nie może "machać" pinem. Dlatego obsługując ws2812b autorzy biblioteki wyłączają przerwania - żeby w trakcie transmisji nie pojawiały się przerwy trwające kilka mikrosekund.

    • Lubię! 1
×
×
  • Utwórz nowe...