Arduino P10 дисплей и счетчик времени - PullRequest
0 голосов
/ 01 октября 2019

каждый, я использую, P10 Dot Matrix Display с Arduino Uno. Я использую библиотеку P10 по этой ссылке. P10_LED и мне нужно отобразить обратный отсчет за один час на модуле дисплея. Данная библиотека использует библиотеку TimerOne . Так что для обратного отсчета я использую библиотеку MsTimer2 , которая использует таймер arduino.

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

Вот код.

#include <MsTimer2.h>
#include <TimerOne.h>
#include"SPI.h"
#include <ledP10.h>

LedP10 myled;
uint8_t minute = 0, second = 0, hour = 1;
volatile bool xIsCountDone = false;
volatile bool xIsInterruptOcuured = false;
char time_buff[100];

void setup() 
{
    Serial.begin(9600);
    myled.init(3,4,8,9 ,3);
    sprintf((char*)time_buff, "    %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
    Serial.println((char*)time_buff);
    myled.showmsg_single_static((char*)time_buff, 0);
    xIsInterruptOcuured = false;
    //myled.showmsg_single_scroll("this is single led test",2,8,0);
    MsTimer2::set(1000, count);
    MsTimer2::start();
}

void loop() {
  if (xIsInterruptOcuured == true)
  {
    sprintf((char*)time_buff, "    %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
    Serial.println((char*)time_buff);
    myled.showmsg_single_static((char*)time_buff, 0);
    xIsInterruptOcuured = false;
  }
}

void count(){
  second--;
  if (second <= 0 || second > 59)
  {
    second = 59;
    minute--;
    if (minute <= 0 || minute > 59)
    {
      minute = 59;
      hour--;
      if (hour <= 0 || hour > 12)
      {
        xIsCountDone =true;
      }
    }
  }
  Serial.println(millis());
  xIsInterruptOcuured = true;
}

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

15: 33: 02.684 -> 119915: 33: 04.371 -> 239615: 33: 06.059 -> 359215: 33: 07.746 -> 478315: 33: 09.434 -> 598615: 33: 11.121 -> 718115: 33: 12.855 -> 837915: 33: 14.543 -> 957815: 33: 16.230 -> 1076815: 33: 17.918 -> 1197415: 33: 19.605 -> 1316815: 33: 21.292 -> 1436515: 33: 22,980 -> 1556215: 33: 24.667 -> 1675115: 33: 26.402 -> 17955

Когда я использую только библиотеку MsTimer2, результаты выглядят примерно так:

15: 37: 21.241 -> 99815: 37: 22.226 -> 199815: 37: 23.257 -> 299815: 37: 24.241 -> 399815: 37: 25.226 -> 499815: 37: 26.257 -> 599815: 37: 27.241 -> 699815: 37: 28.225 -> 799815: 37: 29.257 -> 899815: 37: 30.241 -> 999815: 37: 31.225 -> 1099815: 37: 32.256 -> 1199815: 37: 33.241 -> 1299815: 37: 34.225 -> 1399815: 37: 35.256 -> 14998

Полагаю, это происходит из-за библиотеки TimerOne, но я не смог найти решение. В ledP10.cpp есть метод обратного вызова для timer1, который содержит циклы и может содержать строку кода. Но приоритет прерываний по таймеру1 выше, чем по таймеру2? Но согласно спецификации ATmega328p, вектора нет. для Timer2 меньше, чем Timer1. Разве это не означает, что Timer2 имеет более высокий приоритет? Моя конечная цель - сделать часовой отсчет. Будем благодарны за любую помощь с этой проблемой или любую дополнительную информацию, которую я пропускаю, которая будет полезна или любое другое решение, кроме использования прерывания timer2.

С уважением.

EDIT

Вот код, который я использовал с millis () и дал мне разницу в 12 минут.

uint8_t new_buff[100];
unsigned long startMillis;  //some global variables available anywhere in the program
unsigned long currentMillis;
const unsigned long period = 1000;  //the value is a number of milliseconds
uint8_t minute = 0, second = 0, hour = 1;
char time_buff[100];
void setup() 
{
    myled.init(3,4,8,9,3);
    Serial.begin(9600);
    sprintf((char*)time_buff, "    %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
    //Serial.println((char*)time_buff);
    myled.showmsg_single_static((char*)time_buff, 0);
    startMillis = millis();
}



void loop() {
  currentMillis = millis();  //get the current "time" (actually the number of milliseconds since the program started)
  if (currentMillis - startMillis >= period)  //test whether the period has elapsed
  {
    Serial.println(millis());
    second--;
    startMillis = currentMillis;  //IMPORTANT to save the start time of the current LED state.
    if (second <=0 || second > 59)  {
      second = 59;
      minute--;
      if (minute <=0 || minute > 59)  {
        minute = 59;
        hour--;
        if (hour <= 0 || hour > 12) {
          hour = 0;
        }
      }
    }
    sprintf((char*)time_buff, "    %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
    myled.showmsg_single_static((char*)time_buff, 0);
    startMillis = currentMillis;
  }
}

Ответы [ 2 ]

1 голос
/ 01 октября 2019

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

Также обратите внимание, что вам не нужно сохранять часы, минуты и секунды отдельно, вы можете просто рассчитать их:

unsigned long nextMillis = 1000;
unsigned long targetTime = 1 * 60 * 60 * 1000; // 1 hour in milliseconds

void setup(){
    myled.init(3,4,8,9,3);
    Serial.begin(9600);
    updateMyLed(0);
}


void updateMyLed( unsigned long elapsedTime ){

    char buffer[100];       
    unsigned long timeLeftInSeconds = (targetTime - elapsedTime) / 1000;

    uint8_t hour = timeLeftInSeconds / 3600;
    timeLeftInSeconds -= hour * 3600;
    uint8_t minute = timeLeftInSeconds / 60;
    uint8_t second = timeLeftInSeconds - (minute * 60);

    sprintf((char*)buffer, "    %d%d:%d%d:%d%d", (hour/10), (hour%10), (minute/10), (minute%10), (second/10), (second%10));
    myled.showmsg_single_static((char*)buffer, 0);
}

void loop() {

    if( millis() >= nextMillis ){
        updateMyLed(nextMillis);
        nextMillis += 1000;
    }

}
0 голосов
/ 01 октября 2019

В мире Arduino некоторые библиотеки отключают прерывания.

Это происходит со всеми светодиодами WS2812 и вашими. Без отключения прерываний возникнет проблема синхронизации с внешними устройствами.

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

Вы хотите использовать библиотеку P10? Вы можете, но не использовать прерывания в вашем коде. Не добавляйте другие библиотеки, такие как IR_remote, так как он не будет работать правильно.

Возвращаясь к вашей проблеме, просто обновите свой таймер в цикле. И не ждите, пока закончится секунда, чтобы обновить ваше время на 1 секунду! Это всегда будет больше 1 секунды.

Вы можете, например, преобразовать миллисекунды в секунды seconds = millis() / 1000;. Ваш цикл может быть таким:

void loop() {
  currentSeconds = millis() / 1000;  //get the current "time" (actually the number of milliseconds since the program started)
  if (currentSeconds != savedSeconds)  //test whether the period has elapsed
  {
    savedSeconds = currentSeconds;
    Serial.println(millis());
    second--;
    if (second <=0 || second > 59)  {
      second = 59;
      minute--;
      if (minute <=0 || minute > 59)  {
        minute = 59;
        hour--;
        if (hour <= 0 || hour > 12) {
          hour = 0;
        }
      }
    }
    sprintf((char*)time_buff, "    %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
    myled.showmsg_single_static((char*)time_buff, 0);
  }
}
...