Домой / Windows 7 / Подключение DS1307 к микроконтроллерам AVR. Подключение схемы DS1307 к микроконтроллеру по интерфейсу TWI H – Минуты

Подключение DS1307 к микроконтроллерам AVR. Подключение схемы DS1307 к микроконтроллеру по интерфейсу TWI H – Минуты

DS1307 это небольшой модуль, предназначенный для подсчета времени. Собранный на базе микросхемы DS1307ZN с реализацией питания от литиевой батарейки (LIR2032), что позволяет работать автономно в течение длительного времени. Также на модуле, установлена энергонезависимая память EEPROM объемом 32 Кбайт (AT24C32). Микросхема AT24C32 и DS1307ZN связаны обшей шиной интерфейсом I2C.

Технические параметры

Напряжение питания: 5В
Рабочая температура: – 40℃ … + 85℃
Память: 56 байт (энергонезависимая)
Батарейка: LIR2032 (автоматическое определение источника питания)
Интерфейса: I2C
Габариты: 28мм х 25мм х 8 мм

Общие сведения

Использовании модуля DS1307 зачастую очень оправдано, например, когда данные считываются редко, интервалом более недели, использовать собственные ресурсы контроллера, неоправданно или невозможно. Обеспечивание бесперебойное питание, например платы Arduino, на длительный срок дорого, даже при использовании батареи.
Благодаря собственной памяти и автономностью, можно регистрировать события, (при автономном питании) например изменение температуры и так далее, данные сохраняются в памяти их можно считать из памяти модуля. Так что модуль DS1307 часто используют, когда контроллерам Arduino необходимо знать точное время, для запуска какого то события и так далее.

Обмен данными с другими устройствами осуществляется по интерфейсу I2C с выводов SCL и SDA. Конденсаторы С1 и С2 необходимы для снижения помех по линию питания. Чтобы обеспечить надлежащего уровня сигналов SCL и SDA установлены резисторы R2 и R3 (подтянуты к питанию). Для проверки работоспособности модуля, на вывод 7 микросхему DS1307Z, подается сигнал SQ, прямоугольной формы с частотой 1 Гц. Элементы R4, R5, R6, VD1 необходимы для подзарядку литиевой батарейки. Так же, на плате предусмотрено посадочное место (U1), для установки датчика температуры DS18B20 (при необходимости можно впаять его), считывать показания, можно с вывода DS, который подтянут к пиатнию, через резистор R1 сопротивлением 3.3 кОм. Принципиальную схему и назначение контактов можно посмотреть на рисунках ниже.

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

Первая группа контактов:
DS: вывод DS18B20 (1-wire)


VCC: «+» питание модуля
GND: «-» питание модуля

Вторая группа контактов:
SQ: вход 1 МГц
DS: вывод DS18B20 (1-wire)
SCL: линия тактирования (Serial CLock)
SDA: линия данных (Serial Dфta)
VCC: «+» питание модуля
GND:«-» питание модуля
BAT:

Подзарядка батареи
Как описывал ваше модуль может заряжать батарею, реализовано это, с помощью компонентов R4, R5, R6 и диода D1. Но, данная схема имеет недостаток, через резистор R4 и R6 происходит разряд батареи (как подметил пользователь ALEXEY, совсем не большой). Так как модуль потребляем незначительный ток, можно удалить цепь питания, для этого убираем R4, R5, R6 и VD1, вместо R6 поставим перемычку (после удаления компонентов, можно использовать обычную батарейку CR2032).

Подключение DS1307 к Arduino

Необходимые детали:
Arduino UNO R3 x 1 шт.
Провод DuPont, 2,54 мм, 20 см x 1 шт.
Кабель USB 2.0 A-B x 1 шт.
Часы реального времени RTC DS1307 x 1 шт.

Подключение:
Для подключения часы реального времени DS1307, необходимо впаять впаять штыревые разъемы в первую группу контактов. Далее, подключаем провода SCL (DS1307) к выводу 4 (Arduino UNO) и SDA (DS1307) к выводу 5 (Arduino UNO), осталось подключить питания VCC к +5V и GND к GND. Кстати, в различных платах Arduino вывода интерфейса I2C отличаются, назначение каждого можно посмотреть ниже.

Установка времени DS1307
Первым делом, необходимо скачать и установить библиотеку «DS1307RTC» и «TimeLib» в среду разработки IDE Arduino, далее необходимо настроить время, открываем пример из библиотеки DS1307RTC «Файл» —> «Примеры» —> «DS1307RTC» —> «SetTime» или копируем код снизу.

// Подключаем библиотеку DS1307RTC const char *monthName = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; tmElements_t tm; void setup() { bool parse=false; bool config=false; // get the date and time the compiler was run if (getDate(__DATE__) && getTime(__TIME__)) { parse = true; // and configure the RTC with this info if (RTC.write(tm)) { config = true; } } Serial.begin(9600); while (!Serial) ; // wait for Arduino Serial Monitor delay(200); if (parse && config) { Serial.print("DS1307 configured Time="); Serial.print(__TIME__); Serial.print(", Date="); Serial.println(__DATE__); } else if (parse) { Serial.println("DS1307 Communication Error:-{"); Serial.println("Please check your circuitry"); } else { Serial.print("Could not parse info from the compiler, Time=\""); Serial.print(__TIME__); Serial.print("\", Date=\""); Serial.print(__DATE__); Serial.println("\""); } } void loop() { } bool getTime(const char *str) { int Hour, Min, Sec; if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false; tm.Hour = Hour; tm.Minute = Min; tm.Second = Sec; return true; } bool getDate(const char *str) { char Month; int Day, Year; uint8_t monthIndex; if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false; for (monthIndex = 0; monthIndex < 12; monthIndex++) { if (strcmp(Month, monthName) == 0) break; } if (monthIndex >= 12) return false; tm.Day = Day; tm.Month = monthIndex + 1; tm.Year = CalendarYrToTm(Year); return true; }

