Как заставить 2 функции цикла сотрудничать?

У меня есть OLED SSD1306 и DS3231 RTC, 2 кнопки для установки времени и даты. Мой проект заключается в отображении прокрутки текста в верхней части OLED, времени RTC в середине, дня и даты в последней строке. Моя проблема в том, что я не могу получить текст прокрутки и время для запуска, когда в цикле вместе, по отдельности они работают нормально. Когда они вместе, текст прокрутки в порядке, но время статическое, оно не отображает текущий RTC. Затем после 1 цикла цикла текст time seconds становится смешанным, затем после 2-го цикла минуты и секунды становятся смешанными. Я попытался поместить display.clearDisplay(); display.display(); в конце цикла, делая это, фиксирует время от смешивания, очищая буфер перед следующим циклом, но не фиксирует время от статики. Как я могу получить время, чтобы больше не быть статичным? Любая помощь будет очень ценна. Вот мой скетч:

#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 64 // OLED display height, in pixels
 
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 
#define button1    9                       // Button B1 is connected to Arduino pin 9
#define button2    8                       // Button B2 is connected to Arduino pin 8
 
void setup(void) {
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  delay(1000);
 
  // by default, the high voltage from the 3.3v line
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);  // initialize with the I2C address 
  // init done
 
  // Clear the display buffer.
  display.clearDisplay();
  display.display();
 
  display.setTextColor(WHITE, BLACK);
}
 
char Time[]     = "  :  :  ";
char Calendar[] = "  /  /20  ";
byte i, second, minute, hour, day, month, date, year;

void display_day(){
  switch(day){
    case 1:  draw_text(0, 50, " SUNDAY  ", 1); break; //(Column,Row,Data to be displayed,Font size)
    case 2:  draw_text(0, 50, " MONDAY  ", 1); break; //(Column,Row,Data to be displayed,Font size)
    case 3:  draw_text(0, 50, " TUESDAY ", 1); break; //(Column,Row,Data to be displayed,Font size)
    case 4:  draw_text(0, 50, "WEDNESDAY", 1); break; //(Column,Row,Data to be displayed,Font size)
    case 5:  draw_text(0, 50, "THURSDAY ", 1); break; //(Column,Row,Data to be displayed,Font size)
    case 6:  draw_text(0, 50, " FRIDAY  ", 1); break; //(Column,Row,Data to be displayed,Font size)
    default: draw_text(0, 50, "SATURDAY ", 1); //(Column,Row,Data to be displayed,Font size)
  }
}
 
void DS3231_display(){
  // Convert BCD to decimal
  second = (second >> 4) * 10 + (second & 0x0F);
  minute = (minute >> 4) * 10 + (minute & 0x0F);
  hour   = (hour >> 4)   * 10 + (hour & 0x0F);
  month  = (month >> 4)  * 10 + (month & 0x0F);
  date   = (date >> 4)   * 10 + (date & 0x0F);
  year   = (year >> 4)   * 10 + (year & 0x0F);
  // End conversion
 
  Time[7]     = second % 10 + 48;
  Time[6]     = second / 10 + 48;
  Time[4]     = minute % 10 + 48;
  Time[3]     = minute / 10 + 48;
  Time[1]     = hour   % 10 + 48;
  Time[0]     = hour   / 10 + 48;
  Calendar[9] = year   % 10 + 48;
  Calendar[8] = year   / 10 + 48;
  Calendar[4] = date   % 10 + 48;
  Calendar[3] = date   / 10 + 48;
  Calendar[1] = month  % 10 + 48;
  Calendar[0] = month  / 10 + 48;

//Print text
  draw_text(60, 50, Calendar, 1); // Display the date (format: mm/dd/yyyy)
  draw_text(15, 25, Time, 2); // Display the time
}

void blink_parameter(){
  byte j = 0;
  while(j < 10 && digitalRead(button1) && digitalRead(button2)){
    j++;
    delay(25);
  }
}
 
