Реклама ⓘ
Главная » Микроконтроллеры
Призовой фонд
на май 2024 г.
1. 1000 руб
Сайт Паяльник

Реклама ⓘ

Часы ATtiny 2313, DS1302, TM1637 на ассемблере

Вступление

Решив увеличить свои знания не в «ширину», а в «глубину» получился этот проект. Захотелось реализовать на практики полученные знания по ассемблеру на чипах AVR семейства tiny. В наличии был модуль часов реального времени ds1302, микроконтроллер ATtiny2313A и модуль на контроллере TM1637 с четырьмя элементами для отображения с точками и двоеточием. А готовых и понятных примеров на ассемблере в русскоязычном интернете очень и очень мало!

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

Имеющиеся устройства

Микроконтроллер ATTiny2313a (ссылка)

Модуль часов реального

времени DS1302 (ссылка)

Модуль TM1637 (ссылка)

Микроконтроллер ATTiny2313a модуль часов реального времени ds1302 Модуль TM1637

Режимы и кнопки

Циферблат у часов четырёхэлементный, будем отображать данные с помощью режимов. Для переключения режимов используем кнопку. Назовём её «Clock_mode / Set».

Режимы («Clock_mode») я выбрал такие:

  • Clock_mode = 0 — отображение времени. Пример: 23:26
  • Clock_mode = 1 — отображение минут и секунд. Пример: 26:01
  • Clock_mode = 2 — отображение даты. Пример: 1310

Устанавливаем ещё одну кнопку, назовём её «Mode». При нажатии на неё начинают моргать по очереди: часы, минуты, число, месяц, год и т.д. Что моргает в текущий момент, то и увеличивается на единицу с помощью кнопки «Clock_mode / Set». Независимо от того, что сейчас отображается (какой режим Clock_mode), первое нажатие кнопки «Mode» будет всегда одинаково. То есть отображается сейчас дата (Clock_mode = 2), нажимаем на кнопку «Mode», отображается время и начинает моргать её первая часть, то есть часы. Нажимаем «Mode» ещё раз и, уже, моргают минуты и т. д.

Режимы «Mode»:

  • Mode = 0 — нормальный режим. Отображается то, что выбрано кнопкой «Clock_mode / Set».
  • Mode = 1 — моргают часы. Пример: 12:00
  • Mode = 2 — моргают минуты. Пример: 12:00
  • Mode = 3 — моргает число месяца. Пример: 21.07
  • Mode = 4 — моргает номер месяца. Пример: 21.07
  • Mode = 5моргает год. Пример: 2033
  • Mode = 6 — моргает вкл.\выкл будильника. Пример: A-ON или A-OF
  • Mode = 7 — моргают часы будильника. Пример: 07:30
  • Mode = 8 — моргают минуты будильника. Пример: 07:30
  • Mode = 9 — моргают и часы и минуты. В данный режим микроконтроллер переходит только программно, когда срабатывает будильник!

Обращу внимание на дату. В месяцах максимальное значение числа разное. А ещё есть високосные годы. Високосный год - каждый четвёртый, каждый сотый год не является високосным! Устанавливая дату, мы должны учитывать выше написанное. Ещё можно выставить 31-е число 1-го месяца, а потом сменить месяц на 2-й. Это тоже учитываем. Ограничения для года в своём коде я установил такие: 2000 — 2099, думаю больше/меньше не надо, но всё в ваших руках.

Схема подключения

Схема подключения

Модуль с циферблатом и модуль времени я подключил к порту PORTB с нулевой ножки. Выход на зуммер подключён к ноге PB2, т.к. звук будет генерироваться от таймера TIM0. Кнопки подключаем к ножкам внешних прерываний: «Mode» к INT0, «Clock_mode / Set» к INT1. Изначально планировалось три кнопки. Третья кнопка регулировала бы яркость дисплея, но так как устройство работает автономно было решено настроить минимальную яркость и убрать кнопку. Осциллограф нужен только для отладки программы.

Работа модулей

DS1302

DS1302 работает на собственном 3-х проводном протоколе. Имеет следующие регистры:

Название регистров Адрес чтения Адрес записи 7 бит 6 бит 5 бит 4 бит 3 бит 2 бит 1 бит 0 бит

Примечание

Секунды 0x81 0x80 CH старший разряд младший разряд 00 - 59
Минуты 0x83 0x82 0 старший разряд младший разряд 00 - 59
Часы 0x85 0x84 1 0 AM/PM старший младший разряд 00 - 12
0 старший разряд 00 - 23
День 0x87 0x86 0 0 старший разряд младший разряд 01 - 31
Месяц 0x89 0x88 0 0 0 старший разряд младший разряд 01 - 12
День недели 0x8B 0x8A 0 0 0 0 0 число 01 - 07
Год 0x8D 0x8C старший разряд младший разряд 00 - 99
Защита от записи 0x8F 0x8E WP 0 0 0 0 0 0 0 Флаги управления чипом
Управление 0x91 0x90 TCS3 TCS2 TCS1 TCS0 DS1 DS0 RS1 RS0 Флаги управления устройством заряда малым током
Пакетная передача 0xBF 0xBE                 Пакет из 8-ми регистров: секунды - защита от записи
Свободные регистры

0xC1

0xFD

0xC0

0xFC

               

Регистры могут использоваться для

хранения данных

Пакетная передача 0xFF 0xFE                 Пакет из 31-го свободного регистра

Назначение флагов:

CH (Clock Halt) - флаг отключения часов: значение «1» - останавливает часы, значение «0» - запускает.
WP (Write-Protect) - флаг защиты от записи: значение «1» - запрещает запись данных в регистры модуля, значение «0» - разрешает.
TCS (Trickle Charger Select) - флаги TCS3, TCS2, TCS1, TCS0 включения устройства заряда малым током.
DS (Diode Select) - флаги DS1, DS0 подключения диодов в устройстве заряда малым током.
RS (Resistor Select) - флаги RS1, RS0 подключения резисторов в устройстве заряда малым током.

Распиновка DS1302:

ds1302

Ножки X1 и X2 - подключение кварцевого резонатора на 32,768 кГц. VCC1 - подключение батарейки, VCC2 - основное питание. Ножка RST отвечает за "включение\выключение" контроллера. Когда на ней логическая единица, микроконтроллер принимает и передаёт данные, когда ноль - нет. Ножка SCLK это линия тактирования. Ножка I/O - приём и передача данных. Обращу ваше внимание, что данные передаются младшим битом вперёд, а так же в упакованном формате. По спецификации DS1302 использует  VCC2, если его напряжение больше, чем на VCC1 + 0,2 В. Я выбрал источником питания только VCC2, т.к. устройство и так работает от аккумуляторов. Далее рассмотрим алгоритм работы контроллера на примерах.

Чтение минут Запись часов
Считали 08 минут. Записали 18 часов.

 Алгоритм чтения данных:

  1. Устанавливаем ножку RST (включаем контроллер)
  2. Передаём первый байт - байт адреса регистра. В нашем случае - это 0x83.
  3. Считываем байт данных. На картинке - это 0x08
  4. Сбрасываем ножку RST (выключаем контроллер)

 Алгоритм записи данных:

  1. Устанавливаем ножку RST (включаем контроллер)
  2. Передаём первый байт - адрес регистра. В нашем случае - это 0x84.
  3. Передаём второй данных. На картинке - это 0x18
  4. Сбрасываем ножку RST (выключаем контроллер)

Задержки между операциями я подбирал практически. В моём случае они, приблизительно, равны 10 мкс. Перед отправкой данных выставляется ножка I/O в соответствующее состояние, далее по переднему фронту SCLK, DS1302 считывает данные. При чтении данные считываются по заднему фронту SCLK.

Листинг файла ds1302.asm.

;========================================================
;       Подпрограммы начала и конца передачи данных
;========================================================

DS1302_send_start:
	sbi		PORT_DS1302, DS1302_CE
	rcall	MCU_wait_10mks

	ret

DS1302_send_stop:
	cbi		PORT_DS1302, DS1302_SCLK
	cbi		PORT_DS1302, DS1302_IO
	cbi		PORT_DS1302, DS1302_CE

	rcall	MCU_wait_10mks

	ret

;========================================================
;       Отправка байта из регистра BYTE
;========================================================

DS1302_send_byte:
	push	r16
	push	BYTE

	;------------------------- Вывод DAT на выход

	cbi		PORT_DS1302, DS1302_IO
	sbi		DDR_DS1302, DS1302_IO

	;------------------------- Счётчик цикла

	clr		r16

	;------------------------- Начало цикла

	DS1302_while_send:
		cpi		r16, 0x08
		brsh	DS1302_while_send_end

		cbi		PORT_DS1302, DS1302_SCLK
		rcall	MCU_wait_10mks

		lsr		BYTE			
		brcc	DS1302_cbi_send_bit
			
		DS1302_sbi_send_bit:
			sbi		PORT_DS1302, DS1302_IO
			rjmp	DS1302_while_send_bit

		DS1302_cbi_send_bit:
			cbi		PORT_DS1302, DS1302_IO

		DS1302_while_send_bit:

			;------------------------- Отправка бита

			sbi		PORT_DS1302, DS1302_SCLK
			rcall	MCU_wait_10mks

			inc		r16

		rjmp	DS1302_while_send

	;------------------------- Выход из цикла

	DS1302_while_send_end:
		pop		BYTE
		pop		r16

	ret

;========================================================
;       Получение байта, результат в регистр BYTE
;========================================================

DS1302_receive_byte:
	push	r16

	;------------------------- Вывод DAT на вход

	cbi		PORT_DS1302, DS1302_IO
	cbi		DDR_DS1302, DS1302_IO

	;------------------------- Счётчик цикла

	clr		r16
	clr		BYTE

	;------------------------- Начало цикла

	DS1302_while_receive:
		cpi		r16, 0x07
		brsh	DS1302_while_receive_end

		rcall	DS1302_receive_byte_write_bit

		lsr		BYTE
		sbi		PORT_DS1302, DS1302_SCLK
		rcall	MCU_wait_10mks

		inc		r16
		rjmp	DS1302_while_receive

	;------------------------- Выход из цикла и получение последнего бита

	DS1302_while_receive_end:
		rcall	DS1302_receive_byte_write_bit
		sbi		PORT_DS1302, DS1302_SCLK

		pop		r16

	ret

