Часы на городской ратуше

technomag
Offline
Зарегистрирован: 03.07.2016

Здравствуйте! Делаю проект запуска часов на городской ратуше, использовал плату Arduino Mini и шаговый двигатель NEMA-17 с драйвером Easy driver 4.4
Вот скетч для работы движка:

#include <Stepper.h>

#define STEPS 200 // 1 шаг = 1 градус

Stepper stepper(STEPS, 3, 4); // пины на ардуино

void setup()
{
   stepper.setSpeed(500); // скорость движения вала
}

void loop()
{
   stepper.step(3200); // выполняет пол оборота вала согласно расчетам редуктора стрелок часов (секундной стрелки нет)
   delay(58000);
   
   }

Все было хорошо, часы шли более менее точно, но с приходом жары (а они стоят под металлической черепицей) температура внутри помещения достигает около 45 градусов и часы то дико начали спешить, то идут нормально, как вариант думаю добавить модуль реального времени, но как заставить двигатель каждую минуту согласно данных модуля часов делать пол оборота? Может есть какие-то другие варианты?

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

technomag пишет:

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

Странный вопрос. А как Вы его сейчас заставляете? Вы вызываете функцию  stepper.step(3200);

Ну так и там делайте.

RTC (например DS3231) имеют специальное прерывание, которое Вы можете запрограммировать так, чтобы оно выдавалось точно при переходе минуты. Т.е. точно при переходе на новую минуту у Вас будет проскакивать HIGH на пине SQW RTC. Вот, в ответ на это прерывание и вызывайте  stepper.step(3200); и всё будет нормально.

Radjah
Offline
Зарегистрирован: 06.08.2014

2 ЕвгенийП: А там разве не 1 Гц минимальная частота?

technomag
Offline
Зарегистрирован: 03.07.2016

Все дело в том, что интервал между движением вала не всегда ровно минута как оказалось, это стало зависеть от температуры, и соответственно точность хода минутной стрелки нарушилась.

Mr.Privet
Mr.Privet аватар
Offline
Зарегистрирован: 17.11.2015

Как вариант можно следить за минутной стрелкой, на пример поставить датчики или кнопочки, на которые будет минутная стрелка нажимать на пример на каждом часе (смотря какая точность требуется) если стрелка прибежала (нажала кнопку раньше) то ждет когда нужно бежать дальше, если позже нажала то вперед, в догоняй время. А вообще маханические часы на ардуино... ну не знаю... у меня была идея сделать 2 циферблата ( часы и минуты отдельно), раскидать хаотично цифры на них и  двумя сервами выставлять точное время каждые скажем 5 минут. Может реализую, наверное будет забавно выглядить...

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

Radjah пишет:

2 ЕвгенийП: А там разве не 1 Гц минимальная частота?

Я не понимаю, о какой частоте речь, но DS3231 можно настроить, чтобы они выдавали прерывание при переходе на каждую новую минуту. Посмотрите даташит, там это есть.

ALARM 2 REGISTER MASK BITS (BIT 7)

DY/DT A2M4 A2M3 A2M2
X 1 1 1 Alarm once per minute (00 seconds of every minute)
X 1 1 0 Alarm when minutes match
X 1 0 0 Alarm when hours and minutes match
0 0 0 0 Alarm when date, hours, and minutes match
1 0 0 0 Alarm when day, hours, and minutes match
 
Если правильно настроить регистры, то в момент прерывания она будет ставить в LOW пин SQW, ну а дальше понятно, приаттачиваешь прерывание на FALLING и по этому прерыванию двигаешь стрелку.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

technomag пишет:

Все дело в том, что интервал между движением вала не всегда ровно минута как оказалось, это стало зависеть от температуры, и соответственно точность хода минутной стрелки нарушилась.

Так а я Вам про что?

Ставьте DS3231, настраивайте, чтобы она выдавал Вам прерывание точно при переходе минуты и именно в этот момент, когда RTC скажет, двигайте стрелку.