Скачать скетч

Загружаем данную скетч в контроллер Arduino (время берется с ОС), открываем «Мониторинг порта»

Программа
В библиотеке есть еще один пример, открыть его можно DS1307RTC «Файл» —> «Примеры» —> «DS1307RTC» —> «ReadTest»

/* Тестирование производилось на Arduino IDE 1.6.12 Дата тестирования 23.11.2016г. */ #include // Подключаем библиотеку Wire #include // Подключаем библиотеку TimeLib #include // Подключаем библиотеку DS1307RTC void setup() { Serial.begin(9600); // Устанавливаем скорость передачи данных while (!Serial) ; // Ожидаем подключение последовательного порта. Нужно только для Leonardo delay(200); // Ждем 200 мкс Serial.println("DS1307RTC Read Test"); // Выводим данные на последовательный порт Serial.println("-------------------"); // Выводим данные на последовательный порт } void loop() { tmElements_t tm; if (RTC.read(tm)) { Serial.print("Ok, Time = "); print2digits(tm.Hour); Serial.write(":"); print2digits(tm.Minute); Serial.write(":"); print2digits(tm.Second); Serial.print(", Date (D/M/Y) = "); Serial.print(tm.Day); Serial.write("/"); Serial.print(tm.Month); Serial.write("/"); Serial.print(tmYearToCalendar(tm.Year)); Serial.println(); } else { if (RTC.chipPresent()) { Serial.println("The DS1307 is stopped. Please run the SetTime"); Serial.println("example to initialize the time and begin running."); Serial.println(); } else { Serial.println("DS1307 read error! Please check the circuitry."); Serial.println(); } delay(9000); } delay(1000); } void print2digits(int number) { if (number >= 0 && number < 10) { Serial.write("0"); } Serial.print(number); }

Скачать скетч

Загружаем данную код в контроллер Arduino, открываем «Мониторинг порта»


В этом материале будет рассмотрен топорный вариант работы с TWI модулем на примере часов реального времени - микросхемы DS1307. Топорный, потому что обмен по I2C будет организован без прерываний и анализа статусных кодов, чтобы сильно не грузить начинающих.

DS1307

Микросхема DS1307 предназначена для счета времени - секунд, минут, часов, дней, месяцев и лет. То есть по сути, это часы с календарем.

Она тактируется от собственного кварцевого генератора с частотой 32768 Гц и может работать от двух источников питания - основного и резервного. Важная фишка этой микросхемы заключается в низком потреблении - меньше 500 nA в рабочем режиме. При таком потреблении DS1307 может проработать от трехвольтовой батарейки (типа CR2032 емкостью ~200 мА*ч) несколько лет. Также DS1307 может генерировать на одном из выводов меандр и в ее составе есть 56 байт оперативной памяти, которую можно использовать для хранения данных.

Минимальная схема подключения DS1307 включает в себя часовой кварц и один источник питания. Обмен данными с микросхемой осуществляется по I2C, причем DS1307 может работать на шине только как подчиненное устройство (слейвом).

С чего начать?

Подключение любой микросхемы начинается с изучения даташита. В случае DS1307 нам нужно выяснить: какую скорость обмена поддерживает микросхема, какой она имеет адрес, как выглядит карта памяти, есть ли у нее управляющие биты или регистры, как передать данные и как прочитать их. Ниже приведены скриншоты из даташита, в которых я нашел требуемую информацию.

Скорость обмена DS1307




Адрес, по которому DS1307 отзывается на I2C шине




Карта памяти DS1307

Карта памяти показывает, по каким адресам расположены регистры микросхемы и сколько их вообще.

По нулевому адресу располагается регистр секунд. Младшие 4 разряда регистра отведены для единиц, там может быть число от 0 до 9. Старшие - для десятков секунд.


Это так называемый двоично-десятичный формат представления чисел (BCD).При таком формате один байт может представить числа только от 0 до 99. Остальные регистры часов и календаря содержат данные в таком же формате.

7-й разряд регистра секунд - управляющий. 0 в этом разряде разрешает работу часов, 1 - запрещает. При подачи питания этот разряд устанавливается в 1.

По первому адресу расположен регистр минут. С ним все понятно.

По второму адресу располагается регистр часов. 6-й бит этого регистра задает формат представления времени. Если он установлен в 1 используется 12 часовой формат, если сброшен -24 часовой.

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

Ну и адреса с 8 по 63-й отведены для оперативной памяти. Их можно использовать для хранения данных.

Как записать данные в DS1307

DS1307 может работать в двух режимах: как подчиненный приемник и как подчиненный передатчик. В первом режиме ведущее устройство передает DS1307 данные, а DS1307 принимает их. Во втором - ведущее устройство принимает от DS1307 данные, а та в свою очередь передает их. (Но обмен в обоих случаях начинает ведущий!)

Для каждого режима в даташите есть описание и диаграмма обмена. Запись данных выполняется согласно следующей последовательности.



2. Ведущий выдает на шину адрес DS1307 с нулевым битом квитирования (адресный пакет), что сигнализирует ведомому о последующей записи данных.
3. Если на шине присутствует микросхема DS1307, она отвечает ведущему - ACK.
4. После получения ответа ведущий передает DS1307 адрес регистра, с которого начнется запись данных. Это значение записывается во внутренний счетчик адреса DS1307.
5. DS1307 снова отвечает ведущему.
6. Получив ответ, ведущий передает ведомому байт данных, который предназначен для записи в регистр DS1307.
7. DS1307 отвечает ведущему.
8. Шаги 6, 7 повторяются несколько раз.
9. Ведущий формирует на шине состояние СТОП.

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



