Секундомер + U8glib

qvazio
Offline
Зарегистрирован: 23.11.2017

Привет форум.

Как обычно вопрос из рубрики "я в трёх соснах .. "

Есть програмный секундомер (взятый из примера) и библиотека для отрисовки графики U8glib. В моём случае это ОЛЕД дисплей 128х64 I2C. 

В чем собственно проблема. Когда запускаю секундомер на монитор порта , без графики, всё нормально. Секундомер не врёт и идёт ровно. Но как только подключаю библиотеку и пытаюсь отрисовать это на дисплее появляется отставание и существенное.

Я так понимаю пока draw() отрисует графику это тормозит loop() (ну или непонимаю). Хотелось бы оставить эту графику, красиво рисует. Но секундомер нужен.

Скажите реально ли вообще подружить эту графическую библиотеку с секундомером?

qvazio
Offline
Зарегистрирован: 23.11.2017
 unsigned long time;

#include "U8glib.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);  // инициализируем библиотеку для OLED 128х64 0,96"

volatile bool timer1On;                 //переменная вкл/выкл таймера
volatile unsigned long int timer1;      //переменная подсчета миллисекунд

unsigned long int timer1Loop;           //переменная для хранения значения таймера
bool T1_On;                               //переменная для хранения состояния светодиода

byte Knopka=4;                            // Кнопка активации таймера
byte  Sek =0;                             // значение секунд
byte  Min =0;                             // значение минут
byte  Hour =0;                            // значение часов

bool Timer_On_Off = 0;                    // тригер кнопки таймера, включение и выключение 
bool K1 = 0;                              // Вспомогательная переменная для  тригера нопки включения таймера
bool T1 = 0;                              // Вспомогательная переменная

byte In_Pedal=3;
bool Sost_SS_Sec=0;
byte Sec;                                 // Счет секунд внутри таймер-счетчика
byte Sekund;                              // Переменная для функции byte Format_Secund(byte Sekund)
byte Minute;                              // Переменная для функции byte Format_Minute(byte Minute)

byte Sbros=0;

bool _bounseInputD5S = 0;                 //  Вспомогательная переменная  антидребезг
bool _In_Button_4= 0;                     //  переменная вкючения таймера 1/0 
unsigned long _bounseInputD5P = 0UL;      //  Вспомогательная переменная  антидребезг
bool Tmp;

ISR (TIMER0_COMPA_vect)                    //функция, вызываемая таймером-счетчиком каждые 0,001 сек.
{ 
  if ((Sost_SS_Sec) || (_In_Button_4))      //если нажата педаль (для секундомера) или нажата кнопка включения таймера
  {                                        // более 2 секунд то таймер работает
    timer1++;                              //инкремент переменной таймера (+1)
  }
}

 //++++++++++++++++++++++++++ отдельная функция Секундомера +++++++++++++
byte SS_Sekundomer(bool SS_Sec)
{
     if (SS_Sec == 1)                 // Если педаль нажата
     {
      cli();                              // остановка прерываний для того чтобы считать значение timer1
                                          // остановили - считали - запустили
      timer1Loop = timer1;                // сохранение текущего значения таймера
      sei();                              // разрешение прерываний

        if (timer1Loop >= 1000)           //если таймер отсчитал больше заданного значения, 1сек
    {                  
      Sek  = ++Sek;                       // Увеличиваем значение секунд Sek на 1

    cli();                                // остановка прерываний для того чтобы считать значение timer1
                                          // остановили - считали - запустили
    timer1 = 0;                           //обнулить таймер
    sei();                                //запустить прерывания
    }
   }
  }

 
 void draw(void) {
 
 u8g.drawFrame(1, 1, 127, 63);   // отрисовка прямоугольника по периметру 
                                  // для настройки графики
 u8g.setFont(u8g_font_courB18);     // Установка шрифта u8g_font_cour18  для секундомера

  if  (Hour<=9)
  {
  u8g.setPrintPos(26, 24);          // Начало координат x-верх лево и  y-верх
  u8g.print("0");
  u8g.print(Hour);                  // Вывод  часов
  }
  else
  {
  u8g.setPrintPos(26, 24);     
  u8g.print(Hour);  
  }

  if (Min<=9)
  {
  u8g.setPrintPos(62, 24); 
  u8g.print("0");
  u8g.print(Min); 
  }
  else
  {
  u8g.setPrintPos(62, 24);          // Начало координат x-верх лево и  y-верх
  u8g.print(Min);                   // Вывод  минут
  }

  if (Sek<=9)
  {
  u8g.setPrintPos(98, 24);
  u8g.print("0"); 
  u8g.print(Sek); 
  }
  else 
  {
  u8g.setPrintPos(98, 24);          // Начало координат x-верх лево и  y-верх
  u8g.print(Sek);                   // Вывод  секунд                       
  }
                 

  u8g.drawDisc(58, 12, 1);          // Отрисовываем двоеточие для 
  u8g.drawDisc(58, 19, 1);          // разделения часов и минут

  u8g.drawDisc(94, 12, 1);          // Отрисовываем двоеточие для 
  u8g.drawDisc(94 , 19, 1);         // разделения минут и секунд
}

