;    Использованные компоненты схемы:				      
;    Микроконтроллер - PIC16F84-04I/P				       
;    Часы реального времени - DS1307                                  
;    Термометр - DS1621                                               
;    Индикатор - ИЖЦ21-4/7                                            
;    Регистры индикатора - КР1533ИР24 (4шт)			      
                                                                     
;    Используемые выводы микроконтроллера:                            
;    A0 (17) - строб записи в регистры                                
;    А1 (18) - данные для индикации                                  
;    А2 (1)  - SCL                                                    
;    А3 (2)  - SDA						      
;    А4 (3)  - вход 4кГц для таймера				      
;    В1 (7)  - вход кнопки "+минута"				     
;    В2 (8)  - вход кнопки "+час"				      
;    В3 (9)  - подложка для LCD индикатора			                                                                           
;********************************************************************

	list      p=16F84             
	#include <p16F84.inc>         

	__CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_OFF & _HS_OSC



w_temp        	EQU     0x0C	; переменная для временного хранения
status_temp   	EQU     0x0D	; переменная для временного хранения
count1		EQU	0x0E	; рабочий счетчик
count2		EQU	0x0F	; рабочий счетчик
count3		EQU	0x10	; рабочий счетчик
count4		EQU	0x11	; рабочий счетчик


flags		EQU	0x12	; переменная для хранения флаг-битов
work_data	EQU	0x13	; рабочая переменная	
digit1		EQU	0x14	; значение для первого знакоместа
digit2		EQU	0x15	; значение для второго знакоместа
digit3		EQU	0x16	; значение для третьего знакоместа
digit4		EQU	0x17	; значение для четвертого знакоместа
digitemp	EQU	0x18	; промежуточное значение знакоместа
disp		EQU	0x19	; счетчик времени отображения
bin		EQU	0x1A	; рабочая переменная
tens_and_ones	EQU	0x1B	; рабочая переменная для перекодировки
hundreds	EQU	0x1C	; рабочая переменная для перекодировки
timelenght	EQU	0x06	; длительность индикации времени, сек
templenght	EQU	0x06	; длительность индикации температуры, сек

;*** Импортированные переменные для подпрограмм работы с шиной I2C ***
STATU   	EQU     20	 ; Переменная состояния
FLAG    	EQU     21      ; Переменная флагов общего назначения
I2CDEV  	EQU     22      ; Битовый буфер
ERCODE  	EQU     23      ; Код ошибки (для индикации занятости линии)
ADDR    	EQU     24      ; Переменная адреса
DATAI   	EQU     25      ; Переменная для хранения входных данных
DATAO   	EQU     26      ; Переменная для хранения выходных данных
DATAI_1	        EQU	27	; Переменная для хранения входных данных
DATAO_1	        EQU	28	; Переменная для хранения выходных данных
SLAVE   	EQU     29      ; Адрес устройства (YYYYxxx0)
TXBUF   	EQU     2A      ; Буфер передачи
RXBUF   	EQU     2B      ; Буфер приема
COUNT   	EQU     2C      ; Счетчик битов



;*** Битовые переменные (флаги) подпрограмм работы с шиной I2C***
ERR1    	EQU     0	; Флаг-бит ошибки (здесь не используется)
SDA     	EQU     3	; RA3, вход/выход данных (SDA)
SCL     	EQU     2	; RA2, тактовая частота (SCL)
DI      	EQU     7	; Входной бит подпрограммы I2C
DO      	EQU     6	; Выходной бит подпрограммы I2C

		ORG     0x000       
  		goto    main           	
		ORG     0x004		; вектор прерывания
		
                movwf	 w_temp 	; сохранение текущего значения W
		movf	 STATUS,w 	; перенос содержимого STATUS в W
		movwf	 status_temp 	; сохранение содержимого регистра STATUS
;-------------------------------------------------------------------------------------		
		clrf	TMR0		; очистка таймера 
		bcf	INTCON, T0IF	; сброс флага прерывания
		btfsc	flags, 1	; если flags<1>=0 отображаем время
		goto	tempread	; если flags<1>=1 отображаем температуру
timeread	call	clock		; чтение текущего значения времени
		btfss	flags, 0	; разделитель включен?
		goto	d_point		; если нет,
		bsf	digit2, 0	; то включаем разделитель
		bcf	flags, 0	; сбрасываем флаг разделителя
		goto 	gohome		; и выходим из подпрограммы
