;==============================================================================
;	Драйвер LCD-индикатора с контроллером HD44780
;	(c) Radoslaw Kwiecien	 http://avr-mcu.dxp.pl
;==============================================================================
;Задание символических имён битам конфигурации и управления
#define HD44780_CLEAR					0x01
#define HD44780_HOME					0x02

#define HD44780_ENTRY_MODE				0x04
	#define HD44780_EM_SHIFT_CURSOR		0
	#define HD44780_EM_SHIFT_DISPLAY	1
	#define HD44780_EM_DECREMENT		0
	#define HD44780_EM_INCREMENT		2

#define HD44780_DISPLAY_ONOFF			0x08
	#define HD44780_DISPLAY_OFF			0
	#define HD44780_DISPLAY_ON			4
	#define HD44780_CURSOR_OFF			0
	#define HD44780_CURSOR_ON			2
	#define HD44780_CURSOR_NOBLINK		0
	#define HD44780_CURSOR_BLINK		1

#define HD44780_DISPLAY_CURSOR_SHIFT	0x10
	#define HD44780_SHIFT_CURSOR		0
	#define HD44780_SHIFT_DISPLAY		8
	#define HD44780_SHIFT_LEFT			0
	#define HD44780_SHIFT_RIGHT			4

#define HD44780_FUNCTION_SET			0x20
	#define HD44780_FONT5x7				0
	#define HD44780_FONT5x10			4
	#define HD44780_ONE_LINE			0
	#define HD44780_TWO_LINE			8
	#define HD44780_4_BIT				0
	#define HD44780_8_BIT				16

#define HD44780_CGRAM_SET				0x40

#define HD44780_DDRAM_SET				0x80

	;Описание подключения выводов LCD к порту контроллера
.equ	LCD_PORT 	= PORTD
.equ	LCD_DDR		= DDRD
.equ    LCD_PIN		= PIND

.equ	LCD_D4 		= 3
.equ	LCD_D5 		= 2
.equ 	LCD_D6 		= 1
.equ	LCD_D7 		= 0

.equ	LCD_RS		= 5
.equ	LCD_EN		= 4

;==============================================================================
;	П/пр инициализации дисплея
;------------------------------------------------------------------------------
LCD_Init: 		;Начальное состояние выводов
	sbi		LCD_DDR, LCD_D4
	sbi		LCD_DDR, LCD_D5
	sbi		LCD_DDR, LCD_D6
	sbi		LCD_DDR, LCD_D7
	
	sbi		LCD_DDR, LCD_RS
	sbi		LCD_DDR, LCD_EN
	
	cbi		LCD_PORT, LCD_RS
	cbi		LCD_PORT, LCD_EN
	
	ldi		r16, 100
	rcall	WaitMiliseconds
		;Цикл записи
	ldi		r17, 3
InitLoop:		
	ldi		r16, 0x03
	rcall	LCD_WriteNibble
	ldi		r16, 5
	rcall	WaitMiliseconds
	dec		r17
	brne	InitLoop
	
	ldi		r16, 0x02
	rcall	LCD_WriteNibble
	
	ldi		r16, 1
	rcall	WaitMiliseconds
	
	ldi		r16, HD44780_FUNCTION_SET | HD44780_FONT5x7 | HD44780_TWO_LINE | HD44780_4_BIT
	rcall	LCD_WriteCommand
	
	ldi		r16, HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_OFF
	rcall	LCD_WriteCommand
	
	ldi		r16, HD44780_CLEAR
	rcall	LCD_WriteCommand
	
	ldi		r16, HD44780_ENTRY_MODE |HD44780_EM_SHIFT_CURSOR | HD44780_EM_INCREMENT
	rcall	LCD_WriteCommand
	
	ldi		r16, HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_ON | HD44780_CURSOR_OFF | HD44780_CURSOR_NOBLINK
	rcall	LCD_WriteCommand
	
	rcall user_C				;Запись пользовательского символа в ОЗУ LCD
	
	ret
	
;==============================================================================
;		Запись команды, содержащейся в регистре  R16
;------------------------------------------------------------------------------
LCD_WriteCommand:
	cbi		LCD_PORT, LCD_RS
	push	r16
	swap	r16
	rcall	LCD_WriteNibble
	pop		r16
	rcall	LCD_WriteNibble
	ldi		r16,2
	rcall	WaitMiliseconds
	ret
;==============================================================================
;		Запись ниббла
;------------------------------------------------------------------------------	
LCD_WriteNibble:
	sbi		LCD_PORT, LCD_EN

	sbrs	r16, 0
	cbi		LCD_PORT, LCD_D4
	sbrc	r16, 0
	sbi		LCD_PORT, LCD_D4
	
	sbrs	r16, 1
	cbi		LCD_PORT, LCD_D5
	sbrc	r16, 1
	sbi		LCD_PORT, LCD_D5
	
	sbrs	r16, 2
	cbi		LCD_PORT, LCD_D6
	sbrc	r16, 2
	sbi		LCD_PORT, LCD_D6
	
	sbrs	r16, 3
	cbi		LCD_PORT, LCD_D7
	sbrc	r16, 3
	sbi		LCD_PORT, LCD_D7

	cbi		LCD_PORT, LCD_EN
	ret
	
