как использовать millis () вместо цикла for в этой функции - PullRequest
0 голосов
/ 04 ноября 2019

Я использую Neopixels (64 светодиода), и у меня есть функция level_up, которая каждый раз получает разные led_num. Как правило, это уровень уровня;Уровень [1] будет светить все светодиоды в последовательности от 0 до 28, уровень [2] будет светить все светодиоды от 29 до 48 и т. д. Функция, которую я прикрепил, работает нормально, но мне нужно изменить задержку на миллис () ине уверен как. Есть мысли?

uint8_t level[] = {0, 28, 48, 60, 64};  //levels 0 to 4


void level_up(uint8_t wait, uint8_t led_num) {
    uint8_t start_point;
    if (led_num == level[1]) start_point = 0;   //up from level 0 to 1
    if (led_num == level[2]) start_point = 28;  //up from level 1 to 2
    if (led_num == level[3]) start_point = 48;  //up from level 2 to 3
    if (led_num == level[4]) start_point = 60;  //...

    for (uint8_t i = start_point; i < led_num; i++) {
        strip.setPixelColor(i, strip.Color(0, 0, 255));
        strip.show();
        delay(wait);  //TODO: change it to timer
    }
}

void loop() {
    if (plus_btn.pressed()) {
        score++;
        if (score >= 4) {
            score = 4;
        }
    }
    if (minus_btn.pressed()) {
        score--;
        if (score <= 0) {
            score = 0;
        }
    }

switch (score) {
    case 0:
        if (last_score == 1) level_down(50, level[0]);
        last_score = 0;
        break;
    case 1:
        // if last_score was 0 make the blue effect because level is up
        if (last_score == 0) level_up(50, level[1]);
        // if last_score was 2 make the red effect because level is down
        if (last_score == 2) level_down(50, level[1]);
        last_score = 1;
        break;
    case 2:
        if (last_score == 1) level_up(50, level[2]);
        if (last_score == 3) level_down(50, level[2]);
        last_score = 2;
        break;
    case 3:
        if (last_score == 2) level_up(50, level[3]);
        if (last_score == 4) level_down(50, level[3]);
        last_score = 3;
        break;
    case 4:
        winning_timer.start();
        winning();
        digitalWrite(WINNING_SENSOR_PIN, HIGH);
        break;
}

Serial.println(score);

}

Ответы [ 3 ]

1 голос
/ 04 ноября 2019

Использование millis() не будет блокировать цикл for, как delay(). Поэтому я думаю, что вам придется адаптировать код, который вызывает ваш метод, потому что в данный момент он выглядит так, как будто ваш код зависит от блокировки в цикле for. Но обычно вы используете millis() как в примере кода ниже. Вы сохраняете начальную временную метку, а затем делаете что-то после окончания периода ожидания.

uint8_t level[] = {0, 28, 48, 60, 64};  //levels 0 to 4

uint8_t counter;
uint8_t end_point;
bool show_level;

void level_up(uint8_t wait, uint8_t led_num) {
    if (led_num == level[1]) counter = 0;   //up from level 0 to 1
    if (led_num == level[2]) counter = 28;  //up from level 1 to 2
    if (led_num == level[3]) counter = 48;  //up from level 2 to 3
    if (led_num == level[4]) counter = 60;  //...
    show_level =true;
    end_point = led_num;
}

bool set_pixel_color(uint8_t wait) 
{
    if(timestamp - millis() == wait)
    {
        strip.setPixelColor(counter, strip.Color(0, 0, 255));
        strip.show();
        timestamp = millis();
        return true; // incremented 
    }    
    return false;
}

void show_level_led_strip()
{
    if(show_level)
    {
        if(counter > end_point) // escape when the counter gets bigger then the current led_num
        {
            show_level = false;
        }
        else
        {
            if(set_pixel_color(50))
            {
                counter++;
            }
        }
    }
}