gena
Offline
Зарегистрирован: 04.11.2012

  Как один из вариантов - можно поискать кварцевый генератор на 16 МГц.  На них в тех. описании указывается термостальность. Заменить кварцевый резонатор генератором.

a5021
Offline
Зарегистрирован: 07.07.2013

Это может решить вопрос только ценой дополнительных ухищрений и расходов. Генератор должен быть термокомпнсированным, что увеличивает стоимость в разы. DS3231 теромкоменсирована от рождения и стоит меньше доллара. Точннее и сложнее в разы решение можно придумать, но просто и дешево -- это только DS3231. Причем, дешево тут не означает плохо. Точность у DS3231 очень приличная.

technomag
Offline
Зарегистрирован: 03.07.2016

Мне с ардуино пришлось познакомиться по причине разработки проекта, но я не очень силен в программировании на этом языке, как максимум удалось написать данный скетч, есть у меня этот модуль, но как его прикрутить программно не знаю, с електроникой знаком очень хорошо, телефоны, планшеты, GPS навигаторы прошиваю и ремонтирую легко, но тут совсем другое, все на что меня хватило, так это написать елементарный код, который вы видите в первом топике. Поэтому очень прошу помощи. DS3231 купил, подключил, но дальше этого дело не дошло. Читал разные статьи и многие форумы, но собрать инфу во что-то логическое так и не получилось.

russo
Offline
Зарегистрирован: 20.11.2014

Часы на городской ратуше вешь можно сказать серьезная,

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

Вообще к подобным часам GPS модуль прикручивают и синхронизируються часы по спутникам. Они уж точно дают точное время.

technomag
Offline
Зарегистрирован: 03.07.2016

есть плата Arduino Leonardo и шаговый двигатель NEMA-17 с драйвером Easy driver 4.4 ну и часовой модуль, все уже установлено и прикручено, осталось код написать

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

technomag пишет:

Поэтому очень прошу помощи. DS3231 купил, подключил, но дальше этого дело не дошло. 

Ну, попробуйте так. Есть несколько библиотек для DS3231. Я в своё время скачал вот эту и чуть-чуть её модифицировал. Привожу свой, модифицированный код.

DS3231.h

/*
 * DS3231.h
 *
 * Arduino Library for the DS3231 Real-Time Clock chip
 *
 * (c) Eric Ayars
 * 4/1/11
 * Updated to Arduino 1.0 By john Hubert
 * Feb 7 2012
 * released into the public domain. If you use this, please let me know
 * (just out of pure curiosity!) by sending me an email:
 * eric@ayars.org
 *
 */

#ifndef DS3231_h
#define DS3231_h

#include <Arduino.h>
#include <Wire.h>

class DS3231 {
	public:
			
		//Constructor
		DS3231();

		// Time-retrieval functions
	
		// the get*() functions retrieve current values of the registers.
		// If you only need one element, use that one for simplicity; but 
		// if you need the whole passel then use getTime() to avoid
		// the chance of rollover between reads of the different components.
		void getTime(byte& year, byte& month, byte& date, byte& DoW, byte& hour, byte& minute, byte& second); 
		byte getSecond(); 
		byte getMinute(); 
		byte getHour(void);
		byte getHour(bool& h12, bool& PM); 
			// In addition to returning the hour register, this function
			// returns the values of the 12/24-hour flag and the AM/PM flag.
		byte getDoW(); 
		byte getDate(); 
		byte getMonth(bool& Century); 
			// Also sets the flag indicating century roll-over.
		byte getYear(); 
			// Last 2 digits only

		// Time-setting functions
		// Note that none of these check for sensibility: You can set the
		// date to July 42nd and strange things will probably result.
		
		void setSecond(byte Second); 
			// In addition to setting the seconds, this clears the 
			// "Oscillator Stop Flag".
		void setMinute(byte Minute); 
			// Sets the minute
		void setHour(byte Hour); 
			// Sets the hour
		void setDoW(byte DoW); 
			// Sets the Day of the Week (1-7);
		void setDate(byte Date); 
			// Sets the Date of the Month
		void setMonth(byte Month); 
			// Sets the Month of the year
		void setYear(byte Year); 
			// Last two digits of the year
		void setClockMode(bool h12); 
			// Set 12/24h mode. True is 12-h, false is 24-hour.

