;###############################################################################
;###                            Часы на DS1307                               ###
;###############################################################################
; подводится командой  
;echo '123' > /dev/ttyUSB4; date +set%y%m%d0%u%H%M%S > /dev/ttyUSB4

;.include "tn2313def.inc"
; Оптимизирован для ассемблера gavrasm
.device ATTiny2313

;===============================================================================
;  Определения
;===============================================================================
.def rx1 = R16
.def rx2 = R17
.def rx3 = R18
.def rx4 = R19
.def rx5 = R20
.def rx6 = R21
.def rx7 = R22
.def rx8 = R23
.def rx9 = R24

.def del = R25        ; задержки для IIC ()
.def null = R15       ; число 0
.def uart_ptr = R14   ; указатель буфера UART
.def uart_tsk = R13   ; Бит 0 - была команда 'set'
.def tasks = R12      ; Регистр основных задач
                      ; бит 0 устанавливается в прерывании 1Гц для точки и опроса DS1307
.def bright = R11     ; яркость дисплея
.def points = R10     ; точки на экране
;|7|6|5|4|3|2|1|0|
;    |           |_________________
;    |5 - левый сегмент, 0 - правый
;    |_____________________________
;

.def mode = R9 ; режим работы.
               ; 0 - без показа секунд
               ; 1 - с показом
               ; 2 - режим настроек
               
.def btn_tsk = R8        ; регистр задач по нажатию кнопок.
;|7|6|5|4|3|2|1|0|
;|       | 3-0 действие по нажатию
;|7-4 действие по отпусканию
.def btn_press = R7       ; регистр состояния кнопок (3-0 нажаты)



.equ xtal = 14318180                     ; кварц
.equ baudrate = 9600                     ; скорость UART
.equ bauddivider = xtal/(16*baudrate)-1
.equ write1307adr = 0b11010000           ; адрес DS1307 на шине с битом записи
.equ read1307adr = 0b11010001            ; адрес DS1307 на шине с битом чтения
.equ delay = 5                           ; задержка для работы I2C (экспериментально)
.equ max_bright = 250                    ; максимальная и минимальная яркость
.equ min_bright = 3
.equ btn_delay_cnt = 100                 ; задержка для кнопок

.equ key_port = PORTB
.equ key_bit = 2
.equ shift_port = PORTD
.equ shift_bit = 6
.equ data_port = PORTB
.equ data_bit = 4
.equ reset_port = PORTB
.equ reset_bit = 0
.equ sda_port = DDRD
.equ sda_bit = 3
.equ scl_port = DDRD
.equ scl_bit = 2
.equ sda_pin = PIND
.equ scl_pin = PIND

.equ btn1_pin = PINB
.equ btn1_bit = 1
.equ btn2_pin = PINB
.equ btn2_bit = 3
.equ btn3_pin = PIND
.equ btn3_bit = 4
.equ btn4_pin = PIND
.equ btn4_bit = 5

; Для gavrasm
.def XH = R27
.def XL = R26
.def YH = R29
.def YL = R28
.def ZH = R31
.def ZL = R30


;===============================================================================
;  Макросы
;===============================================================================

.MACRO  pull_sda                ; прижать шину SDA к земле
sbi sda_port,sda_bit
.ENDM

.MACRO  release_sda             ; отпустить SDA
cbi sda_port,sda_bit
.ENDM

.MACRO  pull_scl                ; прижать шину SCL земле
sbi scl_port,scl_bit
.ENDM

.MACRO  release_scl             ; отпустить SCL
cbi scl_port,scl_bit
.ENDM

.MACRO  release_line             ; отпустить обе линии
cbi scl_port,scl_bit
cbi sda_port,sda_bit
.ENDM

.MACRO  set_reset               ; послать импульс на ресет
cbi reset_port,reset_bit
nop
sbi reset_port,reset_bit
.ENDM

.MACRO  push_imp                ; послать тактовый импульс на регистры
sbi shift_port,shift_bit
nop
cbi shift_port,shift_bit
.ENDM

.MACRO  led_on                  ; включить индикаторы
cbi key_port,key_bit
.ENDM

.MACRO  led_off                 ; выключить индикаторы
sbi key_port,key_bit
.ENDM

