Skocz do zawartości

Ćwiczenia z Arduino nano, zagadnienia, problemy.


Gość

Pomocna odpowiedź

Witam wszystkich.

W dniu dzisiejszym postanowiłem uruchomić pomiar z przetwornika ADS1115. Przygotowałem proste stanowisko testowe, oraz połączyłem układ na płytce stykowej.

2.thumb.jpg.6c1a7c2f7877acadc401aff45430407b.jpg

Układ ruszył, oraz zaczął zwracać pomiary. Na początku wyniki skakały po kanałach, ale po wprowadzeniu opóźnienia 10ms przed każdym przełączeniem anomalie się ustabilizowały. Zresztą jest o tym wspomniane w opisie biblioteki iż przy odczycie w czasie zmiany kanału przetwornik potrzebuje trochę czasu. Zbyt szybki odczyt może skutkować odnotowaniem poprzedniej wartości. 

Generalnie wygląda to tak;

1.thumb.jpg.45f7253bdae4ff275033dac51919f698.jpg

Jak widać wszystko ok. Jakieś grosze ze skręconych potencjometrów. Względem multimetru wartości nieco zawyżone, podobnie jak wskazanie napięcia na rozkręconym potencjometrze pływa w granicy 10mV. Myślę, że tutaj kłania się kwestia odsprzęgnięcia zasilania przetwornika ADS. 

Jest jeszcze jedna rzecz której nie rozumiem. Dlaczego wartości z wejść mam przesunięte o jeden kanał ? Czyli zadaję sygnał na kanał A0, a wynik print mam na kanale A1. Zadając na A1 wynik pojawia się na A2 etc. Popełniłem gdzieś błąd, czy może błąd leży w kodzie biblioteki ? 

Korzystam z przykładu autora tej biblioteki.

/***************************************************************************
* Przykładowy szkic dla biblioteki ADS1015_WE
*
* Ten szkic pokazuje, jak używać ADS1015 w trybie ciągłym.
*  
*Więcej informacji można znaleźć na stronie:
* https://wolles-elektronikkiste.de/ads1115 (niemiecki)
* https://wolles-elektronikkiste.de/en/ads1115-konwerter-ad-ze-wzmacniaczem (angielski)
* 
***************************************************************************/

#include<ADS1015_WE.h> 
#include<Wire.h>
#define I2C_ADDRESS 0x48

/* Istnieje kilka sposobów utworzenia obiektu ADS1015_WE:
 * ADS1015_WE adc = ADS1015_WE(); -> używa adresu / I2C Address = 0x48
 * ADS1015_WE adc = ADS1015_WE(I2C_ADDRESS); -> używa adresu / I2C_ADDRESS
 * ADS1015_WE adc = ADS1015_WE(&Wire); -> możesz przekazać dowolny obiekt / I2C Address = 0x48
 * ADS1015_WE adc = ADS1015_WE(&Wire, I2C_ADDRESS); -> all razem
 */
ADS1015_WE adc = ADS1015_WE(I2C_ADDRESS);

void setup() {
  bool useADS1015 = true;
  Wire.begin();
  Serial.begin(9600);
  if(!adc.init(useADS1015)){ // przekazanie wartości true poinformuje bibliotekę, że używany jest ADS1015
    Serial.println("ADS1015 not connected!");
  }

  /* Ustaw zakres napięcia ADC, aby dostosować wzmocnienie
   * Należy pamiętać, że na piny wejściowe nie wolno przykładać napięcia większego niż VDD + 0,3 V!
   * 
   * ADS1015_RANGE_6144  ->  +/- 6144 mV
   * ADS1015_RANGE_4096  ->  +/- 4096 mV
   * ADS1015_RANGE_2048  ->  +/- 2048 mV (default)
   * ADS1015_RANGE_1024  ->  +/- 1024 mV
   * ADS1015_RANGE_0512  ->  +/- 512 mV
   * ADS1015_RANGE_0256  ->  +/- 256 mV
   */
  adc.setVoltageRange_mV(ADS1015_RANGE_6144); //linia komentarza/zmień parametr, aby zmienić zakres

  /* Ustawia dane wejściowe do porównania
   *  
   *  ADS1015_COMP_0_1    ->  compares 0 with 1 (default)
   *  ADS1015_COMP_0_3    ->  compares 0 with 3
   *  ADS1015_COMP_1_3    ->  compares 1 with 3
   *  ADS1015_COMP_2_3    ->  compares 2 with 3
   *  ADS1015_COMP_0_GND  ->  compares 0 with GND
   *  ADS1015_COMP_1_GND  ->  compares 1 with GND
   *  ADS1015_COMP_2_GND  ->  compares 2 with GND
   *  ADS1015_COMP_3_GND  ->  compares 3 with GND
   */
  adc.setCompareChannels(ADS1015_COMP_0_GND); //linia komentarza/zmień parametr, aby zmienić kanał

   /* Ustaw liczbę konwersji, po których aktywuje się pin alertu
   * - lub możesz wyłączyć alert 
   *  
   *  ADS1015_ASSERT_AFTER_1  -> after 1 conversion
   *  ADS1015_ASSERT_AFTER_2  -> after 2 conversions
   *  ADS1015_ASSERT_AFTER_4  -> after 4 conversions
   *  ADS1015_DISABLE_ALERT   -> disable comparator / alert pin (default) 
   */
  //adc.setAlertPinMode(ADS1015_ASSERT_AFTER_1); //odkomentuj, jeśli chcesz zmienić ustawienie domyślne

   /* Ustaw współczynnik konwersji w SPS (próbki na sekundę)
   * Opcje powinny być oczywiste:
   * 
   *  ADS1015_128_SPS 
   *  ADS1015_250_SPS  
   *  ADS1015_490_SPS 
   *  ADS1015_920_SPS  
   *  ADS1015_1600_SPS (default)
   *  ADS1015_2400_SPS 
   *  ADS1015_3300_SPS 
   */
  //adc.setConvRate(ADS1015_3300_SPS); //odkomentuj, jeśli chcesz zmienić ustawienie domyślne

  /* Ustaw tryb ciągłego lub pojedynczego strzału:
   * 
   *  ADS1015_CONTINUOUS  ->  tryb ciągły
   *  ADS1015_SINGLE     ->  tryb pojedynczego strzału (domyślny)
   */
  adc.setMeasureMode(ADS1015_CONTINUOUS); // linia komentarza/zmień parametr, aby zmienić tryb

   /* Wybierz maksymalny limit lub maksymalny i minimalny limit alertu (okno) w Voltach – pin alarmowy będzie
   * stwierdzać, gdy zmierzone wartości przekraczają maksymalny limit lub znajdują się poza oknem
   * Najpierw górny limit: setAlertLimit_V(TRYB, maksimum, minimum)
   * W trybie maksymalnego limitu minimalna wartość to limit, przy którym będzie aktywowany sygnał alarmowy  
   * wyczyszczone (jeśli nie jest zablokowane)  
   * 
   *  ADS1015_MAX_LIMIT
   *  ADS1015_WINDOW
   * 
   */
  //adc.setAlertModeAndLimit_V(ADS1015_MAX_LIMIT, 3.0, 1.5); //odkomentuj, jeśli chcesz zmienić ustawienie domyśln

  /* Włącz lub wyłącz zatrzask. Jeśli zatrzask jest włączony, pin alarmowy będzie aktywny do momentu
   * odczytywany jest rejestr konwersji (funkcje getResult). Jeśli wyłączone, potwierdzenie pinu alarmowego będzie aktywne
   * wyczyszczone następną wartością w granicach. 
   *  
   *  ADS1015_LATCH_DISABLED (default)
   *  ADS1015_LATCH_ENABLED
   */
  //adc.setAlertLatch(ADS1015_LATCH_ENABLED); //odkomentuj, jeśli chcesz zmienić ustawienie domyśln

  /*  Ustawia polaryzację pinu alarmowego, jeśli jest aktywny:
   *  
   * ADS1015_ACT_LOW  ->  active low (default)   
   * ADS1015_ACT_HIGH ->  active high
   */
  //adc.setAlertPol(ADS1015_ACT_LOW); //odkomentuj, jeśli chcesz zmienić ustawienie domyśln

  /* Dzięki tej funkcji pin alarmowy potwierdzi, gdy konwersja będzie gotowa.
   * Aby dezaktywować należy skorzystać z funkcji setAlertLimit_V  
   */
  //adc.setAlertPinToConversionReady(); //uodkomentuj, jeśli chcesz zmienić ustawienie domyśln

  Serial.println("ADS1015 Example Sketch - Continuous Mode");
  Serial.println("All values in volts");
  Serial.println();
}

