Отставание секунд millis()

bwn
Offline
Зарегистрирован: 25.08.2014

А свои millis-ы проверьте тупым счетчиком за час или за сутки. Согласен с Datak, что скорее всего у вас кривая программа. Такой ошибки быть не может.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

Возможно Вольтметр мешает - вот скетч вольтметра:

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
 
  //result = 1125300L / result; // Back-calculate AVcc in mV - первый запуск для калибровки с этой строкой, следующию комментируем
 
 /* Повышаем точность

Пока большие допуски внутреннего источника питания 1.1 В. значительно ограничивают точность
измерений при использовании в серийном производстве, для индивидуальных проэктов мы можем добиться
большей точности. Сделать это просто, просто измерив Vcc с помощью вольтметра и нашей функции показаний Ардуины.
Далее заменяем константу 1125300L новой переменной:

scale_constant = internal1.1Ref * 1023 * 1000

где

internal1.1Ref = 1.1 * Vcc1 (показания_вольтметра) / Vcc2 (показания_Ардуины)

Это калиброванное значение будет хорошим показателем для измерений AVR чипом,
но может зависеть от изменений температуры. Не стесняйтесь экспериментировать с вашим собственным измерениям.
*/
 
 result = 1.1 * 3.63 / 3.66 * 1023 * 1000 / result; // я откалибровал так 3.66 - пказания ардуно, 3.63 - пказания вольтметра, калиброва лучше на средних значениях

 
  return result;
}

void setup() {
 
  lcd.begin(16, 2);
  lcd.clear();

}

void loop() {   


  unsigned int ADCValue;
  double Voltage;
  double Vcc;

Vcc = readVcc()/1000.0;
ADCValue = analogRead(3);
Voltage = (ADCValue / 1023.0) * Vcc;
//Voltage = Voltage / (16.0 / (52.0 + 16.0)); // R2/(R1+R2) // если больше 5 Вольт ставим делитель

  lcd.setCursor(0, 1);  
  lcd.print(Voltage);
  lcd.print(" V ");
  delay(1000);
}

Надо будеь посмотрть чистый millis как работает

ПС: полный скейтч программы смогу выложить только во вторник

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

Тормозит запись СД - если неё убрать то оотставание 30-35сек в час

 


#include <MsTimer2.h>
#include <SD.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 
const byte chipSelect = 2;
const byte relay = A1; 
float Vbat;
float Vrem;
String record;
float Volt;
float Tame=0;
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  
  //result = 1125300L / result; // Back-calculate AVcc in mV - первый запуск для калибровки с этой строкой, следующию комментируем
  
 
 
 result = 1.1 * 3.63 / 3.66 * 1023 * 1000 / result; // я откалибровал так 3.66 - пказания ардуно, 3.63 - пказания вольтметра, калиброва лучше на средних значениях

  
  return result;
}

void setup()
{ 
  
  MsTimer2::set(1000, test); // 1000ms period
  
  lcd.begin(16, 2);
  lcd.clear();
  
  pinMode(relay, OUTPUT);
  digitalWrite(relay, LOW);
 
  if (!SD.begin(chipSelect)) {
     lcd.setCursor(0, 0);  
     lcd.print("Card failed"); 
    return;
  }
  lcd.print("card initialized.");
}

byte key(){
  int val = analogRead(0);
  if (val < 50) return 5;
  else if (val < 150) return 3;
  else if (val < 350) return 4;
  else if (val < 500) return 2;
  else if (val < 800) return 1;
  else return 0;  
}// --- конец функции кнопок


void file(String data) {
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(data);
    dataFile.close(); 
  }  
  // if the file isn't open, pop up an error:
  else {
     lcd.clear();
     lcd.setCursor(0, 0);  
     lcd.print("error open file");    
  } 
}



void test(){ 
 
  digitalWrite(relay, HIGH);
  
    
  { unsigned int ADCValue;
  double Voltage;
  double Vcc;

Vcc = readVcc()/1000.0;
ADCValue = analogRead(3);
Voltage = (ADCValue / 1023.0) * Vcc;

record=String(Voltage)+"              "+String(Vrem, 0);

//file(record);

//lcd.clear();
  lcd.setCursor(0, 0);  
  lcd.print("  !!! test !!!   "); 
  lcd.setCursor(0, 1);  
  lcd.print(Voltage);
  lcd.print(" V "); 
  lcd.print(Vrem, 0);
  Vrem=Vrem +1;

  
 
   if (Voltage < 3 )  {
   
 digitalWrite(relay, LOW);
 MsTimer2::stop();
 lcd.setCursor(0, 0);  
 lcd.print("   !!!END!!!    ");

 } 
 }}
    