d_point		bcf	digit2, 0	; иначе выключаем разделитель
		bsf	flags, 0	; устанавливаем флаг разделителя 
		decfsz	disp, f		; уменьшаем на 1 время индикации. Время 
					; истекло?
		goto 	gohome		; нет, продолжаем отображать часы
		bsf	flags, 1	; иначе устанавливаем флаг индикации 
					; температуры
		movlw	templenght	; и загружаем в disp
		movwf	disp		; длительность ее отображения
		goto	gohome
;-------------------------------------------------------------------------------------
tempread	call 	celsio		; чтение текущего значения температуры
		btfss	flags, 0
		goto	zerot
		bcf	flags, 0
		goto 	gohome		
zerot		bsf	flags, 0
		decfsz	disp, f		; уменьшаем на 1 время индикации. Время 
					; истекло?
		goto 	gohome		; нет, продолжаем отображать термометр
		bcf	flags, 1	; если да, сбрасываем флаг индикации 
					; температуры
		movlw	timelenght	; и устанавливаем длительность отображения 
					; времени
		movwf	disp
		movlw  B'10010010'	;92	; 0x9E обращаемся к термометру
		movwf  SLAVE
		movlw   0xEE		; с командой начать новое измерение
		movwf   ADDR
		call    WRCOM          	
;-------------------------------------------------------------------------------------
gohome		movf    status_temp,w   ; чтение копии регистра STATUS
		movwf	STATUS	        ; восстановление значения регистра STATUS
		swapf   w_temp,f
		swapf   w_temp,w  	; восстановление значения аккумулятора W
		retfie                  ; возврат из прерывания



main		clrf	PORTA
		clrf	PORTB
		clrf	flags
		movlw	timelenght
		movwf	disp
		clrwdt				; очистка WDT и пределителя
		bsf	STATUS, RP0	        ; bank 1
		movlw	b'01110010'		; 
		movwf	OPTION_REG^80		
		movlw	b'11110000'		
		movwf	TRISA^80
		movlw	b'11110111'
		movwf	TRISB^80
		bcf	STATUS, RP0	         ; bank 0 



;*************************************************************;
;       инициализация часов (DS1307) и термометра (DS1621)    		   
;*************************************************************;
	movlw   B'10010010'	;92	; 0x9E записываем в SLAVE адрес термометра
	movwf   SLAVE
	movlw   0xAC		; адрес регистра конфигурации термометра
	movwf   ADDR
	movlw	0x0B		; режимы термометра: POL-high, 1SHOT-yes
	movwf   DATAO		
	call    WRBYTE          ; подпрограмма записи байта
	nop			; защитный интервал
	movlw   B'10010010'	;92	; 0x9E обращение к термометру
	movwf   SLAVE
	movlw   0xEE		; команда начать измерение температуры
	movwf   ADDR
	call    WRCOM		; запись команды в термометр
	nop			; защитный интервал
	movlw   0xD0		; проверка пропадания питания
	movwf   SLAVE		; установили адрес часов
	movlw   0x00		; установили адрес регистра состояния
	movwf   ADDR 
	call	RDBYTE		; считываем регистр состояния
	btfss	DATAI,7		; если питание не пропадало, обходим 
				; инициализацию
	goto	okay		; и начинаем отображать время и температуру
	movlw   0xD0		; иначе - инициализируем часы	
	movwf   SLAVE
	clrw		
	movwf   ADDR		; кварцевый генератор включен,
	movwf   DATAO		; счетчик секунд сброшен
	call    WRBYTE          ; запись байта
	nop
	movlw   0xD0		; адрес для обращения к часам
	movwf   SLAVE
	movlw   0x01
	movwf   ADDR
	movlw	0x00		; обнуление счетчика минут 
	movwf   DATAO
	call    WRBYTE          
	nop
	movlw   0xD0		; адрес для обращения к часам
	movwf   SLAVE
	movlw   0x02
	movwf   ADDR
	movlw	0x00		; обнуление счетчика часов
	movwf   DATAO
	call    WRBYTE          
	nop
	movlw   0xD0		; адрес для обращения к часам
	movwf   SLAVE
	movlw   0x07		; адрес контрольного регистра
	movwf   ADDR		; устанавливаем режим отображения 24 часа
	movlw	0x91		; и частоту 4кГц на выходе Tout
	movwf   DATAO
	call    WRBYTE          
	nop


okay	clrf	TMR0		; очищаем таймер
	movlw	b'10100000'
	movwf	INTCON		; разрешаем прерывание по TMR0

repeat	btfss	PORTB, 1	; нажата кнопка "+минута"?
	call	mninc		; если да, увеличить значение минут
	call	ind_LCD		; иначе обновить значение на индикаторе
	btfss	PORTB, 2	; нажата кнопка "+час"?
	call	hrinc		; если да, увеличить значение часов
	call	ind_LCD		; иначе обновить значение на индикаторе
	goto	repeat