void setup(void){
Serial.begin(9600);
 // TCCR1B = TCCR1B & 0b11111000 | 0x02;       // устанавливает частоту ШИМ до 3906.25 Гц

  /**** настройка прерывания по таймеру каждую 0,001 сек (вызов функции ISR (TIMER0_COMPA_vect)) ****/
  TCCR0A |= (1 << WGM01);              //сброс при совпадении
  OCR0A = 0xF9;                        //начало отсчета до переполнения (249)
  TIMSK0 |= (1 << OCIE0A);             //разрешить прерывание при совпадении с регистром А
  TCCR0B |= (1 << CS01) | (1 << CS00); //установить делитель частоты на 64 
  sei();                               // разрешение прерываний (взято из примера)  
}

void loop(void) 
{
   //+++++++++++++++++++++++++++++++  Секундомер Start Stop по нажатию педали ++++++++++++++++++++++++++++
  if (Timer_On_Off == 1)                  // Активирован или нет таймер.
  {                                       // Разрешение на запуск секундомера дан
      
    Sost_SS_Sec =  digitalRead(In_Pedal);  // Считываем состояние педали
    
  SS_Sekundomer(Sost_SS_Sec);              // функция секундомера


  }
   //+++++++++++++++++++++++++++++ форматирования времени  секундомера+++++++++++++

    if (Sek == 60)
       {
         Min = ++ Min;
         Sek = 0;
          if (Min == 60)
          {
            Hour = 1;
            Min =0;
            Sek =0;
              if (Hour =12)
              {
                Hour = 0;
                Min =0;
                Sek =0;
              }
          }
       }
// ++++++++++++++++++++++++++++ антидребезг кнопки вкючение таймера ++++++++++

     bool  Tmp_4 =  (digitalRead (Knopka));

if (_bounseInputD5S) 
    {
     if (millis() >= (_bounseInputD5P + 40)) 
         {
          _In_Button_4= Tmp_4; _bounseInputD5S=0;
          }
     }
else
    {
     if (Tmp_4 != _In_Button_4)
         {
          _bounseInputD5S=1; 
          _bounseInputD5P = millis();
        } 
      } 

      //+++++++++++++++++++++++++ Сброс секундомера-таймера после 2 сек нажатой кнопки вклч/выкл таймера++++++
      
      if (_In_Button_4 == HIGH)           // Удержание кнопки более 2 секунд
  {
          cli();                         //остановка прерываний для того чтобы считать значение timer1
                                         // остановили - считали - запустили
      timer1Loop = timer1;               //сохранение текущего значения таймера
      sei();                             //разрешение прерываний

        if (timer1Loop >= 2000)         //если таймер отсчитал больше заданного значения (более 2сек)
    {                  
                Hour = 0;               // Обнуляем значение "Часы"
                Min =0;                 // Обнуляем значение "Минуты"
                Sek =0;                 // Обнуляем значение "Секунды"
             
                
    cli();                              //остановить прерыания (остановили - считали - запустили)
    timer1 = 0;                         //обнулить таймер
    sei();                              //запустить прерывания
  }
  }     
  
   Tmp = _In_Button_4;                  //  переменная  получила чёткое состояние (после антидребезга)
                                        //  от кнопки на выводе 4(активация таймера), записана в Tmp 0/1
                                        
 // ++++++++ тригер кнопки, одно нажатие активируем таймер следуюющте нажатие деактивируем таймер +++++++++            
                                // действие первое                      // Действие второе
  if (Tmp)                      // Если активировали таймер ( Tmp = 1)  // Если деактивировали таймер ( Tmp = 0)
  {                             // (кнопка-тригер)                      //
     if (! T1)                  // если T1=0 (кнопка не нажималась)     // если T1=1 (кнопка нажималась)
     K1 = ! K1;                 // инвертируем переменную K1 (с 0 на 1) // инвертируем переменную K1 (с 1 на 0)
   }                            // 
  T1 = Tmp;                     // в T1 записываем значение Tmp (1)     // в T1 записываем значение Tmp (0)
  Timer_On_Off = K1;            // Таймер активируем Timer_On_Off=1     // Таймер активируем Timer_On_Off=0
   
 
  
  // +++++++++++++++++++++++++++++++  picture loop  ++++++++++++++++++++++++
  Serial.print(Timer_On_Off);
  Serial.print(" ");
  Serial.print(Sost_SS_Sec);
  Serial.print(" ");
  Serial.print(Sek);
  Serial.print(":");
  Serial.print(Min);
  Serial.print(":");
  Serial.println(Hour);
  
 u8g.firstPage();                   
 do {
 draw();
  } 
  while( u8g.nextPage() );
  
}