		// Temperature function

		float getTemperature(); 

		// Alarm functions
		
		void getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM); 
/* Retrieves everything you could want to know about alarm
 * one. 
 * A1Dy true makes the alarm go on A1Day = Day of Week,
 * A1Dy false makes the alarm go on A1Day = Date of month.
 *
 * byte AlarmBits sets the behavior of the alarms:
 *	Dy	A1M4	A1M3	A1M2	A1M1	Rate
 *	X	1		1		1		1		Once per second
 *	X	1		1		1		0		Alarm when seconds match
 *	X	1		1		0		0		Alarm when min, sec match
 *	X	1		0		0		0		Alarm when hour, min, sec match
 *	0	0		0		0		0		Alarm when date, h, m, s match
 *	1	0		0		0		0		Alarm when DoW, h, m, s match
 *
 *	Dy	A2M4	A2M3	A2M2	Rate
 *	X	1		1		1		Once per minute (at seconds = 00)
 *	X	1		1		0		Alarm when minutes match
 *	X	1		0		0		Alarm when hours and minutes match
 *	0	0		0		0		Alarm when date, hour, min match
 *	1	0		0		0		Alarm when DoW, hour, min match
 */
		void getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM); 
			// Same as getA1Time();, but A2 only goes on seconds == 00.
		void setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM); 
			// Set the details for Alarm 1
		void setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM); 
			// Set the details for Alarm 2
		void turnOnAlarm(byte Alarm); 
			// Enables alarm 1 or 2 and the external interrupt pin.
			// If Alarm != 1, it assumes Alarm == 2.
		void turnOffAlarm(byte Alarm); 
			// Disables alarm 1 or 2 (default is 2 if Alarm != 1);
			// and leaves the interrupt pin alone.
		bool checkAlarmEnabled(byte Alarm); 
			// Returns T/F to indicate whether the requested alarm is
			// enabled. Defaults to 2 if Alarm != 1.
		bool checkIfAlarm(byte Alarm); 
			// Checks whether the indicated alarm (1 or 2, 2 default);
			// has been activated.

		// Oscillator functions

		void enableOscillator(bool TF, bool battery, byte frequency); 
			// turns oscillator on or off. True is on, false is off.
			// if battery is true, turns on even for battery-only operation,
			// otherwise turns off if Vcc is off.
			// frequency must be 0, 1, 2, or 3.
			// 0 = 1 Hz
			// 1 = 1.024 kHz
			// 2 = 4.096 kHz
			// 3 = 8.192 kHz (Default if frequency byte is out of range);
		void enable32kHz(bool TF); 
			// Turns the 32kHz output pin on (true); or off (false).
		bool oscillatorCheck();;
			// Checks the status of the OSF (Oscillator Stop Flag);.
			// If this returns false, then the clock is probably not
			// giving you the correct time.
			// The OSF is cleared by function setSecond();.

		void EnableMinuteInterrupt(void);

		void clearAlarmSignal(const byte alarm);

		//void EnableSecondInterrupt(void) {
		//	setA1Time(1, 1, 1, 0, 14, 0, 0, 0); 
		//	turnOnAlarm(1);
		//	byte cb;
		//	cb = readControlByte(1);
		//	cb &= (~1);
		//	writeControlByte(cb, 1);
		//	cb = readControlByte(0);
		//	writeControlByte(cb |= 4, 0);
		//}

	//private:

		byte decToBcd(byte val); 
			// Convert normal decimal numbers to binary coded decimal
		byte bcdToDec(byte val); 
			// Convert binary coded decimal to normal decimal numbers
		byte readControlByte(bool which); 
			// Read selected control byte: (0); reads 0x0e, (1) reads 0x0f
		void writeControlByte(byte control, bool which); 
			// Write the selected control byte. 
			// which == false -> 0x0e, true->0x0f.

};

#endif

DS3231.cpp

