Elektroda.pl
Elektroda.pl
X
PCBway
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Arduino oled i modbus rtu

05 Cze 2019 12:59 498 24
  • Poziom 12  
    Witam, konstruuje moduł do tzw. inteligentnego domu moduł znajdzie się w puszce pod włącznikiem. Zadania jakie będzie realizował to:
    - pomiar temperatury DS18b20,
    - enkoder (będzie zrealizowany na przerwaniach),
    - 8 wejść cyfrowych,
    - wyświetlacz oled do wyświetlania parametrów (OLED 0,96' 128x64 na I2C - SSD1306).
    Moduł będzie komunikował się z sercem domu mianowicie z Fatkiem, który będzie sterował wszystkimi urządzeniami w domu. Komunikacja pomiędzy fatkiem, a modułami będzie realizowana poprzez RS485 z MODBUS RTU.
    Jestem na etapie pisania programu do modułu lecz trafiłem na problem. Problem polega na tym, że w pętli głównej programu mam obsługę modbus-a i oleda, który generuje znaczne opóźnienie wykonywanego programu. W jaki sposób mogę rozwiązać obsługę oleda lub modbus-a, aby działała niezależnie od głównej pętli?
    Code:

    #include <SimpleModbusSlave.h>
    #include <OneWire.h>
    #include <DS18B20.h>
    #include <Adafruit_GFX.h>
    #include <Adafruit_SSD1306.h>

    /*
       SimpleModbusSlaveV10 supports function 3, 6 & 16.
       function 3: READ_HOLDING_REGISTERS    **** this example uses this function [4-7]
       function 6: PRESET_SINGLE_REGISTER
       function 16: PRESET_MULTIPLE_REGISTERS **** this example uses this function [0-3]
    */

    //konfiguracja modbus
    #define TxEnablePin 8
    #define   baud 57600   //9600
    #define   SlaveID 1
    #define   HOLDING_REGS_SIZE 220

    //wejscia
    #define SW1 A0
    #define SW2 A1
    #define SW3 A2
    #define SW4 A3
    #define SW5 9
    #define SW6 10
    #define SW7 5
    #define SW8 6
    #define NAPIECIE 7
    #define ENC_L 2
    #define ENC_R 3
    #define ENC_BT 4
    #define DS18B20_pin 13


    //konfiguracja ds18b20
    const byte ONEWIRE_PIN = DS18B20_pin;
    OneWire onewire(ONEWIRE_PIN);
    DS18B20 sensors(&onewire);
    byte address[8];
    float temperatura;

    unsigned int holdingRegs[HOLDING_REGS_SIZE]; // function 3 and 16 register array
    unsigned int successful_requests;

    //konf oleda
    #define SCREEN_WIDTH 128
    #define SCREEN_HEIGHT 64

    #define OLED_RESET 4
    Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


    void setup()
    {
      modbus_configure(&Serial, baud, SERIAL_8N2, SlaveID, TxEnablePin, HOLDING_REGS_SIZE, holdingRegs);
      modbus_update_comms(baud, SERIAL_8N2, SlaveID);

      pinMode(SW1, INPUT_PULLUP); //Przycisk 1
      pinMode(SW2, INPUT_PULLUP); //Przycisk 2
      pinMode(SW3, INPUT_PULLUP); //Przycisk 3
      pinMode(SW4, INPUT_PULLUP); //Przycisk 4
      pinMode(SW5, INPUT_PULLUP); //Przycisk 5
      pinMode(SW6, INPUT_PULLUP); //Przycisk 6
      pinMode(SW7, INPUT_PULLUP); //Przycisk 7
      pinMode(SW8, INPUT_PULLUP); //Przycisk 8
      pinMode(NAPIECIE, INPUT); //pomiar napięcia 
      //pinMode(ENC_L, INPUT); //ENC L
      //pinMode(ENC_R, INPUT); //ENC P
      pinMode(ENC_BT, INPUT); //ENC BT
     
        onewire.reset_search();
      while(onewire.search(address))
      {
        if (address[0] != 0x28)
          continue;
      }
      sensors.begin(10);
      sensors.request(address);

      //oled
      display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
      display.clearDisplay();

    }

    void loop()
    {
     
     modbus_update();
     
      //odczyt czujnika ds18b20
      if (sensors.available())
      {
        temperatura = sensors.readTemperature(address)*10;
        sensors.request(address);
      }
     
      if (digitalRead(SW1) == LOW) {holdingRegs[0] = 1;} else {holdingRegs[0] = 0;}
      if (digitalRead(SW2) == LOW) {holdingRegs[1] = 1;} else {holdingRegs[1] = 0;}
      if (digitalRead(SW3) == LOW) {holdingRegs[2] = 1;} else {holdingRegs[2] = 0;}
      if (digitalRead(SW4) == LOW) {holdingRegs[3] = 1;} else {holdingRegs[3] = 0;}
      if (digitalRead(SW5) == LOW) {holdingRegs[4] = 1;} else {holdingRegs[4] = 0;}
      if (digitalRead(SW6) == LOW) {holdingRegs[5] = 1;} else {holdingRegs[5] = 0;}
      if (digitalRead(SW7) == LOW) {holdingRegs[6] = 1;} else {holdingRegs[6] = 0;}
      if (digitalRead(SW8) == LOW) {holdingRegs[7] = 1;} else {holdingRegs[7] = 0;}
     
      holdingRegs[8] = temperatura; //wystawienie na rejestr 8 temperatury 

      holdingRegs[9] = (analogRead(NAPIECIE)*10) / 20; // pomiar napięcia zasilania

      //enc bt
      if (digitalRead(ENC_BT) == LOW) {holdingRegs[11] = 1;} else {holdingRegs[11] = 0;}
     
      display.clearDisplay();

      display.setTextSize(2);             // Normal 1:1 pixel scale
      display.setTextColor(WHITE);        // Draw white text
      display.setCursor(0,0);             // Start at top-left corner
      display.println(F("Hello, world!"));

      display.display();
    }
  • PCBway
  • Poziom 34  
    cinek_14 napisał:
    W jaki sposób mogę rozwiązać obsługę oleda lub modbus-a, aby działała niezależnie od głównej pętli?

    Na przykład przez umieszczenie obsługi modbus w przerwaniu.
  • Poziom 12  
    Dobrze, a mogą mnie koledzy naprowadzić jak to zrobić. Na przerwaniach zewnętrznych będzie enkoder i to wiem jak wykonać, a jak zrobić przerwania dla biblioteki obsługującej modbus "modbus_update();", i dla oleda. Z góry dziękuję za pomoc.
  • Poziom 34  
    Naprościej to będzie wykorzystać arduinową bibliotekę TimerOne: https://github.com/PaulStoffregen/TimerOne
    Przykład podpięcia pod obsługę przerwania: https://github.com/PaulStoffregen/TimerOne/blob/master/examples/Interrupt/Interrupt.pde

    Dodano po 51 [sekundy]:

    Opis biblioteki: https://www.pjrc.com/teensy/td_libs_TimerOne.html
  • PCBway
  • Poziom 31  
    Na początek naucz się używać millis(), oceń czy potrzebujesz badać temperaturę w każdej pętli loop, upewnij się że pracujesz w trybie asynchronicznym (w czasie pomiaru nie czekasz 750ms na jego zakończenie?), zmierz czas trwania poszczególnych funkcji używając micros(). Jeśli to jest temperatura pokoju to spokojnie możesz mierzyć co 60s, w każdej minucie wykonujesz zlecenie pomiaru np. w 58 sekundzie i odczyt w 59 sekundzie. Przyciski analogicznie, sprawdzać ich stan można co 5ms, ale stan można wysyłać RS485 tylko wtedy gdy się coś zmieni + kontrolnie raz na 1s. Ekran też nie musi być odświeżany w każdej pętli, co sekundę wystarczy, więcej niż 10Hz nie ma z tym oledem sensu. Wszystko sprowadza się do nauczenia się obsługi czasu w Arduino. Mogą to też być przerwania, jeśli np. potrzebujesz idealnie co 100ms odświeżyć ekran.
  • Poziom 34  
    cinek_14 napisał:
    Na przerwaniach zewnętrznych będzie enkoder i to wiem jak wykonać, a jak zrobić

    Jeżeli użyjesz funkcji millis() w pętli loop(), to pamiętaj że funkcja ta blokuje na chwilę przerwania w trakcie swojego działania. Zbyt duża częstotliwość jej użycia w pętli loop() może spowodować zakłócenia w odczycie enkodera.
    Kod: c
    Zaloguj się, aby zobaczyć kod
  • Poziom 31  
    Każde wywołanie millis() trwa w sumie 29 cykli zegara, w tym przerwania blokowane są tylko na mniej niż połowę, czyli około 1us (przy 16MHz), w pętli loop można wywołać millis() raz by potem napełnić swoje zmienne setek ms/sekund/minut/itd. Oczywiście przerwanie zostanie zauważone, po prostu reakcja może być opóźniona o tą 1 milionową część sekundy. Jakby trzeba było zliczać tysiące impulsów/s od enkodera to można by zauważyć jakiś wpływ.
  • Poziom 30  
    cinek_14 napisał:
    a jak zrobić przerwania dla biblioteki obsługującej modbus "modbus_update();", i dla oleda.

    Dla OLED nie sądzę abyś łatwo znalazł gotowca, musisz zrobić sam (przerobić tą, którą masz). W sumie to proste. Znajdź bibliotekę, która buforuje ekran (jeśli ta, która masz działa inaczej). Następnie zamiast dostarczonej funkcji diplay czy innej o podobnej funkcjonalności, daj swoją używającej przerwań I2C. 6..10godzin powinno wystarczyć dla przeciętnego programisty aby zrobić taką funkcjonalność. Raz się pomęczysz a w przyszłości będziesz miał porządną bibliotekę, która nie blokuje CPU w czasie wysyłania danych do wyświetlacza.
  • Poziom 34  
    kaczakat napisał:
    Każde wywołanie millis() trwa w sumie 29 cykli zegara, w tym przerwania blokowane są tylko na mniej niż połowę, czyli około 1us (przy 16MHz), w pętli loop można wywołać millis() raz by potem napełnić swoje zmienne setek ms/sekund/minut/itd.

    Tylko po co w ogóle blokować przerwania w loop() skoro można użyć zasobu procesora w postaci timera do harmonogramowania zadań? Chyba, że dla sportu, co wyjaśnię poniżej.

    Rożnicę pomiędzy harmonogramowaniem zadań z użyciem przerwań a wywoływanie funkcji po odmierzeniu stosowanego czasu w loop() można przedstawić przez następującą analogię:
    W pierwszym przypadku udajemy się na przystanek autobusowy, zgodnie z ustalonym harmonogramem.
    W drugim przypadku biegamy tam i z powrotem do przystanku, aż przyjedzie autobus, a w międzyczasie zawiązujemy kolejne węzły na sznurku.

    LChucki napisał:
    Dla OLED nie sądzę abyś łatwo znalazł gotowca, musisz zrobić sam (przerobić tą, którą masz).

    Nie sądzę, aby w obsłudze przerwania była konieczność umieszczenia obsługi wyświetlacza OLED. Wyświetlanie to mniejszy priorytet niż akwizycja danych z czujników czy czytanie stanu enkodera - opóźnienia rzędu kilkuset milisekund nie będą miały w praktyce żadnego znaczenia w obsłudze wyświetlacza. Zakładam oczywiście, że nie jest to oscyloskop ;)
  • Poziom 30  
    khoam napisał:

    LChucki napisał:
    Dla OLED nie sądzę abyś łatwo znalazł gotowca, musisz zrobić sam (przerobić tą, którą masz).

    Nie sądzę, aby w obsłudze przerwania była konieczność umieszczenia obsługi wyświetlacza OLED. Wyświetlanie to mniejszy priorytet niż akwizycja danych z czujników czy czytanie stanu enkodera - opóźnienia rzędu kilkuset milisekund nie będą miały w praktyce żadnego znaczenia

    Kilu powiadasz! Policzmy:
    1/(400000kHz/9bit/(1adr+1cmd+1024dane ekranu)) =~ 23ms.
    23 to nie kilka tylko kilkadziesiąt.
  • Poziom 34  
    LChucki napisał:
    23 to nie kilka tylko kilkadziesiąt.

    Faktycznie nie wyraziłem się zbyt precyzyjnie. Chodziło mi o czas pomiędzy odczytem sensora a pojawieniem się wyniku tego pomiaru na wyświetlaczu. Oczywiście sam proces "rysowania" literek i cyfr na wyświetlaczu nie może być w trakcie wykonywania tej operacji aż tak opóźniany. Jednakże nie wpływa to na fakt, że można obsługę wyświetlacza umieścić w loop().
  • Poziom 31  
    khoam napisał:
    Tylko po co w ogóle blokować przerwania w loop() skoro można użyć zasobu procesora w postaci timera do harmonogramowania zadań? Chyba, że dla sportu, co wyjaśnię poniżej.

    Przyczepiłeś się millis() i łapiesz mrówki, gdy Ci słonie uciekają. Standardowo biblioteki do DS18B20 korzystają z biblioteki onewire, która też wyłącza przerwania i nie na 1us tylko na 50-70. I to Ci nie przeszkadza, że w loop na okrągło pyta o temperaturę z DS? Przecież jak zacznie używać w przerwaniach gotowych funkcji do obsługi oleda to będzie blokował inne przerwania na kilkanaście ms, tyle trwa wysłanie ekranu z bufora. Ale problem zauważyłeś w 1us? Przerwania są świetnym narzędziem, pod warunkiem, że są używane do najważniejszych rzeczy, wymagających dużej precyzji interwału i trwających pojedyncze us. Jak cały kod będzie robiony w przerwaniach, gdzie w AVR jedno blokuje wszystkie inne to efekt będzie gorszy niż to co ma teraz. Jakie znaczenie ma czy sobie temperaturę odświeży co 750ms czy co 800 gdy wystarczy co minutę, czy ekran oleda odświeży co 90 czy 110ms gdy wystarczy co sekundę? Z napisanego kodu widać, że nie ma pojęcia o zarządzaniu czasem w uC i ile co trwa, kazać @cinek_14 napisać swoje biblioteki od nowa to równie dobrze można mu polecić zrezygnowanie z Arduino i zaczęcie wszystkiego od nowa w czystym C z bezpośrednim grzebaniem w uC.
    Analogia z autobusem jest kiepska, nikt nie każe biegać na przystanek, użycie millis bardziej przypomina zerkanie na zegarek czy już pora wyjść, a przerwanie od timera to ustawienie budzika. Jeśli musiałby odczytać temperaturę z DS dokładnie za 750ms to OK, ale może odczytać nie szybciej niż i będzie tam na niego czekać aż do odłączenia zasilania, to tylko kwestia jak bardzo świeża.
  • Poziom 34  
    kaczakat napisał:
    Standardowo biblioteki do DS18B20 korzystają z biblioteki onewire, która też wyłącza przerwania i nie na 1us tylko na 50-70.

    To jest uzasadnione i nie wiąże się z cyklicznym pomiarem czasu, ale z koniecznością uzyskania poprawnego wyniku pomiaru.

    kaczakat napisał:
    Przecież jak zacznie używać w przerwaniach gotowych funkcji do obsługi oleda to będzie blokował inne przerwania na kilkanaście ms, tyle trwa wysłanie ekranu z bufora.

    Z OLED to nie mój pomysł - patrz post #10. Mogę tylko powtórzyć, że obsługa OLED powinna pozostać w loop().

    kaczakat napisał:
    Jak cały kod będzie robiony w przerwaniach, gdzie w AVR jedno blokuje wszystkie inne to efekt będzie gorszy niż to co ma teraz.

    Nie cały kod, tylko niezbędne rzeczy i krytyczne dla działania programu, czyli obsługa niezbędnych aktuatorów.

    kaczakat napisał:
    kazać @cinek_14 napisać swoje biblioteki od nowa to równie dobrze można mu polecić zrezygnowanie z Arduino i zaczęcie wszystkiego od nowa w czystym C z bezpośrednim grzebaniem w uC.

    Nic takiego nie proponowałem, wręcz przeciwnie. Zaproponowałem użycie gotowej biblioteki TimerOne zamiast pisania własnych algorytmów "zegarmistrzowskich" - patrz post #5.

    kaczakat napisał:
    użycie millis bardziej przypomina zerkanie na zegarek czy już pora wyjść

    No właśnie. Zamiast ciągle zerkać na zegarek można byłoby w tym czasie zrobić coś bardziej pożytecznego np. przespać się w oczekiwaniu na budzik.
  • Poziom 31  
    khoam napisał:
    Nic takiego nie proponowałem, wręcz przeciwnie. Zaproponowałem użycie gotowej biblioteki TimerOne zamiast pisania własnych algorytmów "zegarmistrzowskich" - patrz post #5.

    Timer one był odpowiedzią na pytanie o zastosowanie przerwań dla modbus i oled autora wątku.
    Jeśli nie napisze własnych funkcji do obsługi one wire i DS18B20 to przerwania ze zleceniem pomiaru/odczytu będą blokowały uC na kilkadziesiąt ms, a jak nie będzie umiał użyć tych gotowych to nawet na kilkaset. Wtedy dopiero zaczną się jaja z odczytem enkodera z potencjalnym ugotowaniem pętli loop na amen. Właściwie to ja nie widzę możliwości użycia tu timera w ogóle, jak już użyć przerwań to tylko implementując obsługę na UART zgodnie z propozycją LChucki.
    Jest spora różnica w zerknięciu na zegarek (1us dla uC, kilkaset ms dla ludzkiego oka), a bieganiem na przystanek bez potrzeby.
    To do czego użyć tych przerwań timera1 w końcu, do drukowania temperatury na sieć RS485? To jest ten krytyczny czasowo element programu? Jak to jest slave to i tak powinien odpowiadać na przerwanie związane z UART gdy jest zapytany. Może do przepisania zmiennych do rejestrów ModBUS - zagadka...
  • Poziom 34  
    kaczakat napisał:
    Timer one był odpowiedzią na pytanie o zastosowanie przerwań dla modbus i oled

    Nie, to była moja odpowiedź tylko w sprawie modbus:
    khoam napisał:
    Na przykład przez umieszczenie obsługi modbus w przerwaniu.


    kaczakat napisał:
    Jeśli nie napisze własnych funkcji do obsługi one wire i DS18B20 to przerwania ze zleceniem pomiaru/odczytu będą blokowały uC na kilkadziesiąt ms, a jak nie będzie umiał użyć tych gotowych to nawet na kilkaset.

    Nie ma takiej potrzeby. Istnieje możliwość wykorzystania nieblokującej obsługi czujnika w bibliotece dla DS18B20.

    kaczakat napisał:
    To do czego użyć tych przerwań timera1 w końcu, do drukowania temperatury na sieć RS485? To jest ten krytyczny czasowo element programu? Jak to jest slave to i tak powinien odpowiadać na przerwanie związane z UART gdy jest zapytany. Może do przepisania zmiennych do rejestrów ModBUS - zagadka...

    Dla mnie to też zagadka, bo dopiero w tym momencie taki zagadkowy "temat" wypłynął.

    Generalna moja uwaga jest taka, że nie można z góry zakładać, że Autor tego wątku jest idiotą tylko dlatego, że Szanowny Kolega chce za wszelką cenę przeforsować swoje pomysły, bez żadnego poszanowania w stosunku do opinii innych osób na Forum.
  • Poziom 30  
    kaczakat napisał:
    jak już użyć przerwań to tylko implementując obsługę na UART zgodnie z propozycją LChucki.

    Nawet na AVR można obsłużyć 1-Wire "machając" GPIO w przerwaniach (prędkość standardowa o overdrive zapomni) od timera (czasy 15, 45 i 60us) tyle, że w czasie transmisji 1-Wire CPU będzie bardzo obciążony (zwłaszcza podczas generowania "1" - 15us). Aby to miało sens wstawki ASM są raczej niezbędne.
    Kolejny problem to wysoki priorytet przerwań timera obsługującego 1-Wire. Da się to sztucznie zrobić ale z przerwaniami od UART będzie problem (znów wstawki ASM).

    Podsumowując:
    Da się zrobić ale sporo roboty i wiedza typowo "Arduinowa" to za mało aby to zrobić.
  • Poziom 31  
    khoam napisał:
    Generalna moja uwaga jest taka, że nie można z góry zakładać, że Autor tego wątku jest idiotą tylko dlatego, że Szanowny Kolega chce za wszelką cenę przeforsować swoje pomysły, bez żadnego poszanowania w stosunku do opinii innych osób na Forum.

    Każdy mierzy swoją miarą. Sam się uczę i chętnie poznam lepsze rozwiązania, niestety jak zwykle argumenty w Twoich wypowiedziach zjeżdżają do poziomu depresji.
    @cinek_14 wg mnie wiele tu zmieniać nie musisz, funkcja do modbusa ma być w pętli głównej tak jak jest, ma to być slave, ma możliwie szybko odpowiedzieć masterowi. Z dodaniem millis() wyglądałoby to np. tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Ja nie zrozumiałem jak wykorzystać tu sensownie przerwania timera i jaki konkretnie dadzą zysk. Popatrzę tu jutro, może się przekonam, że jest inaczej.
  • Poziom 30  
    kaczakat napisał:
    Z dodaniem millis() wyglądałoby to np. tak:

    Nie podoba mi się:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Te 100ms może powodować, że enkoder będzie działał "zrywami". Należałoby, jak już pisałem wcześniej, zastąpić "display.display();" swoją funkcją na przerwaniach. Wtedy, przez ok 23ms, program główny byłby zatrzymywany przez przerwanie na max kilkadziesiąt us (mikro!).
    Nie wiadomo jak często musi być wywoływane "modbus_update();". Jeśli częściej niż co 23ms, to......
  • Poziom 34  
    W jakim celu w funkcji czas() są zliczane minuty, godziny i dni? Ta funkcja jest każdorazowo wywoływana w loop().

    Dodano po 43 [minuty]:

    Poniższy fragment kodu nie będzie działał prawidłowo, jeżeli funkcja request() zwróci wartość false ("device not responding"):
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Dodano po 3 [minuty]:

    Ten z kolei fragment kodu nie obsługuję błędu w wypadku, kiedy funkcja readTemperature() zwróci wartość TEMP_ERROR:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Dodano po 14 [minuty]:

    LChucki napisał:
    Nie wiadomo jak często musi być wywoływane "modbus_update();". Jeśli częściej niż co 23ms, to......

    W tej chwili to na pewno wiadomo, że nie wiadomo jak często jest wywoływana ta funkcja w pętli loop().
  • Poziom 12  
    Powiem tak moduł ma być modułem slave, ponieważ fatek będzie masterem i będzie odpytywał około 20 modułów jak poniżej. Co do pomiaru temperatury zgadzam się z wami nie ma sensu odczytywać jej co każde przejście pętli loop, może to być robione spokojnie co 60 sek. Sprawa dotycząca modbus, powinna być wykonywana co każde przejście pętli, bez żadnych opóźnień ponieważ w module mamy obsługę 8 wejść i fatek musi o tym wiedzieć bardzo szybko, aby wykonał odpowiednią procedurą obsługującą dany przycisk. Odświeżanie oleda wolałbym, aby było poniżej 1 sek, ponieważ w przyszłości enkoderem będzie zadawanie temperatury w pomieszczeniu z bezpośrednim wyświetlaniem jej. Temat jest dość rozwojowy. Przetestuje Wasze pomysły i dam znać jak potoczyły się testy.

    Arduino oled i modbus rtu
    Arduino oled i modbus rtu
  • Pomocny post
    Poziom 30  
    khoam napisał:

    LChucki napisał:
    Nie wiadomo jak często musi być wywoływane "modbus_update();". Jeśli częściej niż co 23ms, to......

    W tej chwili to na pewno wiadomo, że nie wiadomo jak często jest wywoływana ta funkcja w pętli loop().

    Zależnie od warunków, loop może wykonać się w kilkadziesiąt us ale równie dobrze, gdy wszystko się nałoży na siebie (realizowana będzie transmisja do OLED, odczytywany termometr i co tam jeszcze jest) czas ten może zawierać się w granicach 50..100ms (nie liczyłem tego).
    Przeważnie czas wykonania pętli głównej na poziomie dziesiątek ms najczęściej nie stanowi problemu, tak ponad 100ms problem może już być.
    Dlatego, co się da, należy realizować na przerwaniach. W tym przypadku bardzo prosto zrealizować obsługę OLED na przerwaniach, co praktycznie spowoduje, że OLED nie zabiera czasu CPU. To samo z 1-Wire, na UART i praktycznie nie obciąża CPU. Naturalnie, w obsłudze termometru wywalamy double (w praktyce float), które obciążają CPU a nic przez to nie zyskujemy.

    Jest też inna droga, o której już było, Modbus na przerwaniach. Wtedy loop moze wykonywać się i kilka sekund. Na Modbus długi czas loop wtedy nie wpłynie ale na obsługę enkodera już tak, dlatego rozwiązanie nr 1 (co się da na przerwania) jest rozsądniejsze.

    Dodano po 3 [minuty]:

    cinek_14 napisał:
    ponieważ fatek będzie masterem i będzie odpytywał około 20 modułów jak poniżej.

    Jeśli czasy reakcji mają być małe nie realizuje się takiej funkcjonalności przez odpytywanie. Slave jak ma coś jakieś zdarzenie wysyła do mastera. Master, profilaktycznie, co np 1sek odpytuje moduły aby sprawdzić "czy żyją".
  • Pomocny post
    Poziom 34  
    cinek_14 napisał:
    Sprawa dotycząca modbus, powinna być wykonywana co każde przejście pętli, bez żadnych opóźnień ponieważ w module mamy obsługę 8 wejść i fatek musi o tym wiedzieć bardzo szybko, aby wykonał odpowiednią procedurą obsługującą dany przycisk.

    Powinieneś więc w pierwszej kolejności przenieść obsługę modbus do przerwania, tylko w ten sposób będziesz miał zagwarantowany stały i przewidywalny czas reakcji, niezależnie od tego jakie cuda znajdą się w pętli loop(), za wyjątkiem sytuacji kiedy będziesz próbował blokować przerwania w loop().
  • Pomocny post
    Poziom 31  
    khoam napisał:
    W jakim celu w funkcji czas() są zliczane minuty, godziny i dni? Ta funkcja jest każdorazowo wywoływana w loop().

    Wyobraź sobie, że nie pisałem na tę okazję specjalnej funkcji tylko wkleiłem taką, jaką kiedyś użyłem. Oczywiście można usunąć część rzeczy i zyskać mnóstwo ns - znowu łapanie mrówek. Błędów nie obsługiwał kod autora wątku i szkoda, że tego nie dodałem - no fakt :D. Zamiast recenzji i grzebania w kodzie, które jest bezcelowe dopóki nie wiesz jakiej biblioteki używa autor tematu i jakie ma możliwości, spodziewałem się Twojej propozycji użycia tu tych przerwań sprzętowych timera, tak niezastąpionych w każdej sytuacji.
  • Pomocny post
    Moderator Mikrokontrolery Projektowanie
    @cinek_14 Jak rozumiem masz gotową płytkę, więc to implikuje jakie masz możliwości rozwiązania problemu. Koledzy upierają się, aby wszystko wrzucić do przerwań, co IMHO nie tylko nie rozwiązuje problemu, ale go całość niepotrzebnie komplikuje. Skoro już masz płytkę, to zrobienie 1-wire na UART raczej nie wchodzi w grę. Zresztą nie jest to potrzebne. Spokojnie obsługa OLED i 1-wire może być zrobiona klasycznie, to, że np. 1-wire zablokuje pętlę na pojedyncze milisekundy lub mniej, jest bez znaczenia (o ile oczywiście nie masz gdzieś błędu typu czekanie 750 ms na zakończenie konwersji temperatury). Podobnie obsługa wyświetlania, nawet jeśli trwa i 100ms jest to bez znaczenia. Więc tego bym nie ruszał. Enkoder można zrobić na przerwaniu timera (unikaj przerwań od pinów, bo to tylko zbędnie obciąży MCU - drgania styków). Ponieważ obsługa enkodera nie jest czasochłonna, więc kolejny problem masz rozwiązany. Pozostaje MODBUS i na tym powinieneś się skupić. To powinno być oparte na przerwaniach, co rozwiązuje wszystkie twoje problemy. Jak to konkretnie połączyć z przerwaniami, zależy od konkretnej implementacji MODBUSa.