Смастерил я двоичные часы. Делюсь.
Вывод информации происходит на светодиодную матрицу в двоичном виде. Горящий светодиод означает единичку, не горящий - ноль. Матрица имеет пять строк по семь столбцов в каждой.
В первом столбце выводится день недели,
во втором - число месяца,
в третьем - час,
в 4 - десятки минут,
в 5 - минуты,
в 6 - десятки секунд,
в 7 - секунды.
День недели и час выводятся не по общим правилам:
для дня недели:
00001 - понедельник
00010 - вторник
00100 - среда
01000 - четверг
10000 - пятница
00000 - суббота
11111 - воскресенье
Можно было сделать и пообычному, но я так захотел. :)

Час выводится в третьем столбике, в двоичном фомате от 0 до 11 (двенадцатичасовой формат), а свободный старший бит светится во второй половине суток (с 12:00 до 24:00)
схема размещения неправильных бит
на рисунке красным цветом выделен первый стобец - день недели, зелёным - столбец, в котором отображается час, а синим - бит, означающий вторую половину дня.

Остальное отображается в простом двоичном формате.

Часы имеют две кнопки - красную и зелёную. В нормальном режиме нажатие на зелёную кнопку выключает (включает) индикацию. Нажатие на красную кнопку меняет режим.
Режим меняется циклически:
Нормальный режим-> настройка 7 столбца ->настройка 6 столбца ->......->настройка 1столбца->нормальный режим
В режиме настройки чего-либо изменение производится зелёной кнопкой. При выключеном экране красная кнопка не рабтает.

Сначала схема была большой, но потом после экспериментов и оптимизаций получилось вот такое убожество
схема часов
Кварц на 4.096Мгц

Вот рабочие часы.
часы



А вот, собственно, ассемблерный код для контроллера:
.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

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


Hosted by uCoz