.MACRO add16z                   ; макрос сложения 16-битного числа c 8-битным
    ldi ZH,High(@0*2)           ; в регистровой паре Z
    ldi ZL,Low(@0*2)            ; @0 - метка, @1 - регистр
    add ZL,@1                  
    adc ZH,null                  
.ENDM                           

;===============================================================================
; RAM
;===============================================================================
.dseg

display_buf: .byte 6     ; буфер индикатора. Готовые данные на выход
display_digit: .byte 6   ; Данные для вывода в десятичном формате
clock_buf: .byte 7       ; Регистр часов. Данные для чтения или записи
time_buf: .byte 18       ; Буфер для синхронизации времени

;===============================================================================
; EEPROM
;===============================================================================
.eseg           ; Хранение настроек
bright_buf:
.db 50          ; яркость  

mode_buf:
.db 0           ; режим

;===============================================================================
; FLASH
;===============================================================================
.cseg                                
;-------------------------------------------------------------------------------
; Векторы прерываний
;-------------------------------------------------------------------------------
.org 0x0000 
rjmp RESET ; Reset Handler
.org 0x0001 
reti ;rjmp INT0 ; External Interrupt0 Handler
.org 0x0002 
reti ;rjmp INT1 ; External Interrupt1 Handler
.org 0x0003 
reti ;rjmp TIM1_CAPT ; Timer1 Capture Handler
.org 0x0004 
rjmp TIM1_COMPA ; Timer1 CompareA Handler
.org 0x0005 
reti ;rjmp TIM1_OVF ; Timer1 Overflow Handler
.org 0x0006 
reti ;rjmp TIM0_OVF ; Timer0 Overflow Handler
.org 0x0007 
rjmp USART0_RXC ; USART0 RX Complete Handler
.org 0x0008 
reti ;rjmp USART0_DRE ; USART0,UDR Empty Handler
.org 0x0009 
reti ;rjmp USART0_TXC ; USART0 TX Complete Handler
.org 0x000A 
reti ;rjmp ANA_COMP ; Analog Comparator Handler
.org 0x000B 
reti ;rjmp PCINT ; Pin Change Interrupt
.org 0x000C 
reti ;rjmp TIMER1_COMPB ; Timer1 Compare B Handler
.org 0x000D 
reti ;rjmp TIMER0_COMPA ; Timer0 Compare A Handler
.org 0x000E 
reti ;rjmp TIMER0_COMPB ; Timer0 Compare B Handler
.org 0x000F 
reti ;rjmp USI_START ; Usi Start Handler
.org 0x0010 
reti ;rjmp USI_OVERFLOW ; USI Overflow Handler
.org 0x0011 
reti ;rjmp EE_READY ; EEPROM Ready Handler
.org 0x0012 
reti ;rjmp rjmp WDT_OVERFLOW ; Watchdog Overflow Handler


.org 0x0013
;===============================================================================
; Инициализация
;===============================================================================
reset:

ldi rx1,low(RAMEND)       
out spl,rx1

clr btn_tsk

ldi rx1,0b00010101              ; Настройка порта B
out DDRB, rx1
sbi PORTB,2                     ; Гасим табло

ldi rx1,0
mov null,rx1                    ; Нулевой регистр

ldi del, delay                  

ldi rx1,0b01000000
out DDRD,rx1
cbi PORTD,2                     ; линии I2C
cbi PORTD,3                     ; отключаем внутреннюю подтяжку

ldi rx1,5
mov bright,rx1                  ; минимальную яркость

mov points,rx1

mov mode,null                   ; режим работы = 0
;-------------------------------------------------------------------------------
; Таймер ШИМа (Timer0)

ldi rx1,(1<<COM0A0)|(1<<COM0A1)|(1<<WGM00) ; Phase correct PWM
out TCCR0A,rx1

ldi rx1,3
out OCR0A,rx1                   ; Значение ШИМа = яркость = 5 по дефолту

ldi rx1,(1<<CS01)               ;1/8
out TCCR0B,rx1

;-------------------------------------------------------------------------------
; Таймер 2Гц (Timer1)
; режим CTC к-т деления 1/1024 
; число в регистр OCR = (xtal/(F*N*2))-1
; OCR = (14318180/(2*2*1024))-1 = 3494