/*
DS3231.cpp: DS3231 Real-Time Clock library
original code by
Eric Ayars
4/1/11

updated to Arduino 1.0 
John Hubert
Feb 7, 2012

Released into the public domain.
*/

#include <DS3231.h>

#define CLOCK_ADDRESS 0x68

// Constructor
DS3231::DS3231() {
	// nothing to do for this constructor.
}

/***************************************** 
	Public Functions
 *****************************************/

void DS3231::getTime(byte& year, byte& month, byte& date, byte& DoW, byte& hour, byte& minute, byte& second) {
	byte tempBuffer;
//	bool PM;
//	bool h12;

	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x00));
	Wire.endTransmission();
	
	Wire.requestFrom(CLOCK_ADDRESS, 7);

	second = bcdToDec(Wire.read());
	minute = bcdToDec(Wire.read());
	tempBuffer = bcdToDec(Wire.read());
//	h12 = tempBuffer & 0b01000000;
//	if (h12) {
//		PM = tempBuffer & 0b00100000;
//		hour = bcdToDec(tempBuffer & 0b00011111);
//	} else {
		hour = bcdToDec(tempBuffer & 0b00111111);
//	}
	DoW = bcdToDec(Wire.read());
	date = bcdToDec(Wire.read());
	month = bcdToDec(Wire.read() & 0b01111111);
	year = bcdToDec(Wire.read());
}

byte DS3231::getSecond() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x00));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte DS3231::getMinute() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x01);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte DS3231::getHour(bool& h12, bool& PM) {
	byte temp_buffer;
	byte hour;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x02);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();
	h12 = temp_buffer & 0b01000000;
	if (h12) {
		PM = temp_buffer & 0b00100000;
		hour = bcdToDec(temp_buffer & 0b00011111);
	} else {
		hour = bcdToDec(temp_buffer & 0b00111111);
	}
	return hour;
}

byte DS3231::getHour(void) {
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x02);
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();
	return bcdToDec(temp_buffer & 0b00111111);
}

byte DS3231::getDoW() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x03));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte DS3231::getDate() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x04));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte DS3231::getMonth(bool& Century) {
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x05));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();
	Century = temp_buffer & 0b10000000;
	return (bcdToDec(temp_buffer & 0b01111111)) ;
}

byte DS3231::getYear() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x06));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

void DS3231::setSecond(byte Second) {
	// Sets the seconds 
	// This function also resets the Oscillator Stop Flag, which is set
	// whenever power is interrupted.
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x00));
	Wire.write(decToBcd(Second));	
	Wire.endTransmission();
	// Clear OSF flag
	byte temp_buffer = readControlByte(1);
	writeControlByte((temp_buffer & 0b01111111), 1);
}

void DS3231::setMinute(byte Minute) {
	// Sets the minutes 
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x01));
	Wire.write(decToBcd(Minute));	
	Wire.endTransmission();
}

void DS3231::setHour(byte Hour) {
	// Sets the hour, without changing 12/24h mode.
	// The hour must be in 24h format.

	bool h12;

	// Start by figuring out what the 12/24 mode is
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x02));
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	h12 = (Wire.read() & 0b01000000);
	// if h12 is true, it's 12h mode; false is 24h.

	if (h12) {
		// 12 hour
		if (Hour > 12) {
			Hour = decToBcd(Hour-12) | 0b01100000;
		} else {
			Hour = decToBcd(Hour) & 0b11011111;
		}
	} else {
		// 24 hour
		Hour = decToBcd(Hour) & 0b10111111;
	}

	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x02));
	Wire.write(Hour);
	Wire.endTransmission();
}

void DS3231::setDoW(byte DoW) {
	// Sets the Day of Week
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x03));
	Wire.write(decToBcd(DoW));	
	Wire.endTransmission();
}

void DS3231::setDate(byte Date) {
	// Sets the Date
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x04));
	Wire.write(decToBcd(Date));	
	Wire.endTransmission();
}

void DS3231::setMonth(byte Month) {
	// Sets the month
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x05));
	Wire.write(decToBcd(Month));	
	Wire.endTransmission();
}