DS1302_receive_byte_write_bit:
	cbi		PORT_DS1302, DS1302_SCLK
	rcall	MCU_wait_10mks
	sbic	PIN_DS1302, DS1302_IO
	ori		BYTE, 0x80
	sbis	PIN_DS1302, DS1302_IO
	andi	BYTE, 0x7f

	ret

;========================================================
;			Считать данные с ds1302 пакетом
;========================================================

DS1302_read_package_data:
	push	r17
	push	r16
	push	r15
	push	XH
	push	XL
	push	YH
	push	YL

	ldi		XH, high(bcd_seconds)
	ldi		XL, low(bcd_seconds)

	ldi		YH, high(tm_s1)
	ldi		YL, low(tm_s1)

	rcall	DS1302_send_start
	ldi		BYTE, 0xBF
	rcall	DS1302_send_byte

	clr		r17

	DS1302_read_package_data_while_receive:
		cpi		r17, 0x07
		brsh	DS1302_read_package_data_while_receive_end

		rcall	DS1302_receive_byte

		st		X+, BYTE
		push	r17
		mov		r17, BYTE
		rcall	conv_ds1302_to_tm1637
		st		Y+, r16
		st		Y+, r15
		pop		r17

		inc		r17

		rjmp	DS1302_read_package_data_while_receive

	DS1302_read_package_data_while_receive_end:

		rcall	DS1302_send_stop

		pop		YL
		pop		YH
		pop		XL
		pop		XH
		pop		r15
		pop		r16
		pop		r17

	ret

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

TM1637

Распиновка модуля:

У него свой 2-х проводной интерфейс. CLK - линия тактирования. DIO - приём\передача данных. Когда CLK на высоком уровне, и DIO меняется с высокого на низкий - начинается ввод данных. Когда CLK находится на высоком уровне и DIO изменяется от низкого к высокому, ввод данных заканчивается. Данные передаются, так же как и с DS1302, младшим битом вперёд.

У микроконтроллера TM1637 есть три вида команд: команды данных, команды адреса и команды контроля дисплея. Они отличаются двумя старшими битами.

Команды данных:

Бит 7 Бит 6 Бит 5 Бит 4 Бит 3 Бит 2 Бит 1 Бит 0 Функция Описание
0 1 0     0 0 Режим чтения\записи Запись данных в регистр дисплея
0 1     1 0 Чтение key scan данных 
0 1   0     Режим адресации Автоматический инкремент адреса 
0 1   1     Фиксированный адрес
0 1 0       Режим тестирования Нормальный режим
0 1 1       Тестовый режим 

Что такое тестовый режим я в документации не нашёл. 

Команды адреса:

Бит 7 Бит 6 Бит 5 Бит 4 Бит 3 Бит 2 Бит 1 Бит 0 Адрес дисплея
1 1 0 0 0 0 0
C0H
1 1 0 0 0 1 C1H
1 1 0 0 1 0 C2H
1 1 0 0 1 1 C3H
1 1 0 1 0 0 C4H
1 1 0 1 0 1 C5H

В нашем случае элементов для отображения 4-и, значит и адреса у нас C0H - C3H. 

Команды контроля дисплея:

Бит 7 Бит 6 Бит 5 Бит 4 Бит 3 Бит 2 Бит 1 Бит 0 Функция Описание
1 0 0   0 0 0 Настройки яркости Ширина импульса устанавливается как 1/16
1 0   0 0 1 Ширина импульса устанавливается как 2/16
1 0   0 1 0  Ширина импульса устанавливается как 4/16
1 0   0 1 1 Ширина импульса устанавливается как 10/16
1 0   1 0 0 Ширина импульса устанавливается как 11/16
1 0   1 0 1 Ширина импульса устанавливается как 12/16
1 0   1 1 0 Ширина импульса устанавливается как 13/16
1 0   1 1 1 Ширина импульса устанавливается как 14/16
1 0 0      

Включение выключение

дисплея

Выключить дисплей
1 0 1       Включить дисплей

Разберём пример:

Байт команды данных Байт команды адреса Байт данных

 Алгоритм записи данных (установка значения одного элемента):

  1. Сигнал START
  2. Передаём байт команды данных. В нашем примере - это 0x44.
  3. Ожидание бита ответа
  4. Сигнал STOP
  5. Сигнал START
  6. Передаём байт адреса регистра. В нашем случае - это второй элемент на дисплее, его адрес - 0xC1
  7. Ожидание бита ответа
  8. Передаём байт данных
  9. Ожидание бита ответа
  10. Сигнал STOP

Разберём этот алгоритм. Сигнал START - при высоком уровне CLK, DIO устанавливается с высокого на низкий. Сигнал STOPT - при высоком уровне CLK, DIO устанавливается с низкого на высокий. Ожидание бита ответа - после приёма 8-ми бит, контроллер TM1637 устанавливает на DIO низкий уровень как сигнал о том, что он всё принял корректно. Первый байт - это байт команды, 0x44 - 0b0100 0100. Смотрим по табличке "Команды данных" и определяем, что режим адресации устанавливается в фиксированное значение. Далее следует байт команды адреса, в котором указывается адрес регистра дисплея для отображения. 0xC1 -  это второй элемент на дисплее. Следующий байт -это сами данные, которые надо отобразить на дисплее. Теперь подробнее про то, что нужно передавать для отображения того или иного числа или буквы. 

Выше нарисован один элемент дисплея разбитый на 8-мь сегментов. Какой бит установлен, тот сегмент и будет гореть на дисплее. Для отображения единицы (бит 1 и бит 2) нужно передать значение 0b00000110. Для отображения нуля - 0b00111111. Седьмой бит отвечает за точку на дисплеях с точками. У меня дисплей и с двоеточием. Для зажигания двоеточия нужно установить 7-й бит у второго элемента. На схеме Proteus я использовал два вида дисплеев - с точками и двоеточием. 7-й бит второго элемента зажигает второю точку, а не двоеточие. Для двоеточия нужно использовать 4-й элемент. На примере выше число 0x86 - это единица с включенным двоеточием, т.к. байт адреса 0xC1 - это второй элемент на дисплее. Если бы мы на примере выше в байте команды установили не фиксированный режим адреса, а автоматический, то нужно было бы после команды адреса передать столько байт, сколько элементов подряд на дисплее мы хотели бы установить. 

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

Листинг файла tm1637.asm

;========================================================
;       Подпрограммы начала и конца передачи данных
;========================================================

TM1637_start:
	sbi		PORT_TM1367, TM1637_CLK
	sbi		PORT_TM1367, TM1637_DATA
	nop
	cbi		PORT_TM1367, TM1637_DATA

	ret

TM1637_stop:
	cbi		PORT_TM1367, TM1637_CLK
	cbi		PORT_TM1367, TM1637_DATA
	nop
	sbi		PORT_TM1367, TM1637_CLK
	sbi		PORT_TM1367, TM1637_DATA

	ret
	
;========================================================
;			Отправка байта из регистра BYTE
;========================================================

TM1637_send_byte:
	push	r16
	push	BYTE

	;------------------------- Счётчик цикла

	clr		r16

	;------------------------- Начало цикла

	TM1637_while_send:
		cpi		r16, 0x08
		brsh	TM1637_wait_ACK

		cbi		PORT_TM1367, TM1637_CLK

		lsr		BYTE
		brcc	TM1637_cbi_send_bit

		TM1637_sbi_send_bit:
			sbi		PORT_TM1367, TM1637_DATA
			rjmp	TM1637_while_send_bit

		TM1637_cbi_send_bit:
			cbi		PORT_TM1367, TM1637_DATA 

		TM1637_while_send_bit:

			;------------------------- Отправка бита, задержки в один nop на 1МГ хватает

			nop
			sbi		PORT_TM1367, TM1637_CLK                    
			nop

			inc		r16

			cbi		PORT_TM1367, TM1637_CLK
			nop
			cbi		PORT_TM1367, TM1637_DATA
			rjmp	TM1637_while_send

	;------------------------- Ожидания бита ответа

	TM1637_wait_ACK:
		sbic	PIN_TM1367, TM1637_DATA
		rjmp	TM1637_wait_ACK          
    
		sbi		DDR_TM1367, TM1637_DATA
		sbi		PORT_TM1367, TM1637_CLK
		nop
		cbi		PORT_TM1367, TM1637_CLK

		pop		BYTE
		pop		r16

	ret

;========================================================
;				 Отображение данных
;========================================================	

TM1637_display:
	push	BYTE

	;------------------------- Команда записи в регистр дисплея c автоматическим инкрементом адреса

	rcall	TM1637_start
	ldi		BYTE, 0x40					
	rcall	TM1637_send_byte
	rcall	TM1637_stop

	;------------------------- Начальный адрес - первый символ дисплея

	rcall	TM1637_start
	ldi		BYTE, 0xC0
	rcall	TM1637_send_byte

	;------------------------- Запись данных для каждого регистра дисплея

	mov		BYTE, TM1637_char1
	rcall	TM1637_send_byte
	mov		BYTE, TM1637_char2
	rcall	TM1637_send_byte
	mov		BYTE, TM1637_char3
	rcall	TM1637_send_byte
	mov		BYTE, TM1637_char4
	rcall	TM1637_send_byte

	rcall	TM1637_stop

	pop		BYTE

	ret

;========================================================
;				Отображение времени
;========================================================

TM1637_display_time:
	lds		TM1637_char1, tm_h1
	lds		TM1637_char2, tm_h2
	sbr		TM1637_char2, 0b10000000
	lds		TM1637_char3, tm_m1
	lds		TM1637_char4, tm_m2

	rcall	TM1637_display

	ret

;========================================================
;				Отображение секунд
;========================================================

TM1637_display_seconds:
	lds		TM1637_char1, tm_m1
	lds		TM1637_char2, tm_m2
	sbr		TM1637_char2, 0b10000000
	lds		TM1637_char3, tm_s1
	lds		TM1637_char4, tm_s2

	rcall	TM1637_display

	ret

