Контакты

Схема часов на микроконтроллере avr. Простые часы на микроконтроллере. Видео работы устройства на Ютуб-канале

Иногда полезно иметь в системе часы отсчитывающие время в секундах, да еще с высокой точностью. Часто для этих целей применяют специальные микросехмы RTC (Real Time Clock) вроде . Вот только это дополнительный корпус, да и стоит она порой как сам МК, хотя можно обойтись и без нее. Тем более, что многие МК имеют встроенный блок RTC. В AVR его правда нет, но там есть асинхронный таймер, служащий полуфабрикатом для изготовления часиков.

Первым делом нам нужен часовой кварц на 32768Герц.

Почему кварц именно 32768Гц и почему его зовут часовым? Да все очень просто — 32768 является степенью двойки. Два в пятнадцатой степени. Поэтому пятнадцати разрядный счетчик, тикающий с частотой 32768 Гц, будет переполняться раз в секунду. Это дает возможность строить часы на обычной логической рассыпухе без каких либо заморочек. А в микроконтроллере AVR организовать часы с секундами можно почти без использования мозга, на рефлексах периферии.

Асинхронный режим таймера
Помните как работают таймеры? Тактовая частота с основного тактового генератора (RC внешняя или внутренняя, внешний кварц или внешний генератор) поступает на предделители, а с выхода предделителей уже щелкает значениями регистра TCNT. Либо сигнал на вход идет с счетного входа Тn и также щелкает регистром TCNT

Для этого на выводы TOSC2 и TOSC1 вешается кварцевый резонатор. Низкочастотный, обычно это часовой кварц на 32768Гц. На он смонтирован справа от контроллера и подключается перемычками. Причем тактовая частота процессора должна быть выше как минимум в четыре раза. У нас тактовая от внутреннего генератора 8Мгц, так что нас это условие вообще не парит:)

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


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

Конфигурирование
Для включения надо всего лишь установить бит AS2 регистра ASSR — и все, таймер работает в асинхронном режиме. Но есть тут одна фича которая мне стоила много головняков в свое время. Дело в том, что при работе от своего кварца все внутренние регистры таймера начинают синхронизироваться по своему же кварцу. А он медленный и основная программа может менять уже введенное значение гораздо быстрей чем оно обработается таймером.

Т.е., например, предустановил ты значение TCNT2, таймер на своей 32кгц молотилке его еще даже прожевать не успел, а твой алгоритм уже пробежал и снова туда что то записал — в результате в TCNT2 наверняка попадет мусор. Чтобы этого не случилось запись буфферизируется. Т.е. это ты думаешь, что записал данные в TCNT2, но на самом деле они попадают во временный регистр и в счетный попадут только через три такта медленного генератора.

Также буфферизируется регистры сравнения OCR2 и регистр конфигурации TCCR2

Как узнать данные уже внеслись в таймер или висят в промежуточных ячейках? Да очень просто — по флагам в регистре ASSR. Это биты TCN2UB, OCR2UB и TCR2UB — каждый отвечает за свой регистр. Когда мы, например, записываем значение в TCNT2 то TCNUB становится 1, а как только наше число из промежуточного регистра таки перешло в реальный счетный регистр TCNT2 и начало уже тикать, то этот флаг автоматом сбрасывается.

Таким образом, в асинхронном режиме, при записи в регистры TCNT2, OCR2 и TCCR2 сначала нужно проверять флаги TCN2UB, OCR2UB и TCR2UB и запись проводить только если они равны нулю. Иначе результат может быть непредсказуемым.

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

  • Запрещаем прерывания от этого таймера
  • Переключаемся в нужный режим (синхронный или асинхронный)
  • Заново настраиваем таймер как нам нужно. Т.е. выставляем предустановку TCNT2 если надо, заново настраиваем TCCR2
  • Если переключаемся в асинхронный режим, то ждем пока все флаги TCN2UB, OCR2UB и TCR2UB будут сброшены. Т.е. настройки применились и готовы к работе.
  • Сбрасываем флаги прерываний таймера/счетчика. Т.к. при всех этих пертурбациях они могут случайно установиться
  • Разрешаем прерывания от этого таймера

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

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

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