void DS3231::setYear(byte Year) {
	// Sets the year
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x06));
	Wire.write(decToBcd(Year));	
	Wire.endTransmission();
}

void DS3231::setClockMode(bool h12) {
	// sets the mode to 12-hour (true) or 24-hour (false).
	// One thing that bothers me about how I've written this is that
	// if the read and right happen at the right hourly millisecnd,
	// the clock will be set back an hour. Not sure how to do it better, 
	// though, and as long as one doesn't set the mode frequently it's
	// a very minimal risk. 
	// It's zero risk if you call this BEFORE setting the hour, since
	// the setHour() function doesn't change this mode.
	
	byte temp_buffer;

	// Start by reading byte 0x02.
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x02));
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();

	// Set the flag to the requested value:
	if (h12) {
		temp_buffer = temp_buffer | 0b01000000;
	} else {
		temp_buffer = temp_buffer & 0b10111111;
	}

	// Write the byte
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x02));
	Wire.write(temp_buffer);
	Wire.endTransmission();
}

float DS3231::getTemperature() {
	// Checks the internal thermometer on the DS3231 and returns the 
	// temperature as a floating-point value.
	byte temp;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x11));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 2);
	temp = Wire.read();	// Here's the MSB
	return float(temp) + 0.25*(Wire.read()>>6);
}

void DS3231::getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM) {
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x07));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 4);

	temp_buffer	= Wire.read();	// Get A1M1 and A1 Seconds
	A1Second	= bcdToDec(temp_buffer & 0b01111111);
	// put A1M1 bit in position 0 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>7;

	temp_buffer		= Wire.read();	// Get A1M2 and A1 minutes
	A1Minute	= bcdToDec(temp_buffer & 0b01111111);
	// put A1M2 bit in position 1 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>6;

	temp_buffer	= Wire.read();	// Get A1M3 and A1 Hour
	// put A1M3 bit in position 2 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>5;
	// determine A1 12/24 mode
	A1h12		= temp_buffer & 0b01000000;
	if (A1h12) {
		A1PM	= temp_buffer & 0b00100000;			// determine am/pm
		A1Hour	= bcdToDec(temp_buffer & 0b00011111);	// 12-hour
	} else {
		A1Hour	= bcdToDec(temp_buffer & 0b00111111);	// 24-hour
	}

	temp_buffer	= Wire.read();	// Get A1M4 and A1 Day/Date
	// put A1M3 bit in position 3 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>4;
	// determine A1 day or date flag
	A1Dy		= (temp_buffer & 0b01000000)>>6;
	if (A1Dy) {
		// alarm is by day of week, not date.
		A1Day	= bcdToDec(temp_buffer & 0b00001111);
	} else {
		// alarm is by date, not day of week.
		A1Day	= bcdToDec(temp_buffer & 0b00111111);
	}
}

void DS3231::getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM) {
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x0b));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 3); 
	temp_buffer	= Wire.read();	// Get A2M2 and A2 Minutes
	A2Minute	= bcdToDec(temp_buffer & 0b01111111);
	// put A2M2 bit in position 4 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>3;

	temp_buffer	= Wire.read();	// Get A2M3 and A2 Hour
	// put A2M3 bit in position 5 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>2;
	// determine A2 12/24 mode
	A2h12		= temp_buffer & 0b01000000;
	if (A2h12) {
		A2PM	= temp_buffer & 0b00100000;			// determine am/pm
		A2Hour	= bcdToDec(temp_buffer & 0b00011111);	// 12-hour
	} else {
		A2Hour	= bcdToDec(temp_buffer & 0b00111111);	// 24-hour
	}

	temp_buffer	= Wire.read();	// Get A2M4 and A1 Day/Date
	// put A2M4 bit in position 6 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>1;
	// determine A2 day or date flag
	A2Dy		= (temp_buffer & 0b01000000)>>6;
	if (A2Dy) {
		// alarm is by day of week, not date.
		A2Day	= bcdToDec(temp_buffer & 0b00001111);
	} else {
		// alarm is by date, not day of week.
		A2Day	= bcdToDec(temp_buffer & 0b00111111);
	}
}

