Как заставить 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
}
@Stiglitz, 👍2
Обсуждение2 ответа
Ответ на ваш главный вопрос "Как заставить 2 функции цикла сотрудничать?" таков: Вы не можете, потому что ваша программа не может иметь две функции с одинаковым именем. Вместо этого вам нужно будет перепроектировать свою программу, имея в виду , что MCU может делать только одну вещь за раз, хотя вы можете разбить каждое из нескольких заданий на части и чередовать их достаточно быстро, чтобы мы, медленные люди, не заметили.
Мой подход, когда у меня возникает такой вопрос, состоит в том, чтобы временно отложить в сторону то, что я знаю о кодировании и о микроконтроллерах, и просто сосредоточиться на том, как выполнять эту работу. Я перечисляю подзадачи, которые должен выполнить мой код, но не слишком задумываюсь о том, как их выполнить. Запишите их в том порядке, в каком они требовались, в любом стиле заметок, который вам подходит.
Теперь разбейте каждую из этих подзадач на свои подзадачи. Повторяйте до тех пор, пока не поймете, как кодировать каждый из них. Тогда вы будете знать достаточно, чтобы начать писать код.
Что вы имеете в виду, говоря, что у меня есть 2 функции с одинаковым именем? Все 3 функции имеют разные имена: scroll_text (), display_day () и DS3231_display(). причина моего названия в том, что у меня проблема с scroll_text () и DS3231_display (), из-за которой моя программа сбоит, когда обе запущены в скетче. Я могу заставить их нормально работать по отдельности., @Stiglitz
Я считаю, что @JRobert означает, что у вас не может быть двух функций с одним и тем же циклом имен., @Nathan Jiang
Способ заставить функции взаимодействовать друг с другом - это перекодировать их, чтобы они не тратили время впустую и передавали управление туда и обратно как можно быстрее. Вы делаете это, используя трюк 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 мс, и вы могли бы сохранить сотни инструкций за цикл ()
.
- Проблема с датчиком температуры и влажности DHT11
- Получение ошибки ets 8 января 2013,rst cause:4,boot mode(1,6) wdt reset
- Выводы прерываний Arduino Mega 2560 и отображение портов с помощью поворотного энкодера
- Данные DHT11 из Arduino UNO в Firebase через ESP8266
- Объяснение кода MPU6050
- Измерение скорости двигателя постоянного тока с помощью поворотного энкодера
- Почему dtostrf() не работает для этого значения?
- Как я могу прервать задержку() при нажатии кнопки?
неудивительно, что ваша функция 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