/* Jeśli zmienisz kanały porównywania, możesz natychmiast odczytać wartości z konwersji
   * zarejestruj się, chociaż mogą należeć do pierwszego kanału, jeśli nie zostaną podjęte żadne środki ostrożności.
   * Uzyskanie poprawnych danych zajmuje mniej więcej tyle czasu, ile potrzeba na dwie konwersje. W singlu
   * w trybie shot możesz skorzystać z funkcji isBusy() w celu oczekiwania na dane z nowego kanału. Ten
   * nie działa w trybie ciągłym.
   * Aby rozwiązać ten problem, biblioteka dodaje opóźnienie po zmianie kanałów, jeśli jesteś w trybie ciągłym
   * tryb. Długość opóźnienia dostosowana jest do współczynnika konwersji. Należy jednak pamiętać, że plik wyjściowy
   * stawka będzie znacznie niższa niż współczynnik konwersji, jeśli często zmieniasz kanały. 
   */

void loop() {
  float voltage = 0.0;

  Serial.print("0: ");
  voltage = readChannel(ADS1015_COMP_0_GND);
  Serial.print(voltage);

 delay(10);

  Serial.print(",   1: ");
  voltage = readChannel(ADS1015_COMP_1_GND);
  Serial.print(voltage);

 delay(10);

  Serial.print(",   2: ");
  voltage = readChannel(ADS1015_COMP_2_GND);
  Serial.print(voltage);

 delay(10);

  Serial.print(",   3: ");
  voltage = readChannel(ADS1015_COMP_3_GND);
  Serial.println(voltage);

  delay(1000);
}

float readChannel(ADS1015_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);
  voltage = adc.getResult_mV(); // alternatywa: getResult_mV dla miliwoltów
  return voltage;
}

który jest dedykowany pod ADS1015 (12bit), a ja korzystam z wersji ADS1115 (16bit) no ale w tym przypadku to chyba nie ma znaczenia? Raczej wartości w rejestrach się nie przesunęły z tego powodu o jeden kanał ? Ale mimo wszystko coś jest nie tak.

Oczywiście opisy zostały przetłumaczone poprzez google, więc trochę dziwnie się to czyta, ale kod jest spójny.  

Kolejna sprawa jest taka, że ta biblioteka nie umożliwia deklaracji portów I2C. Jak to będzie wyglądać w przypadku ESP ? 

No i nieszczęsny return w postaci float. Ja to tego nie rozumiem po co biblioteka zwraca float, a nie int, long, itp. ? Generalnie powinno się unikać działań na floatach, a kropkę zawsze można zrobić przed wysłaniem docelowej wartości na wyświetlacz. Po co biblioteki zwracają float ? Jeżeli ktoś usilnie chce float, to może samodzielnie dokonać konwersji na wartość zmiennoprzecinkową. 

Link do komentarza
Share on other sites

Gość

P.S.

Wgrałem inny przykład pod ADS1115 i kanały/ porty A0 - A1 mam już ok. 

/***************************************************************************
* Example sketch for the ADS1115_WE library
*
* This sketch shows how to use the Auto Range function of the AD1115_WE library.  
*  
* Further information can be found on:
* https://wolles-elektronikkiste.de/ads1115 (German)
* https://wolles-elektronikkiste.de/en/ads1115-a-d-converter-with-amplifier (English)
* 
***************************************************************************/

#include<ADS1115_WE.h> 
#include<Wire.h>
#define I2C_ADDRESS 0x48

/* There are several ways to create your ADS1115_WE object:
 * ADS1115_WE adc = ADS1115_WE(); -> uses Wire / I2C Address = 0x48
 * ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS); -> uses Wire / I2C_ADDRESS
 * ADS1115_WE adc = ADS1115_WE(&Wire); -> you can pass any TwoWire object / I2C Address = 0x48
 * ADS1115_WE adc = ADS1115_WE(&Wire, I2C_ADDRESS); -> all together
 */
ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);