;*************************************************************;
;       чтение текущего времени из часов                      			   
;*************************************************************;
clock	movlw	0xD0
	movwf	SLAVE
	movlw   0x01
	movwf   ADDR 
	call	RDBYTEN		; чтение двух байтов: 01H, 02H
	movf	DATAI,W
	andlw	0x0F
	call	convert		; табличная перекодировка
	movwf	digit4
	swapf	DATAI,F
	movf	DATAI,W
	andlw	0x07
	call	convert		; табличная перекодировка
	movwf	digit3	
	movf	DATAI_1,W
	andlw	0x0F
	call	convert		; табличная перекодировка
	movwf	digit2
	swapf	DATAI_1,F
	movf	DATAI_1,W
	andlw	0x03
	btfsc	STATUS,Z
	movlw	0x0A
	call	convert		; табличная перекодировка
	movwf	digit1		
	return

;*************************************************************;
;       чтение значения температуры из термометра             		   
;*************************************************************;
celsio
	movlw	b'00000000'		; гасим первое знакоместо 
	movwf	digit1			; 
	movlw	B'10001110'		; символ "градус"
	movwf	digit4			; в четвертое знакоместо
	movlw   B'10010010'		;92	;0x9E
	movwf   SLAVE
	movlw   0xAA
	movwf   ADDR 
	call	RDBYTEN			; MSB-LSB
	movlw	B'00000100'		; символ минус		
	btfss	DATAI,7			; если бит 7=1, то температура отрицательная
	goto	rotate
	decf	DATAI,F
	movwf	digit1			; записали символ минус в первое знакоместо		
	movlw	0xFF
	xorwf	DATAI,F
rotate
	movf	DATAI,W
	movwf	bin
	bcf	bin,7
	call	binary_to_bcd		; подпрограмма конвертации
	movf	tens_and_ones,W
	andlw	0x0F
	call	convert
	movwf	digit3
	swapf	tens_and_ones,F
	movf	tens_and_ones,W
	andlw	0x0F
	call	convert
	movwf	digit2
	xorlw	0x3F		
	btfss	STATUS, Z	 
	return			; если в 2-м знакоместе незначащий ноль
	movf	digit3, w	; то для лучшей читаемости значения
	movwf	digit2		; переносим значение из 3-го знакоместа в 2-е
	clrf	digit4		; гасим 4-е знакоместo
	movlw	B'00111010'	; символ "С"
	movwf	digit3		; в третье знакоместо
	movf	digit2, w		
	xorlw	0x3F		
	btfss	STATUS, Z	; если температура ноль градусов,
	return
	clrf	digit1		; то символ "плюс/минус" не отображаем
	return			 
	

;*************************************************************;
;       подпрограмма отображения 4 символов 		      		   
;       на ЖК-индикаторе                                      				   ;
;*************************************************************;
ind_LCD


	call	delay		; все данные выгружены, держим данные
	bcf	PORTB, 3	; уровень на подложке в 0
; на индикаторе
	;bsf	PORTB, 3	; уровень на подложке в 1
	movf	digit1, w	; первая цифра в аккумуляторе
	xorlw	0xFF		; инверсия всех битов
	movwf	digitemp
	call 	loader		; выгрузили биты в индикатор
	movf	digit2, w	; вторая цифра в аккумуляторе
	xorlw	0xFF		; инверсия всех битов
	movwf	digitemp
	call 	loader		; выгрузили биты в индикатор
	movf	digit3, w	; третья цифра в аккумуляторе
	xorlw	0xFF		; инверсия всех битов
	movwf	digitemp
	call 	loader		; выгрузили биты в индикатор
	movf	digit4, w	; четвертая цифра в аккумуляторе
	xorlw	0xFF		; инверсия всех битов
	movwf	digitemp
	call 	loader		; выгрузили биты в индикатор
	bsf	PORTB, 3	; уровень на подложке в 1
	nop
	bcf	PORTB, 3	; уровень на подложке в 0
	call	delay		; все данные выгружены, держим данные 
; на индикаторе
	return