1. Ведущий формирует на шине состояние СТАРТ.
2. Ведущий выдает на шину адрес DS1307 с установленным битом квитирования, что сигнализирует ведомому о последующем чтении данных.
3. DS1307 отвечает ведущему.
4. DS1307 передает ведущему байт данных, на который указывает внутренний счетчик адреса.
5. Ведущий отвечает, что принял данные.
6 . Шаги 4, 5 повторяются несколько раз.
7. DS1307 передает ведущему байт данных.
7. Ведущий неформирует ответ DS1307.
8. Ведущий выдает на шину состояние СТОП.

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

Код

Теперь можно перейти к коду. Нам понадобится минимум три функции:

Функция инициализации,
- функция записи данных,
- функция чтения данных.

Инициализация


#define F_I2C 50000UL
#define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/2)

#if ((TWBR_VALUE > 255) || (TWBR_VALUE == 0))
#error "TWBR value is not correct"
#endif

void RTC_Init(void)
{
TWBR = TWBR_VALUE;
TWSR = 0;
}

Скорость задается с помощью макроса TWBR_VALUE. Здесь используется формула, разобранная в предыдущей части. При компиляции расчетное значение проверяется с помощью директив препроцессора, чтобы убедиться, что оно не выходит за диапазон.

Передача данных

Передача данных начинается с состояния СТАРТ. Чтобы сформировать его, нужно включить TWI модуль, установить бит TWSTA и сбросить флаг прерывания TWINT. Это выполняется в одну строчку, записью в управляющий регистр TWCR.

Когда микроконтроллер выдаст на шину состояние СТАРТ, установится бит TWINT и в статусном регистре TWSR изменится статусный код. Микроконтроллер должен дождаться установки бита TWINT, прежде чем перейдет к следующей операции. Ожидание в нашем случае выполняется циклическим опросом (тупо поллингом.. не путать с троллингом).