void setup() {
  Wire.begin();
  Serial.begin(9600);
  if(!adc.init()){
    Serial.println("ADS1115 not connected!");
  }

  /* Set the voltage range of the ADC to adjust the gain
   * Please note that you must not apply more than VDD + 0.3V to the input pins!
   * 
   * ADS1115_RANGE_6144  ->  +/- 6144 mV
   * ADS1115_RANGE_4096  ->  +/- 4096 mV
   * ADS1115_RANGE_2048  ->  +/- 2048 mV (default)
   * ADS1115_RANGE_1024  ->  +/- 1024 mV
   * ADS1115_RANGE_0512  ->  +/- 512 mV
   * ADS1115_RANGE_0256  ->  +/- 256 mV
   */
  adc.setVoltageRange_mV(ADS1115_RANGE_6144); //comment line/change parameter to change range

  /* Set the inputs to be compared
   *  
   *  ADS1115_COMP_0_1    ->  compares 0 with 1 (default)
   *  ADS1115_COMP_0_3    ->  compares 0 with 3
   *  ADS1115_COMP_1_3    ->  compares 1 with 3
   *  ADS1115_COMP_2_3    ->  compares 2 with 3
   *  ADS1115_COMP_0_GND  ->  compares 0 with GND
   *  ADS1115_COMP_1_GND  ->  compares 1 with GND
   *  ADS1115_COMP_2_GND  ->  compares 2 with GND
   *  ADS1115_COMP_3_GND  ->  compares 3 with GND
   */
  adc.setCompareChannels(ADS1115_COMP_0_GND); //comment line/change parameter to change channel

  /* Set number of conversions after which the alert pin asserts
   * - or you can disable the alert 
   *  
   *  ADS1115_ASSERT_AFTER_1  -> after 1 conversion
   *  ADS1115_ASSERT_AFTER_2  -> after 2 conversions
   *  ADS1115_ASSERT_AFTER_4  -> after 4 conversions
   *  ADS1115_DISABLE_ALERT   -> disable comparator / alert pin (default) 
   */
  //adc.setAlertPinMode(ADS1115_ASSERT_AFTER_1); //uncomment if you want to change the default

  /* Set the conversion rate in SPS (samples per second)
   * Options should be self-explaining: 
   * 
   *  ADS1115_8_SPS 
   *  ADS1115_16_SPS  
   *  ADS1115_32_SPS 
   *  ADS1115_64_SPS  
   *  ADS1115_128_SPS (default)
   *  ADS1115_250_SPS 
   *  ADS1115_475_SPS 
   *  ADS1115_860_SPS 
   */
   adc.setConvRate(ADS1115_64_SPS); //uncomment if you want to change the default

  /* Set continuous or single shot mode:
   * 
   *  ADS1115_CONTINUOUS  ->  continuous mode
   *  ADS1115_SINGLE     ->  single shot mode (default)
   */
  adc.setMeasureMode(ADS1115_CONTINUOUS); //comment line/change parameter to change mode

   /* Choose maximum limit or maximum and minimum alert limit (window) in Volt - alert pin will 
   *  assert when measured values are beyond the maximum limit or outside the window 
   *  Upper limit first: setAlertLimit_V(MODE, maximum, minimum)
   *  In max limit mode the minimum value is the limit where the alert pin assertion will be  
   *  cleared (if not latched)  
   * 
   *  ADS1115_MAX_LIMIT
   *  ADS1115_WINDOW
   * 
   */
  //adc.setAlertModeAndLimit_V(ADS1115_MAX_LIMIT, 3.0, 1.5); //uncomment if you want to change the default
  
  /* Enable or disable latch. If latch is enabled the alert pin will assert until the
   * conversion register is read (getResult functions). If disabled the alert pin assertion will be
   * cleared with next value within limits. 
   *  
   *  ADS1115_LATCH_DISABLED (default)
   *  ADS1115_LATCH_ENABLED
   */
  //adc.setAlertLatch(ADS1115_LATCH_ENABLED); //uncomment if you want to change the default

  /* Sets the alert pin polarity if active:
   *  
   * ADS1115_ACT_LOW  ->  active low (default)   
   * ADS1115_ACT_HIGH ->  active high
   */
  //adc.setAlertPol(ADS1115_ACT_LOW); //uncomment if you want to change the default
 
  /* With this function the alert pin will assert, when a conversion is ready.
   * In order to deactivate, use the setAlertLimit_V function  
   */
  //adc.setAlertPinToConversionReady(); //uncomment if you want to change the default

  /* Enable or disable permanent automatic range selection mode. If enabled, the range will
   * change if the measured values are outside of 30-80% of the maximum value of the current 
   * range.  
   * !!! Use EITHER this function once OR setAutoRange() whenever needed (see below) !!!
   */
  adc.setPermanentAutoRangeMode(true);

  Serial.println("ADS1115 Example Sketch - Continuous Mode with Auto Range");
  Serial.println();
}

void loop() {
  Serial.print("Channel 0 - ");
  readChannel(ADS1115_COMP_0_GND);
  
  Serial.print("Channel 1 - ");
  readChannel(ADS1115_COMP_1_GND);
    
  Serial.print("Channel 2 - ");
  readChannel(ADS1115_COMP_2_GND);
  
  Serial.print("Channel 3 - ");
  readChannel(ADS1115_COMP_3_GND);
  
  Serial.println("-------------------------------");
  delay(1000);
}

void readChannel(ADS1115_MUX channel) {
  float voltage = 0.0;
  adc.setCompareChannels(channel);

  /* setAutoRange() switches to the highest range (+/- 6144 mV), measures the current 
   * voltage and then switches to the lowest range where the current value is still 
   * below 80% of the maximum value of the range. The function is only suitable if you 
   * expect stable or slowly changing voltages. setAutoRange needs roughly the time you
   * would need for three conversions. 
   * If the ADS115 is in single shot mode, setAutoRange() will switch into continuous
   * mode to measure a value and switch back again.
   * !!! Use EITHER this function whenever needed OR setPermanentAutoRangeMode(true) once !!!
   */  
  //adc.setAutoRange(); //use either this or setPermanentAutoRangeMode(true)
  
  voltage = adc.getResult_V(); // alternative: getResult_mV for Millivolt
  printVoltageRange(); // this is just to show that the range is changing with changing voltages 
  Serial.println(voltage);
}
  
void printVoltageRange(){
  unsigned int voltageRange = adc.getVoltageRange_mV();
  Serial.print("Range: ");

  switch(voltageRange){
    case 6144:
      Serial.print("+/- 6144 mV, Voltage [V]: ");
      break;
    case 4096:
      Serial.print("+/- 4096 mV, Voltage [V]: ");
      break;
    case 2048:
      Serial.print("+/- 2048 mV, Voltage [V]: ");
      break;
    case 1024:
      Serial.print("+/- 1024 mV, Voltage [V]: ");
      break;
    case 512:
      Serial.print("+/- 512 mV, Voltage [V]: ");
      break;
    case 256:
      Serial.print("+/- 256 mV, Voltage [V]: ");
      break;
    default:
      Serial.println("Something went wrong");
  }
}

Ciekawy kod. Raz wpisane słowo "range" a drukuje 4 razy do każdego portu tak jak przy wywołaniu jakiejś funkcji. 

Link do komentarza
Share on other sites

54 minuty temu, rafal220 napisał:

Kolejna sprawa jest taka, że ta biblioteka nie umożliwia deklaracji portów I2C.

A dlaczego niby miałaby umożliwiać? To jest biblioteka do ADC a nie do Ogólnego Gmerania po I2C. Nie wiem czy zauważyłeś, ale wywołałeś Wire.begin()

Poza tym co to są "porty I2C"? Chodzi o piny czy może o numer magistrali?

55 minut temu, rafal220 napisał:

Jak to będzie wyglądać w przypadku ESP

Jak dojdziesz do ESP to wtedy zobaczysz  jakie parametry przyjmuje Wire.begin(). Na razie nie zaprzątaj sobie tym głowy.

Link do komentarza
Share on other sites

Gość
1 godzinę temu, ethanak napisał:

A dlaczego niby miałaby umożliwiać? To jest biblioteka do ADC a nie do Ogólnego Gmerania po I2C. Nie wiem czy zauważyłeś, ale wywołałeś Wire.begin()

Poza tym co to są "porty I2C"? Chodzi o piny czy może o numer magistrali?

Właśnie dokładnie nie wiem jak z tym jest. Bo w mega32 jest tylko jedno I2C, ale w tym większym Arduino mega z opisów portów wynika, że tam jest więcej niż jedna magistrala I2C.

 

1 godzinę temu, ethanak napisał:

Jak dojdziesz do ESP to wtedy zobaczysz  jakie parametry przyjmuje Wire.begin(). Na razie nie zaprzątaj sobie tym głowy.

Ok. Na razie to rozgryzłem ten kod z przykładu z "auto range". Już wiem o co tam chodzi. Kurde nieźle jest to poplątane. Na początku nie wiedziałem jak on 4 razy odczytuje tego samego prina z wartościami. I wychodzi na to, że ten "readChannel" to nazwa wywoływanej funkcji w której zadeklarowana jest pętla printVoltageRange(); a w niej switch z warunkiem case dla odczytanego zakresu. Funkcja jest wywoływana 4 razy w pętli głównej, ale zmienne w tej funkcji są tak wymieszane, że przy dłuższym kodzie idzie się przy czymś takim ostro pogubić. 