;*************************************************************;
;       подпрограмма табличной конвертации		     		   
;       принимает и возвращает данные в w                     			   
;*************************************************************;
convert
	addwf	PCL, f
	retlw	B'11111010'	;BCEDFGAH	;0
	retlw	B'11000000'	;00EDFGAH	;1
	retlw	B'10110110'	;00EDFGAH	;2
	retlw	B'11010110'	;00EDFGAH	;3
	retlw	B'11001100'	;00EDFGAH	;4
	retlw	B'01011110'	;00EDFGAH	;5
	retlw	B'01111110'	;00EDFGAH	;6
	retlw	B'11000010'	;00EDFGAH	;7
	retlw	B'11111110'	;00EDFGAH	;8
	retlw	B'11011110'	;00EDFGAH	;9
	retlw	B'00000000'	;00EDFGAH	;
	retlw	B'00000100'	;00EDFGAH	;-
	retlw	B'00111010'	;00EDFGAH	;C
	retlw	B'11110100'	;00EDFGAH	;d
	retlw	B'00111110'	;00EDFGAH	;E








;end table


           
		

;*************************************************************;
;       подпрограмма выгрузки знакоместа в регистры,	      	  	   
;       данные в переменной digitemp                          			   
;*************************************************************;

loader

	movlw	0x08
	movwf	count3
nextbit	bcf	PORTA, 1	; строб в 0
	btfss	digitemp, 7	; проверяем старший бит
	bcf	PORTA, 0	; установили порт A1 в 0, если текущий бит = 0
	btfsc	digitemp, 7
	bsf	PORTA, 0	; установили порт A1 в 1, если текущий бит = 1
	bsf	PORTA, 1	; строб в 1, бит записан в регистр
	rlf	digitemp, f	; сдвиг данных
	decfsz	count3, f
	goto	nextbit
	return
	
;*************************************************************;
;       подпрограмма обработки кнопки "+час"                  			   ;
;*************************************************************;
hrinc	clrf	INTCON	; запрещаем все прерывания
	bcf	flags, 1
	movlw	timelenght	; переустанавливаем время работы в режиме "часы"
	movwf	disp
	movlw   0xD0		; читаем текущее время
	movwf   SLAVE
	movlw   0x02
	movwf   ADDR
	call	RDBYTE
	swapf	DATAI, w
	andlw	0x0F
	movwf	digitemp	; выделили старший полубайт в перем. digitemp
	btfss	digitemp, 1 	; если старший разряд не равен 2
	goto	high1		; уходим на другой фрагмент
	movf	DATAI, w	
	andlw	0x0F		; выделяем младший полубайт
	addlw	0x01
	movwf	w_temp
	btfsc	w_temp, 2	
	clrf	w_temp		; если младший разряд часов =4 обнуляем его
	movf	w_temp, f
	btfsc	STATUS, Z
	clrf	digitemp	; и обнуляем старший разряд, получили 00 часов
	goto	sethr
high1	movf	DATAI, w	
	andlw	0x0F		; выделяем младший полубайт
	addlw	0x01
	movwf	w_temp
	sublw	0x0A
	btfsc	STATUS, Z	
	clrf	w_temp		; если младший разряд часов больше 9, обнуляем 
	btfsc	STATUS, Z
	incf	digitemp, f	; прибавляем 1 в старший разряд
sethr	swapf	digitemp, f
	movf	digitemp, w
	iorwf	w_temp, f	; соединили старший и младший разряды в w_temp
	movlw	0xD0
	movwf	SLAVE
	movlw	0x02
	movwf	ADDR
	movf	w_temp, w
	movwf	DATAO
	call	WRBYTE		; записали новое значение в часы
	call	clock		; читаем новое значение времени
	bsf	digit2, 0	; разделитель постоянно включен
	movlw	0x10
	movwf	count4
reindh	call	ind_LCD		; выполняем задержку в 10h циклов индикации
	decfsz	count4, f
	goto	reindh
	clrf	TMR0		; очищаем таймер
	movlw	b'10100000'
	movwf	INTCON		; разрешаем прерывание по TMR0
	return	
	
;*************************************************************;
;       подпрограмма обработки кнопки "+минута"               		   
;*************************************************************;
mninc	clrf	INTCON
	bcf	flags, 1
	movlw	timelenght	; увеличиваем время работы в режиме "часы"
	movwf	disp
	movlw   0xD0		; читаем текущее время
	movwf   SLAVE
	movlw   0x01
	movwf   ADDR
	call	RDBYTE
	swapf	DATAI, w
	andlw	0x0F
	movwf	digitemp	; выделили старший полубайт в перем. digitemp
	sublw	0x05
	btfss	STATUS, Z 	; если старший разряд не равен 5
	goto	high4		; уходим на другой фрагмент
	movf	DATAI, w	
	andlw	0x0F		; выделяем младший полубайт
	addlw	0x01
	movwf	w_temp
	sublw	0x0A
	btfsc	STATUS, Z	
	clrf	w_temp		; если младший разряд минут =10 обнуляем его
	btfsc	STATUS, Z
	clrf	digitemp	; и обнуляем старший разряд, получили 00 минут
	goto	setmin