;========================================================
;				Отображение даты
;========================================================

TM1637_display_date:
	lds		TM1637_char1, tm_d1
	lds		TM1637_char2, tm_d2
	lds		TM1637_char3, tm_mt1
	lds		TM1637_char4, tm_mt2

	rcall	TM1637_display

	ret

;========================================================
;				Отображение года
;========================================================

TM1637_display_year:
	lds		TM1637_char1, tm_y1
	lds		TM1637_char2, tm_y2
	lds		TM1637_char3, tm_y3
	lds		TM1637_char4, tm_y4

	rcall	TM1637_display

	ret

;========================================================
;				Отображение времени будильника
;========================================================

TM1637_display_alarm:
	lds		TM1637_char1, tm_ah1
	lds		TM1637_char2, tm_ah2
	sbr		TM1637_char2, 0b10000000
	lds		TM1637_char3, tm_am1
	lds		TM1637_char4, tm_am2

	rcall	TM1637_display

	ret

;========================================================
;		Отображение режима будильника (вкл.\ выкл.)
;========================================================

TM1637_display_alarm_mode:
	push	r17
	
	ldi		r17, char_A
	mov		TM1637_char1, r17
	ldi		TM1637_char2, char_minus
	ldi		TM1637_char3, char_0
		
	sbrc	alarm, 0
	ldi		TM1637_char4, char_N
	sbrs	alarm, 0
	ldi		TM1637_char4, char_F

	rcall	TM1637_display

	pop		r17

	ret

;========================================================
;				Моргание
;	Режимы моргания: 
;		1-й и 2-й элементы r17==1
;		3-й и 4-й элементы r17==2
;	значение 1-го моргающего элемента == r18
;	значение 2-го моргающего элемента == r19
;========================================================

TM1637_blink_pair:
	push	r19
	push	r18
	push	r17

	cpi		r17, 0x01
	breq	TM1637_blink_pair_first

	cpi		r17, 0x02
	breq	TM1637_blink_pair_second

	rjmp	TM1637_blink_pair_end

	TM1637_blink_pair_first:
		eor		TM1637_char1, r18
		eor		TM1637_char2, r19

		rjmp	TM1637_blink_pair_end

	TM1637_blink_pair_second:
		eor		TM1637_char3, r18
		eor		TM1637_char4, r19

		rjmp	TM1637_blink_pair_end

	TM1637_blink_pair_end:
		rcall	TM1637_display

		pop		r19
		pop		r18
		pop		r17

	ret

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

  • def_equ.inc - файл описаний. В нём указано какие ножки задействовать для того или иного контроллера, предделители для таймеров, постоянные символы для TM1637 и др.
  • main.asm - основной файл
  • initialization.asm - файл инициализации микроконтроллера
  • ds1302.asm - всё, что связано c DS1302. Листинг выше
  • tm1637.asm - всё, что связано c TM1637. Листинг выше
  • interrupts_vector.asm - файл вектора прерываний
  • interrupts.asm - файл самих прерываний
  • alarm.asm - всё, что связано с будильником
  • lib.asm - файл, в который вошли остальные функции

Листинг файла def_equ.inc

;------------------------- Предделители для таймеров

.equ	kdel1			= 487		; (0,5 сек. для 16-битного таймера на 1МГц)
.equ	kdel2			= 58593		; (60 сек. для 16-битного таймера на 1МГц)
.equ	kdel3			= 233		; (240 мс для 8-битного таймера на 1МГц)
.equ	kdel4			= 125		; (1000 Гц для 8-битного таймера на 1МГц)

;------------------------- Регистры/описания для работы с tm1637

.def	TM1637_char1	= r6
.def	TM1637_char2	= r23
.def	TM1637_char3	= r24
.def	TM1637_char4	= r25

.equ	PORT_TM1367		= PORTB
.equ	DDR_TM1367		= DDRB
.equ	PIN_TM1367		= PINB
.equ	TM1637_CLK		= PB0
.equ	TM1637_DATA		= PB1

;------------------------- Постоянные символы для tm1637

.equ	char_A			= 0b01110111
.equ	char_minus		= 0b01000000
.equ	char_3_dash		= 0b01001001
.equ	char_N			= 0b00110111
.equ	char_F			= 0b01110001
.equ	char_O			= 0b00111111

.equ	char_0			= char_O
.equ	char_1			= 0b00000110
.equ	char_2			= 0b01011011
.equ	char_3			= 0b01001111
.equ	char_4			= 0b01100110
.equ	char_5			= 0b01101101
.equ	char_6			= 0b01111101
.equ	char_7			= 0b00000111
.equ	char_8			= 0b01111111
.equ	char_9			= 0b01101111

;------------------------- Режимы Mode для tm1637

.equ	mode_0			= 0x00
.equ	mode_1			= 0x01
.equ	mode_2			= 0x02
.equ	mode_3			= 0x03
.equ	mode_4			= 0x04
.equ	mode_5			= 0x05
.equ	mode_6			= 0x06
.equ	mode_7			= 0x07
.equ	mode_8			= 0x08
.equ	mode_9			= 0x09

;------------------------- Регистры/описания для работы с ds1302

.equ	PORT_DS1302		= PORTB
.equ	DDR_DS1302		= DDRB
.equ	PIN_DS1302		= PINB
.equ	DS1302_SCLK		= PB3
.equ	DS1302_IO		= PB4
.equ	DS1302_CE		= PB5		; RST

;------------------------- Режимы Clock_mode для ds1302

.equ	clock_mode_0	= 0x00
.equ	clock_mode_1	= 0x01
.equ	clock_mode_2	= 0x02

;------------------------- Регистры/описания для работы с кнопками

.equ	PORT_BUTTON_MODE= PORTD
.equ	DDR_BUTTON_MODE	= DDRD
.equ	PIN_BUTTON_MODE	= PIND
.equ	BUTTON_MODE		= PD2

.equ	PORT_BUTTON_SET	= PORTD
.equ	DDR_BUTTON_SET	= DDRD
.equ	PIN_BUTTON_SET	= PIND
.equ	BUTTON_SET		= PD3

;------------------------- Регистры/описания для зуммера

.equ	PORT_BUZZER		= PORTB
.equ	DDR_BUZZER		= DDRB
.equ	PIN_BUZZER		= PINB
.equ	BUZZER			= PB2

Пытался сделать так, чтобы можно было "безболезненно" поменять порты и ножки для контроллеров и зуммера. Режимы mode и clock_mode - для удобства. Символы для TM1637  для наглядности и для универсальности. Когда в коде встречается несколько раз один символ, здесь его проще менять, да и в коде понятно, что это за символ. Замечу, что я делал код под скорость микроконтроллера в 1 МГц.

Листинг файла main.asm

.include "tn2313adef.inc"
.include "def_equ.inc"

;========================================================
;   Глобальные переменные в РОН
;========================================================

.def   CONST_ZERO   = r0   Постоянный ноль

.def   timer0_counter   = r1   Счетчик для TIM0 при отображении двоеточия
.def   timer0_counter_alarm_unlock   = r2   счетчик для разблокировки будильника


.def   alarm   = r3   Будильник выключен == 0,  включён == 1
.def   alarm_lock   = r4    Блокировка будильника, блокирован == 1, разблокирован == 0.   Блокировка будильника на N-ное время после его отключения, чтобы он не зацикливался.

.def   clock_mode   = r20   Хранение состояния текущего отображения (время == 0, минуты и секунды == 1, дата == 2)
.def   mode   = r21   Хранение позиции элемента, который устанавливается   (ничего не устанавливается == 0, часы == 1, минуты == 2, число == 3, месяц == 4,   год == 5, будильник вкл\выкл == 6, часы будильника == 7, минуты будильника == 8,
   моргать временем во время сигнала будильника == 9)

.def   BYTE   = r22   Байт передачи\приём данных на\из ds1302


;========================================================
;   Глобальные переменные в EEPROM
;========================================================

.eseg   Выбираем сегмент EEPROM
   .org   0x00   Устанавливаем текущий адрес сегмента

;------------------------- Хранение данных в упакованном формате для будильника

bcd_alarm_hours:   .db   0x07    ; по умолчанию поставил на 07:30
bcd_alarm_minutes:   .db   0x30

;========================================================
;   Глобальные переменные в RAM
;========================================================

.dseg   Выбираем сегмент RAM
   .org   0x60   Устанавливаем текущий адрес сегмента

;------------------------- Хранение данных в упакованном формате от DS1302

bcd_seconds:   .byte   1   bcd_minutes:   .byte   1   bcd_hours:   .byte   1   bcd_day:   .byte   1   bcd_month:   .byte   1   bcd_week_day:  .byte   1   bcd_year:   .byte   1   ------------------------- Хранение подготовленных данных для отображения на TM1637 поэлементно

tm_s1:   .byte   1   Десятки секунд
tm_s2:   .byte   1   Единицы секунд

tm_m1:   .byte   1   Десятки минут
tm_m2:   .byte   1   Единицы минут

tm_h1:   .byte   1   Десятки часов
tm_h2:   .byte   1   Единицы часы

tm_d1:   .byte   1   Десятки дня месяца tm_d2:   .byte   1   Единицы дня месяца

tm_mt1:   .byte   1   Десятки месяца
tm_mt2:   .byte   1   Единицы месяца

tm_wd1:   .byte   1   Десятки день недели tm_wd2:   .byte   1   Единицы день недели tm_y3:   .byte   1   Десятки года
tm_y4:   .byte   1   Единицы года

;-------------------------- Последовательность следующих переменных важна!

tm_y1:   .byte   1   Тысячи года, тут будет фиксированное значение дальше по коду
tm_y2:   .byte   1   Сотни года, тут будет фиксированное значение дальше по коду

tm_ah1:   .byte   1   Десятки часов будильника
tm_ah2:   .byte   1   Единицы часов будильника
tm_am1:   .byte   1   Десятки минут будильника
tm_am2:   .byte   1   Единицы минут будильника

;========================================================
;   Начало программного кода
;========================================================

.cseg   Выбор сегмента программного кода
   .org   0   Установка текущего адреса на ноль