Na dziś wystarczy. Ten ADS1115 jest całkiem fajny jak za te pieniądze. Można w sumie podłączyć 4szt. do jednego I2C poprzez zmianę adresu portem ADDR. Do tego posiada programowany port alert który można wykorzystać w funkcji zatrzasku dzięki czemu nie jest konieczne stosowanie przerwania zewnętrznego. Alert można ustawić też i bez zatrzasku. Budując jakiś przyrząd pomiarowy funkcja alert może się przydać np. do szybkiej sygnalizacji przekroczenia zakresu pomiarowego. Daje to również szybszy czas reakcji jakiegoś układu nadzorującego.  

Edytowano przez rafal220
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Gość

A więc tak;

W ramach ćwiczeń stanowisko z Arduino nano, ADS1115 postanowiłem rozszerzyć o obsługę wyświetlaczy graficznych;

 1.thumb.jpg.957123bbea0d945fd53edd9562085d2e.jpg

Na razie chcę się skupić na tych mniejszych OLED.

- 0.91" => SSD1306 (128x32)

- 0.96" => SSD1315 (128x64)

O ile wyświetlacz 0.91"

4.thumb.jpg.c6d52d6a69061f892cce8acf3cb10ca8.jpg

śmiga zgodnie z założeniami demo;

/**************************************************************************
 This is an example for our Monochrome OLEDs based on SSD1306 drivers

 Pick one up today in the adafruit shop!
 ------> http://www.adafruit.com/category/63_98

 This example is for a 128x32 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 Adafruit invests time and resources providing this open
 source code, please support Adafruit and open-source
 hardware by purchasing products from Adafruit!

 Written by Limor Fried/Ladyada for Adafruit Industries,
 with contributions from the open source community.
 BSD license, check license.txt for more information
 All text above, and the splash screen below must be
 included in any redistribution.
 **************************************************************************/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define NUMFLAKES     10 // Number of snowflakes in the animation example

#define LOGO_HEIGHT   16
#define LOGO_WIDTH    16
static const unsigned char PROGMEM logo_bmp[] =
{ 0b00000000, 0b11000000,
  0b00000001, 0b11000000,
  0b00000001, 0b11000000,
  0b00000011, 0b11100000,
  0b11110011, 0b11100000,
  0b11111110, 0b11111000,
  0b01111110, 0b11111111,
  0b00110011, 0b10011111,
  0b00011111, 0b11111100,
  0b00001101, 0b01110000,
  0b00011011, 0b10100000,
  0b00111111, 0b11100000,
  0b00111111, 0b11110000,
  0b01111100, 0b11110000,
  0b01110000, 0b01110000,
  0b00000000, 0b00110000 };

void setup() {
  Serial.begin(9600);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();

  // Draw a single pixel in white
  display.drawPixel(10, 10, SSD1306_WHITE);

  // Show the display buffer on the screen. You MUST call display() after
  // drawing commands to make them visible on screen!
  display.display();
  delay(2000);
  // display.display() is NOT necessary after every single drawing command,
  // unless that's what you want...rather, you can batch up a bunch of
  // drawing operations and then update the screen all at once by calling
  // display.display(). These examples demonstrate both approaches...

  testdrawline();      // Draw many lines

  testdrawrect();      // Draw rectangles (outlines)

  testfillrect();      // Draw rectangles (filled)

  testdrawcircle();    // Draw circles (outlines)

  testfillcircle();    // Draw circles (filled)

  testdrawroundrect(); // Draw rounded rectangles (outlines)

  testfillroundrect(); // Draw rounded rectangles (filled)

  testdrawtriangle();  // Draw triangles (outlines)

  testfilltriangle();  // Draw triangles (filled)

  testdrawchar();      // Draw characters of the default font

  testdrawstyles();    // Draw 'stylized' characters

  testscrolltext();    // Draw scrolling text

  testdrawbitmap();    // Draw a small bitmap image

  // Invert and restore display, pausing in-between
  display.invertDisplay(true);
  delay(1000);
  display.invertDisplay(false);
  delay(1000);

  testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
}

void loop() {
}

void testdrawline() {
  int16_t i;

  display.clearDisplay(); // Clear display buffer

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
    display.display(); // Update screen with each newly-drawn line
    delay(1);
  }
  for(i=0; i<display.height(); i+=4) {
    display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.width(); i+=4) {
    display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=display.width()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.height(); i+=4) {
    display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }
  for(i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000); // Pause for 2 seconds
}

void testdrawrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=2) {
    display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testfillrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=3) {
    // The INVERSE color is used so rectangles alternate white/black
    display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
    display.display(); // Update screen with each newly-drawn rectangle
    delay(1);
  }

  delay(2000);
}

void testdrawcircle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillcircle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
    // The INVERSE color is used so circles alternate white/black
    display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
    display.display(); // Update screen with each newly-drawn circle
    delay(1);
  }

  delay(2000);
}

void testdrawroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfillroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
    // The INVERSE color is used so round-rects alternate white/black
    display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
      display.height()/4, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawtriangle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
    display.drawTriangle(
      display.width()/2  , display.height()/2-i,
      display.width()/2-i, display.height()/2+i,
      display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testfilltriangle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
    // The INVERSE color is used so triangles alternate white/black
    display.fillTriangle(
      display.width()/2  , display.height()/2-i,
      display.width()/2-i, display.height()/2+i,
      display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
    display.display();
    delay(1);
  }

  delay(2000);
}

void testdrawchar(void) {
  display.clearDisplay();

  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font

  // Not all the characters will fit on the display. This is normal.
  // Library will draw what it can and the rest will be clipped.
  for(int16_t i=0; i<256; i++) {
    if(i == '\n') display.write(' ');
    else          display.write(i);
  }

  display.display();
  delay(2000);
}

void testdrawstyles(void) {
  display.clearDisplay();

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

  display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
  display.println(3.141592);

  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.print(F("0x")); display.println(0xDEADBEEF, HEX);

  display.display();
  delay(2000);
}