void loop() {
    if (plus_btn.pressed()) {
        score++;
        if (score >= 4) {
            score = 4;
        }
    }
    if (minus_btn.pressed()) {
        score--;
        if (score <= 0) {
            score = 0;
        }
    }

    switch (score) {
        case 0:
            if (last_score == 1) level_down(level[0]);
            last_score = 0;
            break;
        case 1:
            // if last_score was 0 make the blue effect because level is up
            if (last_score == 0) level_up(level[1]);
            // if last_score was 2 make the red effect because level is down
            if (last_score == 2) level_down(level[1]);
            last_score = 1;
            break;
        case 2:
            if (last_score == 1) level_up(level[2]);
            if (last_score == 3) level_down(level[2]);
            last_score = 2;
            break;
        case 3:
            if (last_score == 2) level_up(level[3]);
            if (last_score == 4) level_down(level[3]);
            last_score = 3;
            break;
        case 4:
            winning_timer.start();
            winning();
            digitalWrite(WINNING_SENSOR_PIN, HIGH);
            break;
    }

    show_level_led_strip();
}

Serial.println(score);
0 голосов
/ 04 ноября 2019

После прочтения вашего поста и всех комментариев, я думаю, я знаю, что вы хотите. Вы просто хотите, чтобы цикл продолжался без остановки в этой функции во время задержки, верно?

millis () не спит или не задерживается, он просто дает вам время в миллисекундах, поскольку Arduino работает.

Итак, вы можете просто добавить это к своему коду, и он будет работать:

uint8_t level[] = {0, 28, 48, 60, 64};  //levels 0 to 4
unsigned long lastTime = 0;  // << add this
uint8_t start_point = 0; // << move here

void update_leds(uint16_t wait, uint8_t led_num) {
    if(start_point >= led_num) return;
    if(millis() - lastTime > wait) {  // << add this
        //uint8_t start_point;
        lastTime = millis(); // << add this

        //for (uint8_t i = start_point; i < led_num; i++) {
            strip.setPixelColor(start_point, strip.Color(0, 0, 255));
            strip.show();
            //delay(wait);  // << remove this
        //}
        start_point++;
    }
}

void level_up(uint8_t led_num) {
    if (led_num == level[1]) start_point = 0;   //up from level 0 to 1
    if (led_num == level[2]) start_point = 28;  //up from level 1 to 2
    if (led_num == level[3]) start_point = 48;  //up from level 2 to 3
    if (led_num == level[4]) start_point = 60;  //...
}

изменить время ожидания с uint8_t на uint16_t, поскольку 255 может быть слишком мало.

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

Существует только одна проблема: если внутри вашего цикла есть другие задержки, возможно,светодиоды обновляются на несколько миллисекунд позже, чем ожидалось ... Если вы понимаете, о чем я.

Редактировать: иногда вы также хотите получать уведомления об обновлении светодиодов. Таким образом, вы можете вернуть bool, чтобы сказать, обновила ли функция светодиоды или нет (может быть, вам это нужно в цикле, чтобы проверить, выровнялся ли он.

0 голосов
/ 04 ноября 2019

Это не отвечает на ваш вопрос напрямую, но стратегия, которую я использую, дает мне любое количество временных событий без блокировки моей программы в millis ().

Установите крайний срок в будущем и заключите отложенное действие в оператор if, который опрашивает millis (), пока этот срок не будет достигнут. Это не идеально, потому что программная синхронизация теряет время из-за обработки, а также из-за проблемы переполнения и обтекания функции millis () (посмотрите на arduino.cc).

/* Global variables (constexpr creates a compile time only constant) */
constexpr uint32_t WAIT_INTERVAL = 10;  // interval is 10ms
uint32_t deadline = 0;  // when to run next

// inside loop()
    uint32_t now = millis();  // capture the current millis() value
    if(now >= deadline)
    {
        deadline = now + WAIT_INTERVAL;  // push the next deadline into the future
        // perform timed periodic operations here (call function or whatever)
    }
...