start:   .include "interrupts_vector.asm"
   .include "interrupts.asm"
   .include "initialization.asm"

   -------------------------- Очистка всех регистров, кроме регистра r0 и Z

   clr   CONST_ZERO

   ldi   ZH, 0
   ldi   ZL, 2

   ldi   r17, 29   mov   r1, r17

   clear_reg:
   dec   r1
   breq   clear_reg_end
   st   Z+, CONST_ZERO
   rjmp   clear_reg
   clear_reg_end:   -------------------------- Очистка RAM под наши переменные

   ldi   ZL,   0x60
   ldi   r17,  28   У нас 27 переменных = 28 минус первый декремент в цикле

   clear_ram:
   dec   r17
   breq   clear_ram_end
   st   Z+, CONST_ZERO
   rjmp   clear_ram
   clear_ram_end:   -------------------------- Первые две цифры отображения года (тысячи и сотни) для TM1637

   ldi   ZH,  high(tm_y1)
   ldi   ZL,  low(tm_y1)

   ldi   r17, char_2
   st   Z+,  r17
   ldi   r17, char_0
   st   Z+,  r17

   -------------------------- Считывание времени будильника из EEPROM для отображения на дисплее TM1637

   ldi   r17, bcd_alarm_hours
   rcall   EEPROM_read
   rcall   conv_ds1302_to_tm1637
   st   Z+, r16
   st   Z+, r15

   ldi   r17, bcd_alarm_minutes
   rcall   EEPROM_read
   rcall   conv_ds1302_to_tm1637
   st   Z+, r16
   st   Z,  r15

   -------------------------- Яркость на минимум. В Proteus с минимальной яркостью ничего не отображается!!!

   ldi   BYTE, 0
   sbr   BYTE, 0x88

   rcall   TM1637_start
   rcall   TM1637_send_byte
   rcall   TM1637_stop

   -------------------------- Считать данные с DS1302

   rcall   DS1302_read_package_data   инициализация DS1302 - первая команда в DS1302 после запуска   МК игнорируется (по крайней мере у меня), без данной строки не сработает следующая команда

    ;-------------------------- Инициализация DS1302 - разрешение на запись и включение часов!

   ldi   BYTE, 0x8E
   rcall   DS1302_send_start
   rcall   DS1302_send_byte

   ldi   BYTE, 0x00

   rcall   DS1302_send_byte
   rcall   DS1302_send_stop   ldi   BYTE, 0x80
   rcall   DS1302_send_start
   rcall   DS1302_send_byte

   ldi   BYTE, 0x00

   rcall   DS1302_send_byte
   rcall   DS1302_send_stop   sei   Разрешение прерываний
 
   -------------------------- Основной бесконечный цикл в ожидании прерывания

   main:   sleep
   rjmp   main

.include "alarm.asm"
.include "ds1302.asm"
.include "tm1637.asm"
.include "lib.asm"

;-------------------------- Массив максимального числа в месяцах. Убрано в конец кода.

max_day_in_month:   .db   31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31

В начале идёт описание РОН, которые используются как глобальные переменные. Изначально это были переменные RAM, но, когда я стал, в очередной раз, оптимизировать код, то выбрал им место в РОН.  Операции с памятью такие как sts, lds используют не одно байт-слово, а два, то есть размер прошивки становиться больше. Теперь по самим переменным:

  1. BYTE - используется при отправке и приёме данных с контроллеров
  2. timer0_counter - счетчик для TIM0 при отображении двоеточия. Двоеточие моргает раз в пол секунды. Таймер TIM0 8-ми битный и для него пол секунды это много, по этому я использую счетчик для подсчёта срабатываний прерываний. Само прерывание срабатывает раз в  240 мс. Через два таких прерывания двоеточие устанавливается или убирается. 
  3. timer0_counter_alarm_unlock - счетчик снятия блокировки будильника. Что такое блокировка будильника? Будильник срабатывает, если он установлен, блокировка будильника снята, время совпало. После срабатывания будильника он отключится или вручную, или через некоторое время. Когда будильник отключился, время-то ещё может совпадать! И для того, что бы не было повторного включения есть данный счетчик. После, примерно, 31 секунды блокировка снимается.
  4. CONST_ZERO - регистр с постоянным нулём. Используется для уменьшения объёма прошивки и самого исходного кода.
  5. alarm - регистр, указывающий включён будильник или нет
  6. alarm_lock - регистр, указывающий включена блокировка будильника или нет
  7. clock_mode - текущий режим clock_mode
  8. mode - текущий режим mode

Следующие переменные хранятся в памяти EEPROM:

  1. bcd_alarm_hours - упакованный формат часов будильника
  2. bcd_alarm_minutes - упакованный формат минут будильника

Место в EPPROM я выбрал уже в конце написания кода. Наверно, больше для разнообразия, чтобы было задействовано как можно больше функций микроконтроллера. Переменные можно спокойно перенести в области RAM памяти.

Переменные RAM памяти:

  1. В переменных с bcd_seconds по bcd_year хранятся данные в упакованном виде по их именам. bcd_seconds - секунды, bcd_minutes - минуты. Такой уровень английского, думаю, есть у каждого.
  2. В переменных с tm_s1 по tm_am2 хранятся преобразованные данные для дисплея поразрядно.  tm_m1 десятки для минут, tm_m2 - единицы для минут и т.д. 

Обращаю внимание, что последовательность переменных с tm_y1 по tm_am2 важна, т.к. далее по коду идет "шагание" по оперативной памяти последовательно по данным переменным. Дальше подключаются необходимые файлы. Потом происходит обнуление всех регистров и оперативной памяти, которая выделена под имеющиеся переменные. Это необходимо для стабильной работы в моменты сброса микроконтроллера. Потом запись значений по умолчанию в переменные, в которых храниться тысячи и сотни для отображения года. Если помните диапазон годов 2000 - 2099. Первые два символа не меняются. Дальше считывание данных с EEPROM. Затем разрешение прерываний и первое считывание данных с DS1302. И, наконец, основной цикл программы с уходом в сон. Сон - для экономии энергии. Весь код выполняется на прерываниях, а между ними можно и "поспать". И в конце файла - массив c максимальным значением числа в каждом месяце, что бы исключить попытки установки недействительных данных. 

Листинг файла initialization.asm

;========================================================
;				 Модуль инициализации
;========================================================

init:

	;-------------------------- Инициализация стека
	
	ldi		r17, RAMEND			; Выбор адреса вершины стека 
	out		SPL, r17			; Запись его в регистр стека

	;--------------------------- Инициализация компаратора

	ldi 	r17, 0x80			; Выключение компаратора
	out		ACSR, r17

	;-------------------------- Инициализация Главного предделителя

	ldi		r17, 0x80		    ; 
	out		CLKPR, r17			; 
	ldi		r17, 0x03			; Записываем 3 в регистр r17
	out		CLKPR, r17			; Записываем это число в CLKPR, указывая, что мы делим частоту на 8.

	;-------------------------- Инициализация портов ВВ по умолчанию (на вход с подтягивающим резистором)

	ser		r17
	out		PORTA, r17
	out		PORTB, r17
	out		PORTD, r17

	out		DDRA, CONST_ZERO
	out		DDRB, CONST_ZERO
	out		DDRD, CONST_ZERO

	;-------------------------- Инициализация порта зуммера

	cbi		PORT_BUZZER, BUZZER
	sbi		DDR_BUZZER, BUZZER

	;-------------------------- Инициализация портов ВВ для чипа TM1367

	sbi		DDR_TM1367, TM1637_CLK
	sbi		DDR_TM1367, TM1637_DATA

	;-------------------------- Инициализация портов ВВ для чипа DS1302

	sbi		DDR_DS1302, DS1302_CE
	sbi		DDR_DS1302, DS1302_SCLK

	;------------------------- Вывод DS1302_DAT устанавливается при отправке или чтении ds1302

	;-------------------------- Инициализация портов ВВ для кнопок

	cbi		DDR_BUTTON_MODE, BUTTON_MODE
	cbi		DDR_BUTTON_SET, BUTTON_SET

	;-------------------------- Инициализация таймеров

	ldi		r17, (1 << WGM12) | (1 << CS12) | (0 << CS11) | (1 << CS10) ; Выбор режима таймера (СТС, предделитель = 1024) 
	out		TCCR1B, r17

	rcall	change_tim1_to_normal_mode
	rcall	change_tim0_to_normal_mode

	;--------------------------- Разрешаем прерывание от таймеров
		
	ldi 	r17, (1 << OCIE1A) | (1 << OCIE0A)
	out		TIMSK, r17

	;--------------------------- Разрешаем прерывание INT0 и INT1 по заднему фронту и режим сна (idle)

	ldi		r17, (1 << ISC01) | (0 << ISC00) | (1 << ISC11) | (0 << ISC10) | (1 << SE)
	out		MCUCR, r17

	ldi		r17, (1 << INT0) | (1 << INT1)
	out		GIMSK, r17

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

Листинг файлов ds1302.asm и tm1637.asm был выше.

Листинг interrupts_vector.asm:

;========================================================
;				 Вектор прерываний
;========================================================		

	rjmp	init	; Переход на начало программы
	rjmp	_INT0	; Внешнее прерывание 0
	rjmp	_INT1	; Внешнее прерывание 1
	reti			; Прерывание по захвату таймера T1
	rjmp 	_TIM1	; Прерывание по совпадению T1 A
	reti			; Прерывание по переполнению T1
	reti			; Прерывание по переполнению T0
	reti			; Прерывание UART прием завершен
	reti			; Прерывание UART регистр данных пуст
	reti			; Прерывание UART передача завершена
	reti			; Прерывание по компаратору
	reti		   ; Прерывание по изменению на любом контакте
	reti			; Таймер/счетчик 1. Совпадение B 
	rjmp 	_TIM0	; Таймер/счетчик 0. Совпадение A
	reti			; Таймер/счетчик 0. Совпадение B 
	reti			; USI Стартовая готовность
	reti			; USI Переполнение
	reti			; EEPROM Готовность
	reti			; Переполнение охранного таймера
	reti			; PCINT1 Handler
	reti			; PCINT2 Handl