Примеры:
Контроллер использует режим энергосбережения и отключения ядра, а пробуждается по прерываниям от асинхронного таймера. Тут надо учитывать тот факт, что если мы будем изменять значения регистров TCNT2, OCR2 и TCCR2, то уход в спячку нужно делат ТОЛЬКО после того, как флаги TCN2UB, OCR2UB и TCR2UB упадут. Иначе получится такая лажа — асинхронный таймер еще не успел забрать данные из промежуточных регистров (он же медленный, в сотни раз медленней ядра), а ядро уже отрубилось. И ладно бы конфигурация новая не применилась, это ерунда.

Хуже то, что на время модификаций регистров TCNT или OCR блокируется работа блока сравнения, а значит если ядро уснет раньше, то блок сравнения так и не запустится — некому его включить будет. И у нас пропадет прерывание по сравнению. Что черевато тем, что событие мы прошляпим и будем их терять до следующего пробуждения из спячки.
А если контроллер будится прерыванием по сравнению? То он уснет окончательно. Опаньки!
Вот и лови такой глюк потом.

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

Еще один прикол с асинхронным режимом и энергосбережением заключается в том, что подсистема прерываний при выходе из спячки стартует за 1 такт медленного генератора. Так что даже если мы ничего не меняли, то обратно в спячку сваливаться нельзя — не проснемся, т.к. прерывания не успеют запуститься.

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

  • Проснулись
  • Что то сделали нужное
  • Заснули

И длительность операции между Проснулись и Заснули НЕ ДОЛЖНА БЫТЬ МЕНЬШЕ чем один тик асинхронного таймера. Иначе анабиоз будет вечным. Можешь delay поставить, а можешь сделать как даташит советует:

  • Проснулись
  • Что то сделали нужное
  • Ради прикола записали что то в любой из буфферизиуемых регистров. Например, в TCNT было 1, а мы еще раз 1 записали. Ничего не изменилось, но произошла запись, поднялся флаг TCN2UB который продержится гарантированно три такта медленного генератора.
  • Подождали пока флаг упадет
  • Уснули.

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

Ну и последний, но важный, момент — после подачи питания, или выхода из глубокой спячки, с отключением не только ядра, а вообще всей периферии, пользоваться медленным генератором настоятельно рекомендуется не раньше чем через 1 секунду (не миллисекунду, а целая секунда!). Иначе генератор может еще быть нестабильным и в регистрах будет еще каша и мусор.

И, в завершение статьи, небольшой примерчик. Запуск асинхронного таймера на Atmega16 (Как полигон используется плата )