void testscrolltext(void) {
  display.clearDisplay();

  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println(F("scroll"));
  display.display();      // Show initial text
  delay(100);

  // Scroll in various directions, pausing in-between:
  display.startscrollright(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrollleft(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrolldiagright(0x00, 0x07);
  delay(2000);
  display.startscrolldiagleft(0x00, 0x07);
  delay(2000);
  display.stopscroll();
  delay(1000);
}

void testdrawbitmap(void) {
  display.clearDisplay();

  display.drawBitmap(
    (display.width()  - LOGO_WIDTH ) / 2,
    (display.height() - LOGO_HEIGHT) / 2,
    logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
  display.display();
  delay(1000);
}

#define XPOS   0 // Indexes into the 'icons' array in function below
#define YPOS   1
#define DELTAY 2

void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  int8_t f, icons[NUMFLAKES][3];

  // Initialize 'snowflake' positions
  for(f=0; f< NUMFLAKES; f++) {
    icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
    icons[f][YPOS]   = -LOGO_HEIGHT;
    icons[f][DELTAY] = random(1, 6);
    Serial.print(F("x: "));
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(F(" y: "));
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(F(" dy: "));
    Serial.println(icons[f][DELTAY], DEC);
  }

  for(;;) { // Loop forever...
    display.clearDisplay(); // Clear the display buffer

    // Draw each snowflake:
    for(f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
    }

    display.display(); // Show the display buffer on the screen
    delay(200);        // Pause for 1/10 second

    // Then update coordinates of each flake...
    for(f=0; f< NUMFLAKES; f++) {
      icons[f][YPOS] += icons[f][DELTAY];
      // If snowflake is off the bottom of the screen...
      if (icons[f][YPOS] >= display.height()) {
        // Reinitialize to a random position, just off the top
        icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
        icons[f][YPOS]   = -LOGO_HEIGHT;
        icons[f][DELTAY] = random(1, 6);
      }
    }
  }
}

tak z tym większym SSD1315 (128 x 64) pojawiły się pewne niewiadome. 

Po pierwsze adres wyświetlacza;

3.thumb.jpg.54e552edd915ecf426962fe0c03e5152.jpg2.thumb.jpg.605ffa11670db4185ce764c3fce1d866.jpg

który zgodnie z opisem na zworce PCB powinien wyśnić 0x78,  ale fizycznie wyświetlacz działa pod adresem 0x3C. Czyli pod takim samym jak wyświetlacz na sterowniku SSD1306 (128 x 32). Trzeba rzecz jasna tylko w kodzie demo zmienić rozdzielczość z 128 x 32 na 128 x 64 w przeciwnym razie wychodzą delikatne artefakty. Kolejną sprawą (pomijając adresację) zastanawiająca jest funkcja akceleracji, ponieważ nie wszystko tutaj działa prawidłowo. W czasie testów na kodzie demo okazało się, że napis "scroll" pływa na boki, ale już to nie działa pod kątami góra dół tj. w przypadku wyświetlacza na sterowniku SSD1306. Wygląda na to, że wyświetlacz albo nie obsługuje pewnych akceleracji, albo znajdują się one pod innymi adresami. W każdym razie nie wszystko z kodu demo działa w tym przypadku prawidłowo.

Zastanawia mnie też jak wrzucić dowolny plik png. W demo jest zaprezentowane logo w formacie png. ale jak wrzucić kilka obrazków? Z opisu wynika, że biblioteka jednorazowo dodaje plik przy stacje programu dla wywołania => display.display(); a mnie interesuje coś takiego, że w nawiasach funkcji => display.display(); po wpisaniu nazwy np. foto1.bmp. kompilator ma w tym miejscu wgrać bitmapę z folderu biblioteki o nazwie foto1.bmp. Czemu tak się nie da? A może jest inny sposób do którego odnoszą się dwa pliki konfiguracyjne w folderze z grafikami? Znajduje się tam złożony kawałek kodu, ale czy umożliwia on deklarację innych plików graficznych?

Ciekawa jest też ta animacja z tymi losowymi, spadającymi gwiazdkami. Tylko trzeba ogarnąć o co chodzi w tym kodzie na samym końcu. Ktoś kto to pisał zapewne specjalizuje się w grafice komputerowej, albo grach platformowych. 

 

Edytowano przez rafal220
Link do komentarza
Share on other sites

Gość

P.S.

Uruchomiłem jeszcze ten większy wyświetlacz 1,8" na ST7735S (128x160) podłączając się pod piny 1-8. Nawet to działa...

7.thumb.jpg.bfa7c49884baa62bb0d9b2a160bf4e18.jpg

Pozostałe piny są od kardy SD i obsługi wyświetlacza. Tylko że po drugiej stronie nie wszystkie piny zostały zdublowane.

6.thumb.jpg.86153ce1b453cace42fc9ccdd916d35b.jpg

Są tam SCK, SDA i RS. Do czego to służy ? Do bezpośredniego sparowania danych z karty SD na wyświetlacz ? Np. min. do strumieniowego przesyłania grafiki bez ich przetwarzania przez mikrokontroler który jedynie nadzoruje synchronizację tych danych z wyświetlaczem?  

Edytowano przez rafal220
Link do komentarza
Share on other sites

Bezpośrednio, bez użycia mikrokontrolera byłoby ciężko, ale inicjacja karty (tutaj jest również po SPI), polecenie odczytu, czekanie na gotowość karty do podawania danych na interfejs i już odczyt 512 bajtów z użyciem DMA. Nie jestem ekspertem od DMA a w szczególności w "Arduino", ale pewnie dałoby się zblokować dwa sprzętowe SPI (tak, żeby oba pracowały w tempie wolniejszego, jeśli jest ich tyle :D), więc już wtedy brawie bezpośrednio. Tylko wydajność. Wyświetlacz przyjmie wszystko bez okresów oczekiwania, ale karta już musi najpierw odczytać z matrycy komórek flash dane i zapisać je do bufora (możliwe, że i na poziomie pamięci i kontrolera są bufory albo te z pamięci są używane tylko w tym celu). I tutaj tutaj trzeba czekać. Np. karta SDHC 4GB Kingston z 2007 roku i microSDHC 32 GB Goodram (kupiona obecnie) mają po blisko 1 MiB/s odczytu na sekundę (przy 24 MHz SPI, efektywnie - wydanie polecenia, czekanie na dane i ich odbiór, konkretnie 128 bloków po 512 B, czyli 64 kiB, a potem przerwanie odczytu), choć ta pierwsza jest szybsza dla odczytu pojedynczego bloku. 25 MHz to maks dla SPI, choć ta pierwsza podaje poprawne dane przy 36 (wtedy jest 1,45 MiB/s przy 64 kiB), druga już nie (pojawiają się błedne dane), nie sprawdzałem ile dokładnie, ale nie ma co kombinować, jak chce się mieć poprawne dane. Szybciej to już tyko interfejs SD, ale trzeba pamiętać, że przy 3,3 V maks. to 50 MHz, potem już 1,8. Ale nie trzeba przerywać odczytu po np. jedne klatce animacji, tylko można odczytywać dalej (jeśli struktura danych jest odpowiednio spreparowana na karcie), a i wyświetlacz nie musi po każdym wypełnieniu ekranu (a dokładniej skonfigurowanego bufora) dostawać nowego polecenia, tylko jak zacznie się przelewać, to rysuje od początku.

Edytowano przez matsobdev
Link do komentarza
Share on other sites

Gość

@matsobdev No właśnie też mi się tak wydaje, że można na takich niewielkich wyświetlaczach ogarnąć grafikę bezpośrednio z karty SD przy niewielkim wykorzystaniu mikrokontrolera. Kiedyś chyba był na podobny temat jakiś artykuł, tylko wyświetlacz był większy. Na razie nie będę sobie tym zawracał głowy. Chcę mieć jedynie wstępne rozeznanie odnośnie możliwości sprzętowych. 

Link do komentarza
Share on other sites

W sumie procesor musi odpowiednio szybko działać, żeby sprawdzić, czy zaczynają przychodzić dane. Przynajmniej u mnie. Więc przy 144 MHz CPU i 24 MHZ SPI - 2,74 MiB/s, więc już interfejs SPI praktycznie wysycony (funkcje blokujące, bez DMA, ale oczywiści są też biblioteki obsługując 4 bitowe SD). Temat jest o Nano, ale zawsze można tysiąc razy mniej i Pico 😄 Ale poważnie, całkiem spoko i zasoby i możliwości i cena jak na płytkę robioną w UK. Tutaj problemu karty SD by w pewnych przypadkach uniknąć, jeśli 2 MB pamięci jest wystarczające, a to już jest skok w porównaniu z Nano. A są ciekawe płytki z 16 MiB. I tutaj nie jest wielkim problemem 63 MiB/s. Nie jest to oczywiście jedyny słuszny wybór.

A co do rozeznania, upewnij się, czy Twój moduł jest zrobiony pod 5 woltową logikę. Dla karty to będzie za dużo, jeśli nie jest.

Edytowano przez matsobdev
Link do komentarza
Share on other sites

Dnia 31.05.2024 o 23:49, matsobdev napisał:

A co do rozeznania, upewnij się, czy Twój moduł jest zrobiony pod 5 woltową logikę. Dla karty to będzie za dużo, jeśli nie jest.

Nie no wyświetlacz ma stabilizator liniowy 3,3V. W przypadku zastosowania karty SD musisz zewrzeć kropką cyny zworkę na zasilaniu.  

Na razie kartę SD sobie odpuszczę. Próbuje opanować same wyświetlacze. Zrobiłem teraz kawałek kodu (trochę koślawo) do określenia/ pozycjonowania współrzędnych pikseli X i Y

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define szer 128 // OLED display width, in pixels
#define dlug 64 // OLED display height, in pixels

#define OLED_RESET     -1
#define SCREEN_ADDRESS 0x3C

Adafruit_SSD1306 display(szer, dlug, &Wire, OLED_RESET);

int show_x, show_y, hide_x, hide_y;

void setup() {

  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
  display.clearDisplay();

}

void loop() {
  //........................ Test wskaźnika.........................
  show_x = map(abs(analogRead(A0) - 20), 0, 4095, 0, 128);
  show_y = map(abs(analogRead(A1) - 10), 0, 4095, 0, 64);

  if (show_x != hide_x || show_y != hide_y) {
    
    display.drawPixel(show_x, show_y, SSD1306_WHITE); // rysuj pixel
    display.drawPixel(hide_x, hide_y, SSD1306_BLACK); // ukryj pixel
    display.display();
    hide_y = show_y; hide_x = show_x;
  }

  Serial.print(F("show_x =   "));
  Serial.print(show_x);
  Serial.print("   ");
  Serial.print(F("show_y =   "));
  Serial.print(show_y);
  Serial.println();

  delay(10);
  //........................ Test wskaźnika.........................

}

 Ale nie bardzo wiem jak się pozbyć loga png. z funkcji display.begin. Jeżeli usuwam SCREEN_ADDRESS, to funkcja przestaje działać. Natomiast display.clearDisplay(); wygasza jedynie samo logo przy starcie, które nadal jest wgrywane w czasie kompilacji kodu. W ogóle ta biblioteka dziwnie jest zrobiona, ponieważ powinna zapewnić możliwość zamieszczenia dowolnych grafik png., w dowolnym miejscu w kodzie. 

Edytowano przez rafal220
Link do komentarza
Share on other sites

P.S.

Wykorzystując kod z poprzedniego postu ułatwiającego wyznaczenie docelowej pozycji kursora na wyświetlaczu OLED, zrobiłem mały eksperyment z LED WS2812 oraz sterowaniem barwy RGB i jasności z wykorzystaniem 12-bitowych wejść ADC ESP32C3.  

2.thumb.jpg.a58a51de88e9d96c229f73eab6b4540a.jpg3.thumb.jpg.c749d4bef6cc0ce07a9cb4fda05742bb.jpg

Na razie wyszedł mi taki kawałek kodu;

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define szer 128 // Szerokość wyświetlacza OLED w pikselach
#define wys 64 // Wysokość wyświetlacza OLED w pikselach

#define OLED_RESET     -1
#define SCREEN_ADDRESS 0x3C

Adafruit_SSD1306 display(szer, wys, &Wire, OLED_RESET);

int wyswietlaczLED = 16; // deklaracja ilości LED wyświetlacza
Adafruit_NeoPixel ws2812 = Adafruit_NeoPixel(wyswietlaczLED, 10, NEO_GRB + NEO_KHZ800);

int czerwony, zielony, niebieski, jasnosc = 0;

void setup() {

  ws2812.begin();
  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);

  display.println(F("CZERWONY  = "));
  display.println();
  display.println(F("ZIELONY   = "));
  display.println();
  display.println(F("NIEBIESKI = "));
  display.println();
  display.println(F("JASNOSC   = "));
  display.display();

}

void loop() {

  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);

  jasnosc = map(abs(analogRead(A3) - 7), 0, 4088, 0, 255); //jasność
  display.fillRect(74, 48, 17, 7, SSD1306_BLACK);
  display.setCursor(74, 48);
  display.print(jasnosc);

  czerwony = map(abs(analogRead(A0) - 20), 0, 4075, 0, jasnosc); //czerwony
  display.fillRect(74, 0, 17, 7, SSD1306_BLACK);
  display.setCursor(74, 0);
  display.print(czerwony);

  zielony = map(abs(analogRead(A1) - 10), 0, 4085, 0, jasnosc); //zielony
  display.fillRect(74, 16, 17, 7, SSD1306_BLACK);
  display.setCursor(74, 16);
  display.print(zielony);

  niebieski = map(abs(analogRead(A4) - 0), 0, 4095, 0, jasnosc); //niebieski
  display.fillRect(74, 32, 17, 7, SSD1306_BLACK); 
  display.setCursor(74, 32);
  display.print(niebieski);

  display.display();

  ws2812.clear();

  for (int i = 0; i < 16; i++) {

    ws2812.setPixelColor(i, ws2812.Color(czerwony, zielony, niebieski)); 

  }
  ws2812.show();
}