Листинг interrupts.asm:

;========================================================
;		 Подпрограмма при нажатии кнопки Mode
;========================================================

_INT0:
	push	r17
	push	r16

	_INT0_wait_release:
		rcall	MCU_wait_20ms
		sbic	PIN_BUTTON_MODE, BUTTON_MODE
		rjmp	Mode_pressed_end

	rcall	DS1302_read_package_data

	;-------------------------- Сброс переменной clock_mode

	clr		clock_mode

	;-------------------------- Инкремент переменной mode 

	inc		mode

	;-------------------------- Выбор режима mode

	cpi		mode, mode_1
	breq	Mode_pressed_1

	cpi		mode, mode_2
	breq	Mode_pressed_2

	cpi		mode, mode_3
	breq	Mode_pressed_3

	cpi		mode, mode_4
	breq	Mode_pressed_4

	cpi		mode, mode_5
	breq	Mode_pressed_5

	cpi		mode, mode_6
	breq	Mode_pressed_6

	cpi		mode, mode_7
	breq	Mode_pressed_7

	cpi		mode, mode_8
	breq	Mode_pressed_8

	cpi		mode, mode_9
	brsh	Mode_pressed_0

	rjmp	Mode_pressed_end

	;------------------------- MODE 0

	Mode_pressed_0:
		rcall	TM1637_display_time

		;------------------------- Если день был установлен больше, чем максимальный в этом месяце, то данный код это исправляет.
		;------------------------- Пример: выставили 31 число, потом 9 месяц (а в сентябре 30 дней). При переходе в режим
		;------------------------- mode == 0 устанавливается 30 число!

		lds		r17, bcd_day
		rcall	bcd8bin
		mov		r16, r17
		rcall	get_max_day
		cp		r16, r17
		brlo	Mode_pressed_0_end
		ldi		BYTE, 0x86
		rcall	DS1302_send_start
		rcall	DS1302_send_byte
		rcall	bin8bcd
		mov		BYTE, r17
		rcall	DS1302_send_byte
		rcall	DS1302_send_stop

		Mode_pressed_0_end:
			rcall	change_tim1_to_normal_mode
			clr		mode

		rjmp	Mode_pressed_end

	;------------------------- MODE 1

	Mode_pressed_1:
		rcall	TM1637_display_time
		rcall	change_tim1_to_blink_mode

		rjmp	Mode_pressed_end

	;------------------------- MODE 2

	Mode_pressed_2:
		rcall	TM1637_display_time

		rjmp	Mode_pressed_end

	;------------------------- MODE 3 или 4 

	Mode_pressed_3:
	Mode_pressed_4:
		rcall	TM1637_display_date

		rjmp	Mode_pressed_end

	;------------------------- MODE 5

	Mode_pressed_5:
		rcall	TM1637_display_year

		rjmp	Mode_pressed_end

	;------------------------- MODE 6

	Mode_pressed_6:
		rcall	TM1637_display_alarm_mode

		rjmp	Mode_pressed_end

	;------------------------- MODE 7 или 8

	Mode_pressed_7:
	Mode_pressed_8:
		rcall	TM1637_display_alarm

	Mode_pressed_end:

		pop		r16
		pop		r17

	reti

;========================================================
;	 Подпрограмма при нажатии кнопки Clock mode \ Set
;========================================================

_INT1:
	push	r17

	_INT1_wait_release:
		rcall	MCU_wait_20ms
		sbic	PIN_BUTTON_SET, BUTTON_SET
		rjmp	Clock_mode_end

	cpi		mode, mode_0
	breq	Clock_mode_clock_mode_pressed

	;-------------------------- Режим Set

	rcall	inc_circle

	rjmp	Clock_mode_end

	;-------------------------- Режим Clock mode

	Clock_mode_clock_mode_pressed:
		inc		clock_mode

		cpi		clock_mode, clock_mode_2+1
		brsh	Clock_mode_reset
		rjmp	Clock_mode_pre_end

		Clock_mode_reset:
			clr		clock_mode

		Clock_mode_pre_end:

			;-------------------------- Чтобы данные сразу отобразились
			
			ldi		r17, high(kdel2)
			out		TCNT1H, r17
			ldi		r17, low(kdel2-10)
			out		TCNT1L, r17

	Clock_mode_end:

		pop		r17

	reti
	
;========================================================
;				Прерывание таймера T1	
;========================================================

_TIM1:		
	push	r17
	push	r18
	push	r19
	
	;-------------------------- Проверка режимов Mode

	cpi		mode, mode_0
	breq	rcall_TIM1_mode_0

	cpi		mode, mode_1
	breq	rcall_TIM1_mode_1

	cpi		mode, mode_2
	breq	rcall_TIM1_mode_2

	cpi		mode, mode_3
	breq	rcall_TIM1_mode_3

	cpi		mode, mode_4
	breq	rcall_TIM1_mode_4

	cpi		mode, mode_5
	breq	rcall_TIM1_mode_5

	cpi		mode, mode_6
	breq	rcall_TIM1_mode_6

	cpi		mode, mode_7
	breq	rcall_TIM1_mode_7

	cpi		mode, mode_8
	breq	rcall_TIM1_mode_8

	cpi		mode, mode_9
	breq	rcall_TIM1_mode_9

	rjmp	_TIM1_end

	;-------------------------- MODE 0

	rcall_TIM1_mode_0:

		rcall	_TIM1_mode_0

		rjmp	_TIM1_end

	;-------------------------- MODE 1

	rcall_TIM1_mode_1:
		lds		r18, tm_h1
		lds		r19, tm_h2
		ldi		r17, 0x01
		rcall	TM1637_blink_pair

		rjmp	_TIM1_end

	;-------------------------- MODE 2

	rcall_TIM1_mode_2:
		lds		r18, tm_m1
		lds		r19, tm_m2
		ldi		r17, 0x02
		rcall	TM1637_blink_pair

		rjmp	_TIM1_end

	;-------------------------- MODE 3

	rcall_TIM1_mode_3:
		lds		r18, tm_d1
		lds		r19, tm_d2
		ldi		r17, 0x01
		rcall	TM1637_blink_pair

		rjmp	_TIM1_end

	;-------------------------- MODE 4

	rcall_TIM1_mode_4:
		lds		r18, tm_mt1
		lds		r19, tm_mt2
		ldi		r17, 0x02
		rcall	TM1637_blink_pair

		rjmp	_TIM1_end

	;-------------------------- MODE 5

	rcall_TIM1_mode_5:
		lds		r18, tm_y3
		lds		r19, tm_y4
		ldi		r17, 0x02
		rcall	TM1637_blink_pair

		rjmp	_TIM1_end

	;-------------------------- MODE 6

	rcall_TIM1_mode_6:
		rcall	alarm_blink

		rjmp	_TIM1_end

	;-------------------------- MODE 7

	rcall_TIM1_mode_7:
		lds		r18, tm_ah1
		lds		r19, tm_ah2
		ldi		r17, 0x01
		rcall	TM1637_blink_pair

		rjmp	_TIM1_end

	;-------------------------- MODE 8

	rcall_TIM1_mode_8:
		lds		r18, tm_am1
		lds		r19, tm_am2
		ldi		r17, 0x02
		rcall	TM1637_blink_pair

		rjmp	_TIM1_end

	;-------------------------- MODE 9

	rcall_TIM1_mode_9:
		clr		ZH
		ldi		ZL, low(tm_m1)
		ld		r18, Z+
		ld		r19, Z+
		ldi		r17, 0x02
		rcall	TM1637_blink_pair

		ld		r18, Z+
		ld		r19, Z
		ldi		r17, 0x01
		rcall	TM1637_blink_pair
		rcall	change_tim0_to_buzzer_mode

	_TIM1_end:
		cpi		mode, mode_9
		breq	_TIM1_end_end
		rcall	alarm_check

	_TIM1_end_end:
		pop		r19
		pop		r18
		pop		r17

	reti

;========================================================
;				Прерывание таймера T0	
;========================================================

_TIM0:
	push	r17
	push	BYTE

	in		r17, OCR0A
	cpi		r17, kdel4
	breq	_TIM0_end

	inc		timer0_counter_alarm_unlock
	sbrc	timer0_counter_alarm_unlock, 7		; 128 * 240 мс ~ 31 с
	clr		alarm_lock

	cpi		clock_mode, clock_mode_1
	breq	_TIM0_display_seconds

	;-------------------------- Если сейчас не Mode 0 и не Clock_mode 0 выходим из прерывания

	push	mode
	add		mode, clock_mode
	pop		mode
	brne	_TIM0_end

	;-------------------------- Счетчик для двоеточия

	inc		timer0_counter
	mov		r17, timer0_counter
	cpi		r17, 0x04
	brne	_TIM0_end

	clr		timer0_counter

	;-------------------------- Для экономии работы микроконтроллеров. Изменяется только значение 2-го элемента на дисплее, а не всех!!!

	rcall	TM1637_start
	ldi		BYTE, 0x44
	rcall	TM1637_send_byte
	rcall	TM1637_stop

	rcall	TM1637_start
	ldi		BYTE, 0xC1
	rcall	TM1637_send_byte
	lds		BYTE, tm_h2
	ldi		r17, 0x80
	eor		BYTE, r17
	sts		tm_h2, BYTE
	rcall	TM1637_send_byte
	rcall	TM1637_stop

	_TIM0_end:
		pop		BYTE
		pop		r17

	reti

	_TIM0_display_seconds:
		rcall	DS1302_read_package_data
		rcall	TM1637_display_seconds

		rjmp	_TIM0_end

;========================================================
;		Подпрограмма выбора режима mode_0
;========================================================

_TIM1_mode_0:

	;-------------------------- Считать данные с ds1302 пакетом

	rcall	DS1302_read_package_data

	cpi		clock_mode, clock_mode_1
	breq	_TIM1_mode_0_end

	cpi		clock_mode, clock_mode_2
	breq	_TIM1_date_mode

	_TIM1_time_mode:
		rcall	TM1637_display_time
		ret

	_TIM1_date_mode:
		rcall	TM1637_display_date
		ret

	_TIM1_mode_0_end:

	ret