.equ ocrb = (xtal/(1*2*1024))-1

ldi rx1,high(ocrb)
out OCR1AH,rx1
ldi rx1,low(ocrb)
out OCR1AL,rx1

ldi rx1,(1<<WGM12)|(1<<CS10)|(1<<CS12) ; CTC, 1/1024
out TCCR1B,rx1

ldi rx1,(1<<OCIE1A) ; прерывание по совпадению A
out TIMSK,rx1

;-------------------------------------------------------------------------------
; UART


ldi rx1, low(bauddivider)
out UBRRL,rx1
ldi rx1, high(bauddivider)
out UBRRH,rx1

ldi rx1,(1<<TXEN)|(1<<RXEN)|(1<<RXCIE)    ; разрешаем только приём и прерывание по нему
out UCSRB,rx1

ldi rx1,(1<<UCSZ0)|(1<<UCSZ1)
out UCSRC, rx1

clr uart_ptr
clr uart_tsk
;-------------------------------------------------------------------------------
clr btn_tsk
clr btn_press

rcall iic_10clock

set_reset

ldi rx2,bright_buf
rcall EEread
mov bright,rx1
out OCR0A,rx1 


ldi rx2,mode_buf
rcall EEread
mov mode,rx1

sei

sbrs rx1,0
rjmp mode_0
rjmp mode_1

;###############################################################################

;===============================================================================
; Режим работы 0 (с показим секунд) 
;=============================================================================== 
mode_0:
ldi rx1,0b00010100              ; точки для времени
mov points,rx1                  ; ставим две статичные
rcall receive_iic               ; опрашиваем часы
rcall decode_time               ; декодируем
rcall decoder                   
rcall print                     ; и выводим
clr btn_tsk                     ; очистим задачи для кнопок
clr mode                        ; установим режим 0
clr rx1
ldi rx2,mode_buf                ; пишем в EEPROM 0 (режим)
rcall EEWrite
m_0:
  rcall btn_test                ; опрашиваем кнопки
  mov rx1,btn_tsk


  sbrc rx1,2                    ; если была нажата кнопка 2, выходим в другой режим
  rjmp mode_1

  rcall btn_0

  mov btn_tsk,rx1
  mov rx1,tasks
  sbrs rx1,0                    ; если небыло сигнала с таймера - зацикливаем
  rjmp m_0
  andi rx1,~(1<<0)           ; сбрасываем бит
  mov tasks,rx1  
  rcall receive_iic             ; опрашиваем часы
  rcall decode_time             ; декодируем
  rcall decoder                   
  rcall print                   ; и выводим
  rjmp m_0

;===============================================================================
; Режим работы 1 (без показа секунд) 
;=============================================================================== 

mode_1:
ldi rx1,0b00001000              ; одна точка для времени
mov points,rx1                 
rcall receive_iic               ; опрашиваем часы
rcall decode_time_no_sec        ; декодируем
rcall decoder                   
rcall print                     ; и выводим
clr btn_tsk                     ; очистим задачи для кнопок
ldi rx1,1
mov mode,rx1                    ; установим режим 1
ldi rx2,mode_buf                ; пишем в EEPROM 1 (режим)
rcall EEWrite
m_1:
  rcall btn_test                ; опрашиваем кнопки
  mov rx1,btn_tsk

  

  sbrc rx1,2                    ; если была нажата кнопка 2, выходим в другой режим
  rjmp mode_0

  rcall btn_0

  mov btn_tsk,rx1
  mov rx1,tasks
  sbrs rx1,0                    ; если небыло сигнала с таймера - зацикливаем
  rjmp m_1
  andi rx1,~(1<<0)           ; сбрасываем бит
  mov tasks,rx1  
  ldi rx1,0b00001000 
  eor points,rx1                ; инвертируем точку
  rcall receive_iic             ; опрашиваем часы
  rcall decode_time_no_sec      ; декодируем
  rcall decoder                   
  rcall print                   ; и выводим
  rjmp m_1

rjmp mode_1