собственно сам код. там реализовано старт/пауза и сброс после ужержания более 2 сек.(1 кнопка) и активация/деактивация (вторая кнопка)

понимаю что там никоторые вещи можно было так не раздувать и сделать поменьше, но чайники пишут так, только так =)

b707
Offline
Зарегистрирован: 26.05.2017

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

Во-вторых - и это главное - зачем вы ПРИ КАЖДОМ цикле loop() заново отрисовываете всю картинку - рамку. ЧАСЫ!!!! МИНУТЫ! СЕКУНДЫ! - это же бред. Поэтому все безбожно тормозит. Обновляйте на экране только то, что изменилось - раз в секунду всего один символ - последний знак секунд.

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

qvazio пишет:

 реально ли вообще подружить эту графическую библиотеку с секундомером?

С этим секундомером - нет.

b707
Offline
Зарегистрирован: 26.05.2017

ЕвгенийП пишет:

С этим секундомером - нет.

ну почему? - если обновлять экран не чаще раза в секунду - наверно будет работать...

Другой вопрос, что я бы вообще весь код секундомера выкинул, эти десятки строк можно заменить одной-двумя на основе миллис :)

b707
Offline
Зарегистрирован: 26.05.2017

Что-то мне вдруг подумалось - как все-таки полезно, что атмел относительно медленный контроллер :) На каком-нить пентиуме этот код, наверно, успевал был исполнятся и автор бы думал, что написал отличную программу. А на атмеле все подобные писатели получают от жизни мощнейшую плюху в строгом соответсвии со своими навыками оптимизации кода :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

b707 пишет:

ну почему? - если обновлять экран не чаще раза в секунду - наверно будет работать...

Ну, во-первых, не будет - Вы сами на ошибку указали и она там не одна такая.

А во-вторых - служба времени, зависящая от скорости выполнения loop - это уже само по себе ошибка. Сейчас Вы ему рисование сократите, он опрос датчиков вставит или ещё чего-нибудь - так просто не делается.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

очень смешной код. вообще не рабочий, но смешной.

Типа "что-то где-то слышал" про микроконтроллеры.

========================

Я, ну чтобы не голословно хаять, скажу, что миллис() работаетот Таймера0, раз его перенастроили, то миллис() уже не работает.

Этого уже достаточно.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:
миллис() работаетот Таймера0, раз его перенастроили, то миллис() уже не работает.

Не очень внимательно смотрел, но, вроде, должно работать. Делитель там 64 (такой же) и прервание не запретили, а просто своё добавили. Так что, вроде,  молли не обидели, разве что я чего-то не заметил.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

У него в таймере сброс по совпадению. Переполнение не наступит, миллис не обновица

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Тоже верно.

Кстати, посмотрел повнимательнее и сделал вывод, что заявление ТС о том, что без графики секундомер работает правильно - есть "типичный случай так-называемого вранья".

qvazio
Offline
Зарегистрирован: 23.11.2017

b707 пишет:

код совершенно бредовый... 