Это самый большой файл! Но описать его не так уж долго. Прерывания INT0 и INT1 в одноимённых процедурах. При работе с кнопками используются задержки для борьбы с дребезгом контактов. Для кнопок, которые я использую - 20 мкс. вполне хватает.  Прерывание TIM1 раз в минуту (для экономии аккумулятора) считывает данные с DS1302 и выводит их на дисплей, но это только в режиме mode_0. В других режимах данное прерывание отвечает за частоту моргания элементов на дисплее, которые устанавливаются или во время будильника. Прерывание TIM0 отвечает за мигание двоеточием, за сброс регистра блокировки будильника и за "подачу звука" на ногу OC0A во время срабатывания будильника.

Листинг файла alarm.asm:

;========================================================
;	Подпрограммы для моргания 3-м и 4-м элементами
;	в режиме включения\выключения будильника
;========================================================

alarm_blink:
	push	r17
	push	r18
	push	r19

	sbrc	alarm, 0
	ldi		r19, char_N	
	sbrs	alarm, 0
	ldi		r19, char_F	

	ldi		r18, char_O	

	ldi		r17, 0x02
	rcall	TM1637_blink_pair
	
	pop		r19
	pop		r18
	pop		r17

	ret

;========================================================
;				Инкремент для будильника
;========================================================

inc_cicle_alarm:
	push	r17
	push	r18
	push	r19
	push	BYTE

	mov		r19, CONST_ZERO			; минимальное число для инкремента во всех ниже случаях

	cpi		mode, mode_6
	breq	inc_circle_alarm_switch

	cpi		mode, mode_7
	breq	inc_circle_alarm_hour

	cpi		mode, mode_8
	breq	inc_circle_alarm_minutes

	rjmp	inc_cicle_alarm_end

	;-------------------------- Вкл.\выкл. будильника 

	inc_circle_alarm_switch:
		mov		r17, alarm
		ldi		r18, 2
		rcall	_inc
		mov		alarm, r17

		rcall	TM1637_display_alarm_mode
		clr		alarm_lock							; снять блокировку будильника

		rjmp	inc_cicle_alarm_end

	;-------------------------- Выставление часов будильника

	inc_circle_alarm_hour:
		ldi		r17, bcd_alarm_hours
		rcall	EEPROM_read

		rcall	bcd8bin
		ldi		r18, 24
		rcall	_inc
		rcall	bin8bcd

		ldi		r18, bcd_alarm_hours
		rcall	EEPROM_write

		rcall	conv_ds1302_to_tm1637
		sts		tm_ah1, r16
		sts		tm_ah2, r15

		rcall	TM1637_display_alarm

		rjmp	inc_cicle_alarm_end

	;-------------------------- Выставление минут будильника

	inc_circle_alarm_minutes:
		ldi		r17, bcd_alarm_minutes
		rcall	EEPROM_read

		rcall	bcd8bin
		ldi		r18, 60
		rcall	_inc
		rcall	bin8bcd

		ldi		r18, bcd_alarm_minutes
		rcall	EEPROM_write

		rcall	conv_ds1302_to_tm1637
		sts		tm_am1, r16
		sts		tm_am2, r15

		rcall	TM1637_display_alarm

	inc_cicle_alarm_end:
		pop		BYTE
		pop		r19
		pop		r18
		pop		r17

	ret

;========================================================
;				Включить будильник
;========================================================

alarm_on:
	push	r18
	push	r17
	push	r16

	out		GIMSK, CONST_ZERO			; отключить прерывания от кнопок 

	rcall	TM1637_display_time

	ldi		mode, mode_9				; mode_9 включается только во время срабатывания будильника

	rcall	change_tim1_to_blink_mode
	rcall	change_tim0_to_buzzer_mode

	sei									; т.к. процедура "alarm_on" вызывается из прерывания TIM1, то до этого момента прерывания отключены 

	;-------------------------- ~ 1 мин. 10 сек. Данный цикл + его команды + прерывание на таймер с его командами. Нужно, чтобы будильник сам выключился через время, а не звонил вечно, до самого обеда!

	ldi		r17, 5
	ser		r16
	alarm_on_wait_loop_L:
		rcall	MCU_wait_20ms

		in		r18, PIND
		andi	r18, 0x0C
		cpi		r18, 0x0C
		brlo	alarm_off

		dec		r16
		brne	alarm_on_wait_loop_L

		alarm_on_wait_loop_H:
			ser		r16
			dec		r17
			brne	alarm_on_wait_loop_L

	;-------------------------- Отключение будильника через время или по нажатию любой кнопки

	alarm_off:
		cli
		clr		mode
		clr		clock_mode

		rcall	change_tim1_to_normal_mode
		rcall	change_tim0_to_normal_mode

		rcall	TM1637_display_time

		clr		timer0_counter_alarm_unlock
		ldi		r16, 0x01			; Блокировка будильника
		mov		alarm_lock, r16

		;-------------------------- Разрешить прерывания от кнопок 

		ldi		r17, (1 << INT0) | (1 << INT1)	
		out		GIMSK, r17
		sei

	pop		r16
	pop		r17
	pop		r18

	ret

;========================================================
;			Подпрограмма проверки будильника
;========================================================

alarm_check:
	push	r18
	push	r17

	sbrs	alarm, 0					
	rjmp	to_alarm_end				; Если будильник отключён, то выходим из процедуры
	sbrc	alarm_lock, 0
	rjmp	to_alarm_end				; Если будильник блокирован, то выходим из процедуры

	ldi		r17, bcd_alarm_hours		; Адрес переменной "bcd_alarm_hours" из EEPROM в регистр r17
	rcall	EEPROM_read					; Результат в регистр r17
	lds		r18, bcd_hours				; Текущие часы в регистр r18
	cp		r17, r18					; Сравниваем текущие часы с часами будильника
	brne	to_alarm_end				; Если текущие часы не совпадают с часами будильника - выходим из процедуры

	ldi		r17, bcd_alarm_minutes		; Адрес переменной "bcd_alarm_minutes" из EEPROM в регистр r17
	rcall	EEPROM_read					; Результат в регистр r17
	lds		r18, bcd_minutes			; Текущие минуты в регистр r18
	cpse	r17, r18					; Сравниваем текущие минуты с минутами будильника
	rjmp	to_alarm_end				; Если текущие минуты не совпадают с минутами будильника - выходим из процедуры

	rcall	alarm_on					; Если всё совпало - включить будильник

	to_alarm_end:
		pop		r17
		pop		r18

	ret

Здесь всего 4-и процедуро-функции. Проверка будильника - звонить сейчас или нет. Включение и там же отключение будильника. Моргание элементов в режиме установки будильника (вкл.\ выкл. ). И функция для инкремента данных будильника. Она очень похожа на такую же функцию в файле lib.asm за исключением того, что данные будильника не передаются на контроллер DS1302

Листинг файла lib.asm:

;========================================================
;   Преобразование данных с ds1302 в   числа для 4-х сегментного дисплея tm1637
;   запись результата в регистры r16:r15   вход - регистр r17
;   выход - r16:r15
;========================================================

conv_ds1302_to_tm1637:
   push   r17

   ------------------------- преобразуем младший разряд в регистр r15

   mov   r18, r17
   andi   r18, 0x0f
   rcall   bin_to_tm1637_digit
   mov   r15, r18

   ------------------------- преобразуем старший разряд в регистр r16

   mov   r18, r17
   andi   r18, 0xf0
   swap   r18
   rcall   bin_to_tm1637_digit
   mov   r16, r18

   pop   r17

   ret

;------------------------- Подфункция преобразования.
;------------------------- Данные из регистра r18 преобразуются в числовое ;------------------------- значение для 7-сегментного индикатора, результат в ;------------------------- регистр r18

bin_to_tm1637_digit:
   cpi   r18, 0x00
   breq   _d0
   cpi   r18, 0x01
   breq   _d1
   cpi   r18, 0x02
   breq   _d2
   cpi   r18, 0x03
   breq   _d3
   cpi   r18, 0x04
   breq   _d4
   cpi   r18, 0x05
   breq   _d5
   cpi   r18, 0x06
   breq   _d6
   cpi   r18, 0x07
   breq   _d7
   cpi   r18, 0x08
   breq   _d8
   cpi   r18, 0x09
   breq   _d9

   rjmp   _dx

   ------------------------- Подпрограммы для чисел 0..9

   _d0:
   ldi   r18, char_0
   rjmp   bin_to_tm1637_digit_end
   _d1:
   ldi   r18, char_1
   rjmp   bin_to_tm1637_digit_end
   _d2:
   ldi   r18, char_2
   rjmp   bin_to_tm1637_digit_end
   _d3:
   ldi   r18, char_3
   rjmp   bin_to_tm1637_digit_end
   _d4:
   ldi   r18, char_4
   rjmp   bin_to_tm1637_digit_end
   _d5:
   ldi   r18, char_5
   rjmp   bin_to_tm1637_digit_end
   _d6:
   ldi   r18, char_6
   rjmp   bin_to_tm1637_digit_end
   _d7:
   ldi   r18, char_7
   rjmp   bin_to_tm1637_digit_end
   _d8:
   ldi   r18, char_8
   rjmp   bin_to_tm1637_digit_end
   _d9:
   ldi   r18, char_9
   rjmp   bin_to_tm1637_digit_end
   _dx:
   ldi   r18, char_3_dash
   bin_to_tm1637_digit_end:

   ret

;========================================================
;   Подпрограммы инкремента с установленными границами
;   входящее\исходящее значение == r17, r19 == min, r18 == max + 1
;========================================================

_inc:
   inc   r17
   cp   r17, r18
   brsh   _inc_reset

   rjmp   _inc_end

   _inc_reset:
   mov   r17, r19

   _inc_end:
   ret
;========================================================
;   Подпрограммы инкремента с установленными границами
;   и вызовом подпрограммы
;========================================================

