RTC DS1307 прерывание через SQW и считывание времени

gorskikh.serg
Offline
Зарегистрирован: 23.05.2018

Здравствуйте, бьюсь уже N-ные сутки. Собрал в Proteus небольшую схему: Arduino Uno + часы DS1307
Задача проста до безобразия. Вывод SQW часов настроен на 1Hz и генерирует прерывание int0 на Arduino. Соответственно, при прерывании считываю с часов время. Почему просто не читаю в loop? потому что в loop будут другие задачи, с delay(), и имеет место считывать время через прерывание. С часами работаю с помощью библиотеки RTCLib. Собственно, проблема в том, что работа зависает при считывании времени с часов с помощью функции rtc.now()... Прерывание работает исправно.  Код пока сыроватый, надо рефакторить, все, что не связно с часами закомментил.

#include <Key.h>
#include <Keypad.h>
#include <RTClib.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

#define LIGHT_MODE_ON  1
#define LIGHT_MODE_OFF  0

RTC_DS1307 rtc;
LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
DateTime dateTime;
volatile unsigned int timerSec = 0;
const byte ROWS = 4;
const byte COLS = 3;
bool isRealTime = true;
bool isMenu = false;
int indexMenu = 1;
char Keys[ROWS][COLS] = {
  {'3','2','1'},
  {'6','5','4'},
  {'9','8','7'},
  {'#','0','*'}
};

byte rowPins[ROWS] = { 11, 10, 9, 8 };
byte colPins[COLS] = { 16, 15, 14 };

Keypad keypad = Keypad(makeKeymap(Keys), rowPins, colPins, ROWS, COLS);


int led_red = 7;
int led_yellow = 6;
int led_green = 5;
int led_green_right = 4;


/*ISR (TIMER0_COMPA_vect) {
  timerSec++;
  if(timerSec == 5000) {
    timerSec = 0;
    // то, что нам надо делать каждую секунду
    //Serial.println('2');
    //showLCDbyTimer();
  }
}*/

void setup()
{
  Serial.begin(9600);   
  //init interrupt int0
  //init LEDS
  pinMode(led_red, OUTPUT);
  pinMode(led_yellow, OUTPUT);
  pinMode(led_green, OUTPUT);
  pinMode(led_green_right, OUTPUT);
  //init LCD
  lcd.init();                       
  lcd.init();
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(3,0);
  lcd.print("Hello, Serg!");
  lcd.setCursor(2,1);
  lcd.print("Arduino Svetofor!");
  lcd.setCursor(0,2);
  lcd.print("Powered by Serg");

  //init Real Time Clock
  rtc.begin();
  if (! rtc.isrunning()) {
     Serial.println("RTC is NOT running!");
     // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(__DATE__, __TIME__));
  }
  rtc.writeSqwPinMode(SquareWave1HZ);
  
  delay(1000);
  lcd.clear();

  //init interrupt RTC
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(0, showLCDbyTimer, FALLING);
  Serial.println("start");
  
    //init timer
/*  TCCR0A |= (1 << WGM01);
  OCR0A = 0xF9;
  TIMSK0 |= (1 << OCIE0A);
  TCCR0B |= (1 << CS01) | (1 << CS00);
  sei();
  dateTime = rtc.now();*/
}