Oczywiście można jeszcze sterować potencjometrem ilością LED, tylko trzeba wykorzystać kolejne wejście ADC oraz przeliczoną wartość zmiennej wprowadzić do pętli for zamiast stałej 16.  Jak widać w funkcjach map występują abs, oraz współczynniki korekcyjne które wynikają z kiepskiej jakości zasilania układu jak również takich sobie połączeń na płytce stykowej oraz braku filtracji zewnętrznej. Do portów ADC po prostu przedostaje się trochę szumów zewnętrznych.  

Link do komentarza
Share on other sites

Przymiarka do zamiennika przekaźnika cewki sprzęgła hydraulicznego napędu;

Wyświetlacz;

//************************************Inicjacja cz. temp. DS18B20*************************************************
#include <OneWire.h>
#include <DallasTemperature.h>

//***********************************Inicjacja wyświetlacza OLED SSD1315 (128x64)**********************************
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128  // Szerokość wyświetlacza OLED w pikselach
#define SCREEN_HEIGHT 64  // Wysokość wyświetlacza OLED w pikselach

#define OLED_RESET -1        // Bez pinu RST
#define SCREEN_ADDRESS 0x3C  // Adres wyświetlacza
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//****************************************************************************************************************

int napiecie, napiecie_cewki, prad = 0;
unsigned int TT;

void setup() {

  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);  // Uruchamianie OLED 128 x 64
  display.clearDisplay();

  Serial.begin(9600);
}

void loop() {

  TT = millis();

  Funkja_wyswietlacz_SSD1315();

  napiecie = map(analogRead(A0), 0, 4095, 0, 300);
  prad = map(analogRead(A1), 0, 4095, 0, 300);
  napiecie_cewki = map(analogRead(A0), 0, 4095, 0, 30000);
}

//******************************************************Funkja wyświetlacz SSD1315 (128x64)***************************************************