inc_circle:
   push   r18
   push   r19
   push   XL
   push   XH
   push   ZL
   push   ZH

   mov   r19, CONST_ZERO
   -------------------------- Выбор режима mode

   cpi   mode, mode_1
   breq   inc_circle_hour

   cpi   mode, mode_2
   breq   inc_circle_minutes

   cpi   mode, mode_3
   breq   inc_circle_day

   cpi   mode, mode_4
   breq   inc_circle_month

   cpi   mode, mode_5
   breq   inc_circle_year

   cpi   mode, mode_6
   breq   rcall_inc_cicle_alarm

   cpi   mode, mode_7
   breq   rcall_inc_cicle_alarm

   cpi   mode, mode_8
   breq   rcall_inc_cicle_alarm

   rjmp   inc_circle_end

   -------------------------- Часы

   inc_circle_hour:
   ldi   XH, high(bcd_hours)   ldi   XL, low(bcd_hours)
   ldi   r18, 24
   ldi   BYTE, 0x84
   ldi   ZH, high(TM1637_display_time)
   ldi   ZL, low(TM1637_display_time)

   rcall   inc_circle_ext

   rjmp   inc_circle_end

   -------------------------- Минуты

   inc_circle_minutes:
   ldi   XH, high(bcd_minutes)   ldi   XL, low(bcd_minutes)
   ldi   r18, 60
   ldi   BYTE, 0x82
   ldi   ZH, high(TM1637_display_time)
   ldi   ZL, low(TM1637_display_time)

   rcall   inc_circle_ext

   rjmp   inc_circle_end

   -------------------------- День месяца

   inc_circle_day:
   ldi   XH, high(bcd_day)   ldi   XL, low(bcd_day)
   ldi   r19, 1
   rcall   get_max_day
   inc   r17
   mov   r18, r17
   ldi   BYTE, 0x86
   ldi   ZH, high(TM1637_display_date)
   ldi   ZL, low(TM1637_display_date)

   rcall   inc_circle_ext

   rjmp   inc_circle_end

   -------------------------- Месяц

   inc_circle_month:
   ldi   XH, high(bcd_month)   ldi   XL, low(bcd_month)
   ldi   r19, 1
   ldi   r18, 13
   ldi   BYTE, 0x88
   ldi   ZH, high(TM1637_display_date)
   ldi   ZL, low(TM1637_display_date)

   rcall   inc_circle_ext

   rjmp   inc_circle_end

   -------------------------- Год

   inc_circle_year:
   ldi   XH, high(bcd_year)   ldi   XL, low(bcd_year)
   ldi   r18, 100
   ldi   BYTE, 0x8C
   ldi   ZH, high(TM1637_display_year)
   ldi   ZL, low(TM1637_display_year)

   rcall   inc_circle_ext

   rjmp   inc_circle_end

   -------------------------- Будильник

   rcall_inc_cicle_alarm:
   rcall   inc_cicle_alarm

   -------------------------- Конец инкрементации
   inc_circle_end:
   pop   ZH
   pop   ZL
   pop   XH
   pop   XL
   pop   r19
   pop   r18

   ret

;========================================================
;   Преобразование 8-битного двоичного
;   значения в упакованный BCD формат
;   Входящее\исходящее значение == r17
;========================================================

bin8bcd:
   push   r18
   push   r16

   .def   digitL   =   r18
   .def   digitH   =   r16

   mov   digitL, r17
   clr   digitH

   bin8bcd_loop:
   subi   digitL, 0x0a
   brmi   bin8bcd_end

   inc   digitH
   rjmp   bin8bcd_loop

   bin8bcd_end:
   subi   digitL, -0x0a

   swap   digitH
   mov   r17, digitH

   andi   digitL, 0x0f
   or   r17, digitL

   .undef   digitL
   .undef   digitH

   pop   r19
   pop   r16

   ret

;========================================================
;   Преобразование 8-битного упакованного
;   значения в двоичный формат
;   Входящее\исходящее значение == r17
;========================================================

bcd8bin:
   push   r18

   .def   result   =   r17
   .def   tens   =   r18

   mov   tens, r17
   andi   tens, 0xf0
   swap   tens

   andi   result, 0x0f
   bcd8bind_loop:
   dec   tens
   brmi   bcd8bin_end

   subi   result, -0x0a
   rjmp   bcd8bind_loop

   bcd8bin_end:
   .undef   result
   .undef   tens

   pop   r18

   ret

;========================================================
;   Инкремент переменной по адресу X.
;   Верхняя граница - max_digit (r18).
;   Нижняя граница - min_digit (r19).
;   Адрес регистра для DS1302 в регистре BYTE.
;   Непрямой вызов подпрограммы по адресу Z
;========================================================

inc_circle_ext:
   push   r17

   ld   r17, X
   rcall   bcd8bin
   inc   r17

   cp   r17,  r18
   brsh   inc_circle_ext_reset

   rjmp   inc_circle_ext_end

   inc_circle_ext_reset:
   mov   r17, r19

   inc_circle_ext_end:
   rcall   DS1302_send_start
   rcall   DS1302_send_byte

   rcall   bin8bcd
   mov   BYTE, r17

   rcall   DS1302_send_byte
   rcall   DS1302_send_stop   rcall   DS1302_read_package_data
   icall

   pop   r17

   ret

;========================================================
;   Нахождение максимального дня текущего месяца
;   с учётом високосных лет
;   Исходящее значение == r17
;========================================================

get_max_day:
   push   ZH
   push   ZL
   push   YH
   push   YL

   ldi   ZH, high(max_day_in_month*2)
   ldi   ZL, low(max_day_in_month*2)

   lds   r17, bcd_month
   rcall   bcd8bin
   dec   r17

   ldi   YH, 0
   mov   YL, r17

   add   ZL, YL
   adc   ZH, YH
   lpm   r17, Z

   rcall   leap_year

   pop   YL
   pop   YH
   pop   ZL
   pop   ZH

   ret

;========================================================
;   Определение високосного года и перезапись   Если год високосный и сейчас 2-й месяц,
;   меняется значение r17 = 29
;========================================================

leap_year:
   push   r16

   ------------------------- Сохраняем значение r17 в r16, чтобы его не изменить в случае,   ------------------------- если сейчас не февраль високосного года

   mov   r16, r17

   ------------------------- Если установлен не февраль, проверка на високосный год не нужна

   lds   r17, bcd_month
   cpi   r17, 0x02
   brne   leap_year_end

   ------------------------- Каждый сотый год не високосный. В нашем случая, только нулевой год

   lds   r17, bcd_year
   cpi   r17, 0x00
   breq   leap_year_end

   ------------------------- Если один из двух младших битов или оба установлены
   ------------------------- число не кратно 4-м. Год високосный

   rcall   bcd8bin
   sbrc   r17, 1
   rjmp   leap_year_end
   sbrc   r17, 0
   rjmp   leap_year_end

   ------------------------- Максимальное значение числа месяца = 29

   ldi   r16, 29
   leap_year_end:
   mov   r17, r16
   pop   r16

   ret

;========================================================
;   Подпрограммы формирования задержки
;========================================================

MCU_wait_10mks:   10 мкс + время на команды   push   r17

   ldi   r17, 10

   MCU_wait_loop:
   dec   r17
   brne   MCU_wait_loop

   pop   r17

   ret

MCU_wait_20ms:   приблизительно 20 мс + время команд
   push   r17
   push   r16

   ldi   r17, 80
   ser   r16

   MCU_wait_loop_L:
   dec   r16
   brne   MCU_wait_loop_L

   MCU_wait_loop_H:
   ser   r16
   dec   r17
   brne   MCU_wait_loop_L

   pop   r16
   pop   r17

   ret

;========================================================
;   Замена предделителя в TIM1
;========================================================

change_tim1_to_blink_mode:
   push   r17

   ldi   r17, high(kdel1)   меняем предделитель на 0,5 сек., чтобы моргало то, что мы меняем в режимах mode 1 - mode 8 или во время работы будильника
   out   OCR1AH, r17
   ldi   r17, low(kdel1)   out   OCR1AL, r17
   ldi   r17, high(kdel1)   чтобы сразу отобразилось
   out   TCNT1H, r17
   ldi   r17, low(kdel1-10)
   out   TCNT1L, r17

   pop   r17

   ret

;========================================================
;   Замена предделителя в TIM1
;========================================================

change_tim1_to_normal_mode:
   push   r17

   ldi   r17, high(kdel2)   60 сек.
   out   OCR1AH, r17
   ldi   r17, low(kdel2)   out   OCR1AL, r17
   ldi   r17, high(kdel2)   чтобы сразу отобразилось
   out   TCNT1H, r17
   ldi   r17, low(kdel2-10)
   out   TCNT1L, r17

   pop   r17

   ret

;========================================================
;   Смена режима TIM0
;========================================================

change_tim0_to_normal_mode:
   push   r17

   ldi   r17, (1 << WGM01)   Выбор режима таймера СТС   out   TCCR0A, r17
   ldi   r17, (1 << CS02) | (0 << CS01) | (1 << CS00) ; Выбор предделителя = 1024   out   TCCR0B, r17
   ldi   r17, kdel3
   out   OCR0A, r17

   pop   r17

   ret

;========================================================
;   Смена режима TIM0
;========================================================

change_tim0_to_buzzer_mode:
   push   r17

   ldi   r17, (1 << WGM01) | (0 << COM0A1) | (1 << COM0A0)   out   TCCR0A, r17
   ldi   r17, (0 << CS02) | (1 << CS01) | (0 << CS00)   out   TCCR0B, r17
   ldi   r17, kdel4
   out   OCR0A, r17

   pop   r17

   ret

;========================================================
;   Запись в EEPROM память
;   адрес - r18
;   данные для записи - r17
;========================================================

EEPROM_write:
   push   r17
   push   r18

   sbic   EECR, EEPE
   rjmp   EEPROM_write
   out   EECR, CONST_ZERO
   out   EEARL, r18
   out   EEDR, r17
   sbi   EECR, EEMPE
   sbi   EECR, EEPE

   pop   r18
   pop   r17

   ret

;========================================================
;   Чтение из EEPROM памяти
;   адрес - r17
;   возвращаемые данные - r17
;========================================================

EEPROM_read:
   sbic   EECR, EEPE
   rjmp   EEPROM_read
   out   EEARL, r17
   sbi   EECR, EERE
   in   r17, EEDR

   ret