void doSvetofor(int lightMode) {
  if(lightMode == LIGHT_MODE_ON) {
    digitalWrite(led_yellow, LOW);
    digitalWrite(led_green, LOW);
    digitalWrite(led_green_right, LOW);
    
    digitalWrite(led_red, HIGH);
    delay(1000);
    digitalWrite(led_yellow, HIGH);
    delay(1000);
    digitalWrite(led_red, LOW);
    delay(1500);
    digitalWrite(led_yellow, LOW);
    digitalWrite(led_green, HIGH);
    delay(9000);
    digitalWrite(led_green, LOW);
    delay(500);
    digitalWrite(led_green, HIGH);
    delay(500);
    digitalWrite(led_green, LOW);
    delay(500);
    digitalWrite(led_green, HIGH);
    digitalWrite(led_yellow, HIGH);
    delay(500);
    digitalWrite(led_green, LOW);
    delay(500);
    digitalWrite(led_green, HIGH);
    delay(500);
    digitalWrite(led_green, LOW);
    digitalWrite(led_yellow, LOW);
    digitalWrite(led_red, HIGH);
    delay(1000);
    digitalWrite(led_green_right, HIGH);   
    delay(10000);
    digitalWrite(led_green_right, LOW);
    delay(500);
    digitalWrite(led_green_right, HIGH);
    delay(500);
    digitalWrite(led_green_right, LOW);
    delay(500);
    digitalWrite(led_green_right, HIGH);
    delay(500);
    digitalWrite(led_green_right, LOW);
    
  } else {
    digitalWrite(led_red, HIGH);
    digitalWrite(led_yellow, HIGH);
    digitalWrite(led_green, HIGH);
    digitalWrite(led_green_right, HIGH);
  }
  
}
void incIndexMenu() {
  if(indexMenu == 3) {
    indexMenu = 1;
  } else {
    indexMenu++;
  }
}
void showDateTime() {
  Serial.println("rtc.now");
    DateTime now = rtc.now();
    Serial.println(now.second());
   /* if(now.hour()>11)
    {
     lcd.setCursor(14, 1);
    lcd.print("PM"); 
  }
   else
  {
    lcd.setCursor(14, 1);
    lcd.print("AM"); 
  }
  
    lcd.setCursor(0, 0);
    lcd.print("Date-");
    lcd.print(now.day(), DEC);
    lcd.print('/');
    lcd.print(now.month(), DEC);
    lcd.print('/');
    lcd.print(now.year(), DEC);
    lcd.print(' ');
    lcd.setCursor(0, 1);
     if (now.hour()<10)
     
    lcd.print('0');
    lcd.print("Time-");
    if(now.hour()>12)
    {
    lcd.print(now.hour()-12, DEC);
    }
    else
    {
     lcd.print(now.hour(), DEC);
    } 
    lcd.print(':');
     if (now.minute()<10)
    lcd.print('0');
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    if (now.second()<10)
    lcd.print('0');
    lcd.print(now.second(), DEC);
    lcd.setCursor(0, 3);
   int dayofweek = now.dayOfTheWeek();
   switch(dayofweek){
     case 1:
     lcd.print("Monday");
     break;
     case 2:
     lcd.print("Tuesday");
     break;
     case 3:
     lcd.print("Wednesday");
     break;
     case 4:
     lcd.print("Thursday");
     break;
     case 5:
     lcd.print("Friday");
     break;
     case 6:
     lcd.print("Saturday");
     break;
     case 0:
     lcd.print("Sunday");
     break;
    delay(1000);
    }*/
}

void showMenu() {
  doSvetofor(LIGHT_MODE_OFF); //светофор в режим конфигурирования
  switch(indexMenu) {
    case 1:
       lcd.setCursor(0,0);
       lcd.print("-->");
       lcd.print(" 06:00 - 12:00");
       lcd.setCursor(0,1);
       lcd.print("    12:00 - 18:00");  
       lcd.setCursor(0,2);
       lcd.print("    18:00 - 00:00");
      break;
    case 2:   
       lcd.setCursor(0,0);
       lcd.print("    06:00 - 12:00");
       lcd.setCursor(0,1);
       lcd.print("--> 12:00 - 18:00");  
       lcd.setCursor(0,2);
       lcd.print("    18:00 - 00:00");
      break;
    case 3:
       lcd.setCursor(0,0);
       lcd.print("    06:00 - 12:00");
       lcd.setCursor(0,1);
       lcd.print("    12:00 - 18:00");  
       lcd.setCursor(0,2);
       lcd.print("--> 18:00 - 00:00");
      break;  
    default:
        break;
  }
}