;===============================================================================
; Общая процедура опроса кнопок для нормальных режимов
;===============================================================================
btn_0:
  sbrc rx1,0                    ; если была нажата кнопка 4, увеличим яркость
  rcall inc_br
  sbrc rx1,1                    ; если была нажата кнопка 3, уменьшим яркость
  rcall dec_br
  sbrc rx1,3                    ; если была нажата кнопка 1, показываем дату
  rcall show_date
  sbrc rx1,4                    ; если была отпущена кнопка 4, пишем EEPROM
  rcall EEWrite_bright
  sbrc rx1,5                    ; если была отпущена кнопка 3, пишем EEPROM
  rcall EEWrite_bright
  sbrc uart_tsk,0
  rcall tst_settime
ret

;===============================================================================
; Подпрограмма записи яркости в EEPROM
;=============================================================================== 

EEWrite_bright:
andi rx1,~((1<<5)|(1<<4)|(1<<1)|(1<<0))
                                ; сбрасываем задачу для нажатия и отпускания
push rx1
mov rx1,bright
ldi rx2,bright_buf
rcall EEWrite
pop rx1
ret

;===============================================================================
; Подпрограммы увеличения и уменьшения яркости
;=============================================================================== 

inc_br:
  mov rx2,bright
  cpi rx2,max_bright
  breq inc_br_ret
  inc rx2
  mov bright,rx2
  out OCR0A,rx2
inc_br_ret:
rcall btn_delay
ret

dec_br:
  mov rx2,bright
  cpi rx2,min_bright
  breq dec_br_ret
  dec rx2
  mov bright,rx2
  out OCR0A,rx2
dec_br_ret:
rcall btn_delay
ret

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

btn_delay:
  ldi rx2,0  
  ldi rx3,btn_delay_cnt                      
  btn_delay_loop:     
  dec rx2                
  brne btn_delay_loop 
  dec rx3
  brne btn_delay_loop 
             
ret

;===============================================================================
; Подпрограмма показа даты
;=============================================================================== 
show_date:                      ; при входе в rx1 у нас btn_tsk
andi rx1,~(1<<3)                ; сбросим бит действия
push rx1
push points
ldi rx1,0b00010100              ; точки для времени
mov points,rx1                  ; ставим две статичные


ldi XH,high(clock_buf+7)
ldi XL,low(clock_buf+7)

ldi YH,high(display_digit+6)
ldi YL,low(display_digit+6)

ldi rx1,3
sd_1:
  ld rx2,-X
  mov rx3,rx2
  andi rx3,0b00001111
  st -Y,rx3
  swap rx2
  mov rx3,rx2
  andi rx3,0b00000111
  st -Y,rx3
  dec rx1
  brne sd_1

rcall decoder
rcall print

sd_del:
ldi rx4,0                         
ldi rx5,0
ldi rx6,200

sd_del_0:     
dec rx4                
brne sd_del_0            

dec rx5
brne sd_del_0

dec rx6
brne sd_del_0

pop points
pop rx1
ret

;===============================================================================
; Подпрограмма записи в EEPROM (rx2 - адрес, rx1 - данные)
;=============================================================================== 

EEWrite:    

  sbic EECR,EEWE    ; Ждем готовности памяти к записи. Крутимся в цикле
  rjmp EEWrite

  cli               ; запрещаем прерывания.
  out EEAR,rx2      ; Загружаем адрес нужной ячейки
  out EEDR,rx1      ; и сами данные из rx1

  sbi EECR,EEMWE    ; взводим предохранитель
  sbi EECR,EEWE     ; записываем байт
  sei               ; разрешаем прерывания
ret 

;===============================================================================
; Подпрограмма чтения в EEPROM (rx2 - адреса, rx1 - данные)
;=============================================================================== 
EERead: 
  sbic EECR,EEWE     ; Ждем пока будет завершена прошлая запись.
  rjmp EERead         
  out EEAR, rx2      ; загружаем адрес нужной ячейки
    
  sbi EECR,EERE      ; Выставляем бит чтения
  in rx1,EEDR        ; Грузим из регистра данных результат
ret

;===============================================================================
; Подпрограмма опроса кнопок
;=============================================================================== 
btn_test:

mov rx1,btn_press
mov rx2,btn_tsk

