.include "2313def.inc" ; Внешний кварц на 4.096МГц. .equ CLK_IN_SEC = 250 ; переполнений 0-ого счётчика в секунду .equ BLINC_TIME = 180 ; кол-во переполнений за время свечения диода при мигании .equ VBR_TIME = 15 ; время дребезга контактов (в переполнениях) ; биты статусного регистра .equ DSPL = 2 ;если =1 экран включен .equ TBTN = 5 ; зелёная кнопка перевода часов/экрана. Он же номер соответствующего пина порта .equ MBTN = 6 ; красная кнопка смены режима он же номер пина ; Регистры, используемые как переменные. .def col_reg = R20 ; маска отображаемой колонки .def stat_reg = R21 ; статусный регистр .def blinc_reg = R22 ; маска мигаемой колонки. ; ( x00, если ничего не мигает). ; Установленный бит,означает, что соответствующая ; колонка мигает .def clock = R23 ; счётчик переполнений тайменра .def col_cnt = R24 ; номер отображаемой колонки .def blinc_cnt = R25 ; номер мигаемой колонки. ; вывод на порт разрешения для отображения колонки. .MACRO COL_OUT COM @0 ORI @0, 128 ; неиспользуемая ножка OUT PORTB, @0 .ENDMACRO ; вывод значения, которое нужно отобразить в разрешённой колонке. .MACRO T_OUT ORI @0, 32+64+128 ; неиспользуемые ножки OUT PORTD, @0 .ENDMACRO .CSEG .ORG 0x0000 RJMP Reset .ORG INT0addr ; External Interrupt Request 0 RETI .ORG INT1addr ; External Interrupt Request 1 RETI .ORG ICP1addr ; Timer/Counter1 Capture Event RETI .ORG OC1addr ; Timer/Counter1 Compare Match A RETI .ORG OVF1addr ; Timer/Counter1 Overflow RETI .ORG OVF0addr ; Timer/Counter0 Overflow RJMP Over .ORG URXCaddr ; USART, Rx Complete RETI .ORG UDREaddr ; USART Data Register Empty RETI .ORG UTXCaddr ; USART, Tx Complete RETI .ORG ACIaddr ; Analog Comparator RETI ; Сама программа .ORG INT_VECTORS_SIZE Reset: LDI R16,low(RAMEND) OUT SPL, R16 ;Здесь инициализация портов и памяти LDI R16, (1<<TBTN)|(1<<MBTN) OUT PORTD, R16 LDI R16, ~((1<<TBTN)|(1<<MBTN)) OUT DDRD, R16 SER R16 OUT PORTB, R16 LDI R16, 128 COM R16 OUT DDRB, R16 LDI stat_reg, 1<<DSPL LDI col_reg, 1 CLR col_cnt CLR blinc_reg CLR blinc_cnt CLR CLOCK LDI R16,VBR_TIME ; включим защиту от дребезга контактов на всякий случай. STS VIBR, R16 LDI ZL, low(SECND) LDI ZH, high(SECND) CLR R16 ST Z+, R16 ; 0 секунд ST Z+, R16 ST Z+, R16 ; 0 минут ST Z+, R16 ST Z+, R16 ; 0 часов INC R16 ST Z+, R16 ; 1 числа ST Z+, R16 ; понедельника ; Таймер LDI R16,0 OUT TCNT0,R16 LDI R16, (1<<CS01)|(1<<CS00) OUT TCCR0,R16 LDI R16,1<<TOIE0 OUT TIMSK,R16 SEI ; Основной цикл Main: CLR R16 COL_OUT R16 ; отключим столбцы RCALL Next_col ; переключимся на отображение следующей колонки. SBRC stat_reg, DSPL ; Если нужно, то RCALL Display ; отобразим её. ; Проверка кнопок No_disp: ; проверка антидребезга LDS R16, VIBR TST R16 BRNE Main RCALL Ch ; Проверка кнопок и включение антидребезга SBRC R16, TBTN ; Если настройка времени,то RJMP Time_pres SBRC R16, MBTN ; Если перевод режима,то RJMP Mode_chg RJMP Main ; переключает колонки по кругу. Next_col: LSL col_reg INC col_cnt CPI col_reg, 1<<7 BRNE NcE LDI col_reg, 1 CLR col_cnt NcE: RET ;если нажали зелёную кнопку. Time_pres: TST blinc_reg ; в режиме настройки времени BRNE Not_norm ; перейдём сюда. LDI R16, 1<<DSPL ; в нормальном режиме - включим/выключим дисплей. EOR stat_reg, R16 RJMP Main ;если нажали красную кнопку. Mode_chg: SBRS stat_reg, DSPL RJMP Main CLI TST blinc_reg BRNE N_F_B ; если был норм. режим LDI blinc_reg, 1 ; начнём мигать первой колонкой. CLR blinc_cnt RJMP N_N_B ; если не норм N_F_B: INC blinc_cnt ; переключаем мигание на следующую колонку. LSL blinc_reg CPI blinc_reg, 1<<7 BRNE N_N_B CLR blinc_cnt CLR blinc_reg N_N_B: SEI RJMP Main Not_norm: ; увеличить время мигающей колонки (настройка), выход. LDI ZL, low(SECND) LDI ZH, high(SECND) CLR R16 ADD ZL, blinc_cnt ADC ZH, R16 MOV R16, blinc_cnt TST R16 BREQ Set_MS DEC R16 BREQ Set_TMS DEC R16 BREQ Set_MS DEC R16 BREQ Set_TMS DEC R16 BREQ Set_H DEC R16 BREQ Set_D RJMP Set_WD Set_MS: CLI RCALL Inc_MS SEI RJMP Main Set_TMS: CLI RCALL Inc_TMS SEI RJMP Main Set_H: CLI RCALL Inc_H SEI RJMP Main Set_D: CLI RCALL Inc_D SEI RJMP Main Set_WD: CLI RCALL Inc_WD SEI RJMP Main /* Dec_D: LD R16, Z DEC R16 BRNE Dec_DJ LDI R16, 31 Dec_DJ: ST Z, R16 RET */ ; проверка нажатия кнопок. ; в статусный регистр будет заненсено состояние, прочитанное с портов ; в 16 регистре возвращены флаги нажатия на кнопки: ; если был переход состояния кнопки ОТЖАТА->НАЖАТА, то флаг устанавливается в 1, ; в остальных случаях - 0 ; В случае нажатия/отпускания хотя бы одной кнопки устанавливается защита от дребезга контактов. Ch: PUSH R18 IN R17, PIND MOV R16, stat_reg COM R17 MOV R18, R17 ; в 18 и 17 значения кнопок. 1 - если нажата EOR R17, R16 ANDI R17, 1<<TBTN|1<<MBTN BREQ NCh LDI R17, VBR_TIME STS VIBR, R17 NCh: MOV R16, stat_reg COM R16 AND R16, R18 ANDI R16, 1<<TBTN|1<<MBTN ANDI R18, 1<<TBTN|1<<MBTN ANDI stat_reg, ~((1<<TBTN)|(1<<MBTN)) OR stat_reg, R18 POP R18 RET ; отображение столбика. Display: CP blinc_reg, col_reg ; если отображаемый столбик совпадает с мигаемым, то BRNE No_blinc MOV R16, CLOCK ; сравниваем значение счётчика переполнения с SUBI R16, BLINC_TIME ; установленной константой BRSH END_disp ; и решаем отбражать, или нет (Получается мигание 1 раз в сек.) No_blinc: ;загрузить и показать LDI YL, low(SECND) LDI YH, high(SECND) CLR R16 ADD YL, col_cnt ADC YH, R16 LD R16, Y T_OUT R16 MOV R16, col_reg COL_OUT R16 RCALL Wait_disp ; тупо ждём какое-то время, пока горит столбик. END_disp: RET Wait_disp: SER R16 Wloop: nop nop nop nop nop nop DEC R16 BRNE Wloop RET ; Обработчик прерывания по переполнению счётчика Over: PUSH R16 IN R16, SREG PUSH R16 PUSH R17 PUSH ZL PUSH ZH ; щёлкаем счётчиком переполнений MOV R16, CLOCK LDI R17, CLK_IN_SEC RCALL Circle MOV CLOCK, R16 TST R17 BREQ Inc_sec ; если счётчик больше CLK_IN_SEC, то щёлкнем секундами ; иначе топаем на проверку дребезга End_sec: LDS R17, VIBR TST R17 ; если выключен дребезг BREQ VDR_CL ; ничего не делаем DEC R17 ; иначе, уменьшаем время до отключения защиты от дребезга. STS VIBR, R17 VDR_CL: POP ZH POP ZL POP R17 POP R16 OUT SREG, R16 POP R16 RETI Inc_sec: LDI ZL, low(SECND) ; загрузим адрес места хранения секунд LDI ZH, high(SECND) ; он автоматически будет увеличиватся. RCALL Inc_MS TST R17 BRNE End_sec RCALL Inc_TMS TST R17 BRNE End_sec RCALL Inc_MS TST R17 BRNE End_sec RCALL Inc_TMS TST R17 BRNE End_sec RCALL INC_H TST R17 BRNE End_sec RCALL Inc_D RCALL Inc_WD RJMP End_sec ; вход: в Z - адрес обрабатываемой ячейки 16 и 17 регистры должны быть свободны ; выход: 17 регистр - 0, если была прокрутка счётчика. Inc_MS: LD R16, Z LDI R17, 10 RCALL Circle ST Z+, R16 RET Inc_TMS: LD R16, Z LDI R17, 6 RCALL Circle ST Z+, R16 RET ; индикация часов - 12-ти часовая (4 светодиода), а верхний светодиод горит после полудня Inc_H: PUSH R18 LD R16, Z MOV R18, R16 ANDI R16, 15 ; оставим только часы ANDI R18, 16 ; а тут - только половину суток LDI R17, 12 RCALL Circle ; прокрутим TST R17 BRNE STORE_H ; если не прокрутка счётчика, валим сохранять PUSH R16 LDI R16, 16 EOR R18, R16 POP R16 TST R18 BREQ NEW_DAY SER R17 RJMP STORE_H NEW_DAY: CLR R17 STORE_H: OR R16, R18 ST Z+, R16 POP R18 RET Inc_D: LD R16, Z LDI R17, 31 RCALL Circle1 ST Z+, R16 RET ; понедельник - горит первый светодиод ; вторник - 2 ; .......... ; пятница - 5 ; суббота - ничего не горит ; воскресенье - горит весь ряд. Inc_WD: LD R16, Z CPI R16, 16 BREQ WD_6 TST R16 BREQ WD_7 CPI R16, 1+2+4+8+16 BREQ WD_1 LSL R16 RJMP WD_STORE WD_6: CLR R16 RJMP WD_STORE WD_7: LDI R16, 1+2+4+8+16 RJMP WD_STORE WD_1: LDI R16, 1 WD_STORE: ST Z+, R16 RET ; Циклическое увеличение велечины, начиная с 0. ; R16 - что увеличить, R17 - до каких пор увеличить ; результат: R16 - увеличенное, R17 = 0 если было переполнение. Circle: INC R16 CP R16, R17 BRCS Less SUB R16, R17 CLR R17 RET Less: SER R17 RET ; Циклическое увеличение велечины, начиная с 1. ; R16 - что увеличить, R17 - до каких пор увеличить ; результат: R16 - увеличенное, R17 = 0 если было переполнение. Circle1: INC R16 CP R16, R17 BRCS Less1 SUB R16, R17 INC R16 CLR R17 RET Less1: SER R17 RET .DSEG VIBR: .BYTE 1 SECND: .BYTE 7