void DS3231::setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM) {
	//	Sets the alarm-1 date and time on the DS3231, using A1* information
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x07));	// A1 starts at 07h
	// Send A1 second and A1M1
	Wire.write(decToBcd(A1Second) | ((AlarmBits & 0b00000001) << 7));
	// Send A1 Minute and A1M2
	Wire.write(decToBcd(A1Minute) | ((AlarmBits & 0b00000010) << 6));
	// Figure out A1 hour 
	if (A1h12) {
		// Start by converting existing time to h12 if it was given in 24h.
		if (A1Hour > 12) {
			// well, then, this obviously isn't a h12 time, is it?
			A1Hour = A1Hour - 12;
			A1PM = true;
		}
		if (A1PM) {
			// Afternoon
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A1Hour) | 0b01100000;
		} else {
			// Morning
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A1Hour) | 0b01000000;
		}
	} else {
		// Now for 24h
		temp_buffer = decToBcd(A1Hour); 
	}
	temp_buffer = temp_buffer | ((AlarmBits & 0b00000100)<<5);
	// A1 hour is figured out, send it
	Wire.write(temp_buffer); 
	// Figure out A1 day/date and A1M4
	temp_buffer = ((AlarmBits & 0b00001000)<<4) | decToBcd(A1Day);
	if (A1Dy) {
		// Set A1 Day/Date flag (Otherwise it's zero)
		temp_buffer = temp_buffer | 0b01000000;
	}
	Wire.write(temp_buffer);
	// All done!
	Wire.endTransmission();
}

void DS3231::EnableMinuteInterrupt(void) {
	setA2Time(1, 1, 1, 0x70, 0, 0, 0);
	turnOnAlarm(2);
	byte cb = readControlByte(1);
	cb &= (~2);
	writeControlByte(cb, 1);
	writeControlByte(readControlByte(0) | 4, 0);
}

void DS3231::clearAlarmSignal(const byte alarm) {
	byte cb = readControlByte(1);
	cb &= (~alarm);
	writeControlByte(cb, 1);
}

void DS3231::setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM) {
	//	Sets the alarm-2 date and time on the DS3231, using A2* information
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x0b));	// A2 starts at 0bh
	// Send A2 Minute and A2M2
	Wire.write(decToBcd(A2Minute) | ((AlarmBits & 0b00010000) << 3));
	// Figure out A2 hour 
	if (A2h12) {
		// Start by converting existing time to h12 if it was given in 24h.
		if (A2Hour > 12) {
			// well, then, this obviously isn't a h12 time, is it?
			A2Hour = A2Hour - 12;
			A2PM = true;
		}
		if (A2PM) {
			// Afternoon
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A2Hour) | 0b01100000;
		} else {
			// Morning
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A2Hour) | 0b01000000;
		}
	} else {
		// Now for 24h
		temp_buffer = decToBcd(A2Hour); 
	}
	// add in A2M3 bit
	temp_buffer = temp_buffer | ((AlarmBits & 0b00100000)<<2);
	// A2 hour is figured out, send it
	Wire.write(temp_buffer); 
	// Figure out A2 day/date and A2M4
	temp_buffer = ((AlarmBits & 0b01000000)<<1) | decToBcd(A2Day);
	if (A2Dy) {
		// Set A2 Day/Date flag (Otherwise it's zero)
		temp_buffer = temp_buffer | 0b01000000;
	}
	Wire.write(temp_buffer);
	// All done!
	Wire.endTransmission();
}

void DS3231::turnOnAlarm(byte Alarm) {
	// turns on alarm number "Alarm". Defaults to 2 if Alarm is not 1.
	byte temp_buffer = readControlByte(0);
	// modify control byte
	if (Alarm == 1) {
		temp_buffer = temp_buffer | 0b00000101;
	} else {
		temp_buffer = temp_buffer | 0b00000110;
	}
	writeControlByte(temp_buffer, 0);
}