sbic  btn1_pin,btn1_bit
rjmp bt01                       ; если кнопка нажата
  sbrc rx1,3                    ; проверяем, была ли она нажата ранее
  rjmp bt02                     ; если да - выходим
  ori rx2,(1<<3)                ; если нет - устанавливаем действие по нажатию
  ori rx1,(1<<3)                ; и факт нажатия
  rjmp bt02
bt01:                           ; если не нажата
  sbrs rx1,3                    ; и не была нажата ранее
  rjmp bt02                     ; выходим
  andi rx1,~(1<<3)              ; иначе сбрасываем факт нажатия
  ori rx2,(1<<7)                ; ставим действие по отпусканию
bt02:
;--------------
sbic  btn2_pin,btn2_bit
rjmp bt03                       ; если кнопка нажата
  sbrc rx1,2                    ; проверяем, была ли она нажата ранее
  rjmp bt04                     ; если да - выходим
  ori rx2,(1<<2)                ; если нет - устанавливаем действие по нажатию
  ori rx1,(1<<2)                ; и факт нажатия
  rjmp bt04
bt03:                           ; если не нажата
  sbrs rx1,2                    ; и не была нажата ранее
  rjmp bt04                     ; выходим
  andi rx1,~(1<<2)              ; иначе сбрасываем факт нажатия
  ori rx2,(1<<6)                ; ставим действие по отпусканию
bt04:
;--------------
sbic  btn3_pin,btn3_bit
rjmp bt05                       ; если кнопка нажата
  sbrc rx1,1                    ; проверяем, была ли она нажата ранее
  rjmp bt06                     ; если да - выходим
  ori rx2,(1<<1)                ; если нет - устанавливаем действие по нажатию
  ori rx1,(1<<1)                ; и факт нажатия
  rjmp bt06
bt05:                           ; если не нажата
  sbrs rx1,1                    ; и не была нажата ранее
  rjmp bt06                     ; выходим
  andi rx1,~(1<<1)              ; иначе сбрасываем факт нажатия
  ori rx2,(1<<5)                ; ставим действие по отпусканию
bt06:
;--------------
sbic  btn4_pin,btn4_bit
rjmp bt07                       ; если кнопка нажата
  sbrc rx1,0                    ; проверяем, была ли она нажата ранее
  rjmp bt08                     ; если да - выходим
  ori rx2,(1<<0)                ; если нет - устанавливаем действие по нажатию
  ori rx1,(1<<0)                ; и факт нажатия
  rjmp bt08
bt07:                           ; если не нажата
  sbrs rx1,0                    ; и не была нажата ранее
  rjmp bt08                     ; выходим
  andi rx1,~(1<<0)               ; иначе сбрасываем факт нажатия
  andi rx1,~(1<<0)               ; иначе сбрасываем факт нажатия
  ori rx2,(1<<4)                ; ставим действие по отпусканию
bt08:
;--------------

mov btn_press,rx1
mov btn_tsk,rx2

ret


;===============================================================================
; Подпрограмма записывает новое значение времени, пришедшее с UART'а
;=============================================================================== 
tst_settime:
push rx1
sbrs uart_tsk,0
ret

ldi XH,high(time_buf+3)
ldi XL,low(time_buf+3)
ldi YH,high(clock_buf+7)
ldi YL,low(clock_buf+7)

ldi rx1,7
set_0:
  clr rx3
  ld rx2,X+
  subi rx2,48
  or rx3,rx2
  swap rx3
  ld rx2,X+
  subi rx2,48
  or rx3,rx2
  st -Y,rx3 
  dec rx1  
  brne set_0

rcall transmit_iic

clr uart_tsk
clr uart_ptr
pop rx1
ret

;===============================================================================
; Подпрограмма декодирования данных для индикатора
;===============================================================================
decoder:

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

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

ldi rx3,0b00100000              ; маска для точек
ldi rx2,3
decoder_0:
  mov rx4,points
  ld rx1,Y+
  add16z arr_h,rx1
  lpm rx1,Z 
  and rx4,rx3                   ; наложим маску на регистр точек
  breq dec_1                    ; если 0, включим точку (установим в 0)
  andi rx1,0b11110111
  rjmp dec_2
  dec_1:
  ori rx1,0b00001000            ; иначе выключим
  dec_2:
  st X+,rx1
  lsr rx3

  mov rx4,points
  ld rx1,Y+
  add16z arr_l,rx1
  lpm rx1,Z
  and rx4,rx3                   ; наложим маску на регистр точек
  breq dec_3                    ; если 0, включим точку (установим в 0)
  andi rx1,0b11110111
  rjmp dec_4
  dec_3:
  ori rx1,0b00001000            ; иначе выключим
  dec_4:
  st X+,rx1
  
  lsr rx3
  dec rx2
  brne decoder_0