byte edit(byte x_pos, byte y_pos, byte parameter){
  char text[3];
  sprintf(text,"%02u", parameter);
  while(!digitalRead(button1));                      // Wait until button B1 released
  while(true){
    while(!digitalRead(button2)){                    // If button B2 is pressed
      parameter++;
      if(i == 0 && parameter > 12)                   // If month > 12 ==> month = 1
        parameter = 1;      
      if(i == 1 && parameter > 31)                   // If date > 31 ==> date = 1
        parameter = 1;
      if(i == 2 && parameter > 99)                   // If year > 99 ==> year = 0
        parameter = 0;
      if(i == 3 && parameter > 23)                   // If hours > 23 ==> hours = 0
        parameter = 0;
      if(i == 4 && parameter > 59)                   // If minutes > 59 ==> minutes = 0
        parameter = 0;
      sprintf(text,"%02u", parameter);
      draw_text(x_pos, y_pos, text, 1);
      delay(200);                                    // Wait 200ms
    }
    draw_text(x_pos, y_pos, "  ", 1);
    blink_parameter();
    draw_text(x_pos, y_pos, text, 1);
    blink_parameter();
    if(!digitalRead(button1)){                       // If button B1 is pressed
      i++;                                           // Increment 'i' for the next parameter
      return parameter;                              // Return parameter value and exit
    }
  }
}
 
void draw_text(byte x_pos, byte y_pos, char *text, byte text_size) {
  display.setCursor(x_pos, y_pos);
  display.setTextSize(text_size);
  display.print(text);
  display.display();
}

void scroll_text(){
  draw_text(0, 0, "The Pumpkin Express", 1);
  display.display();
  display.startscrollright(0x00, 0x00);
  delay(8500);
  display.stopscroll();
  delay(4000);
  }

void loop() {

  if(!digitalRead(button1)){                         // If button B1 is pressed
    i = 0;
    while(!digitalRead(button1));                    // Wait for button B1 release
    while(true){
      while(!digitalRead(button2)){                  // While button B2 pressed
        day++;                                       // Increment day
        if(day > 7) day = 1;
        display_day();                               // Call display_day function
        delay(200);                                  // Wait 200 ms
      }
      draw_text(0, 50, "         ", 1);
      blink_parameter();                             // Call blink_parameter function
      display_day();                                 // Call display_day function
      blink_parameter();                             // Call blink_parameter function
      if(!digitalRead(button1))                      // If button B1 is pressed
        break;
    }
    //set position of text when editing on button press
    month  = edit(60, 50, month);                    // Edit month
    date   = edit(80, 50, date);                      // Edit date
    year   = edit(110,50, year);                    // Edit year
    hour   = edit(14, 25, hour);                     // Edit hours
    minute = edit(50, 25, minute);                   // Edit minutes
 
    // Convert decimal to BCD
    minute = ((minute / 10) << 4) + (minute % 10);
    hour = ((hour / 10)  << 4) + (hour % 10);
    date = ((date / 10) <<  4) + (date % 10);
    month = ((month / 10)  << 4) + (month % 10);
    year = ((year / 10)  << 4) + (year % 10);
    // End conversion
     
    // Write data to DS3231 RTC
    Wire.beginTransmission(0x68);               // Start I2C protocol with DS3231 address
    Wire.write(0);                              // Send register address
    Wire.write(0);                              // Reset seconds and start oscillator
    Wire.write(minute);                         // Write minute
    Wire.write(hour);                           // Write hour
    Wire.write(day);                            // Write day
    Wire.write(month);                          // Write month
    Wire.write(date);                           // Write date
    Wire.write(year);                           // Write year
    Wire.endTransmission();                     // Stop transmission and release the I2C bus
    delay(200);                                 // Wait 200ms
  }
  
  Wire.beginTransmission(0x68);                 // Start I2C protocol with DS3231 address
  Wire.write(0);                                // Send register address
  Wire.endTransmission(false);                  // I2C restart
  Wire.requestFrom(0x68, 7);                    // Request 7 bytes from DS3231 and release I2C bus at end of reading
  second = Wire.read();                         // Read seconds from register 0
  minute = Wire.read();                         // Read minutes from register 1
  hour   = Wire.read();                         // Read hour from register 2
  day    = Wire.read();                         // Read day from register 3
  month  = Wire.read();                         // Read month from register 4
  date   = Wire.read();                         // Read date from register 5
  year   = Wire.read();                         // Read year from register 6
  Wire.beginTransmission(0x68);                 // Start I2C protocol with DS3231 address
  Wire.write(0x11);                             // Send register address
  Wire.endTransmission(false);                  // I2C restart
  Wire.requestFrom(0x68, 2);                    // Request 2 bytes from DS3231 and release I2C bus at end of reading
                                
  DS3231_display();                             // Display time & calendar
  display_day();                                // Display date
  
  scroll_text();                                // Display scrolling text
              // If i dont use the clear buffers(next 2 line codes), on the 
             //next loop cycle the seconds & minute text becomes jumbled
  //display.clearDisplay();
  //display.display();
 
  delay(50);                                    // Wait 50ms
}