void DS3231::turnOffAlarm(byte Alarm) {
	// turns off alarm number "Alarm". Defaults to 2 if Alarm is not 1.
	// Leaves interrupt pin alone.
	byte temp_buffer = readControlByte(0);
	// modify control byte
	if (Alarm == 1) {
		temp_buffer = temp_buffer & 0b11111110;
	} else {
		temp_buffer = temp_buffer & 0b11111101;
	}
	writeControlByte(temp_buffer, 0);
}

bool DS3231::checkAlarmEnabled(byte Alarm) {
	// Checks whether the given alarm is enabled.
	byte result = 0x0;
	byte temp_buffer = readControlByte(0);
	if (Alarm == 1) {
		result = temp_buffer & 0b00000001;
	} else {
		result = temp_buffer & 0b00000010;
	}
	return result;
}

bool DS3231::checkIfAlarm(byte Alarm) {
	// Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly.
	// Turns flag off, also.
	// defaults to checking alarm 2, unless Alarm == 1.
	byte result;
	byte temp_buffer = readControlByte(1);
	if (Alarm == 1) {
		// Did alarm 1 go off?
		result = temp_buffer & 0b00000001;
		// clear flag
		temp_buffer = temp_buffer & 0b11111110;
	} else {
		// Did alarm 2 go off?
		result = temp_buffer & 0b00000010;
		// clear flag
		temp_buffer = temp_buffer & 0b11111101;
	}
	writeControlByte(temp_buffer, 1);
	return result;
}

void DS3231::enableOscillator(bool TF, bool battery, byte frequency) {
	// turns oscillator on or off. True is on, false is off.
	// if battery is true, turns on even for battery-only operation,
	// otherwise turns off if Vcc is off.
	// frequency must be 0, 1, 2, or 3.
	// 0 = 1 Hz
	// 1 = 1.024 kHz
	// 2 = 4.096 kHz
	// 3 = 8.192 kHz (Default if frequency byte is out of range)
	if (frequency > 3) frequency = 3;
	// read control byte in, but zero out current state of RS2 and RS1.
	byte temp_buffer = readControlByte(0) & 0b11100111;
	if (battery) {
		// turn on BBSQW flag
		temp_buffer = temp_buffer | 0b01000000;
	} else {
		// turn off BBSQW flag
		temp_buffer = temp_buffer & 0b10111111;
	}
	if (TF) {
		// set ~EOSC to 0 and INTCN to zero.
		temp_buffer = temp_buffer & 0b01111011;
	} else {
		// set ~EOSC to 1, leave INTCN as is.
		temp_buffer = temp_buffer | 0b10000000;
	}
	// shift frequency into bits 3 and 4 and set.
	frequency = frequency << 3;
	temp_buffer = temp_buffer | frequency;
	// And write the control bits
	writeControlByte(temp_buffer, 0);
}

void DS3231::enable32kHz(bool TF) {
	// turn 32kHz pin on or off
	byte temp_buffer = readControlByte(1);
	if (TF) {
		// turn on 32kHz pin
		temp_buffer = temp_buffer | 0b00001000;
	} else {
		// turn off 32kHz pin
		temp_buffer = temp_buffer & 0b11110111;
	}
	writeControlByte(temp_buffer, 1);
}

bool DS3231::oscillatorCheck() {
	// Returns false if the oscillator has been off for some reason.
	// If this is the case, the time is probably not correct.
	byte temp_buffer = readControlByte(1);
	bool result = true;
	if (temp_buffer & 0b10000000) {
		// Oscillator Stop Flag (OSF) is set, so return false.
		result = false;
	}
	return result;
}

/***************************************** 
	Private Functions
 *****************************************/

byte DS3231::decToBcd(byte val) {
// Convert normal decimal numbers to binary coded decimal
	return ( (val/10*16) + (val%10) );
}

byte DS3231::bcdToDec(byte val) {
// Convert binary coded decimal to normal decimal numbers
	return ( (val/16*10) + (val%16) );
}

