;Программа реализует простенькие электронные часы с будильником на AT90S2313. ;Кварц 10 Мгц .cseg .include "2313def.inc" .org $000 rjmp main_program ;Переход на основную программу .org $005 rjmp clock ;Переход на обработчик, считающий время .org $006 rjmp leds_scan ;Переход на обработчик, сканирующий ;индикатор ;Переменные для будильника .def alarm_hours = r9 ;Ячейка для хранения часов .def alarm_minutes = r10 ;Ячейка для хранения минут .def alarm_ten_hours = r11 ;Десятки часов .def alarm_one_hours = r12 ;Единицы часов .def alarm_ten_minutes = r13 ;Десятки минут .def alarm_one_minutes = r14 ;Единицы минут .def alarm_minutes_low = r28 ;Младший байт минут .def alarm_minutes_high = r29 ;Старший байт минут ;Переменные для часов .def clock_hours = r18 ;Ячейка для хранения часов .def clock_minutes = r19 ;Ячейка для хранения минут .def seconds = r21 ;Секунды .def clock_ten_hours = r22 ;Десятки часов .def clock_one_hours = r23 ;Единицы часов .def clock_ten_minutes = r24 ;Десятки минут .def clock_one_minutes = r25 ;Единицы минут .def clock_minutes_low = r26 ;Младший байт минут .def clock_minutes_high = r27 ;Старший байт минут ;Общие переменные .def alarm_active = r15 ;Признак активности режима будильника. ;Если R15=1, то часы находятся в режиме ;будильника .def scan_mask = r20 ;Маска для сканирования индикатора ;Обработчик прерывания от таймера 1, выполняющий счет времени. Он вызывается ;один раз на каждую секунду clock: push r16 ldi r16,high($ffff - 4883 * 2);Считать от нуля out tcnt1h,r16 ;до 4883 * 2 ldi r16,low($ffff - 4883 * 2) ;Считать от нуля out tcnt1l,r16 ;до 4883 * 2 push r0 ; in r0,sreg ; inc seconds ;Плюс одна секунда cpi seconds,60 ;Прошла минута ? brne check_keys ;Нет - проверим состояние клавиш ;Увеличить общий счетчик минут clr seconds ;Сбросить счетчик секунд adiw clock_minutes_low,1 ;На одну минуту больше cpi clock_minutes_low,$a0 ;Закончились - ли сутки ? brne check_keys ;В сутках 1440 =05A0h минут cpi clock_minutes_high,$05;Проверим, brne check_keys ;сколько минут прошло clr clock_minutes_high ;Да, сутки закончились. clr clock_minutes_low ;Сбросим счетчик минут ... ;Проверка состояния клавиш check_keys:in r16,pind ;Получить состояние порта "D" ori r16,$b3 ; cpi r16,$bf ; breq it_is_alarm ;Нажата - ли клавиша будильника ? cpi r16,$fb ; breq it_is_hours ;Нажата - ли клавиша перевода часов ? cpi r16,$f7 ; breq it_is_minutes ;Нажата клавиша перевода минут ? ;Нажаты несколько клавиш одновременно или не нажата ни одна - звонит ;будильник (если нужно). ;Здесь реализован будильник rcall alarm_clock rjmp display it_is_hours: mov r16,alarm_active ;В каком режиме находятся часы ? cpi r16,1 ;Если в режиме будильника, то breq tune_alarm_hours ;нужно настраивать именно будильник ; tune_clock_hours: adiw clock_minutes_low,60 ;Увеличить количество минут на 60 push clock_minutes_high ; push clock_minutes_low ; clr r16 ; new_sub_10:sbiw clock_minutes_low,60 ;Деление на brcs end_div_10 ;60 inc r16 ;методом rjmp new_sub_10 ;вычитания end_div_10:cpi r16,24 ; brlo to_pops_1 ; adiw clock_minutes_low,60 ;Восстановим остаток от деления pop r16 ; pop r16 ; rjmp display ; to_pops_1: pop clock_minutes_low ; pop clock_minutes_high ; rjmp display ; tune_alarm_hours: adiw alarm_minutes_low,60 ;Увеличить количество минут на 60 push alarm_minutes_high ; push alarm_minutes_low ; clr r16 ; new_sub_11:sbiw alarm_minutes_low,60 ;Деление на brcs end_div_11 ;60 inc r16 ;методом rjmp new_sub_11 ;вычитания end_div_11:cpi r16,24 ; brlo to_pops_2 ; adiw alarm_minutes_low,60 ;Восстановим остаток от деления pop r16 ; pop r16 ; rjmp display ; to_pops_2: pop alarm_minutes_low ; pop alarm_minutes_high ; rjmp display ; ; it_is_minutes: mov r16,alarm_active ;В каком режиме находятся часы ? cpi r16,1 ;Если в режиме будильника, то breq tune_alarm_minutes ;нужно настраивать именно будильник ; tune_clock_minutes: adiw clock_minutes_low,1 ;Да - увеличить количество минут на одну cpi clock_minutes_low,$a0 ;Закончились - ли сутки ? brne display ;В сутках 1440 =05A0h минут cpi clock_minutes_high,$05;Проверим, brne display ;сколько минут прошло clr seconds ;Да, сутки закончились. clr clock_minutes_high ;Сбросим счетчик секунд ... clr clock_minutes_low ;Сбросим счетчик минут ... rjmp display ; tune_alarm_minutes: adiw alarm_minutes_low,1 ;Да - увеличить количество минут на одну cpi alarm_minutes_low,$a0 ;Закончились - ли сутки ? brne display ;В сутках 1440 =05A0h минут cpi alarm_minutes_high,$05;Проверим, brne display ;сколько минут прошло clr alarm_minutes_high ;Да, сутки закончились. clr alarm_minutes_low ;Сбросим счетчик минут ... rjmp display ;Отображение времени ; it_is_alarm: ldi r16,$01 eor alarm_active,r16 in r16,portb push r16 ldi r16,$ff out portb,r16 mov r16,alarm_active cpi r16,$1 brne pic_for_clock pic_for_alarm: rcall pic_pic rcall delay_100ms pic_for_clock: rcall pic_pic pop r16 out portb,r16 display: rcall clock_convert_to_time ;Преобразуем минуты в обычное время rcall alarm_convert_to_time ;Преобразуем показания будильника to_reti_2: out sreg,r0 ; pop r0 ; pop r16 ; reti ;Выход из обработчика ;Обработчик прерывания от таймера 0, выполняющий сканирование индикатора - ;каждая цифра сканируется около 200 раз в секунду leds_scan: push r16 ldi r16,250 ; out tcnt0,r16 ; push r0 ; in r0,sreg ; in r16,portd ; and r16,scan_mask ; out portd,scan_mask ;Зажечь нужный сегмент индикатора mov r16,alarm_active ;Прочитать признак активности будильника cpi scan_mask,$fe ;Сейчас отображаются десятки часов ? breq show_one_hours ;Да cpi scan_mask,$fd ;Сейчас отображаются единицы часов ? breq show_ten_minutes ;Да cpi scan_mask,$ef ;Сейчас отображаются десятки минут ? breq show_one_minutes ;Да cpi scan_mask,$df ;Сейчас отображаются единицы минут ? breq show_ten_hours ;Да rjmp to_reti_1 ;На выход ;Отобразить единицы часов show_one_hours: ldi scan_mask,$fd ; cpi r16,0 breq show_clock_1 out portb,alarm_ten_hours ; rjmp to_reti_1 show_clock_1: out portb,clock_ten_hours ; rjmp to_reti_1 ; ;Отобразить десятки минут show_ten_minutes: ; ldi scan_mask,$ef ; cpi r16,0 breq show_clock_2 out portb,alarm_one_hours ; rjmp to_reti_1 show_clock_2: out portb,clock_one_hours ; rjmp to_reti_1 ; ;Отобразить единицы минут show_one_minutes: ; ldi scan_mask,$df ; cpi r16,0 breq show_clock_3 out portb,alarm_ten_minutes rjmp to_reti_1 ; show_clock_3: out portb,clock_ten_minutes rjmp to_reti_1 ; ;Отобразить десятки часов show_ten_hours: ; ldi scan_mask,$fe ; cpi r16,0 breq show_clock_4 out portb,alarm_one_minutes rjmp to_reti_1 show_clock_4: out portb,clock_one_minutes to_reti_1: out sreg,r0 ; pop r0 ; pop r16 ; reti ;Выход из обработчика ;Основная программа main_program: sei ;Разрешить прерывания ldi r16,$df ;Настроить начало стека out spl,r16 ;на конец SRAM ldi r16,$0 ;Включить mov alarm_active,r16 ;Режим часов ldi alarm_minutes_high,high(415);Настроить будильник ldi alarm_minutes_low,low(415) ;На 6 часов 55 минут - время на WORK rcall ports_tuning ;Настроить порты процессора rcall scanning ;Запустить цикл сканирования индикатора rcall clocking ;Запустить цикл счета времени non_ends: rjmp non_ends ;Остается только крутить пустой цикл ... ;----------------------------------------------------------------------------- ;Область процедур: ;----------------------------------------------------------------------------- ;Процедура запускает цикл сканирования индикатора scanning: push r17 ;Сохранить R17 push r0 ;Сохранить in r0,sreg ;SREG ldi scan_mask,$ef ;Индицировать десятки часов in r17,timsk ;Разрешить прерывания ori r17,$02 ;по out timsk,r17 ;переполнению Т/С0 ; ldi r17,$05 ;Коэффициент деления out tccr0,r17 ;равен 1024 + запустим таймер 0 ; ldi r17,250 ;Прерывание out tcnt0,r17 ;примерно 1600 раз в секунду ; out sreg,r0 ;Восстановить pop r0 ;SREG pop r17 ;Восстановить R17 ret ;Возврат из подпрограммы ;Процедура запускает цикл счета времени clocking: push r17 ;Сохранить R17 push r0 ;Сохранить in r0,sreg ;SREG clr clock_minutes_high ;Ноль минут clr clock_minutes_low ; clr seconds ;Ноль секунд ldi clock_ten_hours,$c0 ;На ldi clock_one_hours,$c0 ;индикаторе - ldi clock_ten_minutes,$c0 ;одни ldi clock_one_minutes,$c0 ;нули in r17,timsk ;Разрешить прерывания ori r17,$80 ;по out timsk,r17 ;переполнению Т/С1 ; ldi r17,$05 ;Коэффициент деления out tccr1b,r17 ;равен 1024 + запустим таймер 1 ; ldi r17,high($ffff - 4883 * 2);Считать от нуля out tcnt1h,r17 ;до 4883 * 2 ldi r17,low($ffff - 4883 * 2) ;Считать от нуля out tcnt1l,r17 ;до 4883 * 2 ; out sreg,r0 ;Восстановить pop r0 ;SREG pop r17 ;Восстановить R17 ret ;Возврат из подпрограммы ;----------------------------------------------------------------------------- ;Процедуры для часов реального времени ;Процедура преобразует количество минут в обычное представление времени и ;помещает результат в ячейки: "clock_ten_hours", "clock_one_hours", ;"clock_ten_minutes", ""clock_one_minutes" clock_convert_to_time: ;Разделить общее количество минут на часы и минуты rcall clock_minutes_to_h_and_m push clock_hours push clock_minutes rcall clock_hours_to_ascii; rcall clock_minutes_to_ascii pop clock_minutes pop clock_hours ret ; ;Процедура выделяет из общего количества минут часы и минуты. Общее количество ;минут не изменяется, сохраняясь в стеке ;Вход: Общее количество минут в паре R27:R26 ;Выход: Часы в ячейке "clock_hours" и минуты в ячейке "clock_minutes" clock_minutes_to_h_and_m: push clock_minutes_high push clock_minutes_low push r0 in r0,sreg ldi clock_hours,0 ;Деление new_sub_1: sbiw r26,60 ;на brcs end_div_1 ;60 inc clock_hours ;методом rjmp new_sub_1 ;вычитания end_div_1: adiw r26,60 ;Восстановим остаток от деления mov clock_minutes,r26 ;Остаток от деления - это минуты out sreg,r0 pop r0 pop clock_minutes_low pop clock_minutes_high ret ;Процедура преобразует двоичное значение часов в формат индикатора ;Вход: Часы в ячейке "clock_hours" ;Выход: Ячейки "clock_Ten_hours" и "clock_One_hours" clock_hours_to_ascii: push r31 push r30 push clock_minutes_high push clock_minutes_low push r1 in r1,sreg push r0 ldi clock_ten_hours,0 ;Деление new_sub_2: subi clock_hours,10 ;на brcs end_div_2 ;10 inc clock_ten_hours ;методом rjmp new_sub_2 ;вычитания end_div_2: ldi clock_one_hours,10 ;Второе слагаемое add clock_hours,clock_one_hours ;Восстановим остаток от деления mov clock_one_hours,clock_hours ;Остаток от деления - это единицы ;часов ldi r31,high(cross_array * 2) ldi r30,low(cross_array * 2) clc add r30,clock_ten_hours clr r0 adc r31,r0 lpm mov clock_ten_hours,r0 ldi r31,high(cross_array * 2) ldi r30,low(cross_array * 2) clc add r30,clock_one_hours clr r0 adc r31,r0 lpm mov clock_one_hours,r0 pop r0 out sreg,r1 pop r1 pop clock_minutes_low pop clock_minutes_high pop r30 pop r31 ret ;Процедура преобразует двоичное значение минут в формат индикатора ;Вход: Минуты в ячейке "clock_Minutes" ;Выход: Ячейки "clock_Ten_minutes" и "clock_One_minutes" clock_minutes_to_ascii: push r31 push r30 push clock_minutes_high push clock_minutes_low push r1 in r1,sreg push r0 ldi clock_ten_minutes,0 ;Деление new_sub_3: subi clock_minutes,10 ;на brcs end_div_3 ;10 inc clock_ten_minutes ;методом rjmp new_sub_3 ;вычитания end_div_3: ldi clock_one_minutes,10 ;Второе слагаемое add clock_minutes,clock_one_minutes ;Восстановим остаток от деления mov clock_one_minutes,clock_minutes ;Остаток от деления - это единицы минут ldi r31,high(cross_array * 2) ldi r30,low(cross_array * 2) clc add r30,clock_ten_minutes clr r0 adc r31,r0 lpm mov clock_ten_minutes,r0 ldi r31,high(cross_array * 2) ldi r30,low(cross_array * 2) clc add r30,clock_one_minutes clr r0 adc r31,r0 lpm mov clock_one_minutes,r0 pop r0 out sreg,r1 pop r1 pop clock_minutes_low pop clock_minutes_high pop r30 pop r31 ret ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ;Процедуры для будильника ;Процедура преобразует количество минут в обычное представление времени и ;помещает результат в ячейки: "alarm_ten_hours", "alarm_one_hours", ;"alarm_ten_minutes", "alarm_one_minutes" alarm_convert_to_time: ;Разделить общее количество минут на часы и минуты rcall alarm_minutes_to_h_and_m push alarm_hours push alarm_minutes rcall alarm_hours_to_ascii; rcall alarm_minutes_to_ascii pop alarm_minutes pop alarm_hours ret ; ;Процедура выделяет из общего количества минут часы и минуты. Общее количество ;минут не изменяется, сохраняясь в стеке ;Вход: Общее количество минут в паре R29:R28 ;Выход: Часы в ячейке "alarm_hours" и минуты в ячейке "alarm_Minutes" alarm_minutes_to_h_and_m: push r16 push alarm_minutes_high push alarm_minutes_low push r0 in r0,sreg ldi r16,0 mov alarm_hours,r16 ;Деление new_sub_4: sbiw r28,60 ;на brcs end_div_4 ;60 inc alarm_hours ;методом rjmp new_sub_4 ;вычитания end_div_4: adiw r28,60 ;Восстановим остаток от деления mov alarm_minutes,r28 ;Остаток от деления - это минуты out sreg,r0 pop r0 pop alarm_minutes_low pop alarm_minutes_high pop r16 ret ;Процедура преобразует двоичное значение часов в формат индикатора ;Вход: Часы в ячейке "alarm_hours" ;Выход: Ячейки "alarm_Ten_hours" и "alarm_One_hours" alarm_hours_to_ascii: push r16 push r31 push r30 push alarm_minutes_high push alarm_minutes_low push r1 in r1,sreg push r0 ldi r16,0 mov alarm_ten_hours,r16 ;Деление new_sub_5: mov r16,alarm_hours ; subi r16,10 ;на mov alarm_hours,r16 ; brcs end_div_5 ;10 inc alarm_ten_hours ;методом rjmp new_sub_5 ;вычитания end_div_5: ldi r16,10 ; mov alarm_one_hours,r16 ;Второе слагаемое add alarm_hours,alarm_one_hours ;Восстановим остаток от деления mov alarm_one_hours,alarm_hours ;Остаток от деления - это единицы ;часов ;Конвертим значение часов в формат индикатора ldi r31,high(cross_array * 2) ldi r30,low(cross_array * 2) clc add r30,alarm_ten_hours clr r0 adc r31,r0 lpm mov alarm_ten_hours,r0 ldi r31,high(cross_array * 2) ldi r30,low(cross_array * 2) clc add r30,alarm_one_hours clr r0 adc r31,r0 lpm mov alarm_one_hours,r0 pop r0 out sreg,r1 pop r1 pop alarm_minutes_low pop alarm_minutes_high pop r30 pop r31 pop r16 ret ;Процедура преобразует двоичное значение минут в формат индикатора ;Вход: Минуты в ячейке "alarm_Minutes" ;Выход: Ячейки "alarm_Ten_minutes" и "alarm_One_minutes" alarm_minutes_to_ascii: push r16 push r31 push r30 push alarm_minutes_high push alarm_minutes_low push r1 in r1,sreg push r0 ldi r16,0 mov alarm_ten_minutes,r16 ;Деление new_sub_6: mov r16,alarm_minutes ; subi r16,10 ;на mov alarm_minutes,r16 ; brcs end_div_6 ;10 inc alarm_ten_minutes ;методом rjmp new_sub_6 ;вычитания end_div_6: ldi r16,10 mov alarm_one_minutes,r16 ;Второе слагаемое add alarm_minutes,alarm_one_minutes ;Восстановим остаток от деления mov alarm_one_minutes,alarm_minutes ;Остаток от деления - это единицы минут ;Конвертим значение минут в формат индикатора ldi r31,high(cross_array * 2) ldi r30,low(cross_array * 2) clc add r30,alarm_ten_minutes clr r0 adc r31,r0 lpm mov alarm_ten_minutes,r0 ldi r31,high(cross_array * 2) ldi r30,low(cross_array * 2) clc add r30,alarm_one_minutes clr r0 adc r31,r0 lpm mov alarm_one_minutes,r0 pop r0 out sreg,r1 pop r1 pop alarm_minutes_low pop alarm_minutes_high pop r30 pop r31 pop r16 ret ;----------------------------------------------------------------------------- ;Процедура настраивает порты при включении часов ports_tuning: ldi r16,$ff ;Настроить порт PB out ddrb,r16 ;на вывод данных ldi r16,$b3 ;Настроить порт PD на вывод, кроме out ddrd,r16 ;выводов PD2, PD3 и PD6 sbi portd,6 ;Использовать внутренние PULLUP's sbi portd,3 ;для выводов sbi portd,2 ;PD2, PD3 и PD6 ret ;Процедура генерирует звуковой сигнал будильника pic_pic: push r16 ;Сохранить R16 push r0 ;Сохранить in r0,sreg ;SREG ldi r16,160 ring_1: cbi portb,7 rcall delay_1ms sbi portb,7 rcall delay_1ms dec r16 brne ring_1 ldi r16,255 ring_2: cbi portb,7 rcall delay_05ms sbi portb,7 rcall delay_05ms dec r16 brne ring_2 out sreg,r0 ;Восстановить pop r0 ;SREG pop r16 ;Восстановить R16 ret ;Процедура формирует задержку - примерно 100 миллисекунд delay_100ms: push r16 ;Сохранить R16 push r0 ;Сохранить in r0,sreg ;SREG ldi r16,100 l_5: rcall delay_1ms dec r16 brne l_5 out sreg,r0 ;Восстановить pop r0 ;SREG pop r16 ;Восстановить R16 ret ;Процедура формирует задержку - примерно 1 миллисекунду delay_1ms: push r0 ;Сохранить in r0,sreg ;SREG rcall delay_05ms ;Два вызова rcall delay_05ms ;следующей процедуры out sreg,r0 ;Восстановить pop r0 ;SREG ret ;Процедура формирует задержку - примерно 0,5 миллисекунд delay_05ms:push r16 ;Сохранить R16 push r17 ;Сохранить R17 push r0 ;Сохранить in r0,sreg ;SREG ldi r17,$6 l_4: ldi r16,$160 l_3: nop dec r16 brne l_3 dec r17 brne l_4 out sreg,r0 ;Восстановить pop r0 ;SREG pop r17 ;Восстановить R17 pop r16 ;Восстановить R16 ret alarm_clock: push r16 ;Сохранить R16 push r0 ;Сохранить in r0,sreg ;SREG cp clock_hours,alarm_hours brne to_ret cp clock_minutes,alarm_minutes brne to_ret in r16,portb push r16 ldi r16,$ff out portb,r16 rcall pic_pic pop r16 out portb,r16 to_ret: out sreg,r0 ;Восстановить pop r0 ;SREG pop r16 ;Восстановить R16 ret ;***************************************************************************** ;Изображения цифр на индикаторе cross_array: .db $c0, $f9, $a4, $b0, $99, $92, $82, $f8, $80, $90, $7f