;==============================================================================
; Установить адрес символа в ПЗУ LCD
;------------------------------------------------------------------------------
LCD_SetAddressDD:
	ori		r16, HD44780_DDRAM_SET
	rcall	LCD_WriteCommand
	ret
;==============================================================================
;Установить адрес символа в ОЗУ знакогенератора CGRAM (Character Generator RAM)
;------------------------------------------------------------------------------
LCD_SetAddressCG:
	ori		r16, HD44780_CGRAM_SET
	rcall	LCD_WriteCommand
	ret
;==============================================================================
;		Вывод на дисплей символа, 16-ричный код, которого в R16
;------------------------------------------------------------------------------
LCD_WriteData:
		cpi R16,168						;Если это код буквы Ё
		brne fnt_2						;
		ldi R16,0xA2					; загрузить код буквы Ё и
		rjmp fnt_out					; перейти к её выводу
		
		cpi R16,184						;Если это код буквы ё
		brne fnt_2						;
		ldi R16,0xB5					; загрузить код буквы ё и
		rjmp fnt_out					; перейти к её выводу
		
fnt_2:	cpi R16,128						;Если код символа меньше 128,
		brmi fnt_out					; перейти к латинским буквам, иначе -
		subi R16,0xC0					; перейти к русским буквам
		
		clr R17 						;
		push ZL 						;
		push ZH 						;
		ldi ZL,low(Cirill*2) 			;Загрузить нач. адрес
		ldi ZH,high(Cirill*2) 			;  таблицы символов
		add ZL,R16 						;Прибавить к нач. адресу
		adc ZH,R17 						;
		
		lpm 							;Загружаем байт символа
		mov R16,R0						;
		pop ZH 							;
		pop ZL 							;
		
fnt_out:			;Вывод символа
	sbi		LCD_PORT, LCD_RS
	push	r16
	swap	r16
	rcall	LCD_WriteNibble
	pop		r16
	rcall	LCD_WriteNibble

	clr		XH
	ldi		XL,250
	rcall	Wait4xCycles
	ret

;==============================================================================
;	Вывод на дисплей строки символов, нач. адр. которой задан в ZH:ZL
;	Признаком окончания строки явл. код 0
;------------------------------------------------------------------------------
LCD_WriteString:
	lpm		r16, Z+
	cpi		r16, 0
	breq	exit
	rcall	LCD_WriteData
	rjmp	LCD_WriteString
exit:
	ret
;==============================================================================
;	Вывод на дисплей цифры, 16-ричный код которой содержится в рег. R16
;------------------------------------------------------------------------------
LCD_WriteHexDigit:
	cpi		r16,10
	brlo	Num
	ldi		r17,'7'
	add		r16,r17
	rcall	LCD_WriteData
	ret
Num:
	ldi		r17,'0'
	add		r16,r17
	rcall	LCD_WriteData
	ret
;==============================================================================
;	Вывод на дисплей значения рег. R16 в 16-ричном виде: например - "F8"
;------------------------------------------------------------------------------
LCD_WriteHex8:
	push	r16
	
	swap	r16
	andi	r16,0x0F
	rcall	LCD_WriteHexDigit

	pop		r16
	andi	r16,0x0F
	rcall	LCD_WriteHexDigit
	ret
;==============================================================================
;	Вывод на дисплей десятичного значения байта
;------------------------------------------------------------------------------
LCD_WriteDecimal:
	clr		r14
LCD_WriteDecimalLoop:
	ldi		r17,10
	rcall	div8u
	inc		r14
	push	r15
	cpi		r16,0
	brne	LCD_WriteDecimalLoop
	
LCD_WriteDecimalLoop2:
	ldi		r17,'0'
	pop		r16
	add		r16,r17
	rcall	LCD_WriteData
	dec		r14
	brne	LCD_WriteDecimalLoop2
	
	ret
	
;=================================================================================
; Подпрограммы выдержек времени для  F_CPU >= 4MHz
;------------------------------------------------------------------------------
	;Нужно указать тактовую частоту процессора
 #ifndef F_CPU
   #error "F_CPU must be defined!"
 #endif
	;Слишком мала тактовая частота, возможны ошибки
 #if F_CPU < 4000000
   #warning "F_CPU too low, possible wrong delay"
 #endif
	
 #define CYCLES_PER_US (F_CPU/1000000)
 #define C4PUS (CYCLES_PER_US/4)
 #define DVUS(x) (C4PUS*x)
	