int Funkja_wyswietlacz_SSD1315() {
  static int ret, zmiana_pomiaru = 0;
  static int napiecie_1, napiecie_01, napiecie_001 = 0;
  static int prad_1, prad_01, prad_001 = 0;
  static int rezystancja_cewki, rezystancja_1, rezystancja_01, rezystancja_001 = 0;
  static int temperatura_1, temperatura_01, temperatura_001 = 0;
  static float odczyt_temp = 0;
  static int temperatura = 0;
  static unsigned int t0, t1 = 0;

  t0 = (TT - t1) / 100;

  OneWire oneWire(10);
  DallasTemperature sensors(&oneWire);
  sensors.begin();
  sensors.setWaitForConversion(0);
  sensors.requestTemperatures();
  odczyt_temp = sensors.getTempCByIndex(0) * 10;
  temperatura = odczyt_temp;  // konwersja float > int

  switch (t0) {
    case 1:
      napiecie_1 = napiecie / 100;
      napiecie_01 = (napiecie % 100) / 10;
      napiecie_001 = napiecie % 10;


      display.setTextSize(2);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(0, 0);
      display.setTextColor(WHITE, BLACK);
      display.println(F("  VOLTAGE  "));
      display.setTextSize(4);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(6, 27);
      display.setTextColor(WHITE, BLACK);
      if (napiecie_1 > 0) {
        display.print(napiecie_1);
      } else {
        display.print(' ');
      }
      display.print(napiecie_01);
      display.print('.');
      display.print(napiecie_001);
      display.print('V');
      display.display();
      break;
      //------------------------------------------------------------------------------------------------------------------------
    case 26:
      prad_1 = prad / 100;
      prad_01 = (prad % 100) / 10;
      prad_001 = prad % 10;

      display.setTextSize(2);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(0, 0);
      display.setTextColor(WHITE, BLACK);
      display.println(F("  CURRENT  "));
      display.setTextSize(4);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(6, 27);
      display.setTextColor(WHITE, BLACK);
      display.print(prad_1);
      display.print('.');
      display.print(prad_01);
      display.print(prad_001);
      display.print('A');
      display.display();
      break;
      //------------------------------------------------------------------------------------------------------------------------
    case 46:
      rezystancja_cewki = napiecie_cewki / prad;
      rezystancja_1 = rezystancja_cewki / 100;
      rezystancja_01 = (rezystancja_cewki % 100) / 10;
      rezystancja_001 = rezystancja_cewki % 10;

      display.setTextSize(2);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(4, 0);
      display.setTextColor(WHITE, BLACK);
      display.println(F("RESISTANCE"));
      display.setTextSize(4);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(6, 27);
      display.setTextColor(WHITE, BLACK);
      if (rezystancja_1 > 0) {
        display.print(rezystancja_1);
      } else {
        display.print(' ');
      }
      display.print(rezystancja_01);
      display.print('.');
      display.print(rezystancja_001);
      display.print('R');
      display.display();
      break;
    //----------------------------------------------------------------------------------------------------------------------
    case 66:
      temperatura_1 = temperatura / 100;
      temperatura_01 = (temperatura % 100) / 10;
      temperatura_001 = temperatura % 10;
      display.clearDisplay();
      display.setTextSize(2);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(6, 0);
      display.println(F(" TEMPERAT"));
      display.setTextSize(3);
      display.setTextColor(SSD1306_WHITE);
      display.setCursor(25, 30);
      if (temperatura_1 > 0) {
        display.print(temperatura_1);
      } else {
        display.print(' ');
      }
      display.print(temperatura_01);
      display.print('.');
      display.print(temperatura_001);
      display.print('C');
      display.display();
      break;

    default:
      if (t0 > 86) { t1 = TT; }
  }
  return ret;
}

Logika; (w  trakcie weryfikacji)


#define przycisk 8
#define przekaznik 3
#define sygnalizacja_LED 4


bool przycisk_status, przekaznik_status, sygnalizacja_LED_status, awaria_cewki = 0;
int czas_zal = 1500;
int prad_cewki;
unsigned int TT;  // Zmienna globalna millis

void setup() {
  Serial.begin(9600);

  pinMode(przycisk, INPUT_PULLUP);
  pinMode(przekaznik, OUTPUT);
  pinMode(sygnalizacja_LED, OUTPUT);
}

void loop() {

  //************************************Inicjacja funkcji*******************************************
  funkcja_przycisk();

  funkcja_przekaznik();

  funkcja_sygnalizacja_LED();
  //*************************************************************************************************
  Serial.println(prad_cewki);
  delay(100);


  //************************************************************************************************
  TT = millis();  // Główny zegar systemowy millis "TT"

  prad_cewki = analogRead(A5);

  digitalWrite(przekaznik, !przekaznik_status);
  digitalWrite(sygnalizacja_LED, !sygnalizacja_LED_status);
}
//*************************************************************************************************


//****************************************Funkcja przycisk*****************************************
int funkcja_przycisk() {

  static unsigned int t0, t1 = 0;

  if (digitalRead(przycisk) == 0) {
    t0 = TT - t1;
    if (t0 > czas_zal) { przycisk_status = 1; }  // opóźnione załączenie
  } else {
    t1 = TT;
    przycisk_status = 0;
  }
}
//*************************************************************************************************

//*****************************************Funkcja przekaznik***************************************
int funkcja_przekaznik() {

  static bool test_cewki, prad_ok, d0 = 0;
  static unsigned int t0, t1 = 0;

  if (przycisk_status == 1 && awaria_cewki == 0) {
    t0 = TT - t1;
    if (t0 < 500) { test_cewki = 1; }  // Test cewki 500ms
    if (t0 > 500 || prad_cewki > 800 && prad_cewki < 500) {
      test_cewki = 0;
      awaria_cewki = 1;
    }
  } else {
    t1 = TT;
  }

  if (prad_cewki < 800 && prad_cewki > 500 || test_cewki == 1) {  // Pomiar prądu cewki zaworu
    prad_ok = 1;
  } else {
    przekaznik_status = 0;
    prad_ok = 0;
    d0 = 0;
    czas_zal = 1500;
  }

  if (prad_ok == 1 && przycisk_status == 1) {
    d0 = 1;
    czas_zal = 100;
    przekaznik_status = 1;
  }
  if (przycisk_status == 0 && d0 == 1) {  // Wyłączenie cewki zaworu
    przekaznik_status = 0;
    if (przekaznik_status == 0 && d0 == 1 && przycisk_status == 1) {
      czas_zal = 1500;
      d0 = 0;
    }
  }
}
//************************************************************************************************************

//***********************************Funkcja sygnalizacji pracy i awarii**************************************
int funkcja_sygnalizacja_LED() {

  static int i0 = 0; 
  static unsigned int t0, t1 = 0;

  if (przekaznik_status == 1) {
    sygnalizacja_LED_status = 1;  // Sygnalizacja LED pracy cewki
  } else {
    sygnalizacja_LED_status = 0;
  }

  if (awaria_cewki == 1 && i0 < 5) {  // Sygnalizacja awarii cewki
    t0 = TT - t1;
    if (t0 < 600 || t0 > 1200 && t0 < 1800 || t0 > 2400 && t0 < 3000) { sygnalizacja_LED_status = 1; }
    if (t0 > 600 && t0 < 1200 || t0 > 1800 && t0 < 2400 || t0 > 3000) { sygnalizacja_LED_status = 0; }
    if (t0 > 6000) {
      sygnalizacja_LED_status = 0;
      t1 = TT;
      ++i0;  // Zakończ sygnalizację awarii po 5min.
    }
  } else {
    t1 = TT;
    i0 = 0;
    awaria_cewki = 0;
  }
}

To są 2 szkice które po weryfikacji zamierzam połączyć w jeden kod docelowy.

Link do komentarza
Share on other sites