ret

;===============================================================================
; Подпрограмма вывода данных
;===============================================================================
print:
ldi XH,high(display_buf+6)
ldi XL,low(display_buf+6)
ldi rx1,6 

print_2:                        ; Если дисплей не горит
  sbic PINB,2                   ; ждём когда включится
  rjmp print_2
print_3:                        ; и как только потухнет
  sbis PINB,2                   ; начинаем вывод  
  rjmp print_3

print_0:
  ld rx3,-X
  ldi rx2,8
  print_1:
    sbrc rx3,0
    sbi data_port,data_bit
    sbrs rx3,0
    cbi data_port,data_bit
    push_imp
    lsr rx3
    dec rx2
    brne print_1
    
  dec rx1
  brne print_0
ret

;===============================================================================
; Подпрограмма декодирования времени с секундами
;===============================================================================
decode_time:
ldi XH,high(clock_buf)
ldi XL,low(clock_buf)

ldi YH,high(display_digit+6)
ldi YL,low(display_digit+6)

ldi rx1,3
dec_time_0:
  ld rx2,X+
  mov rx3,rx2
  andi rx3,0b00001111
  st -Y,rx3
  swap rx2
  mov rx3,rx2
  andi rx3,0b00000111
  st -Y,rx3
  dec rx1
  brne dec_time_0
ret

;===============================================================================
; Подпрограмма декодирования времени без секунд
;===============================================================================
decode_time_no_sec:
ldi XH,high(clock_buf+1)
ldi XL,low(clock_buf+1)

ldi YH,high(display_digit+6)
ldi YL,low(display_digit+6)

ldi rx1,11                      ; пустой символ
st -Y,rx1

ldi rx1,2
dec_time_s_0:
  ld rx2,X+
  mov rx3,rx2
  andi rx3,0b00001111
  st -Y,rx3
  swap rx2
  mov rx3,rx2
  andi rx3,0b00000111
  st -Y,rx3
  dec rx1
  brne dec_time_s_0

ldi rx1,11                      ; пустой символ
st -Y,rx1

ret


;===============================================================================
; Массивы индикатора
;===============================================================================

arr_h:
;     fgabhcde    fgabhcde
.db 0b01001000, 0b11101011 ;01
.db 0b10001100, 0b10001001 ;23
.db 0b00101011, 0b00011001 ;45
.db 0b00011000, 0b11001011 ;67
.db 0b00001000, 0b00001001 ;89
.db 0b10111111, 0b11111111 ;знак '-' (10) и пустой символ (11)
.db 0b10111100, 0b10111000 ;знак 'c' (12) и 'o' (13)
.db 0b10111110, 0b00011100 ;знак 'r' (14) и 'E' (15)

arr_l:
;     efabhcgd    efabhcgd 
.db 0b00001010, 0b11101011 ;01
.db 0b01001100, 0b11001000 ;23
.db 0b10101001, 0b10011000 ;45
.db 0b00011000, 0b11001011 ;67
.db 0b00001000, 0b10001000 ;89
.db 0b11111101, 0b11111111 ;знак '-' (10) и пустой символ (11)
.db 0b01111100, 0b01111000 ;знак 'c' (12) и 'o' (13)
.db 0b01111101, 0b00011100 ;знак 'r' (14) и 'E' (15)


;===============================================================================
; Работа с IIC 
;===============================================================================
iic_10clock:                   ; даёт 10 тактовых импульсов на линию
ldi rx1,10                     ; для приведения в чувства slave'а 
iic_10_1:                      ; после перезагрузки или подачи питания
  pull_scl
  rcall iic_delay
  release_scl
  dec rx1
  brne iic_10_1