high4	movf	DATAI, w	
	andlw	0x0F		; выделяем младший полубайт
	addlw	0x01
	movwf	w_temp
	sublw	0x0A
	btfsc	STATUS, Z	
	clrf	w_temp		; если младший разряд минут больше 9, обнуляем 
	btfsc	STATUS, Z
	incf	digitemp, f	; прибавляем 1 в старший разряд
setmin	swapf	digitemp, f
	movf	digitemp, w
	iorwf	w_temp, f	; соединили старший и младший разряды в w_temp
	movlw	0xD0
	movwf	SLAVE
	movlw	0x01
	movwf	ADDR
	movf	w_temp, w
	movwf	DATAO
	call	WRBYTE		; записали новое значение в часы
	call	clock		; читаем новое значение времени
	bsf	digit2, 0	; разделитель постоянно включен
	movlw	0x10
	movwf	count4
reindm	call	ind_LCD
	decfsz	count4, f
	goto	reindm
	clrf	TMR0		; очищаем таймер
	movlw	b'10100000'
	movwf	INTCON		; разрешаем прерывание по TMR0
	return	

;*************************************************************;
;       подпрограмма задержки                                 				   ;
;*************************************************************;			
delay	movlw	0x10		;0x10
	movwf	count1	
loop2	movlw	0xA0	;0xA0
	movwf	count2
loop1	decfsz	count2, 1
	goto	loop1
	decfsz	count1, 1
	goto	loop2
	return

;*************************************************************;
;       ИМПОРТИРОВАННЫЕ ПОДПРОГРАММЫ                          	   ;
;*************************************************************;	

;*************************************************************;
; Запись одного байта данных в устройство I2C                		   
; Входные данные: DATAO = байт данных для записи              		   
;                 ADDR  = адрес назначения (в устройстве)     		   
;                 SLAVE = адрес устройства в схеме (1010ххх0)     		   
;*************************************************************;

WRBYTE	
	MOVF    SLAVE,W			; Переносим адрес устройства
	MOVWF   TXBUF 			; в буфер передачи
	CALL    BSTART   		; Генерируем бит START
	CALL    TX                    ; Передаем адрес устройства
	MOVF    ADDR,W   		; Переносим адрес назначения
	MOVWF   TXBUF   		; в буфер передачи
	CALL    TX            	 	; Передаем адрес назначения
	MOVF    DATAO,W			; Переносим данные 
	MOVWF   TXBUF 			; в буфер передачи
	CALL    TX                    ; Передаем данные и анализируем подтверждение
	CALL    BSTOP    		; Генерируем бит STOP
	RETURN

;*************************************************************;
; Запись команды в устройство I2C                             			   ;
; Входные данные: ADDR = команда			      		   
;                 SLAVE = адрес устройства в схеме (1010ххх0) 		   
;*************************************************************;

WRCOM	
	MOVF    SLAVE,W			; Переносим адрес устройства
	MOVWF   TXBUF			; в буфер передачи
	CALL    BSTART			; Генерируем бит START
	CALL    TX              	; Передаем адрес устройства
	MOVF    ADDR,W			; Переносим слово команды
	MOVWF   TXBUF 			; в буфер
	CALL    TX              	; Передаем команду
	CALL    BSTOP 			; Генерируем бит STOP
	RETURN

;*************************************************************;
; Запись ДВУХ байтов данных в устройство I2C                  		   
; Входные данные: DATAO = 1-й байт данных для записи          	   
;                 DATAO_1 = 2-й байт данных для записи        			   ;
;                 ADDR  = адрес назначения (в устройстве)     			   ;
;                 SLAVE = адрес устройства в схеме (1010ххх0) 		   
;*************************************************************;

WRBYTEN	
	MOVF    SLAVE,W 		; Переносим адрес устройства
	MOVWF   TXBUF   		; в буфер передачи
	CALL    BSTART   		; Генерируем бит START
	CALL    TX              	; Передаем адрес устройства
	MOVF    ADDR,W 			; Переносим адрес назначения
	MOVWF   TXBUF 			; в буфер передачи
	CALL    TX 			; Передаем адрес назначения
	MOVF    DATAO,W			; Переносим первый байт данных
	MOVWF   TXBUF 			; в буфер передачи
	CALL    TX              	; Передаем данные и анализируем подтверждение
	MOVF    DATAO_1,W       	; Переносим второй байт данных 
	MOVWF   TXBUF    		; в буфер передачи
	CALL    TX              	; Передаем данные и анализируем подтверждение
	CALL    BSTOP    		; Генерируем бит STOP
	RETURN