В данном файле описаны функции которые не вошли в другие файлы. Здесь такие функции как преобразование бинарного числа в упакованный вид и обратно, преобразование данных для дисплея TM1637, инкремент числа с заданными границами, определение максимального числа месяца и високосность года, процедуры задержки МК, процедуры смены режимов таймеров, и функции чтения и записи памяти EEPROM.

Код можно  уменьшить за счёт удаления многих команд push и pop, но если потом что-то изменить, то можно долго искать логическую ошибку в программе. По этому, в каждой функции я помещал значение регистров в стек и возвращал их при выходе. Регистров - только, которые изменяются в данной функции и не являются регистрами возвращаемого значения. Если посмотреть файл main.asm, то видно что в коде используются глобальные переменные, хранящиеся в регистрах и их можно переписать в любой функции! Я неоднократно уменьшал код из-за большого размера прошивки и многие места являются "нечеловекочетаемыми", если следовать логики ООП. Но какое тут ООП - это же ассемблер!   

Fuse биты:

3D модель платы в программе KiCad:

Замеры потребления тока:

В режиме отображения время потребление тока менее 2 мА. Аккумулятор я выбрал типа 18650.  В моей модели аккумулятора заявлено 19800 мАч. 19800 / 2 / 24 / 30 = 13,75 месяцев!!! Но это в теории. И я не думаю, что в китайских аккумуляторах с алиэкспресса будет столько мАч. На практике ещё посмотрим...

P.S.

Кварц на разработанной плате так и не удалось запустить, по этому разработка корпуса приостановлена.

Обновлена прошивка. Сделан упор на работу от сети, так что экономии питания больше нет. Добавлены и исправлены следующие позиции:

  • Проект разбит на большее количество файлов для более удобной работы с кодом;
  • Добавлен режим регулировки яркости дисплея;
  • Настройки будильника (состояние вкл./выкл., часы, минуты) и уровень яркости теперь хранятся в памяти EEPROM;
  • При любом "моргании" (любом режиме кроме отображения текущего времени, а так же и в режиме будильника) часы пробудут ~60 сек. Дальше идёт возврат в режим отображения текущего времени;
  • При включении идет режим теста устройства - небольшая "анимация" и звук будильника;
  • Не сбрасываются секунды при включении устройства.

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

Список радиоэлементов

Обозначение Тип Номинал Количество ПримечаниеМагазинМой блокнот
U2 МК AVR 8-бит
ATtiny2313A
1 Поиск в магазине ОтронВ блокнот
U1, U3, U5 TM1637модуль со светодиодным дисплеем1 Поиск в магазине ОтронВ блокнот
U3 Часы реального времени (RTC)
DS1302
1 Поиск в магазине ОтронВ блокнот
Q1 Биполярный транзистор
S9014-B
1 Поиск в магазине ОтронВ блокнот
X1 кварцевый резонатор32.768 кГЦ1 Поиск в магазине ОтронВ блокнот
BUZ1 пьезоэлектрический звуковой излучатель1 Поиск в магазине ОтронВ блокнот
RV1 Подстроечный резистор2 кОм1 Поиск в магазине ОтронВ блокнот
R0, R1 Резистор
10 кОм
2 Поиск в магазине ОтронВ блокнот
MODE, CLOCK MODE / SET КнопкаКнопка без фиксации2 Поиск в магазине ОтронВ блокнот
Аккумулятор 186503.7В 19800мАч1 Поиск в магазине ОтронВ блокнот
Добавить все

Скачать список элементов (PDF)

Прикрепленные файлы:

Теги:

Опубликована: Изменена: 06.07.2021 0 4
Я собрал 0 4
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 5 Проголосовало: 4 чел.

Комментарии (30) | Я собрал (0) | Подписаться

0
Публикатор #
На форуме автоматически создана тема для обсуждения статьи.
Ответить
+2
111284 #
Какова неточность хода у данных чесов и чем они хуже или лучше DS3231?
Ответить
+4

[Автор]
verbkinm #
По поводу неточности - это в документации надо смотреть! Два месяца мониторю, идут нормально (в бытовом плане, а не в лабораторном). А вот чем они (DS1302) хуже это видно сразу. Используют свой протокол в отличии от DS3231 (который на TWI), и занимают три ноги на МК! Да и к тому же на DS3231 встроены два будильника!!!
Отредактирован 27.10.2020 06:45
Ответить
-3
oleg #
Решив увеличить свои знания не в «ширину», а в «глубину» получился этот проект. Ребята, ну нельзя так выражаться, это совершенно не по русски!
Ответить
+2
Лекс 59 #
Аккумуляторы 18650 не могут иметь емкость выше 3000 мАч. Рекорд что-то типа 3800. Технологически больше не запихнуть.
Поэтому время работы будет значительно меньше.
Для возможности горячей замены аккумулятора на заряженный, я бы предусмотрел конденсатор на пару-тройку тысяч микрофарад, ну либо прямо в устройстве их заряжать. МК и модуль 5 вольт должны выдерживать. И лучше поставить для этого нечто типа модуля на 4056.
Ответить
+3

[Автор]
verbkinm #
За 18650 я уже понял! Моих хватает только на неделю!!! Там 1000мАч не будет, наверное!
Ответить
0

[Автор]
verbkinm #
У меня в наличии как раз есть пару таких модулей, но после неудачного опыта с моими аккумуляторами 18650 закрались сомнения... Сейчас проверяю работу от двух пальчиковых батареек. В сумме заряд у них может доходить до 2500 мАч. А так как напряжение меньше, то и потребляемый ток меньше.
Ответить
+2
mj #
А когда устройство будет уже готово? Плата, корпус, видео?
Ответить
+3

[Автор]
verbkinm #
На данном этапе идёт создание платы, а после будет печать корпуса для устройства. Как только всё будет готово, я приложу видео работы устройства и файлы для 3D печати корпуса.
Ответить
+5
rafo #
Я так и не смог побороть страх перед ассемблером, он абсолютно не воспринимается.. У Вас отлично получилось!
Маленькие замечания по схемотехнике.
* Всё же просится конденсатор по питанию, хотя бы небольшая керамика.
* Так подключать нагрузку к NPN транзистору не желательно. Излучатель с резистором перебросьте на коллектор.
* Про внешние подтяжки для кнопок промолчу, видимо антидребезг вы реализуете программно. Но внутренние не всегда помогают.
Ответить
+4

[Автор]
verbkinm #
В схемотехнику я пришёл сам из долины программирования и знаний у меня в ней очень мало. Антидребезг реализован програмно, посмотрим как будет себя вести... А ассемблер для души =))
Отредактирован 28.10.2020 21:12
Ответить
0

[Автор]
verbkinm #
Очень плохо разбираюсь в электронике, не могли бы вы посмотреть на модель платы. На макетке всё работает, а на плате rtc не тикают, то есть проблема в сторону кварца, но нигде не могу найти свою проблему! Спасибо.
Ответить
+2
vr #
Я так понимаю, что звук будильника это просто воспроизведение определённой частоты, даже без пауз?
Ответить
+1

[Автор]
verbkinm #
Да, так оно и есть. Если хватит места в памяти, то попробую сделать звук как на старых электронных часах с небольшими паузами.
Ответить
+1
andro #
Спасибо за проект, хотелось бы видеть концовку и работающее устройство, и слишком много кода в самой статье, лучше помещать его в прикрепляемые файлы, а для чтения показывать фрагменты, а то немного устаёшь читать. При том, что лично Я распечатываю статьи и получается много ненужных листов, с них ведь код не скомпилируешь :)
Ответить
0

[Автор]
verbkinm #
Спасибо за комментарий, я исходник специально выкладывал в статью. Что хочешь, то и копируешь и наглядно всё сразу видно, как с ds1302 работать, а как с tm1637. Сейчас тестирую работу от батареек, на сколько их хватит. До концовки ещё несколько этапов. На днях пришли готовые платы. Закончу работу с ними и начну разработку корпуса, который буду печатать на 3d принтере. И сразу всё выложу и фото и видео, и исходники stl. А если вы печатаете статью, то можно в браузере ненужные компоненты сразу удалять перед печатью!
Отредактирован 10.11.2020 21:19
Ответить
+1
andro #
Да, так и приходится делать. Ну удачи Вам, примечательно, что пишите на Ассемблере, наверное можете разобраться в декомпилированной прошивке?!
Ответить
0

[Автор]
verbkinm #
Ещё не пробовал!
Отредактирован 11.11.2020 09:30
Ответить
0

[Автор]
verbkinm #
Пришла плата, всё собрал как на рисунке, но часики не тикают. Время устанавливается, а секунды не бегут. Кварц припаял другой - потикали 10 секунд и снова стоят. Опять отпаял, припаял - та же история. Не могу разобраться с этими кварцами!
Ответить
0
dem-vr #
У Вас нет данных о фузи-битах. Добавьте пожалуйста.
Ответить
0

[Автор]
verbkinm #
Хорошо добавлю, а так всё по умолчанию!
Ответить
0
nastya #
В какой версии Proteus был сделан проект?
Ответить
0

[Автор]
verbkinm #
Proteus 8.9 SP2
Прикрепленный файл: Снимок.JPG
Ответить
0
valeon #
ds1307 подойдет?
Ответить
0

[Автор]
verbkinm #
Нет, ds1307 работает с интерфейсом i2c, а это совсем другая история.
Ответить
0
valeon #
Эх, кто бы взялся на реализацию с ds1307
Ответить
0

[Автор]
verbkinm #
Так там ещё легче, в плане написании кода, всё на I2C. Вот только возможностей у ds1307 побольше будет. И будильник встроенный, по моему, там есть.
Ответить
0
Равиль BURAEV #
Можно ли использовать AT90S2313 вместо ATINY2313?
Ответить
0

[Автор]
verbkinm #
Нет, надо будет переписывать код!
Ответить
0
RAVIL #
Atmel Studio 7.0 не поддерживает AT90S2313
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Программатор Pickit3
Программатор Pickit3
Raspberry Pi 2 Arduino UNO
вверх