, 👍2

Обсуждение

неудивительно, что ваша функция scroll_text() останавливает выполнение программы на 12,5 секунды, @jsotola

Thx для предложений, я изменил название своего поста, и я попробовал закомментировать задержки и start & stop в моем void scrolling_text(); текст не прокручивается, но глючит, и время все перемешано, @Stiglitz

Причина задержек в void scrolling_text(); заключается в том, что я хочу, чтобы текст прокрутился на 1 круг вокруг, сделал паузу для отображения текста, а затем прокрутка снова началась. Так же, как ЖК-экран в поезде, когда он показывает следующую остановку поезда, @Stiglitz

возможно, вам нужна единая функция отображения ... функция прокрутки не выполняет никакого отображения .. он устанавливает только символьный буфер ... функция времени также не отображается ... он задает значение переменной времени ... функция отображения использует значения в символьном буфере и в переменной времени и выводит их на экран, @jsotola

Итак, вы говорите, что я должен создать новую пустоту под названием void display_text(); & в нее поместить: draw_text(60, 50, Calendar, 1); draw_text(15, 25, Time, 2); draw_text(0, 0, "The Pumpkin Express", 1); ?, @Stiglitz

возможно, что-то в этом роде ... он не обязательно должен быть в своей собственной функции .. он может быть частью loop() ... каким бы способом вы ни хотели организовать свой код, @jsotola