ret
;-------------------------------------------------------------------------------
iic_full_reset:
ldi XH,high(clock_buf)         ; полный сброс часов
ldi XL,low(clock_buf)
ldi rx1,7
full_res1:
  st X+,null  
  dec rx1
  brne full_res1
rcall transmit_iic
rjmp reset
ret

;-------------------------------------------------------------------------------
iic_test_sda:                  ; проверка занятости SDA  
clt                            ; устанавливает флаг Т если линия занята      
sbis sda_pin,sda_bit
set 
ret
;-------------------------------------------------------------------------------
iic_test_scl:                  ; проверка занятости SCL  
clt                            ; устанавливает флаг Т если линия занята      
sbis scl_pin,scl_bit
set 
ret
;-------------------------------------------------------------------------------
iic_test2line:                  ; проверка занятости двух линий
rcall iic_test_sda              ; если хоть одна занята, ставится флаг T
brtc iic_test2line0
ret
iic_test2line0:
rcall iic_test_scl
ret
;-------------------------------------------------------------------------------
iic_delay:                      ; простая задержка
ldi del,delay
i2c_delay0:
  dec del
  brne i2c_delay0
ret
;-------------------------------------------------------------------------------
iic_start:                      ; даёт на линию команду start
pull_sda
rcall iic_delay
pull_scl
rcall iic_delay
release_sda
ret
;-------------------------------------------------------------------------------
iic_stop:                       ; даёт на линию команду stop
pull_sda
rcall iic_delay
release_scl
rcall iic_delay
release_sda
ret
;-------------------------------------------------------------------------------
iic_check_ask:                  ; Проверяет сигнал ASK на линии
release_sda                     ; Если есть - ставит бит T
rcall iic_delay
release_scl
rcall iic_delay
rcall iic_test_sda
pull_scl
pull_sda
ret
;-------------------------------------------------------------------------------
transmit_iic:                   ; Передача 7-ми байтного буфера
ldi XH,high(clock_buf)
ldi XL,low(clock_buf)
rcall iic_test2line             ; если хоть одна линия занята
brts iic_error0                 ; выводится ошибка 0 и зацикливается
rcall iic_start                 ; даётся на линию start                 
ldi rx1,write1307adr ;          ; передаётся адрес и команду на запись
rcall send_byte
clr rx1                         ; адрес, с какого писать (ноль)
rcall send_byte
ldi rx9,7
tr_iic_0:                       ; пересылаем весь буфер
  ld rx1, X+
  rcall send_byte
  dec rx9
  brne tr_iic_0
rcall iic_stop                  ; отправляется stop на линию
ret
;-------------------------------------------------------------------------------
send_byte:                      ; отправка байта
                                ; в rx1 должен быть пересылаемый байт
ldi rx2,8                       ; обе линии в нуле
send_byte_0:
  mov rx3,rx1                   
  andi rx3,128                  ; выделим старший бит.
  breq send_byte_1              ; если он в нуле, оставляем линии как есть
  release_sda                   ; иначе поднимаем SDA
  send_byte_1:
  rcall iic_delay
  release_scl                   
  rcall iic_delay
  pull_scl
  rcall iic_delay
  pull_sda
  lsl rx1
  dec rx2
  brne send_byte_0
rcall iic_delay                 ; Слушаем ASK. Если нет - ошибка 1
rcall iic_check_ask
brtc iic_error1                 
ret
;-------------------------------------------------------------------------------
iic_error0:                     ; Ошибки iic
ldi rx1,0
ldi rx2,0
rjmp iic_error

iic_error1:
ldi rx1,0
ldi rx2,1
rjmp iic_error

iic_error2:
ldi rx1,0
ldi rx2,2
rjmp iic_error

iic_error3:
ldi rx1,0
ldi rx2,3
rjmp iic_error
;-------------------------------------------------------------------------------

receive_iic:                    ; Приём 7-ми байтового буфера
ldi XH,high(clock_buf)
ldi XL,low(clock_buf)
rcall iic_test2line             ; если хоть одна линия занята
brts iic_error0                 ; выводится ошибка 0 и зацикливается
rcall iic_start                 ; даётся на линию start                 
                                ; сначала установим указатель 
                                ; адреса внутри 1307 на 0, записав туда