TWCR = (1< while(!(TWCR & (1<

Каждая установка бита TWINT сопровождается определенным статусным кодом в регистре TWSR. По хорошему, мы должны проверять эти коды, чтобы контролировать успешность операций. Но поскольку код у нас торный (учебный), мы не будем этого делать.

Далее на шину нужно выдать адресный пакет. В регистр данных TWDR загружаем адрес, а бит квитирования устанавливаем нулевым. После загрузки адреса сбрасываем бит TWINT, инициируя дальнейшую работу TWI модуля, и дожидаемся, когда она завершится, опрашивая TWINT.


/*выдаем на шину пакет SLA-W*/
TWDR = (DS1307_ADR<<1)|0;
TWCR = (1<while(!(TWCR & (1<

Посылаем на шину адрес, с которого будет производиться запись в DS1307. Для этого загружаем в регистр данных требуемое значение, сбрасываем бит TWINT и дожидаемся его установки.



TWDR = adr;
TWCR = (1<while(!(TWCR & (1<


/*формируем состояние СТОП*/
TWCR = (1<

Полный код функции записи будет выглядеть примерно так.


void RTC_SetValue(uint8_t adr, uint8_t data)
{
/*формируем состояние СТАРТ*/
TWCR = (1< while(!(TWCR & (1<

/*выдаемна шину пакет SLA-W*/
TWDR = (DS1307_ADR<<1)|0;
TWCR = (1< while(!(TWCR & (1<

/*передаем адрес регистра ds1307*/
TWDR = adr;
TWCR = (1< while(!(TWCR & (1<

/*передаем данные или пропускаем*/
if (data != RTC_RESET_POINTER){
/*это чтобы привести данные к BCD формату*/
data = ((data/10)<<4) + data%10;

TWDR = data;
TWCR = (1< while(!(TWCR & (1< }

/*формируем состояние СТОП*/
TWCR = (1<}

С помощью этой функции можно производить запись отдельных регистров и инициализировать внутренний регистр адреса DS1307 для последующей операции чтения данных. Пример использования функции есть в тестовых проектах.

Чтение данных из DS1307

Формируем состояние СТАРТ.


/*формируем состояние СТАРТ*/
TWCR = (1< while(!(TWCR & (1<

Посылаем на шину адресный пакет - адрес и установленный бит квитирования.


/*выдаемна шину пакет SLA-R*/
TWDR = (DS1307_ADR<<1)|1;
TWCR = (1< while(!(TWCR & (1<

Получаем данные. Сбрасываем бит TWINT, инициирую работу TWI модуля. Бит TWEA должен быть установлен в 1, чтобы ведущее устройство сигнализировало ведомому о приеме очередного байта.
Когда бит TWINT снова установится в 1, в регистре данных будет байт принятый от ведомого.


/*считываем данные с подтверждением*/
TWCR = (1< while(!(TWCR & (1< data = TWDR;


/*считываем данные без подтверждения*/
TWCR = (1< while(!(TWCR & (1< data = TWDR;

/*формируем состояние СТОП*/
TWCR = (1<

Полный код функции чтения одного байта данный из DS1307 будет выглядеть примерно так.


uint8_t RTC_GetValue(void)
{
uint8_t data;

/*формируем состояние СТАРТ*/
TWCR = (1< while(!(TWCR & (1<

/*выдаем на шину пакет SLA-R*/
TWDR = (DS1307_ADR<<1)|1;
TWCR = (1< while(!(TWCR & (1<

/*считываем данные*/
TWCR = (1< while(!(TWCR & (1< data = TWDR;

/*формируем состояние СТОП*/
TWCR = (1<

Return data;
}

Тестовый проект для DS1307

Как обычно тестовый проект, объединяющий все выше сказанное. Программа простая. Инициализируем периферию, загружаем в DS1307 начальное значение. Далее в цикле считываем временя и выводим на LCD. Для общения с DS1307 используются всего три функции.

DS1307 - микросхема часов реального времени с интерфейсом I2C(TWI) . Часы / календарь хранят следующую информацию: секунды, минуты, часы, день, дату, месяц и год. Конец месяца автоматически подстраивается для месяцев, в которых менее 31 дня, включая поправку для високосного года. Часы работают в 24-часовом или 12-часовом формате с индикатором AM/PM. DS1307 имеет встроенную схему контроля питания, которая обнаруживает пропадание питания и автоматически переключает схему на питание от батареи.

Vbat - вход батареи для любого стандартного 3 Вольтового литиевого элемента или другого источника энергии. Для нормальной работы напряжение батареи должно поддерживаться между 2.5 и 3.5 В. Уровень, при котором запрещён доступ к часам реального времени и пользовательскому ОЗУ, установлен внутренней схемой равным 1.25 x Vbat. Литиевая батарея ёмкостью 35 mAh или больше достаточна для питания DS1307 в течение более чем 10 лет при отсутствии питания.
SCL (Последовательный Тактовый Вход) - SCL используется, чтобы синхронизировать передачу данных через последовательный интерфейс.
SDA (Вход/Выход Последовательных Данных) - SDA - вход / выход данных для 2-проводного последовательного интерфейса. Это выход с открытым стоком, который требует внешнего притягивающего резистора.
SQW/OUT (Меандр / Выходной Драйвер) - Когда бит SQWE установлен в 1, на выходе SQW/OUT вырабатываются импульсы в форме меандра одной из четырех частот: 1 Гц., 4 кГц., 8 кГц., 32 кГц. Вывод SQW/OUT - с открытым стоком, требует внешнего притягивающего резистора.
X1, X2 - выводы для подключения стандартного кристалла кварца 32.768 кГц. Внутренняя схема генератора рассчитана на работу с кристаллом, имеющим номинальную емкость (CL) 12.5 пФ.
GND – Земля.
VCC – питание 5 вольт.

DS1307 работает как ведомое устройство на последовательной шине. Для доступа к нему надо установить состояние START и передать код идентификации устройства, сопровождаемый адресом регистра. К последующим регистрам можно обращаться последовательно, пока не установлено состояние STOP . Когда VСС падает ниже 1.25 x Vbat, устройство прекращает связь и сбрасывает адресный счетчик. В это время оно не будет реагировать на входные сигналы, чтобы предотвратить запись ошибочной информации. Когда VСС падает ниже Vbat, устройство переключается в режим хранения с низким потреблением. При включении питания устройство переключает питание с батареи на VСС , когда напряжение питания превысит Vbat + 0.2V, и реагирует на входные сигналы, когда VСС станет более 1.25 x Vbat. Когда питание находится в пределах нормы, устройство полностью доступно, и данные могут быть записаны и считаны. Когда к устройству подключена трёхвольтовая батарея и VСС ниже 1.25 x Vbat, чтение и запись запрещены. Однако отсчёт времени при этом работает. Когда VСС падает ниже Vbat, питание ОЗУ и отсчёта времени переключается на внешнюю батарею 3 В.

Информацию о времени и дате получают, считывая соответствующие регистры. Регистры часов показаны в таблице ниже. Время и календарь устанавливаются или инициализируются путём записи байтов в соответствующие регистры. Содержание регистров времени и календаря хранится в двоично-десятичном (BCD) формате, поэтому перед выводом информации на LCD дисплей или семисегментный индикатор необходимо преобразовать двоично-десятичный код в двоичный или ANSII - код.

Бит 7 регистра 0 - это бит остановки хода часов (Clock Halt). Когда этот бит установлен в 1, генератор остановлен. Когда сброшен в ноль, генератор работает, а часы считают время.

DS1307 может работать в 12-часовом или 24-часовом режиме. Бит 6 регистра часов задаёт один из этих режимов. Когда он равен 1, установлен 12-часовой режим. В 12-часовом режиме высокий уровень бита 5 сообщает о послеполуденном времени. В 24-часовом режиме бит 5 - второй бит 10 часов (20-23 часа).

Регистр управления DS1307 предназначен для управления работой вывода SQW/OUT . Бит OUT - управление выходом. Этот бит управляет выходным уровнем на выводе SQW/OUT , когда генерация меандра запрещена. Если SQWE = 0, логический уровень на выводе SQW/OUT равен 1, если OUT = 1, и 0 - если OUT = 0. SQWE - Разрешение меандра. Когда этот бит установлен в 1, разрешается генерация меандра. Частота меандра зависит от значений битов RS0 и RS1. Эти биты управляют частотой меандра, когда его генерация разрешена. В таблице ниже показаны частоты, которые могут быть заданы RS битами.

DS1307 поддерживает двунаправленные 2-проводную шину и протокол передачи данных. Устройство, которое посылает данные на шину, называется передатчиком, а устройство, получающее данные - приемником. Устройство, которое управляет передачей, называется ведущим. Устройства, которые управляются ведущим - ведомые. Шина должна управляться ведущим устройством, которое вырабатывает последовательные такты (SCL), управляет доступом к шине, и генерирует состояния СТАРТ и СТОП. DS1307 работает как ведомое на 2-х проводной шине.

Для работы с DS1307 необходимо организовать функцию чтения из микросхемы и функцию записи.

1. Режим записи в DS1307 . Последовательные данные и такты получены через SDA и SCL. После передачи каждого байта передаётся подтверждающий бит ASK . Состояния START и STOP опознаются как начало и конец последовательной передачи. Распознавание адреса выполняется аппаратно после приема адреса ведомого и бита направления. Байт адреса содержит семибитный адрес DS1307, равный 1101000, сопровождаемым битом направления (R/W), который при записи равен 0. После получения и расшифровки байта адреса DS1307 выдаёт подтверждение ASK на линии SDA. После того, как DS1307 подтверждает адрес ведомого и бит записи, ведущий передает адрес регистра DS1307. Тем самым будет установлен указатель регистра в DS1307. Тогда ведущий начнет передавать байты данных в DS1307, который будет подтверждать каждый полученный байт. По окончании записи ведущий сформирует состояние STOP .

2. Режим чтения из DS1307 . Первый байт принимается и обрабатывается как в режиме ведомого приёмника. Однако в этом режиме бит направления укажет, что направление передачи изменено. Последовательные данные передаются по SDA от DS1307, в то время как последовательные такты - по SCL в DS1307. Состояния START и STOP опознаются как начало и конец последовательной передачи. Байт адреса - первый байт, полученный после того, как ведущим сформировано состояние START . Байт адреса содержит семибитный адрес DS1307, равный 1101000, сопровождаемым битом направления (R/W), который при чтении равен 1. После получения и расшифровки байта адреса DS1307 выдаёт подтверждение ASK на линии SDA. Тогда DS1307 начинает передавать данные, начинающиеся с адреса регистра, на которые указывает указатель регистра. Если указатель регистра не записан перед инициированием режима чтения, то первый адрес, который читается - это последний адрес, оставшийся в указателе регистра. DS1307 должен получить неподтверждение NOASK , чтобы закончить чтение.

Рассмотрим особенности работы с DS1307 на примере простых часов, которые будут показывать часы, минуты и секунды. Данные будут выводиться на LCD дисплей 16х2. Две кнопки "Часы+" и "Минуты+" позволят подвести нужное время. Микроконтроллер Atmega 8 тактируется от внутреннего генератора частотой 1 MHz, поэтому не забудьте поменять фьюзы. Ниже представлена схема подключения.

Управляющая программа включает в себя наборы функций работы с шиной TWI, часами DS1307, LCD дисплеем.

I2CInit - инициализация шины;
I2CStart - передача условия START;
I2CStop - передача условия STOP;
I2CWriteByte - запись данных;
I2CReadByte - чтение данных;
DS1307Read - функция чтения данных из DS1307;
DS1307Write - Функция записи данных в DS1307;
lcd_com - передача команды в LCD;
lcd_data - передача данных в LCD;
lcd_string - функция вывода строки в LCD;
lcd_num_to_str - функция вывода символа типа int;
lcd_init - инициализация LCD.

Ниже представлен код программы:

#include #include // Функция инициализация шины TWI void I2CInit(void) { TWBR = 2; // Настройка частоты шины TWSR = (1 << TWPS1)|(1 << TWPS0); // Предделитель на 64 TWCR |= (1 << TWEN); // Включение модуля TWI } // Функция СТАРТ void I2CStart(void) { TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTA); // Передача условия СТАРТ while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT } // Функция СТОП void I2CStop(void) { TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTO); // Передача условия СТОП while(TWCR & (1 << TWSTO)); // Ожидание завершения передачи условия СТОП } // Функция записи данных по шине uint8_t I2CWriteByte(uint8_t data) { TWDR = data; // Загрузка данных в TWDR TWCR = (1 << TWEN)|(1 << TWINT); // Сброс флага TWINT для начала передачи данных while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT // Проверка статуса // Если адрес DS1307+R и принято "подтверждение"(0x18) // или адрес DS1307+W и принято "подтверждение"(0x40) // или передается байт данных и принято "подтверждение"(0x28) if((TWSR & 0xF8) == 0x18 || (TWSR & 0xF8) == 0x40 || (TWSR & 0xF8) == 0x28) return 1; // OK else return 0; // ОШИБКА } // Функция чтения данных по шине uint8_t I2CReadByte(uint8_t *data,uint8_t ack) { // Возвращаем "подтверждение" после приема if(ack) TWCR |= (1 << TWEA); // Возвращаем "неподтверждение" после приема // Ведомое устройство не получает больше данных // обычно используется для распознования последнего байта else TWCR &= ~(1 << TWEA); // Разрешение приема данных после сброса TWINT TWCR |= (1 << TWINT); while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT // Проверка статуса // Если принят байт данных и возвращается "подтверждение"(0x50) // или принят байт данных и возвращается "ненеподтверждение"(0x58) if((TWSR & 0xF8) == 0x50 || (TWSR & 0xF8) == 0x58) { *data = TWDR; // Читаем данные из TWDR return 1; // OK } else return 0; // ОШИБКА } // Функция чтения данных из DS1307 uint8_t DS1307Read(uint8_t address,uint8_t *data) { uint8_t res; I2CStart(); // СТАРТ res = I2CWriteByte(0b11010000); // адрес DS1307+W if(!res) return 0; // ОШИБКА // Передача адреса необходимого регистра res = I2CWriteByte(address); if(!res) return 0; // ОШИБКА I2CStart(); // Повторный СТАРТ res = I2CWriteByte(0b11010001); // адрес DS1307+R if(!res) return 0; // ОШИБКА // Чтение данных с "неподтверждением" res = I2CReadByte(data,0); if(!res) return 0; // ОШИБКА I2CStop(); // СТОП return 1; // OK } // Функция записи данных в DS1307 uint8_t DS1307Write(uint8_t address,uint8_t data) { uint8_t res; I2CStart(); // СТАРТ res = I2CWriteByte(0b11010000); // адрес DS1307+W if(!res) return 0; // ОШИБКА // Передача адреса необходимого регистра res = I2CWriteByte(address); if(!res) return 0; // ОШИБКА res = I2CWriteByte(data); // Запись данных if(!res) return 0; // ОШИБКА I2CStop(); // СТОП return 1; // OK } // Функции работы с LCD #define RS PD0 #define EN PD2 // Функция передачи команды void lcd_com(unsigned char p) { PORTD &= ~(1 << RS); // RS = 0 (запись команд) PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл _delay_us(100); PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл _delay_us(100); PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); } // Функция передачи данных void lcd_data(unsigned char p) { PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл _delay_us(100); PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл _delay_us(100); PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); } // Функция вывода строки на LCD void lcd_string(unsigned char command, char *string) { lcd_com(0x0C); lcd_com(command); while(*string != "\0") { lcd_data(*string); string++; } } // Функция вывода переменной void lcd_num_to_str(unsigned int value, unsigned char nDigit) { switch(nDigit) { case 4: lcd_data((value/1000)+"0"); case 3: lcd_data(((value/100)%10)+"0"); case 2: lcd_data(((value/10)%10)+"0"); case 1: lcd_data((value%10)+"0"); } } // Функция инициализации LCD void lcd_init(void) { PORTD = 0x00; DDRD = 0xFF; _delay_ms(50); // Ожидание готовности ЖК-модуля // Конфигурирование четырехразрядного режима PORTD |= (1 << PD5); PORTD &= ~(1 << PD4); // Активизация четырехразрядного режима PORTD |= (1 << EN); PORTD &= ~(1 << EN); _delay_ms(5); lcd_com(0x28); // шина 4 бит, LCD - 2 строки lcd_com(0x08); // полное выключение дисплея lcd_com(0x01); // очистка дисплея _delay_us(100); lcd_com(0x06); // сдвиг курсора вправо lcd_com(0x0C); // включение дисплея, курсор не видим } int main(void) { _delay_ms(100); DDRC = 0x00; PORTC = 0xFF; lcd_init(); // Инициализация LCD I2CInit(); // Инициализация шины I2C lcd_string(0x81, "«acГ Ѕa DS1307"); // Часы на DS1307 lcd_string(0xC4, " : : "); // Запускаем ход часов uint8_t temp; DS1307Read(0x00,&temp); temp &= ~(1 << 7); // обнуляем 7 бит DS1307Write(0x00,temp); while(1) { unsigned char hour, minute, second, temp; // Читаем данные и преобразуем из BCD в двоичную систему DS1307Read(0x00,&temp); // Чтение регистра секунд second = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F); DS1307Read(0x01,&temp); // Чтение регистра минут minute = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F); DS1307Read(0x02,&temp); // Чтение регистра часов hour = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F); lcd_com(0xC4); lcd_num_to_str(hour, 2); // Выводим на экран часы lcd_com(0xC7); lcd_num_to_str(minute, 2); // Выводим на экран минуты lcd_com(0xCA); lcd_num_to_str(second, 2); // Выводим на экран секунды if((PINC & (1 << PC0)) == 0) // Если нажата кнопка { while((PINC & (1 << PC0)) == 0){} // Ждем отпускания кнопки hour++; // Увеличиваем часы на 1 if(hour > 23) hour = 0; // Преобразуем из двоичной системы в BCD и записываем в DS1307 uint8_t temp; temp = ((hour/10) << 4)|(hour%10); DS1307Write(0x02, temp); _delay_ms(100); } if((PINC & (1 << PC1)) == 0) // Если нажата кнопка { while((PINC & (1 << PC1)) == 0){} // Ждем отпускания кнопки minute++; // Увеличиваем минуты на 1 if(minute > 59) minute = 0; // Преобразуем из двоичной системы в BCD и записываем в DS1307 uint8_t temp; temp = ((minute/10) << 4)|(minute%10); DS1307Write(0x01, temp); _delay_ms(100); } } }

Подсчет реального времени в секундах, минутах, часах, датах месяца, месяцах, днях недели и годах с учетом высокосности текущего года вплоть до 2100 г.

56 байт энергонезависимого ОЗУ для хранения данных

2-х проводной последовательный интерфейс

Программируемый генератор прямоугольных импульсов. Может выдавать 1 ГЦ, 4.096 кГЦ, 8,192 кГЦ и 32,768 кГц.

Автоматическое определение отключения основного источника питания и подключение резервного

24-х часовой и 12-ти часовой режим

Потребление не более 500 нA при питании от резервной батареи питания при температуре 25C°

Микросхема выпускается в восьмипиновых DIP и SOIC корпусах. Распиновка для всех одинакова. Далее приведу строки из даташита для полноты картины.

Документация на микросхему (datasheet)

Назначение выводов:

. X1, X2 - Служат для подключения 32.768 кГц кварцевого резонатора

. Vbat - Вход для любой стандартной трёхвольтовой литиевой батареи или другого источника энергии. Для нормальной работы DS1307 необходимо, чтобы напряжение батареи было в диапазоне 2.0 ... 3.5 В. Литиевая батарея с ёмкостью 48 мА/ч или более при отсутствии питания будет поддерживать DS1307 в
течение более 10 лет при температуре 25°C.

. GND - общий минус

. Vcc - Это вход +5 В. Когда питающее напряжение выше 1.25 * VBAT, устройство полностью,доступно, и можно выполнять чтение и запись данных. Когда к устройству подключена батарея на 3 В, и Vcc ниже, чем 1.25 * VBAT, чтение и запись запрещены, однако функция отсчёта времени продолжает работать. Как только Vcc падает ниже VBAT, ОЗУ и RTC переключаются на батарейное питание VBAT.

. SQW/OUT - Выходной сигнал с прямоугольными импульсами.

. SCL - (Serial Clock Input - вход последовательных синхроимпульсов) - используется для синхронизации данных по последовательному интерфейсу.

. SDA - (Serial Data Input/Output - вход/выход последовательных данных) - вывод входа/выхода для двухпроводного последовательного интерфейса.

Работа с выводом SQW/OUT .

Для начала рассмотрим структуру регистров DS1307.

Структура регистров микросхемы DS1307

Нас интересует "Управляющий регистр" находящийся по адресу 0x7, т.к. он определяет работу вывода SQW/OUT.

Если бит SQWE = 1. то начинается формирование прямоугольных импульсов, если SQWE = 0, то на выходе вывода будет значение бита OUT.

За частоту импульсов отвечают биты RS0 и RS1, а именно:

RS0 RS1 Частота
0 0 1 Гц
0 1 4.096 кГц
1 0 8.192 кГц
1 1 32.768 кГц

Приведем пример:

Если нам нужно начать формирование прямоугольных импульсов с частотой 1 Гц, то необходимо в 0x7 регистр микросхемы, которая имеет адрес 0x68 отправить байт 00010000 или 0x10 в шестнадцатиричной системе счисления.

При помощи библиотеки Wire.h , это можно сделать следующим образом:

Wire .beginTransmission (0x68); Wire .write (0x7); Wire .write (0x10); Wire .endTransmission ();

Подключение к Arduino:

Выводы отвечающие за интерфейс I2C на платах Arduino на базе различных контроллеров разнятся.

Необходимые библиотеки:

для работы с DS1307: http://www.pjrc.com/teensy/td_libs_DS1307RTC.html
для работы со временем: http://www.pjrc.com/teensy/td_libs_Time.html

Установка времении

. Вручную в коде

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

Пример программного кода

#include #include void setup () { Serial .begin (9600); while (!Serial ) ; // Только для платы Leonardo // получаем время с RTC Serial //синхронизация не удаласть else Serial .println ("RTC has set the system time" ); //установим вручную 16.02.2016 12:53 TimeElements te; te.Second = 0; //секунды te.Minute = 53; //минуты te.Hour = 12; //часы te.Day = 16; //день te.Month = 2; // месяц te.Year = 2016 - 1970; //год в библиотеке отсчитывается с 1970 time_t timeVal = makeTime(te); RTC .set (timeVal); setTime (timeVal); } void loop () { digitalClockDisplay(); //вывод времени delay (1000); } void digitalClockDisplay() { Serial Serial .print (" " ); Serial .print (day ()); Serial .print (" " ); Serial .print (month ()); Serial .print (" " ); Serial .print (year ()); Serial //выводим время через ":" Serial .print (":" ); if (digits < 10) Serial .print ("0" ); Serial .print (digits); }

. Установкой из "Монитора порта"

Более точный вариант установки времени. Время задается через "монитор порта" по ходу работы контроллера.

Открываем монитор, вводим данные в нужном формате, смотрим на эталонные часы, подлавливаем момент и шелкаем "отправить".

Пример программного кода

//формат указания текущего времени "ДД.ММ.ГГ чч:мм:сс" //где ДД - день, ММ - месяц, ГГ - год, чч - часы, мм - минуты, сс - секунлы //ГГ - от 00 до 99 для 2000-2099 годов #include #include bool isTimeSet = false ; //флаг, указывающий на то, была ли уже задана дата void setup () { Serial .begin (9600); while (!Serial ) ; // Только для платы Leonardo setSyncProvider (RTC .get ); // получаем время с RTC if (timeStatus () != timeSet) Serial .println ("Unable to sync with the RTC" ); //синхронизация не удаласть else Serial .println ("RTC has set the system time" ); } void loop () { if (Serial .available ()) { //поступила команда с временем setTimeFromFormatString(Serial .readStringUntil ("\n" )); isTimeSet = true ; //дата была задана } if (isTimeSet) //если была задана дата { digitalClockDisplay(); //вывод времени } delay (1000); } void digitalClockDisplay() { Serial .print (hour ()); printDigits(minute ()); printDigits(second ()); Serial .print (" " ); Serial .print (day ()); Serial .print (" " ); Serial .print (month ()); Serial .print (" " ); Serial .print (year ()); Serial .println (); } void printDigits(int digits) { //выводим время через ":" Serial .print (":" ); if (digits < 10) Serial .print ("0" ); Serial .print (digits); } void setTimeFromFormatString(String time) { //ДД.ММ.ГГ чч:мм:сс int day = time.substring(0, 2).toInt(); int month = time.substring(3, 5).toInt(); int year = time.substring(6, 8).toInt(); int hours = time.substring(9, 11).toInt(); int minutes = time.substring(12, 14).toInt(); int seconds = time.substring(15, 17).toInt(); TimeElements te; te.Second = seconds; te.Minute = minutes; te.Hour = hours; te.Day = day ; te.Month = month ; te.Year = year + 30; //год в библиотеке отсчитывается с 1970. Мы хотим с 2000 time_t timeVal = makeTime(te); RTC .set (timeVal); setTime (timeVal); }

Отзывы об этих часах в интернете самые противоречивые. Кто-то говорит что часы замечательные, а кто-то называет их убогой поделкой Далласа. И вот я, дабы развеять все недостоверные слухи, достал микруху из загашника начала экспериментировать.

Особенности:

  • Очень маленькое энергопотребление. Производитель обещает 10 лет работы часов от одной стандартной батарейки CR2032
  • 56 байт памяти для хранения пользовательских данных. Думаю не особо нужная опция, но может кому-то и пригодится.
  • Программируемый вывод для тактирования внешних устройств. Может выдавать 1 Гц, 4.096 кГц, 8.192 кГц и 32.768 кГц.
  • 24-х часовой и 12-ти часовой режим

Распиновка

Выводы часов расположены следующим образом:

X1, X2 — Выводы для подключения кварцевого резонатора на частоту 32.768 кГц
VBAT — Вывод для подключения 3-х вольтовой батареи резервного питания
GND — Земля
SDA — линия данных шины i2c
SCL — линия тактовых импульсов шины i2c
SQW/OUT – выходной сигнал для тактирования внешних устройств
VCC — питание 5 вольт

Подключение к контроллеру
Обвязка минимальна. Потребуется кварц 32.768 кГц, пара резисторов для работы шины i2c и батарейка на три вольта.

Правильная разводка платы
Точность хода да и вообще работоспособность часов, зависит от разводки печатной платы. Даллас в своем даташите рекомендует сократить до минимума длинну проводников от микросхемы до кварцевого резонатора и окружить эти проводники прямоугольником подключенным к земле. Кроме этого для надежности я припаял к корпусу кварца проводок идущий к земле и параллельно питанию поставил конденсатор на 0.1 мкф.

Кстати может работать и без кварца. Для этого на ногу X1 подают внешний тактовый сигнал с частотой 32.768 кГц, а X2 остаётся висеть в воздухе.

Организация памяти часов
Данная микруха наделена 64 байтами памяти. Первые восемь байт — рабочие. В них хранится время, дата, день недели. Остальные выделены под нужды пользователя. В них можно хранить например какие-нибудь настройки или еще что-нибудь. Естественно, когда резервное питание пропадает, вся информация в этой памяти разрушается. Вся работа с часами (чтение и установка времени/даты) сводится к тому, чтобы читать и записывать нужные ячейки памяти.

Все числа в памяти хранятся в двоично-десятичном формате. Это значит что в одном байте может хранится сразу две цифры. Например число 0x23 — содержит в себе цифру 2 и цифру 3. На каждую цифру выделяется по 4 бита. Зачем так сделано? Для удобства и экономии памяти. Кроме времени и даты в памяти хранятся несколько бит настроек:

  • Clock Halt — управляет часами. Когда бит установлен то часы стоят. Чтобы запустить ход часов необходимо записать в этот бит 0. После подключения батареи резервного питания, этот бит уставлен и часы не считают время! Об этом нужно помнить.
  • 24/12 — этот бит выбора режима часов. Когда этот бит равен единице то используется 12-ти часовой режим. В противном случае 24-х часовой. Если используется 12-ти часовой режим то пятый бит показывает AM или PM сейчас. Если бит равен 1 то значит PM. В 24-х часовом режиме этот бит используется для хранения десятков часов совместно с битом 4.
  • Output — управляет состоянием ноги SQW/OUT. Бит установлен — на ноге лог 1. Сброшен — на ноге 0. Для управления таким образом, бит SQWE должен быть сброшен.
  • SQWE — когда бит установлен, на ноге SQW/OUT появляются прямоугольные импульсы.
  • RS1, RS0 — этими битами задается частота импульсов. Зависимость частоты от комбинации бит находится в таблице ниже:

Софт

Для работы с часами DS1307 была написана нехитрая библиотека содержащая следующие базовые функции:

DS_start — запускает часы. Запустить часы можно так же установив время.
DS_stop — останавливает часы
DS_set_time — Установка времени. Перед вызовом процедуры нужно поместить в tmp1 — секунды в tmp2 — минуты и в tmp3-часы. Часы в 24-х часовом формате.
DS_get_time: — считывание времени из часов. секунды будут записаны в tmp1, минуты в tmp2, часы в tmp3
DS_get_date: — считывание даты из часов. День будет записан в tmp1, месяц в tmp2, год в tmp3
DS_set_date: — установка даты. Перед вызовом процедуры нужно поместить в tmp1 — день в tmp2 — месяц и в tmp3-год (последние 2 цифры)

Процедуры установки/чтения времени и даты могут воспринимать/возвращать входные данные в двоично-десятичном формате и в десятичном. Для выбора желаемого формата нужно закомментировать или раскомментировать по три строчки в каждой процедуре (в коде есть примечания по этому поводу).

Тестовая программа позволяет управлять часами через UART (скорость 9600, контроллер работает на частоте 8 мГц). При запуске сразу выдаются время, дата и приглашение ввести команду от 1 до 3. При выборе варианта 1 происходит повторное считывание времени/даты. Вариант 2 позволяет установить время, а вариант 3 дату. Если хочется попробовать поиграть с часами в то в архив с исходником включён файл для симуляции.

Точность
Тут очень многое зависит от применяемого кварца и разводки платы. Даташит сообщает что емкость кварца должна быть 12.5 пф. Говорят, что лучше всего применять кварцы от материнских плат. Для коррекции хода можно подпаять к резонатору подстроечный конденсатором и при помощи него в небольших пределах менять частоту. Лично у меня эти часы работают вторые сутки и отстали на 3 секунды. Что-то мне подсказывает, что дело в ёмкости кварца, попробую другой отпишусь.

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