Wyszukałem, oraz usunąłem bugi (chyba wszystkie) z części logicznej kodu (wyjdzie w praniu). Celowo nie poprawię błędów w powyższym kodzie tak aby każdy sam zobaczył czym się różnią od siebie te kody, oraz jakie pułapki logiczne można w czymś takim zaobserwować. Jak widać już przy tak krótkim odcinku kodu można mieć drobne problemy co świadczy o skali trudności pisania kodu logiczno-analogowego. W dłuższych kodach zalecana jest fragmentaryzacja na funkcje oraz podfunkcje ponieważ naprawdę można się pogubić. Podobny problem można również zaobserwować w programowaniu drabinkowym czy też przestrzennym.


#define przycisk 8
#define przekaznik 3
#define sygnalizacja_LED 4


bool przycisk_status, przekaznik_status, sygnalizacja_LED_status, awaria_cewki = 0;
int czas_zal = 1200;
int prad_cewki;
unsigned int TT;  // Zmienna globalna millis

void setup() {
  Serial.begin(9600);

  pinMode(przycisk, INPUT_PULLUP);
  pinMode(przekaznik, OUTPUT);
  pinMode(sygnalizacja_LED, OUTPUT);
}

void loop() {

  //************************************Inicjacja funkcji*******************************************
  funkcja_przycisk();

  funkcja_przekaznik();

  funkcja_sygnalizacja_LED();
  //*************************************************************************************************

  delay(100);


  //************************************************************************************************
  TT = millis();  // Główny zegar systemowy millis "TT"

  prad_cewki = analogRead(A5);

  digitalWrite(przekaznik, !przekaznik_status);
  digitalWrite(sygnalizacja_LED, !sygnalizacja_LED_status);
}
//*************************************************************************************************


//****************************************Funkcja przycisk*****************************************
int funkcja_przycisk() {

  static unsigned int t0, t1 = 0;

  if (digitalRead(przycisk) == 0) {
    t0 = TT - t1;
    if (t0 > czas_zal) { przycisk_status = 1; }  // opóźnione załączenie
  } else {
    t1 = TT;
    przycisk_status = 0;
  }
}
//*************************************************************************************************

//*****************************************Funkcja przekaznik***************************************
int funkcja_przekaznik() {

  static bool test_cewki, prad_ok, d0 = 0;
  static unsigned int t0, t1 = 0;

  //----------------------------------------------------------------------------------------------
  if (przycisk_status == 1 && awaria_cewki == 0) {
    t0 = TT - t1;
    if (t0 < 500) {
      test_cewki = 1;
    } else {
      test_cewki = 0;
    }

    if (t0 > 500 && prad_cewki > 800 || t0 > 500 && prad_cewki < 500) {  // Test cewki 500ms
      test_cewki = 0;
      awaria_cewki = 1;
    }
  } else {
    t1 = TT;
    test_cewki = 0;
  }
  Serial.println(czas_zal);
  Serial.println(prad_cewki);
  //-------------------------------------------------------------------------------------------------
  if (prad_cewki < 800 && prad_cewki > 500 || test_cewki == 1) {  // Pomiar prądu cewki zaworu
    prad_ok = 1;
  } else {
    przekaznik_status = 0;
    prad_ok = 0;
    d0 = 0;
    czas_zal = 1200;
  }
  //-----------------------------------------------------------------------------------------------

  if (prad_ok == 1 && przycisk_status == 1) {
    przekaznik_status = 1;
  } else if (przycisk_status == 0 && przekaznik_status == 1) {  // Włączenie cewki zaworu
    d0 = 1;
    czas_zal = 100;
  }
  if (przycisk_status == 1 && d0 == 1) {  // Wyłączenie cewki zaworu
    przekaznik_status = 0;
  } else if (przekaznik_status == 0 && d0 == 1) {
    d0 = 0;
  }
}

//************************************************************************************************************

//***********************************Funkcja sygnalizacji pracy i awarii**************************************
int funkcja_sygnalizacja_LED() {

  static int i0 = 0;
  static unsigned int t0, t1 = 0;

  if (przekaznik_status == 1) {
    sygnalizacja_LED_status = 1;  // Sygnalizacja LED pracy cewki
  } else {
    sygnalizacja_LED_status = 0;
  }

  if (awaria_cewki == 1 && i0 < 50) {  // Sygnalizacja awarii cewki
    t0 = TT - t1;
    if (t0 < 600 || t0 > 1200 && t0 < 1800 || t0 > 2400 && t0 < 3000) { sygnalizacja_LED_status = 1; }
    if (t0 > 600 && t0 < 1200 || t0 > 1800 && t0 < 2400 || t0 > 3000) { sygnalizacja_LED_status = 0; }
    if (t0 > 6000) {
      sygnalizacja_LED_status = 0;
      t1 = TT;
      ++i0;  // Zakończ sygnalizację awarii po 5min.
    }
  } else {
    t1 = TT;
    i0 = 0;
    awaria_cewki = 0;
  }
}

Oczywiście to co zrobiłem zapewne można uprościć, ponieważ tego typu sposób myślenia oraz wirtualizacji logiki wynika z faktu iż wcześniej programy robiłem wyłącznie metodą graficzną. 

Link do komentarza
Share on other sites

2 godziny temu, rafal220 napisał:

to co zrobiłem zapewne można uprościć

A owszem. Po pierwsze nawiasy nie gryzą, czyli nie:

if (a && b || c && d)

ale

if ((a && b) || (c && d))

Poza tym tu masz zakończenie sygnalizacji po iluś tam sekundach, a gdyby tych sekund było dziesięć razy więcej?

Może prosta arytmetyka:

sygnalizacjaLed = t0 % 1200 < 600;

No ale wróćmy do wczorajszego programu.

Pierwsze pytanie: czy jeśli chcesz zobaczyć jaka jest temperatura na balkonie za każdym razem kupujesz nowy termometr, przykręcasz go za oknem, sprawdzasz jaka jest temperatura a potem go wyrzucasz? Czy może termometr instalujesz sobie raz, a potem tylko odczytujesz? 

Czyli: oneWire i sensors powinny być globalne i zainicjalizowane raz. To jest ten Wiel...

Drugie pytanie: czy nawet jeśli uprzesz się że za każdym razem będziesz nowy termometr instalować to od razu po zainstalowaniu pokaże ci temperaturę? Użyłeś setWaitForConversion(0) aby nie czekał na zakończenie konwersji... no ale przecież jeśli termometr nie zmierzy temperatury bo nie dałeś szans aby to zrobił - dostaniesz jakieś bzdury, a najprawdopodobniej 85° (zgadnij dlaczego).

No i tu taki niby problem...

Bo z jednej strony nie chcesz opóźniać pętli (w bibliotece siedzi sobie normalny delay(cośtam), a z drugiej warto by było to przeczytać.

Ale masz przecież funkcję isConversionComplete!

Czyli przykładowo:

W setup dajesz requestTemperatures()

W loop robisz coś w stylu:

// pierwsza możliwość
  if (sensors.isConversionComplete()) {
    odczyt temperatury;
    sensors.requestTemperatures();
    funkcja obsługi termometru;
  }
// druga możliwość
  static float temp=85.0;
  if (sensors.isConversionComplete()) {
    temp = sensors.getTempCByIndex(0);
    sensors.requestTemperatures();
  }

      

No i to był ten ...Błąd 🙂

Czyli: pierwsza możliwość - wywołanie funkcji obsługi termometru zaraz po odczytaniu wartości; druga możliwość - w zmiennej temp masz zawsze najnowszą odczytaną wartość.

Wybierz sobie.

 

  • Lubię! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.