void showLCDbyTimer() {
//  char key = keypad.getKey();
  
 /* if(key) {
    /*Serial.println(key);
    if(isMenu) {                                            
      switch(key) {
        case '#':
          showDateTime();
          isMenu = false;
          isRealTime = true; 
          lcd.clear();
          break;
        case '0':
          incIndexMenu();
          showMenu();
          break;
        default:
          Serial.print("keeek - ne prokatilo");
          break;
      }
    } else if(isRealTime) {                               
      Serial.println("knock - isRunTime");
      switch(key) {
        case '*' : // листаем меню
          isMenu = true;
          isRealTime = false;
          lcd.clear();
          showMenu();
          break;
        default:  //выводим также рантайм
          Serial.println("keeek - ne prokatilo");
          showDateTime();
          break; 
      }
    }  */ 
 /* } else {                                                
    Serial.println("else");
    if(isMenu) {
      Serial.println("Show menu");
     // showMenu();
    } else if(isRealTime) {
    //  showDateTime();
    } else {
     Serial.println("kwa"); 
    }
  }*/
  showDateTime();
}

void loop()
{
      //dateTime = rtc.now();
      //doSvetofor(LIGHT_MODE_ON); 
}

 

 

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

при входе в прерывание, прерывания автоматически запрещаются.  Serial унутре прерывания использовать нельзя. 

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

gorskikh.serg пишет:
Почему просто не читаю в loop?
Потому, что в loop читать неправильно. Вы же поступаете правильно - читаете именно тогда, когда секунда сменилась в часах, а не когда loop сподобился прочитать.

Попробуйте поставить команду sei(); между строками №№ 155 и 156.

gorskikh.serg
Offline
Зарегистрирован: 23.05.2018

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

gorskikh.serg пишет:
Почему просто не читаю в loop?
Потому, что в loop читать неправильно. Вы же поступаете правильно - читаете именно тогда, когда секунда сменилась в часах, а не когда loop сподобился прочитать.

Попробуйте поставить команду sei(); между строками №№ 155 и 156.

Спасибо большое! Добавив sei(), все заработало! Но как это объяснить? Библиотека для работы с часами испульзует прерывания?

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

Serial использует

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

gorskikh.serg пишет:
Библиотека для работы с часами испульзует прерывания?
Не просто использует, она ждёт прерывания от своего интерфеса I2C. И пока не дождётся - хрен чего заработает.

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

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

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

DetSimen пишет:

Serial унутре прерывания использовать нельзя. 

С "нельзя" я согласен, но не так категорично. Нельзя не потому, что работать не будет, а чисто концептуально, потому, что это плохая идея. Технически же, всё работает без проблем. Попробуйте вот такой скетч:

void setup() {
	Serial.begin(57600); 
	//
	// Таймер 1 дергается раз в секунду
	//
	TCCR1A = 0; // CTC, пины отключены
	TCCR1B = bit(WGM12) | bit(CS12) | bit(CS10); // CTC + делитель 1024
	OCR1A = 15625; // 15625*1024/16 = 1000000 микросекунд
	TIMSK1 = bit(OCIE1A);
	//
}

void loop() {}

ISR(TIMER1_COMPA_vect) {
	static int counter = 0;
	Serial.println(++counter);
}

Вполне себе считает секунды и в сериал выводит.

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

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

С "нельзя" я согласен, но не так категорично. Нельзя не потому, что работать не будет, а чисто концептуально, потому, что это плохая идея. 

Дак и я с Вами согласен.  Просто, на той ступеньке обучения, куда только-только вползает ТС, Serial в прерывании использовать нельзя.  Пока нельзя.  :)  

А вообще, мне иногда просто писать лень.  Я уже зарекся писать здесь какие-либо длиннотексты. :)