;------------------------------------------------------------------------------
 ; Input : XH:XL - number of CPU cycles to wait (divided by four)
 ;------------------------------------------------------------------------------
 Wait4xCycles:
	   sbiw   XH:XL, 1
	   brne   Wait4xCycles
	   ret
 ;------------------------------------------------------------------------------
 ; Input : r16 - number of miliseconds to wait
 ;------------------------------------------------------------------------------
 WaitMiliseconds:
   		push r16
 WaitMsLoop: 
	   ldi    XH,HIGH(DVUS(500))
	   ldi    XL,LOW(DVUS(500))
	   rcall  Wait4xCycles
	   ldi    XH,HIGH(DVUS(500))
	   ldi    XL,LOW(DVUS(500))
	   rcall  Wait4xCycles
	   dec    r16
	   brne   WaitMsLoop
	   pop    r16
	   ret
;=================================================================================
; Преобразование десятичного значения байта в ASCII
;---------------------------------------------------------------------------------

;***** Subroutine Register Variables

.def	drem8u	=r15		;remainder
.def	dres8u	=r16		;result
.def	dd8u	=r16		;dividend
.def	dv8u	=r17		;divisor
.def	dcnt8u	=r18		;loop counter

;***** Code

div8u:
	sub		drem8u,drem8u	; clear remainder and carry
	ldi		dcnt8u,9		; init loop counter
d8u_1:	
	rol		dd8u			; shift left dividend
	dec		dcnt8u			; decrement counter
	brne	d8u_2			; if done
	ret						; return
d8u_2:	
	rol		drem8u			; shift dividend into remainder
	sub		drem8u,dv8u		; remainder = remainder - divisor
	brcc	d8u_3			; if result negative
	add		drem8u,dv8u		; restore remainder
	clc						; clear carry to be shifted into result
	rjmp	d8u_1			; else
d8u_3:	
	sec						; set carry to be shifted into result
	rjmp	d8u_1

;=================================================================================
;	Запись пользовательских символов в память LCD
;---------------------------------------------------------------------------------
		
user_C:	LOAD_ADDR Z,Smile*2				;Загрузить нач. адрес кодов символов
		ldi r16,0x40			 		;Установить адрес в ОЗУ LCD
		rcall LCD_SetAddressCG			;
		ldi r17,64			 			;Число байт для записи в ОЗУ
		 
zp_k:	lpm	r16, Z+						;Взять байт из FLASH
		rcall	LCD_WriteData			;Записать в ОЗУ LCD
		dec r17							;Подсчёт кол-ва переданных байт
		tst r17							;Если не все байты переданы, -
		breq kon						;
		rjmp zp_k						; продолжить передачу
		
kon:	ret
		
;=================================================================================
;			Коды пользовательских символов
;---------------------------------------------------------------------------------
Smile: 	.DB 0x00, 0x0A, 0x0A, 0x00, 0x11, 0x0E, 0x06, 0x00		;0 Смайлик
		.DB 0x02, 0x06, 0x1E, 0x1A, 0x1E, 0x06, 0x02, 0x00		;1 Динамик
		.DB 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00		;2 Уровень 0
		.DB 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x1B, 0x00		;3 Уровень 1
		.DB 0x00, 0x00, 0x03, 0x03, 0x1B, 0x1B, 0x1B, 0x00		;4 Уровень 2
		.DB 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1B, 0x00		;5 Уровень 3
		.DB 0x03, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00		;6 Уровень 4
		.DB 0x15, 0x15, 0x1F, 0x04, 0x04, 0x04, 0x0E, 0x00		;7 Антенна
		
;=================================================================================
;			Коды символов дисплеев с кириллицей
;---------------------------------------------------------------------------------
Cirill:
	  	.db 0x41,0xA0,0x42,0xA1,0xE0,0x45,0xA3,0xA4			;АБВГДЕЖЗ
  		.db 0xA5,0xA6,0x4B,0xA7,0x4D,0x48,0x4F,0xA8			;ИЙКЛМНОП
  		.db 0x50,0x43,0x54,0xA9,0xAA,0x58,0xE1,0xAB			;РСТУФХЦЧ
  		.db 0xAC,0xE2,0xAD,0xAE,0x62,0xAF,0xB0,0xB1			;ШЩЪЫЬЭЮЯ
  		.db 0x61,0xB2,0xB3,0xB4,0xE3,0x65,0xB6,0xB7			;абвгдежз
  		.db 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0x6F,0xBE			;ийклмноп
  		.db 0x70,0x63,0xBF,0x79,0xE4,0x78,0xE5,0xC0			;рстуфхцч
  		.db 0xC1,0xE6,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7			;шщъыьэюя
  		.db 0xA2,0xB5,0										;Ёё
  		

;=================================================================================																			;юя