Проект типовой, на базе диспетчера, одно лишь отличие — диспечтер переброшен на таймер0, чтобы освободить таймер2.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int main(void ) { InitAll() ; // Инициализируем периферию InitRTOS() ; // Инициализируем ядро RunRTOS() ; // Старт ядра. UDR = "R" ; // Маркер старта, для отладки SetTimerTask(InitASS_Timer, 1000 ) ; // Так как таймер в асинхронном режиме // запускается медленно, то делаем // Выдержку для запуска инициализации таймера. while (1 ) // Главный цикл диспетчера { wdt_reset() ; // Сброс собачьего таймера TaskManager() ; // Вызов диспетчера } return 0 ; }

int main(void) { InitAll(); // Инициализируем периферию InitRTOS(); // Инициализируем ядро RunRTOS(); // Старт ядра. UDR = "R"; // Маркер старта, для отладки SetTimerTask(InitASS_Timer,1000); // Так как таймер в асинхронном режиме // запускается медленно, то делаем // Выдержку для запуска инициализации таймера. while(1) // Главный цикл диспетчера { wdt_reset(); // Сброс собачьего таймера TaskManager(); // Вызов диспетчера } return 0; }

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

При последующих входах проверяются флаговые биты готовности регистров таймера. Если они все по нулям, то мы на всякий случай зануляем флаги прерывания таймера, чтобы не было глюков и ложных срабатываний, а потом разрешаем нужное нам прерывание. И выходим.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void InitASS_Timer(void ) { if (ASSR & (1 << AS2) ) //Если это второй вход то { if (ASSR & (1 << TCN2UB | 1 << OCR2UB | TCR2UB) ) // проверяем есть ли хоть один бит флаговый { SetTask(InitASS_Timer) ; // Если есть, то отправляем на повторный цикл ожидания } else // Если все чисто, то можно запускать прерывания { TIFR |= 1 << OCF2 | 1 << TOV2; // Сбрасываем флаги прерываний, на всякий случай. TIMSK |= 1 << TOIE2; // Разрешаем прерывание по переполнению return ; } } TIMSK &= ~(1 << OCIE2 | 1 << TOIE2) ; // Запрещаем прерывания таймера 2 ASSR = 1 << AS2; // Включаем асинхронный режим TCNT2 = 0 ; TCCR2 = 5 << CS20; // Предделитель на 128 на 32768 даст 256 тиков в секунду // Что даст 1 прерывание по переполнению в секунду. SetTask(InitASS_Timer) ; // Прогоняем через диспетчер, чтобы зайти снова. }

void InitASS_Timer(void) { if(ASSR & (1<

ISR(TIMER2_OVF_vect) // Прерырвание по переполнению таймера 2 { UDR = i; i++; }

Можно было сделать переменные содержащие часы:минуты:секунды и щелкать этими переменными со всей их логикой переполнения часов/минут, но мне было лень. И так все понятно.

Обновлено 23.07.2018. Всем привет. Для работы с часами, в прошлой статье был рассмотрен интерфейс TWI, на который мы сегодня будем ссылаться. Ну что ж начнем. Данные часы являются TWI совместимыми, т.е. принцип обмена данными по шине будет таким же как мы и рассматривали.

На рисунке ниже представлено расположение выводов, описание, и сам вид наших часов или как далее будем их называть RTC (Real-time clock) — часы реального времени или генератор импульсов времени. Данный “девайс” DS1307 считает секунды, минуты, часы, день месяца, месяц, день недели и год вместе с високосными. Календарь действителен до 2100 года. Я думаю на наш век хватит:).

Как видно из описания имеется вход для аварийного питания от батареи, при отключенном внешнем питание. В этом режиме RTC поддерживает только свое основное назначение – отсчет времени, без внешних запросов. Напряжение питания батареи должно быть 2 – 3.5V. В техническом описание пишется что при заряде более 48 мА/ч, при температуре 25 град Цельсия, наша схема продержится около 10 лет. Более чем надо. На рисунке ниже представлена “таблеточка” CR2032 и крепление, которые будем использовать.

Теперь пройдемся по внешнему питанию. Рабочее напряжение часов 5В с небольшим диапазоном 4,5 -5,5В. Напряжение от батареи 3В(минимум 2, максимум 3,5В) Работа RTC делится на три режима по напряжению:

1. Vcc=5В – чтение, запись, отсчет;
2. Vcc= ниже 1,25*Vbat , но выше Vbat +0.2V — только отсчет батареи от внешнего питания.
3. Vcc ниже Vbat: RTC и ОЗУ переходит на питание от батареи. Потребление в активном состоянии 1,5 мА, от батареи 500-800нА.
Напряжение для передачи/приема информации:
Логический 0: -0.5В — +0.8В
Логическая 1: 2.2 В – Vcc+0.3В

Как и в прошлых постах попробуем запустить в Proteus. Отладим код. И перенесем все в железо. Ниже приведена схема подключения.

Где SQW/OUT – это вывод часов который можно запрограммировать на вывод частоты 1Гц, 4.096Гц, 8.192Гц и 32,768Гц. Т.е. можно использовать для внешнего прерывания контроллера с периодичностью в 1 с. Очень полезная функция. Но нам не пригодится. Кстати он тоже с открытым коллектором, поэтому необходим подтягивающий резистор. Номинал 4,7 кОм.

Выводы Х1 и Х2 – к ним подключаем кварцевый резонатор с частотой 32,768 кГц. Либо можно применить внешний тактовый генератор с той же частотой. Но при этом вывод X1 подключается к сигналу, а X2 остаётся неподключенным (висеть в воздухе.).

Ну и выводы SDA и SCL, с которыми мы познакомились в прошлой статье.

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

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

3. Контур в виде защитного кольца необходимо поместить вокруг кристалла, что помогает изолировать кристалл от шума.

4. Проводники поместить в кольцо и и подключить к заземлению.

5. Припаиваем резонатор к земле. Если земля разведена верно и есть уверенность.

На рисунке ниже видно контур и место припая к земле.

Как подключать разобрались. Идем далее – разберемся как с ним работать. RTC является программируемым и имеет 8 байт специальных регистров для его конфигурации и энергонезависимую статическую память 56 байтов. Для обмена информации необходима 2-х проводная шина данных, т.е. последовательная шина данных- который мы рассмотрели в прошлой статье. Итак для работы пробежимся по даташиту. Что нам необходимо:

Таблица регистров. Рисунок ниже. Первые восемь регистров – для вывода и программирования наших часов. При обращении по адресу 00H к 7-му биту(CH) и установкой его в 0 –запускаем часы. Хочется отметить, что конфигурация регистров может быть любая, поэтому при первом запуске необходимо его настроить под свои требования. Остальные семь битов единицы и десятки секунд.

01H – Минуты.
02H – Часы, которые настраиваются:
— Бит 6 – при 1 вывод 12 часовой формат, 0 – 24.
— Бит 5 – при 1 (при 12 часовом формате) PM , 0-AM
— Бит 5 – (при 24 ч формате) это вывод второго десятка часов (20-23часа.)
— Бит4 – первый десяток часов, остальные биты это единицы часов.
03H – день недели;
04H – дата;
05H – месяц года
06H – год.

Ну и последний регистр 07H. Данный регистр является управляющим.Где OUT отвечает за управление выводом SQW/OUT. Ниже таблица включения вывода.

OUT
SQWE
SQW/OUT
1
0
1
0
0
0

SQWE – при установке этого бита в,1 на вывода выходят импульсы с заданной частотой,которая устанавливается,битами RS1 и RS0.

Этот вывод нам не пригодится в проекте. Хотя для него я развел на плате дорожку. В качестве экспериментов может быть где то в будущем и применим, ведь здесь можно сделать прерывании в 1 с.

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

Обратите внимание, что резонатор в Proteus, можно и не подключать к часам(обведенное красным).

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

Программа. Рассмотрев основные настройки часов напишем функцию инициализации.

/*Функция инициализации включает в себя установку скорости обмена данных по формуле(в предыдущей статье), установка пред делителя и включение модуля TWI*/
void init_DS1307 (void)
{
TWBR = 2; /*При частоте 1 МГц */
TWSR = (0 << TWPS1)|(0 << TWPS0); /*Пред делитель на 64*/
TWCR |= (1 << TWEN); /*Включение модуля TWI*/
}

void write_DS1307 (uint8_t reg, uint8_t time) /*передаем два параметра: адрес регистра, к которому будем обращаться и передаваемую информацию*/
{
/* Формируем состояние СТАРТ, выставляя разряды регистра управления*/
TWCR = (1<
/*Разрешить работу модуля TWEN; Сформировать состояние старт TWSTA; Сбросить флаг TWINT */
/*Ждем окончания формирования условия старт, т.е. пока не установится флаг, код статуса = 08*/
while (!(TWCR & (1<
/*Далее перелаем пакет адреса (адрес устройства). Содержимое пакета загружается в регистр TWDR*/
TWDR = 0xd0; /*0b1101000 + 0 – адрес + бит записи*/
/*Сбрасываем флаг для передачи информации*/
TWCR = (1<
/*Ждем установки флага*/
while (!(TWCR & (1<
/*передаем регистр к которому будем обращаться*/
TWDR = reg;
TWCR = (1<
while (!(TWCR & (1<
/*Передаем информацию для записи в байт регистра*/
TWDR = time;
TWCR = (1<
while (!(TWCR & (1<
/*формируем состояние СТОП*/
TWCR = (1<
}

В этой функции мы передали три байта, адрес устройства, адрес регистра и байт информации для записи в этот регистр и сформировали состояние СТОП.

Осталась последняя функция чтения. Ниже формат чтения.

В данной функции выполняется передача байта адреса устройства +бит записи, байт адреса регистра для установки на него указатель, выполнение условия ПОВСТАР, передача байта адреса устройства +бит чтения, чтение регистра, адрес которого мы передали ранее.

Если мы будем обращаться к часам в формате чтения, то при повторном обращении к часам указатель сдвигается на один байт вниз включая 56 байт ОЗУ, от 00H до 3FH. При достижении последнего адреса, указатель переходит на адрес 00.

/*Функция чтения данных из DS1307*/
uint8_t read_DS1307 (uint8_t reg) /*Передаем адрес регистра*/
{
uint8_t time;
/*формируем состояние СТАРТ*/
TWCR = (1<
while (!(TWCR & (1<
TWDR = 0xd0; /*Передаем адрес + бит записи*/
TWCR = (1<
while (!(TWCR & (1<
TWDR = reg; /*Адрес регистра*/
TWCR = (1<
while (!(TWCR & (1<
/*формируем состояние ПОВСТАР*/
TWCR = (1<
while (!(TWCR & (1<
TWDR = 0xd1; /*Передаем адрес + бит чтения*/
TWCR = (1<
while (!(TWCR & (1<
/*считываем данные*/
TWCR = (1<
while (!(TWCR & (1<
time = TWDR;
time = (((time & 0xF0) >> 4)*10)+(time & 0x0F);
/*формируем состояние СТОП*/
TWCR = (1<
return time;
}

Итак выше мы написали три функции, которые нам необходимы для работы с часами. Используя эти функции запустим программу в Proteus. Выведем, например дату.

#include
#include
uint8_t time;
void init_DS1307 (void);
uint8_t read_DS1307 (uint8_t reg);
void write_DS1307 (uint8_t reg, uint8_t time);
int main (void)
{
DDRC = 0×00; /*Выставляем порт как вход*/
PORTC = 0xFF; /*Подтягиваем сопротивление*/
init_DS1307;
while (1)
{
_delay_ms (50);
read_DS1307 (0×04); /*Чтение регистра даты*/
}
}

Ниже результат выполнения программы чтение даты.

В окне отладчика I2C (TWI ) видно что сначала посылается адрес регистра в RTC (зеленый кружочек), в данном случае 04, который отвечает за дату месяца, и далее часы передают ответ 21 (красный кружочек).

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

while (1)
{
_delay_ms (500);
read_DS1307 (0×01); /*Считываем минуту*/
_delay_ms (500);
write_DS1307 (0×01, 15); /*Записываем необходимую минуту*/
_delay_ms (500);
read_DS1307 (0×01); /*Считываем минуту*/
}

На рисунке видно, что сначала идет обращение к регистру 01, считывается минута 23. Далее мы используем функцию записи, и вносим значение 15. При следующей функции чтения у нас на табло часов значение 15.

Ну и последний пример программы это вывод значений всех регистров

while (1)
{
delay_ms (500);
read_DS1307 (0×00);
_delay_ms (500);
read_DS1307 (0×01);
_delay_ms (500);
read_DS1307 (0×02);
_delay_ms (500);
read_DS1307 (0×03);
_delay_ms (500);
read_DS1307 (0×04);
_delay_ms (500);
read_DS1307 (0×05);
_delay_ms (500);
read_DS1307 (0×06);
_delay_ms (500);
}

На рисунке ниже видно, что вывелись данные 7-ми регистров.

Исходник с проектом прилагается:

(Скачали: 601 чел.)

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

Схема и программа очень простых часов на микроконтроллере AVR с использованием микросхемы реального времени DS1307

Доброго дня уважаемые радиолюбители!
Приветствую вас на сайте “ “

Сегодня, уважаемые радиолюбители, вашему вниманию предлагается очень простая схема часов на микроконтроллере AVR и часов реального времени с последовательным интерфейсом I2C DS1307 .

Конструкция собрана на микроконтроллере ATyni26 (просто именно этот МК был под рукой). Но вы можете применить любой другой МК, главное чтобы у него было 13 свободных входов – 11 для вывода текущего времени на четырехразрядный семисегментный светодиодный индикатор и 2 вывода – на кнопки установки и коррекции времени.

Схема часов:

В схеме применены следующие детали:
- Микроконтроллер – ATyni26 в DID корпусе
– Часы реального времени – DS1307 в DIP корпусе
– Кварц – 32,768 кГц, с входной емкостью 12 пф (можно взять с материнской платы компьютера), от этого кварца зависит точность хода часов
– резервное питание DS1307 – 3 вольтовый литиевый элемент CR2032
– 4-разрядный семисегментный светодиодный индикатор – FYQ-5641UB -21 с общим катодом (ультраяркий, голубого цвета свечения)
– все транзисторы – NPN-структуры, можно применить любые (КТ3102, КТ315 и их зарубежные аналоги), я применил ВС547С
– микросхемный стабилизатор напряжения типа 7805
– все резисторы мощностью 0,25 ватт
– полярные конденсаторы на рабочее напряжение 50 вольт
Ток потребления устройством составляет до 30 мА.
Для питания конструкции можно использовать любое ненужное зарядное устройство от телефона или подходящий блок питания с выходным напряжением 7-9 вольт.
Общение микроконтроллера с часами DS1307 происходит по шине I2C и организовано программным путем.
Батарейку резервного питания часов DS1307 можно и не ставить, но в этом случае, при пропадании напряжения в сети, текущее время придется устанавливать заново.
Печатная плата устройства не приводится, конструкция была собрана в корпусе от неисправных механических часов. Светодиод (с частотой мигания 1 Гц) служит для разделения часов и минут в конструкции.

Работа программы.
Тактовая частота работы микроконтроллера – 1 мГц (заводская установка, FUSE-биты трогать и устанавливать не надо). Размер программы – 1 килобайт.
При запуске программы происходит:
- запуск таймера Т0 с предустановленной частотой СК/8 и вызовом прерывания по переполнению (при такой предустановленной частоте вызов прерывания происходит каждые 2 миллисекунды)
– инициализация портов (порты РА0-6 и РВ0-3 настраиваются на вывод, РА7 и РВ6 на ввод)
– инициализация шины I2C (выводы РВ4 и РВ5)
– при первом запуске, или повторном запуске при отсутствии резервного питания DS307, проверяется 7 бит (СН) нулевого регистра DS1307 и происходит переход в первоначальную установку текущего времени. При этом, кнопка S1 – для установки времени, кнопка S2 – переход к следующему разряду. Установленное время – часы и минуты записываются в DS1307 (секунды устанавливаются в ноль), а также вывод SQW/OUT (7-й вывод) настраивается на генерацию прямоугольных импульсов с частотой 1 Гц
– разрешается глобальное прерывание
– программа переходит в цикл с опросом копки S2
При переполнения счетчика таймера Т0 программа переходит к обслуживанию прерывания (каждые 2 мс):
– считывается текущее время с DS1307 которое записывается в четыре переменные SRAM (десятки часов, единицы часов, десятки минут, единицы минут)
– подпрограммой вывода текущего времени производится динамическая индикация текущего времени на светодиодном индикаторе
– при нажатии кнопки S2 программа запрещает глобальное прерывание и переходит в подпрограмму коррекции времени (кнопками S1 и S2 устанавливаются десятки и единицы минут, затем, с 0 секунд, нажатием кнопки S2 происходит запись уточненного времени в DS1307, разрешение глобального прерывания и возвращение в основную программу).

Примененные в схеме часы DS1307 позволяют выводить на индикацию секунды, минуты, часы, день недели, дату и год.
Если в схеме вместо светодиодных индикаторов применить LCD дисплей, к примеру WH0802 (двухстрочный, с выводом восьми символов в строке) или аналогичный, то можно организовать полноценные часы с полным выводом текущего времени, а питание устройства организовать от гальванических элементов или аккумуляторных батарей.

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

Расположение выводов DS1307:

Типовая схема подключения D1307:

Урок 23

Часть 1

Собираем часы на DS1307 и LED индикаторе

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

Но, несмотря на все наши знания урок не обещает быть коротким, а наоборот будет очень обширным, так как сборка часов на таких индикаторах требует решение очень многих дополнительных задач особенно потому, что у нас не 32 знакоместа, как на нашем LCD, а всего 4 и точка или двоеточие. И все показания надо выводить будет в порядке определённой очереди, а также потребует от нас умения организовать изменение показаний (перевода) с помощью кнопок, а может быть вообще с помощью одной только кнопки в рамках использования всего четырех цифр.

Но мы не боимся трудностей, с ними даже интереснее.

Поэтому начнём что-нибудь выдумывать.

Индикатор будет вот такого вот типа, из них именно типа с общим анода

А вот так индикатор выглядит вживую

Судя по клеточкам размером 5 милиметров несложно оценить его размеры.

Вот его вид с обратной стороны

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

Вот они оба для сравнения

Вот маркировка маленького индикатора

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

Данную схему мы хорошо помним из урока по . Только там было всего 2 индикатора, поэтому подключим ещё два, также будут использованы ещё два транзисторных ключа, на базу которых пойдут команды через токоограничивающие резистора на 2 килоома от ножек портов PB2 и PB4 . Третью ножку порта B мы пропустим в целях её дальнейшего использования в другом альтернативном качестве, в качестве ножки аппаратного ШИМ для регулировки яркости свечения индикатора.

Создадим проект с именем MyClock1307LED , а код весь в главный модуль возьмём как раз из урока по динамической индикации, из проекта Test08 . В ту пору у нас пока не было модульного программирования и весь полезный код содержался в одном главном модуле.

Для начала соберём наш проект, подключим контроллер и его прошьём и посмотрим для интереса результат работы кода

Мы видим что два правых индикатора у нас нормально работают. Но нам нужно четыре.

Для начала для этого мы добавим ещё две переменные для двух неиспользуемых разрядов

unsigned int i ;

unsigned char R1 =0, R2 =0, R3 =0, R4 =0 ; //цифры разрядов индикатора

Объявим ещё соответствующие данным анодам ножки порта на выход в функции port_ini(), также включая и ножку для ШИМ

DDRB = 0b000111 11;

Также добавим код в функцию ledprint

void ledprint ( unsigned int number )

R1 = number %10;

R2 = number %100/10;

R3 = number %1000/100;

R4 = number /1000;

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

Переменные мы рассчитали, осталось их как-то отобразить на индикаторе.

Для этого мы изменим код в обработчике прерывания от таймера

ISR ( TIMER1_COMPA_vect )

if ( n_count ==0) { PORTB &=~(1<< PORTB0 ); PORTB |=(1<< PORTB1 )|(1<< PORTB2 )|(1<< PORTB4 ); segchar ( R1 );}

if ( n_count ==1) { PORTB &=~(1<< PORTB1 ); PORTB |=(1<< PORTB0 )|(1<< PORTB2 )|(1<< PORTB4 ); segchar ( R2 );}

if ( n_count ==2) { PORTB &=~(1<< PORTB2 ); PORTB |=(1<< PORTB0 )|(1<< PORTB1 )|(1<< PORTB4 ); segchar ( R3 );}

if ( n_count ==3) { PORTB &=~(1<< PORTB4 ); PORTB |=(1<< PORTB0 )|(1<< PORTB1 )|(1<< PORTB2 ); segchar ( R4 );}

N_count ++;

If ( n_count >3 ) n_count =0;

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

С этим всё.

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

OCR1AH = 0b00001111 ; //записываем в регистр число для сравнения

OCR1AL =0b01000010 ;

Ну и сделаем возможность нашему счётчику считать не до 99, а до 9999 для этого в цикле мы напишем не 100, а 10000

for ( i =0; i < 10000 ; i ++)

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

Delay_ms (10 );

Соберём код, прошьём контроллер и посмотрим результат нашей работы

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

5 542 Этот вариант часов сделан таким образом, чтобы максимально упростить схему, снизить энергопотребление, и в итоге получить прибор, который легко помещается в кармане. Выбрав миниатюрные аккумуляторы для питания схемы, SMD - монтаж и миниатюрный динамик (например от нерабочего мобильного телефона), Вы можете получить конструкцию, размером чуть больше спичечного коробка.
Применение сверхъяркого индикатора позволяет снизить ток, потребляемый схемой. Снижение тока потребления также достигается в режиме "LoFF" - индикатор погашен, при этом включена только мигающая точка младшего разряда часов.

Индикация
Регулируемая яркость индикаторов позволяет выбрать наиболее комфортное отображение показаний (и опять же снизить энергопотребление).
В часах реализовано 9 режимов индикации. Переход по режимам осуществляется с помощью кнопок "плюс" и "минус". Перед выводом на индикацию самих показаний, на индикаторы выводится короткая подсказка названия режима. Длительность вывода подсказки - одна секунда. Применение кратковременных подсказок позволило достичь хорошей эргономичности часов. При переходах по режимам отображения (которых получилось достаточно много, для такого простого прибора, как обычные часы) не возникает путаницы, и всегда понятно, какие именно показания выведены на индикатор.

Коррекция показаний, выведенных на индикатор включается при нажатии на кнопку "Коррекция". При этом кратковременная подсказка выводится на 1/4 секунды, после чего корректируемое значение начинает мигать с частотой 2 Гц. Корректируются показания кнопками "плюс" и "минус". При длительном нажатии на кнопку, включается режим автоповтора, с заданной частотой. Частоты автоповтора нажатия кнопки составляют: для часов, месяцев и дня недели - 4 Гц; для минут, года и яркости индикатора - 10 Гц; для корректирующего значения - 100 Гц.
Все откорректированные значения, кроме часов, минут и секунд, записываются в EEPROM и восстанавливаются после выключения - включении питания. Секунды при коррекции обнуляются. Из всех режимов, кроме часы-минуты, минуты-секунды и LoFF организован автоматический возврат. Если в течение 10 секунд ни одна из кнопок не нажата, то часы переходят в режим отображения часов - минут.
Нажатием на кнопку "Вкл/Выкл буд." включается/выключается будильник. Включение будильника подтверждается коротким двухтональным звуком. При включенном будильнике светится точка в младшем разряде индикатора.
В режиме "Corr" на индикатор выведена корректирующая константа, начальное значение которой 5000 микросекунд в секунду. При отставании часов константу увеличиваем на величину отставания, вычисленное в микросекундах за одну секунду. Если часы спешат, то константу уменьшаем по тому же принципу.

Схема



Понравилась статья? Поделитесь ей