;*************************************************************;
; Чтение байта данных из устройства I2C                       			   ;
; Входные данные:  ADDR  = адрес назначения (в устройстве)    	   
;                  SLAVE = адрес устройства в схеме (1010ххх0)		   
; Выходные данные: DATAI = байт данных                        		   
;*************************************************************;

RDBYTE	
	MOVF    SLAVE,W			; Переносим адрес устройства 
	MOVWF   TXBUF  			; в буфер передачи (R/W = 0)
	CALL    BSTART 			; Генерируем бит START
	CALL    TX 			; Передаем адрес устройства
	MOVF    ADDR,W			; Переносим адрес назначения
	MOVWF   TXBUF			; в буфер передачи
	CALL    TX              	; Передаем адрес назначения
	CALL    BSTART			; Генерируем бит START
	MOVF    SLAVE,W			; Переносим адрес устройства 
	MOVWF   TXBUF			; в буфер передачи
	BSF     TXBUF,0			; Устанавливаем режим READ (R/W = 1)
	CALL    TX			; Передаем адрес устройства
	CALL    RX			; Читаем байт и подтверждение
	CALL    BSTOP 			; Генерируем бит STOP
	MOVF    RXBUF,W			; Сохраняем данные
	MOVWF   DATAI			; в буфере приема DATAI
	RETURN

;*************************************************************;
; Чтение ДВУХ байтов данных из устройства I2C                 		   
; Входные данные:  	ADDR  = адрес назначения (в устройстве)         	   
;                  		SLAVE = адрес устройства в схеме (1010ххх0)	   
; Выходные данные: DATAI = 1-й байт данных                    		   
;                  		DATAI_1 = 2-й байт данных                  		   ;
;*************************************************************;

RDBYTEN	
	MOVF    SLAVE,W 		; Переносим адрес устройства 
	MOVWF   TXBUF 			; в буфер передачи (R/W = 0)
	CALL    BSTART 			; Генерируем бит START
	CALL    TX              	; Передаем адрес устройства
	MOVF    ADDR,W			; Переносим адрес назначения
	MOVWF   TXBUF			; в буфер передачи
	CALL    TX              	; Передаем адрес назначения
	CALL    BSTART 			; Генерируем бит START
	MOVF    SLAVE,W			; Переносим адрес устройства 
	MOVWF   TXBUF 			; в буфер передачи
	BSF     TXBUF,0			; Устанавливаем режим READ (R/W = 1)
	CALL    TX			; Передаем адрес устройства
	CALL    RX0			; Читаем первый байт и подтверждение
	MOVF    RXBUF,W			; Сохраняем данные 
	MOVWF   DATAI			; в буфере приема DATAI
	CALL    RX              	; Читаем второй байт и подтверждение
	MOVF    RXBUF,W			; Сохраняем данные
	MOVWF   DATAI_1			; в буфере приема DATAI_1
	CALL    BSTOP			; Генерируем бит STOP
	RETURN


; Подпрограмма генерации бита START                    		 	   
; Генерируется бит START (SCL удерживается в состоянии 1,     	   
; пока на SDA происходит переход от высокого уровня к низкому)	   
; затем проверяется состояние линии SCL.			      	   
; Входных и выходных данных нет.			      		   
;*************************************************************;	

BSTART	
	MOVLW   B'00000000'		; Настраиваем SCL и SDA на вывод
	MOVWF   PORTA			; В СООТВЕТСТВИИ СО СХЕМОЙ!!!
	BSF     PORTA,SDA		; надо быть уверенным, что SDA=1
	BSF     PORTA,SCL  		; устанавливаем SCL=1
	MOVLW   1               	; предполагаемый код ошибки 1
	BTFSS   PORTA,SCL    		; линия блокирована?
	CALL    ERR             	; если да, обработка кода ошибки
	BCF     PORTA,SDA       	; переводим SDA в 0, пока SCL=1
	NOP                     	; защитный временной интервал
	NOP
	NOP
	BCF     PORTA,SCL       	; сбрасываем SCL, START выполнен
	RETLW   0

;*************************************************************;
; Генерация бита STOP                                   				   ;
; Генерируется бит STOP (состояние SDA переходит из 0 в 1,    	   
; пока на SCL удерживается высокий уровень)		      		   
; Входных и выходных данных нет.                              			   ;
;*************************************************************;	