byte DS3231::readControlByte(bool which) {
	// Read selected control byte
	// first byte (0) is 0x0e, second (1) is 0x0f
	Wire.beginTransmission(CLOCK_ADDRESS);
	if (which) {
		// second control byte
		Wire.write(uint8_t(0x0f));
	} else {
		// first control byte
		Wire.write(uint8_t(0x0e));
	}
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return Wire.read();	
}

void DS3231::writeControlByte(byte control, bool which) {
	// Write the selected control byte.
	// which=false -> 0x0e, true->0x0f.
	Wire.beginTransmission(CLOCK_ADDRESS);
	if (which) {
		Wire.write(uint8_t(0x0f));
	} else {
		Wire.write(uint8_t(0x0e));
	}
	Wire.write(control);
	Wire.endTransmission();
}

Если Вы установите эту библиотеку, то Вам нужно будет сделать следующее:

1. Соединить пин SQW модуля часов (или микросхемы - не знаю, что у Вас) с пином 2 или 3 Ардуино.

2. В скетче прописать вот такие строки ...

static DS3231 Clock;

#define	PIN_SQW	3 // если у Вас второй, то поставьте здесь 2


static void everyMinuteAlarm(void) {
	const byte oldSReg = SREG;
	sei();
	Clock.clearAlarmSignal(2);
//
//   В этом месте вставьте код для движения стрелки
//   Он будет исполняться раз в минуту точно при переходе
//   на новую минуту (т.е. когда секунды равны 0)
//
	SREG = oldSReg;
}

void setup(void) {
	pinMode(PIN_SQW, INPUT);
	Wire.begin();
	Clock.turnOffAlarm(2);
	Clock.EnableMinuteInterrupt();
	attachInterrupt(PIN_SQW - 2, everyMinuteAlarm, FALLING);
         ,,,,,,,,,,,
}

с поправкой на Ваш код, разумеется.

По идее, всё должно заработать. Для проверки, сначала поставьте в то место, где собираетесь двигать стрелку вывод слова "privet" в Serial и убедитесь, что оно выводится точно раз в минуту.

P.S. Только сейчас заметил, что у Вас леонардо. Я их никогда не видел и не знаю, какие там пины за прерывания отвечают, так что может быть не 2 и 3, а что-то другое. Разберитесь сами. Идея простая- функцию everyMinuteAlarm необходимо повесить на прерывание по заднему фронту сигнала на пине SQW.

technomag
Offline
Зарегистрирован: 03.07.2016

Большое спасибо за помощь, на данный момент использую этот код в качестве тестового, если не пройдет тест попробую Ваш. Сегодня как раз первый тестовый запуск, вроде работает, посмотрю сутку, как будет себя вести. Как по Вашему мнению он, имеет право на жизнь?

#include <Stepper.h>

#include <RTC.h>
#define STEPS 200 // 1 шаг = 1 градус
Stepper stepper(STEPS, 3, 4); // пины на ардуино
byte current_minute;
RTC time;
unsigned long timer;
 
void setup()
{
Serial.begin(9600);
stepper.setSpeed(500); // скорость движения вала
time.begin(RTC_DS3231); //Либо другой
current_minute = atoi(time.gettime("i"));
}
 
void loop()
{
if (atoi(time.gettime("i")) != current_minute) { //Сравниваем текущую минуту с записанной, если не сошлось
Serial.println("Pass a minute...");
Serial.println("timer start...");
timer=millis();
stepper.step(3200); // выполняет пол оборота вала согласно расчетам редуктора стрелок часов (секундной стрелки нет)
Serial.print("Timer value is ");
Serial.println(millis()-timer);
current_minute = atoi(time.gettime("i")); //Обновляем текущее значение минуты.
}
}

 

P.S. Леонардо очень хорошая платформа, изготовлена в Италии, с ней очень просто как оказалось, даже выведены специальные пины для подкючения модуля реального временни.

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

Да, иметь-то имеет, но там проще. Там loop у Вас вовсе пустой - всё прерывание делает. Да и формально точнее, хотя, это уже чисто формальная придирка. Точность и у Вас более чем достаточная.