void loop()
{   unsigned int ADCValue;
  double Voltage;
  double Vcc;

Vcc = readVcc()/1000.0;
ADCValue = analogRead(3);
Voltage = (ADCValue / 1023.0) * Vcc;


  lcd.setCursor(0, 1);  
  lcd.print(Voltage);
  lcd.print(" V "); 
  
   
  if (key() == 1){
    MsTimer2::start();             
  }   

 
 delay(200); 
}

 

bwn
Offline
Зарегистрирован: 25.08.2014

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

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

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


#include <LiquidCrystal.h>
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
    
  void setup()
 {
    lcd.begin(16, 2);
    lcd.clear();
 }
 
   void loop()
 {
     lcd.setCursor(0, 0); 
     lcd.print(millis());
 }

За 10мин максимум секунда

в следующий раз поточней измерю

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

Чистый millis - за час отстал на 4 секунды

bwn
Offline
Зарегистрирован: 25.08.2014

Дмитрий@ пишет:

Чистый millis - за час отстал на 4 секунды

Вот это наверно уже реальная ошибка, хотя за сутки было бы точнее.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Дмитрий@ пишет:

Чистый millis - за час отстал на 4 секунды

Вот это наверно уже реальная ошибка, хотя за сутки было бы точнее.

Таймер и millis почему то останавливаются при записи на СД карту - это хорошо видно если сделать запись 10 раз.

Это нормально или millis должен считать пока ардуино пишет на СД?

bwn
Offline
Зарегистрирован: 25.08.2014

Дмитрий@ пишет:

bwn пишет:

Дмитрий@ пишет:

Чистый millis - за час отстал на 4 секунды

Вот это наверно уже реальная ошибка, хотя за сутки было бы точнее.

Таймер и millis почему то останавливаются при записи на СД карту - это хорошо видно если сделать запись 10 раз.

Это нормально или millis должен считать пока ардуино пишет на СД?