ldi rx1,write1307adr ;          ; передаётся адрес и команду на запись
rcall send_byte
clr rx1                         ; адрес, с какого писать (ноль)
rcall send_byte
rcall iic_stop                  ; отправляется stop на линию
rcall iic_delay
                                ; теперь начинаем читать
ldi rx1,read1307adr ;           ; передаётся адрес и команда на чтение
rcall iic_start                 ; отправляется start на линию
rcall send_byte
release_sda                     ; и отпускаем линию данных
ldi rx9,7
rcall iic_delay
res_0:
  rcall read_byte               ; читаем байт
  st X+,rx1                     ; сохраняем в буфер
  cpi rx9,1                     ; если этот байт не последний
  breq res_1                    ; тогда подтягиваем линию для ASK
  pull_sda                      
  release_scl                   ; и даём синхримпульс
  rcall iic_delay
  pull_scl
  release_sda
  res_1:
  dec rx9
  brne res_0
  rcall iic_delay
rcall iic_stop
ret

;-------------------------------------------------------------------------------

read_byte:                      ; Чтение байта из линии
clr rx1                         ; Байт будет записан в rx1
ldi rx2,8                       ; обе линии должны быть опущены
read_byte_0:
  lsl rx1
  release_scl                   ; подняли тактовый
  rcall iic_delay
  rcall iic_test_sda 
  brts read_byte_1              ; если линия в нуле, пропустим
  ori rx1,1                     ; иначе добавляем бит 
  read_byte_1:
  pull_scl                      ; опустили тактовый
  rcall iic_delay
  dec rx2
  brne read_byte_0
ret

;-------------------------------------------------------------------------------

iic_error:
cli
ldi XH,high(display_digit)
ldi XL,low(display_digit)
ldi rx3,10
st X+,rx3
st X+,rx1
st X+,rx2
st X+,rx3
rcall decoder

;iic_e:
rcall print

iic_e:
rjmp iic_e
ret

;===============================================================================
; Прерывание по совпадению Timer0
;===============================================================================

TIM1_COMPA:
push rx1
in rx1,SREG
push rx1

ldi rx1,1
or tasks,rx1

pop rx1
out SREG,rx1
pop rx1
reti


;===============================================================================
; Прерывание UART
;===============================================================================

USART0_RXC:
;rcall iic_error2
push rx1
in rx1,SREG
push rx1
push XH
push XL

in rx1, UDR                     ; Отправим эхо
us_re:
sbis UCSRA,UDRE
rjmp us_re
out UDR,rx1

push rx1
                                ; проверим на слово 'set' в начале
mov rx1,uart_ptr                ; если указатель не равен 0
cpi rx1,0
brne us_1                       ; следующая проверка
pop rx1                         ; иначе проверим на букву 's'
cpi rx1,'s'
breq us_x                       ; если верно, берём байт
rjmp us_4                       ; иначе очищаем указатель

us_1:
cpi rx1,1
brne us_2                       ;
pop rx1                         ;
cpi rx1,'e'
breq us_x                       ; 
rjmp us_4                       ; 

us_2:
cpi rx1,2
brne us_3                       ; 
pop rx1                         ; 
cpi rx1,'t'
breq us_x                       ; 

us_4:
clr uart_tsk
clr uart_ptr
rjmp us_0 

us_3:
pop rx1

us_x:
ldi XH,high(time_buf)
ldi XL,low(time_buf)
add XL,uart_ptr
adc XH,null

st X,rx1

inc uart_ptr
mov rx1,uart_ptr
cpi rx1,18
brne us_0
clr uart_ptr
ser rx1
mov uart_tsk,rx1

us_0:
pop XL
pop XH
pop rx1
out SREG,rx1
pop rx1

reti





;###############################################################################
big_delay:
ldi rx1,0                         
ldi rx2,0
ldi rx3,100

loop:     
dec rx1                
brne loop            

dec rx2
brne loop

dec rx3
brne loop
ret


;------------------------------------------
test: ; провера правильности вывода
ldi XH,high(time_buf)
ldi XL,low(time_buf)

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


ldi rx1,6
test0:
 ld rx2,X+
 subi rx2,48
 st y+,rx2
 dec rx1
 brne test0

test1:
rcall decoder
rcall print
rcall big_delay
rjmp test1