BSTOP	
	MOVLW   B'00000000'     	; Настраиваем SCL и SDA на вывод
	MOVWF	PORTA			; В СООТВЕТСТВИИ СО СХЕМОЙ!!!
	BCF     PORTA,SDA		; Устанавливаем SDA=0
	BSF     PORTA,SCL		; Устанавливаем SCL=0
	NOP				; Защитный временной интервал
	NOP
	NOP
	MOVLW   1               	; Предполагаемый код ошибки 1
	BTFSS   PORTA,SCL		; SCL=1?
	CALL    ERR             	; Нет, SCL удерживается устройством в 0
	BSF     PORTA,SDA		; Переводим SDA из 0 в 1, пока SCL=0
	MOVLW   4               	; Предполагаемый код ошибки 4
	BTFSS   PORTA,SDA		; SDA=1?
	CALL    ERR             	; Нет, SDA не освобождается для  STOP
	RETLW   0

;*************************************************************;
; Получение бита данных из устройства I2C в PIC               		   
;*************************************************************;	

BITIN	MOVLW   B'00001000'     	; SDA - ввод, SCL - вывод
	MOVWF   PORTA			; В СООТВЕТСТВИИ СО СХЕМОЙ
	BSF     PORTA,SDA       	; 
	BCF     I2CDEV,DI		; DI=0
	BSF     PORTA,SCL       	; SCL=1
	MOVLW   1			; Предполагаемый код ошибки
	BTFSC   PORTA,SCL       	; Пропустить, если SCL=0
	GOTO    BIT1
	BTFSS   FLAG,ERR1       	; Первая ли ошибка обнаружена?
	MOVWF   ERCODE          	; Сохранить код ошибки
	BSF     FLAG,ERR1       	; Установить флаг ошибки

BIT1	BTFSC   PORTA,SDA       	; Читать данные с SDA
	BSF     I2CDEV,DI       	; DI=1 (если считана 1)
	NOP				; Защитная задержка
	NOP                    
	BCF     PORTA,SCL       	; SCL=0    
	RETLW   0

;*************************************************************;
; Передача бита данных из PIC в устройство I2C                		   
;*************************************************************;	

BITOUT	
	MOVLW   B'00000000'		; SDA, SCL на вывод 
	MOVWF   PORTA			; В СООТВЕТСТВИИ СО СХЕМОЙ
	BTFSS   I2CDEV,DO		; DO=0?
	GOTO    BIT0			; если да, перейти на BIT0
	BSF     PORTA,SDA       	; SDA=1 
	MOVLW   2			; предполагаемый код ошибки 2
	BTFSC   PORTA,SDA       	; проверка на наличие ошибки 2
	GOTO    CLK1			; если ошибки нет, уйти на CLK1
	BTFSS   FLAG,ERR1       	; первая ли ошибка обнаружена?
	MOVWF   ERCODE          	; сохраняем код ошибки
	BSF     FLAG,ERR1       	; устанавливаем флаг наличия ошибки
	GOTO    CLK1            

BIT0	BCF     PORTA,SDA		; SDA=0
	NOP                     		; защитная задержка
	NOP
	NOP
	NOP
	NOP

CLK1	BSF     PORTA,SCL		; SCL=1
	MOVLW   1               		; предполагаемый код ошибки 1
	BTFSC   PORTA,SCL       	; SCL удерживается в 0?
	GOTO    BIT2            		; если нет, перейти на BIT2
	BTFSS   FLAG,ERR1       	; если да,
	MOVWF   ERCODE          	; сохранить код ошибки
	BSF     FLAG,ERR1       	; установить флаг наличия ошибки

BIT2	NOP				; защитная задержка
	NOP
	NOP
	BCF     PORTA,SCL       	; возврат SCL в 0
	RETLW   0

;*************************************************************;
; Получение 8-битных данных из устройства I2C в PIC           		   
; Входных данных нет.					      		   
; Выходные данные: принятый байт в переменной RXBUF           	   
;*************************************************************;	

RX	MOVLW   .8              	; количество принимаемых битов
	MOVWF   COUNT
	CLRF    RXBUF			; очистка буфера приема

RXLP	RLF     RXBUF, F  		; сдвиг данных в буфере из бита переноса
	SKPC				; пропустить, если CARRY=1
	BCF     RXBUF,0 		; очистка нулевого бита RXBUF
	SKPNC				; пропустить, если CARRY=0
	BSF     RXBUF,0			; установить в 1 нулевой бит RXBUF
	CALL    BITIN			; прочитать бит из устройства
	BTFSC   I2CDEV,DI		; если входной бит =1, установить 
	BSF     RXBUF,0			; в 1 нулевой бит RXBUF
	DECFSZ  COUNT, F		; все биты получены?
	GOTO    RXLP			; нет, продолжить
	BSF     I2CDEV,DO		; установить бит протокола ASK=1
	CALL    BITOUT 			; для прекращения дальнейшего обмена данными
	RETLW   0	