Не знаю. Скорее всего такая реализация библиотеки. При нормальных условиях millis() останавливатся не должен. Может другие что присоветуют.(((

fagci
Offline
Зарегистрирован: 12.01.2015

Используйте RTC, независящие от тактов ардуинки.

Все же, время тратится на запись на SD, таймер arduino работает на прерываниях, по идее, несколько тактов может захватить код записи на SD.

https://code.google.com/p/arduino/source/browse/trunk/hardware/cores/arduino/wiring.c?r=565

В Вашем случае возможно будет лучше использовать http://arduino.ru/Reference/Micros

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Дмитрий@ пишет:

bwn пишет:

Дмитрий@ пишет:

Чистый millis - за час отстал на 4 секунды

Вот это наверно уже реальная ошибка, хотя за сутки было бы точнее.

Таймер и millis почему то останавливаются при записи на СД карту - это хорошо видно если сделать запись 10 раз.

Это нормально или millis должен считать пока ардуино пишет на СД?

Не знаю. Скорее всего такая реализация библиотеки. При нормальных условиях millis() останавливатся не должен. Может другие что присоветуют.(((

Скетч пост №53

Удалял всё кроме таймера и СД и выводил millis на экран, тормозит при записи СД - если карту вытащить всё ОК

Буду брать JY-MCU mini_RTCpro DS3231

Вот думаю взять Arduino Compatible Keyes USB Host Shield - как я понял к нему надо брать МЕГУ

 
 

 

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Дмитрий@ пишет:
bwn пишет:

Так и берите секундные интервалы от часов. Погрешность небольшая будет, но думаю это не критично. Главное, чтобы основной цикл у вас не длился больше секунды. Без delay-ев этого достичь сложно.

Спасибо. А меньше секунды не получится сделать? ПС: В нете полно проектов-секундомеров даже с микросекундами - да кому и зачем он с такой погрешностью нужен?

Вы заказали секунду, я по ней ответил в наиболее простой реализации. А так на том модуле должен быть выход SQ (может ошибаюсь)  на нем можете получить такты  от 32768 до 1Гц и использовать их как опорные.

Покрутил DS3231 так и не понял как использовать её в моём проекте - запускать каждую секунду это легко: Alarm.timerRepeat(1, Repeats); , а вот как это остановить?

А примеры есть по использованию опорных тактов?

bwn
Offline
Зарегистрирован: 25.08.2014

Устанавливаете на выходе SQ требуемую частоту, выход на вход прерывания дуины (помним про подтяжку, возможно она уже на шилде разведена). Далее курим: http://arduino.ru/Reference/AttachInterrupt

Помним - в прерывании находится минимально возможное время (в идеале поднять флаг или изменить счетчик), в loop постоянно мониторим переменную изменяемую в прерывании, когда условие стало true - выполняем нужную нам функцию, сбрасываем переменную. Все пошло по новой. Как то так.

noxic
Offline
Зарегистрирован: 15.03.2013

Скетч счетчика времени в 24 часовом формате.

Отсчет происходит в минутах!

unsigned long watch_millis;//значение millis() для текущего времени 
int watch(int watch_t)//расчет текущего времени в минутах
{
 unsigned long time_;
 int watch_delta;
 
 time_ = millis();
  
 watch_delta = int((time_ - watch_millis)/60000); // изменение в минутах
 if (watch_delta>0)
 {
   watch_millis = time_;
   
 }
 if (watch_t + watch_delta > 1439) return 0;
   else return watch_t + watch_delta;
}

Пример использования

int watch_time=0;//текущее время в минутах(00:00 - 23:59)
 
void loop()
{
    watch_time=watch(watch_time); //получаем новое значение времени
}
 
unsigned long watch_millis;//значение millis() для текущего времени 
int watch(int watch_t)//расчет текущего времени в минутах
{
 unsigned long time_;
 int watch_delta;
 
 time_ = millis();
  
 watch_delta = int((time_ - watch_millis)/60000); // изменение в минутах
 if (watch_delta>0)
 {
   watch_millis = time_;
   
 }
 if (watch_t + watch_delta > 1439) return 0;
   else return watch_t + watch_delta;
}

 

 

bwn
Offline
Зарегистрирован: 25.08.2014

noxic, а вы хоть тему читали? ТС по этим граблям уже ходил. Не работает у него millis() с SD-шкой. Да и код в менее извращенном виде можно вставлять, глаза ведь не железные.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Устанавливаете на выходе SQ требуемую частоту, выход на вход прерывания дуины (помним про подтяжку, возможно она уже на шилде разведена). Далее курим: http://arduino.ru/Reference/AttachInterrupt

Помним - в прерывании находится минимально возможное время (в идеале поднять флаг или изменить счетчик), в loop постоянно мониторим переменную изменяемую в прерывании, когда условие стало true - выполняем нужную нам функцию, сбрасываем переменную. Все пошло по новой. Как то так.

 

Подтяжка вроде уже есть - резисторная сборка 4,7 кОм

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

Вот как то так

 

int pin = 13;
volatile int state = LOW;
#include <Wire.h>
#include <LiquidCrystal.h>
float Vrem;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 
void setup()
{ 
          Serial.begin(9600);
	  Wire.beginTransmission(104); // 104 is DS3231 device address
	  Wire.write(0x0E); 
	
	  Wire.write(B00000000); // Start 1Hz
	  Wire.endTransmission();
  lcd.begin(16, 2);
  lcd.clear();
  pinMode(pin, OUTPUT);
  attachInterrupt(0, test, RISING);
}
 
void loop()
{
  digitalWrite(pin, state);
}
 
void test()
{

  state = !state;
  lcd.setCursor(0, 1);
  lcd.print(Vrem, 0);
  Vrem=Vrem +1;
  
  
  
	 
	 
  
}

 

bwn
Offline
Зарегистрирован: 25.08.2014

Кроме 30 и 33 строки больше ничего в прерывании быть не должно.  Зашли, что то изменили, тут же вернулись.

Не хочу даташит искать, а что у вас Vrem делает?

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Кроме 30 и 33 строки больше ничего в прерывании быть не должно.  Зашли, что то изменили, тут же вернулись.

Не хочу даташит искать, а что у вас Vrem делает?

А куда тогда мне засунуть считование показаний вольтметра и запись на СД?

Vrem - это прсто переменная с помощью её я просто вижу через какое время запустилось и сколько раз.

 

ПС: мне 1,2,18,24 и 30 строки не нужны - это просто светодиод моргает. Мне просто нужно чтоб каждую секунду считывался вольтметр и его показания писались на СД и если аккумулятор меньше 3-х вольт - разрывалась нагрузка и программа останавливалась

 

 

 

bwn
Offline
Зарегистрирован: 25.08.2014

Поставили в прерывании вашу state=1; вышли, по этому условию в loop произвели замер и запись, сделали state=0; loop крутится, ждет когда state снова будет 1.

bwn
Offline
Зарегистрирован: 25.08.2014

Ой, чертежи попутал, у вас state диодиком мигает. Короче любую переменную используйте в качестве флага (событие произошло)

bwn
Offline
Зарегистрирован: 25.08.2014

И зачем у вас state int-ом инициирована, у нее всего два состояния, а не 65535. boolean или byte.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Поставили в прерывании вашу state=1; вышли, по этому условию в loop произвели замер и запись, сделали state=0; loop крутится, ждет когда state снова будет 1.

Понял - Спасибо!!!

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

И зачем у вас state int-ом инициирована, у нее всего два состояния, а не 65535. boolean или byte.

Это не ко мне - я просто скопировал: http://arduino.ru/Reference/AttachInterrupt

и тут так же: http://arduino.cc/en/Reference/AttachInterrupt

bwn
Offline
Зарегистрирован: 25.08.2014

Похоже просто бездумный перевод.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

Надеюсь теперь всё правильно:

volatile byte state = LOW;
#include <Wire.h>
#include <LiquidCrystal.h>
double Vrem;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup()
{      
  lcd.begin(16, 2);
  lcd.clear();

  Wire.beginTransmission(104); // 104 is DS3231 device address
  Wire.write(0x0E); 
  Wire.write(B00000000); // Start 1Hz
  Wire.endTransmission();

  attachInterrupt(0, test, RISING); // Задает функцию обработки внешнего прерывания.
}

void loop()
{
  if (state==1) 
  {
    lcd.setCursor(7, 1);
    lcd.print(Vrem, 0);

    Vrem=Vrem +1;

    state = 0 ; 
  }

  if (Vrem > 25) 
  {
    detachInterrupt(0); // Выключает обработку внешнего прерывания.
   
    lcd.setCursor(6, 0);
    lcd.print("END");
    
  }
  
}


void test()
{

  state = !state;

}

 

bwn
Offline
Зарегистрирован: 25.08.2014

Если работает, то вроде да. Из мелких придирок, зачем Vrem объявлена float? Она по вашему коду может когда либо принять значение с десятичным знаком или превысить 255? Старайтесь использовать только типы, необходимые для выполнения программы, кроме экономии памяти это спасет вас от граблей. Попробуйте конструкцию if (Vrem == 25), большие шансы, что она не сработает.

Joiner
Offline
Зарегистрирован: 04.09.2014

Datak пишет:

Кварц, даже самый гнилой и самый китайский, не может ошибаться на "примерно 10 сенунд в минуту" или "в минуту почти на 15 секунд".
Я бы, всё-таки, постарался выяснить в чём дело. Там или действительно кварц не на ту частоту, или частота неправильно указана в настройках проекта, или этот кварц вообще отключен какими-нибудь фьюзами.

Ну, или программа написана всё же неправильно - бывает и такое, в конце концов.

У меня был случай с Про Мини. Скетчи на ней выполнялись в десять раз медленнее чем на других ардуинках. Так как в каких-то там фьюзах не понимаю, я просто по совету (извините, не помню кого) с этого форума, просто перезаписал загрузчик. Проблема исчезла. Ардуинка стала работать нормально.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Если работает, то вроде да. Из мелких придирок, зачем Vrem объявлена float? Она по вашему коду может когда либо принять значение с десятичным знаком или превысить 255? Старайтесь использовать только типы, необходимые для выполнения программы, кроме экономии памяти это спасет вас от граблей. Попробуйте конструкцию if (Vrem == 25), большие шансы, что она не сработает.

1 В данном случае Вы правильно подметили, в полной версии моей программы Vrem считает секунды-циклы и это значение пишется на СД карту и его значение будет 0 - 3000.
2 программа будет останавливается если батарея разредилась меньше 3-Вольт и возможно ==3 проскочит и не сработает.

bwn
Offline
Зарегистрирован: 25.08.2014

Вы меня не совсем поняли по float, c напряжением на батарее если не принимать никаких мер, придется мирится с вещественным числом. А там где вы оперируете целыми числами при конструкции типа float a=25*4/5 сравнение if(a==20) из за особенности представления вещественного числа в памяти может оказатся ложным. Это надо учитывать

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Вы меня не совсем поняли по float, c напряжением на батарее если не принимать никаких мер, придется мирится с вещественным числом. А там где вы оперируете целыми числами при конструкции типа float a=25*4/5 сравнение if(a==20) из за особенности представления вещественного числа в памяти может оказатся ложным. Это надо учитывать

И как это можно исправить?

bwn
Offline
Зарегистрирован: 25.08.2014

Так исправлять не надо. Просто помнить, что == на float  часто ведет себя не как планировалось. Соответственно >= или <=.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Так исправлять не надо. Просто помнить, что == на float  часто ведет себя не как планировалось. Соответственно >= или <=.

А так почему нельзя разделить?

int x;
int y;
float z;

x = 1;
y = x / 2;            // y теперь равен 0, тип int не может хранить дробные числа
z = (float)x / 2.0;   // z равна .5 (следует использовать 2.0, а не 2)
bwn
Offline
Зарегистрирован: 25.08.2014

Можно , но нет гарантии, что какой-нибудь 10-й разряд после запятой будет равен 0. А 0,5 не равно 0,500000000001

Хотя так, может и получится. Лень проверять.

bwn
Offline
Зарегистрирован: 25.08.2014

Действительно, в таком варианте получается.

Joiner
Offline
Зарегистрирован: 04.09.2014

Не знаю правильно ли я понял проблему. Но мне кажется что if(a==20) нужно заменить на if(a<20). При первом же ближайшем значении все сработает как надо.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Действительно, в таком варианте получается.

А как Вы проверяете?

У меня выходит так (x = 1):

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

Вот так что то вышло (x = 1) :

bwn
Offline
Зарегистрирован: 25.08.2014

Дмитрий@ пишет:

Вот так что то вышло (x = 1) :

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

nevkon
Offline
Зарегистрирован: 20.01.2015

По каким причинам может сильно отставать время на модуле с DS3231 при питании от 3В батарейки-таблетки?

bwn
Offline
Зарегистрирован: 25.08.2014

nevkon пишет:

По каким причинам может сильно отставать время на модуле с DS3231 при питании от 3В батарейки-таблетки?

Недели две назад у человека отставало из-за дохлой батарейки. И у вас батарейка или аккумулятор на 3,6 должен стоять?

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

А чем KEYES DS3234 RTC Real Time Clock Module for Arduino лучше?

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

Кривой андроид и удалить нельзя.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

А чем KEYES DS3234 RTC Real Time Clock Module for Arduino лучше?

bwn
Offline
Зарегистрирован: 25.08.2014

Дмитрий@ пишет:
А чем KEYES DS3234 RTC Real Time Clock Module for Arduino лучше?

Лучше чего?

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn пишет:

Дмитрий@ пишет:
А чем KEYES DS3234 RTC Real Time Clock Module for Arduino лучше?

Лучше чего?


3231

bwn
Offline
Зарегистрирован: 25.08.2014

Судя по даташиту ничем, шина только другая и ног больше.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

Что то у меня сомнение по поводу подтяжки - на модуле стоит подтяжка к 3,3В  т.е. после регулятора напряжения тока SOT-23 10pcs XC6206P332MR (662K) 3.3V/0.5A

К 5 Вольтам нужно подтягивать?

 

 

bwn
Offline
Зарегистрирован: 25.08.2014

Не нужно, все на плате есть. 3,3 вольта - уровень уверенно определяемый как HIGH.

Дмитрий@
Offline
Зарегистрирован: 04.01.2015

bwn не подскажете какие диоды надо использовать в этой схеме http://arduino.ru/forum/apparatnye-voprosy/initializing-sd-cardinitializ...