Согласен, но по другому пока неумею. Мы чайники такие. Пишем бредятину, а потом бегаем плакаться к вам на форум "мол, помогите ... не работает..."  Тем самым поднимаем свой уровень и вашу самооценку =) . И слава богу, что вы есть. И до тех пор пока этот симбиоз существует, нас таких будет еще ооочень много. =)

b707 пишет:

Во-вторых - и это главное - зачем вы ПРИ КАЖДОМ цикле loop() заново отрисовываете всю картинку - рамку. ЧАСЫ!!!! МИНУТЫ! СЕКУНДЫ! - это же бред. Поэтому все безбожно тормозит. Обновляйте на экране только то, что изменилось - раз в секунду всего один символ - последний знак секунд.

Насколько я понял как раболтает эта библиотека, то .. Она отрисовывает всё в контроллере а потом выкидавает это на дисплей двумя половинками экрана.   Как отрисовать отдельно я незнаю. Знаете поскажите.

b707 пишет:

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

Спасибо. Увидел. исправил.

 

qvazio
Offline
Зарегистрирован: 23.11.2017

ЕвгенийП пишет:

А во-вторых - служба времени, зависящая от скорости выполнения loop - это уже само по себе ошибка.

Вот тут полностью согласен. Я это понял до того как сюда писать стал, но как сделать то,  без loop() ?

ЕвгенийП пишет:

Сейчас Вы ему рисование сократите, он опрос датчиков вставит или ещё чего-нибудь - так просто не делается.

ЕССТЕСТВЕННО , это еще не вся программа. И при каждой добавленной строкой loop() растягивается.

 

qvazio
Offline
Зарегистрирован: 23.11.2017

А можно имея модуль часов реального времени (например на DS1307)  реализовать  секундомер ?

vvadim
Offline
Зарегистрирован: 23.05.2012

qvazio пишет:

А можно имея модуль часов реального времени (например на DS1307)  реализовать  секундомер ?

легко, в сети примеров тыщи))))

qvazio
Offline
Зарегистрирован: 23.11.2017

тыщу не надо..  хоть бы один.   а то гугл непоказывает 

vvadim
Offline
Зарегистрирован: 23.05.2012

вы барин или ленитесь или гуглить не умеете.

вот за вас сделал

https://www.google.com.ua/search?q=+DS1307+%D1%87%D0%B0%D1%81%D1%8B+%D0%...

qvazio
Offline
Зарегистрирован: 23.11.2017

Ну спасибо конечно. Просто у меня секундомер должен не просто идти, а останавливаться и потом опять идти. И потом его надо обнулять. Вопрос, я смогу такое реализовать используя библиотеку ....RTC.h ? ( пойду покопаю в эту сторону)

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

qvazio пишет:

Насколько я понял как раболтает эта библиотека, то .. Она отрисовывает всё в контроллере а потом выкидавает это на дисплей двумя половинками экрана.   Как отрисовать отдельно я незнаю. Знаете поскажите.

Ну, то есть это библиотека заставляет Вас вызывать свои методы на каждом проходе loop().

Какая настойчивая библиотека, однако.

b707
Offline
Зарегистрирован: 26.05.2017

qvazio пишет:

Ну спасибо конечно. Просто у меня секундомер должен не просто идти, а останавливаться и потом опять идти. И потом его надо обнулять. Вопрос, я смогу такое реализовать используя библиотеку ....RTC.h ? ( пойду покопаю в эту сторону)

я вам больше скажу  - это можно реализовать и без RTC.h :)

qvazio
Offline
Зарегистрирован: 23.11.2017

b707 пишет:

я вам больше скажу  - это можно реализовать и без RTC.h :)

а поподробнее ?

qvazio
Offline
Зарегистрирован: 23.11.2017

andriano пишет:

Ну, то есть это библиотека заставляет Вас вызывать свои методы на каждом проходе loop()....

Т.е. вызывать процедуру отрисовки только при смене значений секунд?

b707
Offline
Зарегистрирован: 26.05.2017

qvazio пишет:

Т.е. вызывать процедуру отрисовки только при смене значений секунд?

по-моему, вам уже несколько человек посоветовали это очевидное решение

b707
Offline
Зарегистрирован: 26.05.2017

qvazio пишет:

а поподробнее ?

а что "подробнее"? - вот, к примеру. в вашем собственном коде секундомер реализован без библиотеки. Не так важно, что написано громоздко и с ошибками - их можно поправить, важнее то, что практически любую задачу в программировании можно решить 1001 способом