Не могу настроить многопоточность на Уно

Тема в разделе "Arduino & Shields", создана пользователем Knowkip, 7 авг 2016.

  1. Knowkip

    Knowkip Нуб

    Здравствуйте. Только начал осваивать ардуино уно и уже возникли вопросы. Пытаюсь избавиться от функции "delay()" в коде так как нужно чтобы ардуино не "зависала" на время обработки этих delay.

    Решил попробовать на простейшем скетче, который попеременно выводит два экрана дисплея 1602 с I2C. На первом экране температура (1 строка) и влажность (2 строка) с DHT11, потом задержка 5 сек, потом второй экран со временем (1 строка) и датой (2 строка) с DS3231, потом опять задержка и цикл повторяется по новой.
    Скетч ниже, от delay во втором экране я ушел - сделал через while и millis

    Код (C++):
    #include <Time.h>
    #include <DS1307RTC.h>
    #include <LiquidCrystal_I2C.h>
    LiquidCrystal_I2C lcd(63,16,2); // Устанавливаем дисплей

    #include <DHT.h>
    #define DHTPIN 2 // what pin we're connected to
    #define DHTTYPE DHT11 // DHT 11
    DHT dht(DHTPIN, DHTTYPE);

    byte degree[8] = // кодируем символ градуса
    {
    B00111,
    B00101,
    B00111,
    B00000,
    B00000,
    B00000,
    B00000,
    };

    byte l [8] = // кодируем символ л
    {
    B00111,
    B01001,
    B01001,
    B01001,
    B01001,
    B01001,
    B10001,
    };

    byte g [8] = // кодируем символ л
    {
    B10101,
    B10101,
    B10101,
    B01110,
    B10101,
    B10101,
    B10101,
    };

    byte mz [8] = // кодируем символ л
    {
    B10000,
    B10000,
    B11110,
    B10001,
    B10001,
    B10001,
    B11110,
    };

    byte p [8] = // кодируем символ п
    {
    B11111,
    B10001,
    B10001,
    B10001,
    B10001,
    B10001,
    B10001,
    };

    byte y [8] = // кодируем символ у
    {
    B10001,
    B10001,
    B10001,
    B11111,
    B00001,
    B00001,
    B11110,
    };

    byte i=0;
    unsigned long previousMillis = 0;
    const long interval =5000;


    void setup() {
    setSyncProvider(RTC.get);
    //Устанавливаем время в формате:
    //Часы, минуты, секунды, день, месяц, год
    //setTime(14,29,0,30,7,2016);
    //Применяем:
    //RTC.set(now());

    dht.begin();
    lcd.init();

    lcd.backlight();// Включаем подсветку дисплея

    lcd.createChar(1, degree); // Создаем символ градус под номером 1
    lcd.createChar(2, l); // Создаем символ л под номером 2
    lcd.createChar(3, g); // Создаем символ ж под номером 3
    lcd.createChar(4, mz); // Создаем символ ь под номером 4
    lcd.createChar(5, p); // Создаем символ ж под номером 5
    lcd.createChar(6, y); // Создаем символ ь под номером 6
    }

    void loop() {


    int h = dht.readHumidity();
    int t = dht.readTemperature();

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("TEM\5EPAT\6PA: ");
    lcd.print(t);
    lcd.print("\1");

    lcd.setCursor(0, 1);
    lcd.print("B\2A\3HOCT\4: ");
    lcd.print(h);
    lcd.print("%");


    delay(5000);
    lcd.clear();
    i=0;

    //обновляем показания часов через цикл каждую секунду
    while(i<=1){

    digitalClockDisplay(); //вызываем функцию, которая числа меньше 10 выводит с нулем впереди

    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval){

    previousMillis = currentMillis;

    i++;
    }
    }



    }

    void digitalClockDisplay(){
    // digital clock display of the time
    lcd.setCursor(4, 0);
    printDigits(hour());
    lcd.print(":");
    printDigits(minute());
    lcd.print(":");
    printDigits(second());

    lcd.setCursor(3, 1);
    printDigits(day());
    lcd.print(".");
    printDigits(month());
    lcd.print(".");
    lcd.print(year());

    }

    void printDigits(int digits){
    // utility function for digital clock display: prints preceding colon and leading 0
    if(digits < 10)
    lcd.print('0');
    lcd.print(digits);
    }
    При попытке заменить delay(5000) на такой же цикл while и millis как у времени (даже с другими переменными начального времени и интервала) начинается какая-то ахинея: то первый экран вообще не показывается, то идет наложение двух экранов друг на друга, причем второй экран поверх первого.

    И вообще так и не понял как эта функция millis работает. Почему, например, при таком коде
    Код (C++):
    while(i<=1){

    digitalClockDisplay(); //вызываем функцию, которая числа меньше 10 выводит с нулем впереди

    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval){

    previousMillis = currentMillis;

    i++;
    }
    Экран со временем отображается 5 сек и данные обновляются каждую секунду (то есть виден ход секунд), а при коде
    Код (C++):
    while(i<=1){

    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval){

    previousMillis = currentMillis;
    digitalClockDisplay(); //вызываем функцию, которая числа меньше 10 выводит с нулем впереди
    i++;
    }
    хода секунд не видно - время обновляется просто каждые 5 секунд (если поставить условие while(i<=1) другим, например, while(i<=3) для удобства наблюдения)

    Вобщем вопросы на которые не могу найти ответа:
    1. Как избавиться от delay на первом экране (с температурой и влажностью)?
    2. Почему время в цикле while обновляется каждую секунду несмотря на то, что цикл считает до одного и время интервала для millis стоит 5 секунд?
    3. Конструкция вида while --- millis вообще лучше чем delay? В плане не тормозит ли она программу, например, если в программе будет еще модуль подсчета количества импульсов на дискретном входе (от счетчиков воды), при том что входы это не входы прерываний.
     
  2. ostrov

    ostrov Гуру

    На первый взгляд проблема в том, что вы просто заменяете delay() циклом который выжидает некоторое время, что то же самое. Функция проверки интервала не должна останавливать программу, а должна встраиваться в нее ожидая своего времени.
     
  3. Knowkip

    Knowkip Нуб

    В том то и проблема что если убираю while то millis перестает работать - на секунду выводит экран с временем и датой а потом опять переходит на температуру. Хотя интервал задан в 5000 мс. Не могу понять почему так и как сделать правильно
     
  4. DIYMan

    DIYMan Guest

    Если надо вот это, то навскидку - примерно так:

    Код (C++):
    #define SCREEN_SHOW_TIME 5000 // сколько мс показывать один экран
    enum {SCREEN_ONE, SCREEN_TWO, DUMMY_SCREEN};
    int8_t whichScreen; // переменная, которая держит в себе номер экрана
    unsigned long lastScreenChangeCheck; // сюда будем складывать millis

    // функция показа определённого экрана
    void showScreen(byte whichScreen)
    {
        switch(whichScreen)
        {
            case SCREEN_ONE:
                // Показываем первый экран
            break;
         
            case SCREEN_TWO:
                // показываем второй экран
            break;
        }
     
    }

    void setup()
    {
        lastScreenChangeCheck = 0;
     
        whichScreen = SCREEN_ONE;
        // при старте сразу показываем первый экран
        showScreen(whichScreen);
    }

    void loop()
    {
        unsigned long curTime = millis();
     
        if(curTime - lastScreenChangeCheck > SCREEN_SHOW_TIME)
        {
            // настало время показать новый экран
            lastScreenChangeCheck = curTime;
         
            whichScreen++; // прибавляем номер экрана
         
            // если зашли за последний - переваливаемся на первый
            if(whichScreen == DUMMY_SCREEN)
                whichScreen = SCREEN_ONE;
             
            // рисуем новый экран
            showScreen(whichScreen);
        }
     
        // если надо рисовать в каждом вызове loop,
        // то просто раскомментируем
        // showScreen(whichScreen);
       // тогда первый вызов showScreen (выше) можно убрать.
    }
    Код расширяем, достаточно перед DUMMY_SCREEN поставить ещё идентификатор, и в функцию showScreen дописать вывод нужной информации на экран, когда показывается экран с новым идентификатором. Разбирайтесь ;)
     
    Tomasina и Knowkip нравится это.
  5. Knowkip

    Knowkip Нуб

    Попробовал код - работает. Вот только если функцию digitalClockDisplay(); вставить перед break, то часы показываются 5 секунд как и должно, но хода секунд нету. Если я вставлю свою старую конструкцию в "экран"

    Код (C++):
    //обновляем показания часов через цикл каждую секунду
    while(i<=1){

    digitalClockDisplay(); //вызываем функцию, которая числа меньше 10 выводит с нулем впереди

    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis >= interval){

    previousMillis = currentMillis;

    i++;
    }
    это позволит ардуино работать в режиме многопоточности (имея ввиду что она фоном должна еще считать импульсы от счетчиков)? Или в данном случае применение while равноценно delay?
     
  6. DIYMan

    DIYMan Guest

    Допишите в конечный автомат действия через каждую секунду, делов-то.
     
  7. qwone

    qwone Гик