"есть ли способ изолировать эти задержки...?" Процессор может делать только одну вещь за раз, поэтому, если задерживается (), все остальное, что он должен делать, задерживается, а не только содержимое функции, содержащей delay(). [Отвечая здесь в ожидании того, что вы отредактируете свой "ответ" в вопрос., @JRobert

Поэтому я попытался создать новую пустоту со всеми 3 draw_text, как календарь, так и время не будут работать за пределами void DS3231_display(). Таким образом, я понял, что моя проблема заключается в том, что задержки в void scrolling_text () - это то, как читается как часть всего скетча. Я думал, что создание пустоты предотвратит это и будет запускать его только при вызове. Есть ли способ кодирования, чтобы предотвратить одну пустую задержку от воздействия всей программы, как есть способ изолировать эти задержки,или программа только считывает задержки только при выполнении display.scrollleft(0x00, 0x00);?, @Stiglitz

Джроберт, ты хочешь сказать, что у меня не может быть прокрутки текста в моем скетче, потому что Arduino не может справиться с ним во время выполнения остальной части скетча? Причина задержки заключается в том, что я хочу, чтобы OLED прокручивал текст, затем приостанавливал текст, чтобы показать его полностью на дисплее, затем прокручивал снова, полоскал и повторял. Причина, по которой первая задержка составляет 8500, заключается в том, что именно столько времени требуется тексту, чтобы полностью выйти из поля зрения., @Stiglitz

@Стиглиц Неясно,что подразумевается под "задержками в" void scrolling_text ()", которые каким-то образом считываются как часть всего скетча". Задержка *задержки*: это останавливает мир (за исключением прерываний)-во время "задержки" больше ничего не произойдет, это задержка (кроме прерываний). Если вы не хотите, чтобы скетч задерживался так, что мир остановится, тогда вам нужно будет задержаться по-другому. [Моргнуть Без Промедления](https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay) *может* быть тем, о чем вы в конечном счете просите., @Dave Newton


2 ответа


1

Ответ на ваш главный вопрос "Как заставить 2 функции цикла сотрудничать?" таков: Вы не можете, потому что ваша программа не может иметь две функции с одинаковым именем. Вместо этого вам нужно будет перепроектировать свою программу, имея в виду , что MCU может делать только одну вещь за раз, хотя вы можете разбить каждое из нескольких заданий на части и чередовать их достаточно быстро, чтобы мы, медленные люди, не заметили.

Мой подход, когда у меня возникает такой вопрос, состоит в том, чтобы временно отложить в сторону то, что я знаю о кодировании и о микроконтроллерах, и просто сосредоточиться на том, как выполнять эту работу. Я перечисляю подзадачи, которые должен выполнить мой код, но не слишком задумываюсь о том, как их выполнить. Запишите их в том порядке, в каком они требовались, в любом стиле заметок, который вам подходит.

Теперь разбейте каждую из этих подзадач на свои подзадачи. Повторяйте до тех пор, пока не поймете, как кодировать каждый из них. Тогда вы будете знать достаточно, чтобы начать писать код.

,

Что вы имеете в виду, говоря, что у меня есть 2 функции с одинаковым именем? Все 3 функции имеют разные имена: scroll_text (), display_day () и DS3231_display(). причина моего названия в том, что у меня проблема с scroll_text () и DS3231_display (), из-за которой моя программа сбоит, когда обе запущены в скетче. Я могу заставить их нормально работать по отдельности., @Stiglitz

Я считаю, что @JRobert означает, что у вас не может быть двух функций с одним и тем же циклом имен., @Nathan Jiang


0

Способ заставить функции взаимодействовать друг с другом - это перекодировать их, чтобы они не тратили время впустую и передавали управление туда и обратно как можно быстрее. Вы делаете это, используя трюк BlinkWithoutDelay, чтобы переписать "блокирующий" код, чтобы он был неблокирующим, и доверяете loop(), чтобы вернуть управление вашей функции за разумное время.

Например, возьмем эту функцию, которую @jsotola определил как убийцу производительности:

void scroll_text(){
  draw_text(0, 0, "The Pumpkin Express", 1);
  display.display();
  display.startscrollright(0x00, 0x00);
  delay(8500);
  display.stopscroll();
  delay(4000);
  }

Ему действительно не нужно монополизировать процессор на 12500 микросекунд - вместо этого он может просто проверять время и время от времени включать и выключать прокрутку, а также быстро передавать управление обратно в loop() для чтения и обновления часов.

У меня нет вашего оборудования или настроек, поэтому вот несколько непроверенных кодов:

void scroll_text(){
  // некоторые локальные переменные состояния, чтобы помнить, где мы находимся
  static bool isInitialized = false; // возможно, глобальная, если требуется повторная инициализация.
  static bool isScrolling = false;
  static unsigned long interval = 1;
  static unsigned long last = -interval;
  unsigned long now = millis();
  if (now - last >= interval ) { // время, чтобы что-то сделать
    last = now;
    if( !isInitialized ){
      draw_text(0, 0, "The Pumpkin Express", 1);
      display.display();
      isIinitialized = true;
    }
    if ( !isScrolling ) {
        isScrolling = true;
        display.startscrollright(0x00, 0x00);
        // delay(8500); // вместо этого используйте трюк BWOD
        interval = 8500;
     } else { // isScrolling
       isScrolling = false;
       display.stopscroll();
       //delay(4000);
       interval = 4000;
     }
  }

Эта выпадающая функция scroll_text() может вызываться много раз в миллисекунду и в основном ничего не делает и немедленно возвращает управление. Но когда он поймет, что настало время, он обновит свои собственные переменные состояния и выходные состояния.

Заменяя функцию scroll_text() на неблокирующую форму, она ускоряет цикл () на 12,5 секунд за цикл. Выполнение аналогичной замены задержек, снижающих производительность процессора,может еще больше повысить быстродействие. Однако это большой сдвиг в сознании, чтобы перестать использовать последовательный, блокирующий код и перейти к событиям. Каждый раз, когда вы видите цикл delay(), a while(...)или for(), подумайте о том, действительно ли вам нужно поместить процессор в ловушку внутри этого цикла или какие данные вам понадобятся, чтобы пропустить его сейчас и вернуться к нему в следующий раз через main цикл событий().

Я думаю, что следующим шагом было бы обернуть чтение чтения DS3231 с помощью трюка BWOD - вам нужно считывать часы только один раз за 1000 мс, и вы могли бы сохранить сотни инструкций за цикл ().

,