; Получение 8-битных данных, в случае, если ASK=0             		   
; Полученный байт данных помещается в переменную RXBUF                
;*************************************************************;	

RX0	MOVLW   .8             	 ; количество принимаемых битов
	MOVWF   COUNT
	CLRF    RXBUF			; очистка буфера приема

RXLP0	
	RLF     RXBUF, F        
	SKPC
	BCF     RXBUF,0         
	SKPNC
	BSF     RXBUF,0
	CALL    BITIN
	BTFSC   I2CDEV,DI
	BSF     RXBUF,0         
	DECFSZ  COUNT, F        
	GOTO    RXLP0
	BCF     I2CDEV,DO		; установить бит протокола ASK=0(!!!)
	CALL    BITOUT			; для прекращения дальнейшего обмена данными
	RETLW   0

;*************************************************************;
; Побитная передача данных из PIC в устройство I2C            		   
; Входные данные: байт в переменной TXBUF                     		   
; Выходные данные: нет                                        				   ;
; Данные выводятся побитно через бит переноса CARRY           	   
;*************************************************************;

TX	MOVLW   .8			; количество передаваемых битов
	MOVWF   COUNT

TXLP	BCF     I2CDEV,DO		; фиксируем выходной бит DO=0
	BTFSC   TXBUF,7			; если выводимый бит = 0, DO=0
	BSF     I2CDEV,DO		; иначе DO=1
	CALL    BITOUT			; подпрограмма вывода бита
	RLF     TXBUF, F		; сдвиг TXBUF влево
	SKPC                    	; f(6) ---> f(7)
	BCF     TXBUF,0			; f(7) ---> carry
	SKPNC				; carry ---> f(0)
	BSF     TXBUF,0
	DECFSZ  COUNT, F		; все биты выведены?
	GOTO    TXLP			; нет, продолжить
	CALL    BITIN           	; читаем бит протокола ASK 
	MOVLW   3			; предполагаемый код ошибки
	BTFSC   I2CDEV,DI		; проверяем бит ASK
	CALL    ERR			; нет подтверждения от устройства
	RETLW   0

;*************************************************************;
; Подпрограмма обработки кода ошибок шины I2C                 		   
; Входные данные: регистр W - код ошибки		      		   
; Выходные данные: переменная ERCODE - код ошибки             	   
; 		   FLAG(ERR1)=1				       		   	
;							       			   
;  код        причина появления ошибки                        			   
; -----       ---------------------------------------------   				   ;
;  1   :       SCL удерживается в 0 (шина постоянно занята)   		   
;  2   :       SDA удерживается в 0 (шина постоянно занята)   		   
;  3   :       Нет ответа от устройства                       				   ;
;  4   :       SDA не освобождается для генерации бита STOP   		   
;							      			  
;Подпрограмма идентифицирует состояние линий SCL и SDA в      	   
;соответствии с таблицей кодов ошибок и выставляет флаг ошибки	   
;*************************************************************;	

ERR	BTFSS   FLAG,ERR1		; Первая ли ошибка встречена?
	MOVWF   ERCODE 			; Если да, сохранить код ошибки
	BSF     FLAG,ERR1		; Установить флаг наличия ошибки
	RETLW   0			; Возврат с обнулением W



; Преобразование двоичного числа в двоично-десятичное         		   
; Входные данные:  bin - исходное двоичное число              		   
; Выходные данные: hundreds - сотни                           			   ;
;                  tens_and_ones - десятки и единицы          			   ;
;*************************************************************;

binary_to_bcd	
	CLRF    hundreds 
	SWAPF   bin, W 
	ADDWF   bin, W 
	ANDLW   b'00001111' 
	SKPNDC 
	ADDLW	0x16 
	SKPNDC 
	ADDLW	0x06 
	ADDLW	0x06 
	SKPDC 
	ADDLW  	-0x06 
	BTFSC	bin,4 
	ADDLW	0x16 - 1 + 0x6 
	SKPDC 
	ADDLW  	-0x06 
	BTFSC	bin, 5 
	ADDLW  	0x30 
	BTFSC   bin, 6 
	ADDLW  	0x60 
	BTFSC   bin, 7 
	ADDLW  	0x20 
	ADDLW   0x60 
	RLF     hundreds, F 
	BTFSS   hundreds, 0 
	ADDLW  	-0x60 
	MOVWF   tens_and_ones 
	BTFSC   bin,7 
	INCF   	hundreds, F 
	RETURN		
;********************* КОНЕЦ ПРОГРАММЫ ***********************
	END





