[Lcdproc] Re: lcdproc-0.5.1 and serialized HD44780 on k-data wake-up 2.0 module

Markus Schorer ms@plain.de
Thu Mar 1 18:08:02 2007


This is a multi-part message in MIME format.
--------------070907070901030500020802
Content-Type: text/plain; charset=ISO-8859-15; format=flowed
Content-Transfer-Encoding: 8bit

hi matteo,

i've merged my changes with your driver, the diff ist against 
lcdproc_cvs (>0.5.1).
i'm not too glad about how i had to integrate my protocol into your code.

changes to hd44780:
- adding the infrastructure for backlight_brightness (on and off) and 
contrast
- scankeypad uses select on the serial-port
- added new serial protocol "los2", my avr-based serial converter

> As you see, I'm a lot busy right now... I don't recall any property on
> the driver, I donated it to the lcdproc project ;-). Feel free to make
> changes you like, only Peter can decide to approve your patches or not :-D.

so you don't work on it anymore?
how can we get this into cvs?

@erik: i'm appending my firmware here. as is said earlier, this is not a 
los. it uses another processor and other pins. but it shouldn't be hard 
to port to any avr.
i'm sending both here for completeness. i hope to have it some web-pages 
of its own soon.

i started this to learn avr and get my lcd's working, please do not 
blame me if it's not working for you. ;-)

anyone interested in it at all? feedback?

markus
-- 
_______________________________________________________________________
plain GmbH        | daiserstrasse 15      | 81371 münchen
fon 089.540.149.0 | fax 089.540.149.44    | http://www.plain.de

--------------070907070901030500020802
Content-Type: text/plain;
 name="los2_display.asm"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="los2_display.asm"

;==================================================================================
;
;==================================================================================
;
; serial interface to a hd44780 lcd
;
; (c) markus schorer, ms@msite.de
;
; based on LoS code from Erik Brouwer (based on Bas Lummen)
;
; License: GPL
;
; v0.71: 20070301
;
;

;==================================================================================

.include "m16def.inc"			; include processor definitions

.def	char			=r0
.def	temp			=r16	; temporary variable
.def	temp1			=r17	; temporary variable
.def	buffer			=r19
.def	buffer2			=r20
.def	code			=r21
.def	temp20			=r22

.def	wt0	  			=r23
.def	wt1	  			=r24
.def	wt2				=r25

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

.equ	KEYBITMASK		=$F0			; upper nybble;

;.equ	CLOCK			=3686000		; programmable clock on stk500
;.equ	CLOCK			=17850000		; 12MHz in stk crystal for USB operation
.equ	CLOCK			=12000000		; 12MHz external crystal-osc

.equ	CYCLES_DLOOP	=4
.equ	CLOCKS_100U		=(CLOCK / 10000) / CYCLES_DLOOP	; 100us as clock loops

.equ	BAUDRATE		=9600					; Baudrate at 9600 
.equ	UBRRVAL			=CLOCK/(BAUDRATE*16)-1	; uart bitrate reg-value

.equ	REP_RATE		=100

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

.equ	RXD				=PD0
.equ	TXD				=PD1

.equ	E				=PD2		; E: latch data
.equ	RS				=PD3		; adress bit
.equ	RD				=PD7		; rd/wr

.equ	DB0				=PB0
.equ	DB1				=PB1
.equ	DB2				=PB2
.equ	DB3				=PB3
.equ	DB4				=PB0
.equ	DB5				=PB1
.equ	DB6				=PB2
.equ	DB7				=PB3

.equ	PWM1B			=PD4
.equ	PWM1A			=PD5

.equ	LED0			=PD6		; led1
;.equ	LED1			=PD7		; led2

.equ	DATAMASK		=$0F
.equ	BUTTONMASK		=$F0

;----------------------------------------------------
; function codes for protocol

.equ	CODE_ESCAPE		=$FE
.equ	CODE_INSTR		=$01
.equ	CODE_BACKLIGHT	=$02
.equ	CODE_CONTRAST	=$03

;----------------------------------------------------
; counter and keycode for key detection

.equ	last_key		=$80
.equ	keys_off		=$82

;----------------------------------------------------
; structs for ringbuffer's

.equ	RB_RX			=$90
.equ	RB_TX			=$94

;------------------------------------
; struct offsets for rb data

.equ	RB_IN			=0
.equ	RB_ADRH			=1
.equ	RB_OUT			=2
.equ	RB_MASK			=3

;----------------------------------------------------
;HD44780 commands

.equ	HD44780_RESET	=$03
.equ	HD44780_4BIT	=$02

.equ	HD44780_CLEAR	=$01
.equ	HD44780_HOME	=$02

;================================================================================

;================================================================================
; reserve ringbuffers

.dseg
.org	$100

RX_BUF:			.byte 64
TX_BUF:			.byte 16

;================================================================================
; set interrupt table (for atmega16)

.cseg
.org	$000				; begin interrupt vector table
		jmp		RESET		; adress for reset routine
		jmp		noop		; irq0
		jmp		noop		; irq1
		jmp		noop		; timer2 compare
		jmp		noop		; timer2 overflow
		jmp		noop		; timer1 capture
		jmp		noop		; timer1 compareA
		jmp		noop		; timer1 compareB
		jmp		noop		; timer1 overflow
		jmp		noop		; timer0 overflow
		jmp		noop		; spi complete
		jmp		int_uart_rx	; usart rxc
		jmp		noop		; usart udre
		jmp		int_uart_tx	; usart txc
		jmp		noop		; adc complete
		jmp		noop		; eeprom ready
		jmp		noop		; analog comparator
		jmp		noop		; two wire
		jmp		noop		; irq2
		jmp		noop		; timer0 compare
		jmp		noop		; prog mem ready

noop:	reti

;================================================================================

;================================================================================
; static data

.org	$40

ID_STRING:	.DB "LoS V0.71 bcki serial",0	

INIT_SEQ:	.DB	$28,$0c,$06,HD44780_CLEAR,HD44780_HOME,$00


;================================================================================

;================================================================================
; processor initialisation

.org	$060

RESET:
		ldi		temp,LOW(RAMEND)
		out		SPL,temp
		ldi		temp,HIGH(RAMEND)
		out		SPH,temp

		ldi		temp,DATAMASK		; set databits for output 
		out		DDRB,temp
									; set all pins for output exceptRXD
		ldi		temp,(1 << TXD)|(1 << E)|(1 << RS)|(1 << PWM1A)|(1 << PWM1B)|(1 << LED0)|(1 << RD)	;|(1 << LED1)
		out		DDRD,temp

//		ldi		temp,(1 << RXD)		;pull-ups on all inputs of port D (for key inputs)
//		out		PORTD,temp

		ldi		temp,(1<<TXCIE)|(1<<RXCIE)|(1<<TXEN)|(1<<RXEN)	; rxcie,rxen,txen
		out		UCSRB,temp			; enable rx/tx Complete Interrupt 

		ldi		temp,$06			; Asynchronous Operation Parity disable
		out		UCSRC,temp			; 1 stop bit

		ldi		temp,HIGH(UBRRVAL)
		out		UBRRH,temp
		ldi		temp,LOW(UBRRVAL)
		out		UBRRL,temp

		ldi		temp,0				; Set Output Comp Reg H to 0
		out		OCR1BH,temp
		ldi		temp,0				; Set pulse width
		out		OCR1BL,temp

		ldi		temp,0				; Set Output Comp Reg H to 0
		out		OCR1AH,temp
		ldi		temp,0				; Set pulse width
		out		OCR1AL,temp

		ldi		temp, 0b10100001	; Set Timer/Counter1 as an 8-bit PWM     
		out		TCCR1A, temp	
	;
		ldi		temp, 0b00001010	; Start Timer/Counter1 , set PWM mode to clear OC1(PB3) at upcounting
		out		TCCR1B, temp


									; setup rx ringbuffer
		ldi		YL, LOW(RB_RX)
		ldi		YH,HIGH(RB_RX)

		ldi		temp, LOW( RX_BUF)	; low has to be 0

		std		Y+RB_IN,temp		; reset in and out pointers
		std		Y+RB_OUT,temp

		ldi		temp, HIGH( RX_BUF)	; preset high-address
		std		Y+RB_ADRH,temp

		ldi		temp,0b00111111		; set for 64-byte buffer
		std		Y+RB_MASK,temp


									; setup rx ringbuffer
		ldi		YL,LOW(RB_TX)
		ldi		YH,HIGH(RB_TX)

		ldi		temp,LOW(TX_BUF)	; low has to be 0

		std		Y+RB_IN,temp
		std		Y+RB_OUT,temp		; reset in and out pointers

		ldi		temp,HIGH(TX_BUF)	; preset high adress
		std		Y+RB_ADRH,temp

		ldi		temp,0b00001111		; adjust for 16-byte buffer
		std		Y+RB_MASK,temp

;		sbi		PORTD,LED0			; signal empty
;		cbi		PORTD,LED1			; signal empty

		clr		temp					; preset
		out		OCR1BL,temp				; clear backlight
		out		OCR1AL,temp				; clear contrast

		sei								; enable interrupts

;================================================================================

;================================================================================
; setup LCD

begin:
/*
		ldi		temp,$00			;long wait for LCD power-up
powerup:rcall	wait2
		dec		temp
		breq	powerup
*/
		cbi		PORTD,RS			; clear adress for instruction
		cbi		PORTD,RD			; clear RD for write

		ldi		buffer,HD44780_RESET	; go through hd44780 reset sequence

		out		PORTB,buffer		; write 3 times

		sbi		PORTD,E	
		rcall	delay_E
		cbi		PORTD,E
		rcall	delay_E

		ldi		buffer,45			; wait for 4,5ms (=45 * 100us)
		rcall	delay_100u

		sbi		PORTD,E				; 2. write
		rcall	delay_E
		cbi		PORTD,E	
		rcall	delay_E		

		ldi		buffer,2			; wait for 200u (=2 * 100us)
		rcall	delay_100u

		sbi		PORTD,E				; 3. write
		rcall	delay_E
		cbi		PORTD,E
		rcall	delay_E
		
		ldi		buffer,1			; wait for 100u (=1 * 100us)
		rcall	delay_100u

		ldi		buffer,HD44780_4BIT	; switch to 4 bit mode
		out		PORTB,buffer

		sbi		PORTD,E	
		rcall	delay_E
		cbi		PORTD,E
		rcall	delay_E

		ldi		buffer,1			; wait for 100u (=1 * 100us)
		rcall	delay_100u

;-------------------------------------------------------------
; from now on, busy is valid

; loop through other setup codes

		ldi		ZL,LOW( INIT_SEQ*2)		; low pointer points to beginning of the table
		ldi		ZH,HIGH(INIT_SEQ*2)		; high pointer points to beginning table

init_loop:
		lpm								; get char from table
		mov		buffer,char
		cpi		buffer,$0				; zero-terminated table
		breq	init

	    rcall	LCD_inst
		inc		ZL
		rjmp	init_loop

init:									; init display

loadscr:
		ldi		buffer,HD44780_CLEAR	; clear screen
		rcall	LCD_inst

		ldi		buffer,HD44780_HOME		; cursor to home
		rcall	LCD_inst		


;-------------------------------------------------------------
; Welcome text on LCD and over serial

		ser		temp
		out		OCR1BL,temp

;		cbi		PORTD,LED0				; turn on backlight first

		ldi		ZL, LOW(ID_STRING*2)	; low pointer points to beginning of the table
		ldi		ZH,HIGH(ID_STRING*2)	; high pointer points to beginning table

		ldi		YL,LOW(RB_TX)

tloop:	lpm								; get char from table

		mov		temp,char
		cpi		temp,0					; check if end-of-table reached
		breq	init_screen_done

		mov		buffer,char				; 

		rcall	LCD_data				; write to screen
		rcall 	uart_xmit				; write over serial

		inc		ZL 						; next character
		brne	tloop 					; next character

init_screen_done:						
		ldi		buffer2, 80				; wait 2 * 40 * 250*100us


init_screen_wait:
		ldi		buffer,$fa
		rcall	delay_100u

		dec		buffer2
		brne	init_screen_wait

; enter main loop

;==================================================================================

;==================================================================================

; sync to protocol, first esc after non-escapes is in sync

		ldi		YL,LOW(RB_RX)
										; check if we jump in a stream
		rcall	avail_rb				; anything received yet?
		breq	clear_screen			; no: we are synced

										; wait for first non-escape char
rd_first:
		rcall	peek_rb					; peek at first in rx
		breq	rd_first				; loop until not empty

		cpi		buffer,CODE_ESCAPE		; is is ESCAPE
		brne	rd_second				; no: gon on waiting for escape

		rcall	pop_rb					; consume ESCAPE from rx buffer
		rjmp	rd_first

										; wait for escape char
rd_second:
		rcall	peek_rb					; peek at first in rx
		breq	rd_second				; loop until not empty

		cpi		buffer,CODE_ESCAPE		; is it ESCAPE
		breq	start					; yes: go on

		rcall	pop_rb					; consume non escapes
		rjmp	rd_second


;-----------------------------------------------------------------------------------
; clear screen after being synced

clear_screen:
		ldi		buffer,HD44780_CLEAR	; clear screen
		rcall	LCD_inst
		ldi		buffer,HD44780_HOME		; cursor to 
		rcall	LCD_inst

;-----------------------------------------------------------------------------------
; main loop

start:
		; read keys
		in		temp,PINB				; check if any key is pressed
		andi	temp,KEYBITMASK			;
		cpi		temp,KEYBITMASK			;

		breq	no_key					; if so then go read keys

		ldi		temp, 0					; reset off counter
		sts		keys_off, temp

		rcall	keyread					; read keys
		rjmp	handle_commands

no_key:	lds		temp,keys_off
		inc		temp					; increment consecutive off-s counter
		sts		keys_off, temp
		cpi		temp,REP_RATE			; trigger reached
		brpl	handle_commands			; have we seen keys off for enough time?

		ldi		temp20, 0				; clear last key button
		sts		last_key, temp20		; reenable keyboard

handle_commands:
		ldi		YL,LOW(RB_RX)

		rcall	peek_rb					; try to read first char
		brne	check_rx_buffer			; skip if empty

do_sleep:
		; set sleep flags				; nothing to do, wait for interrupt
		; sleep
		; clear sleep flags

		rjmp start

check_rx_buffer:
		cpi		buffer,CODE_ESCAPE		; is first char escape?
		breq	wait_cmd
		
		rcall	pop_rb					; read data from buffer
		rcall	LCD_data				; write data to display

		rjmp	start					; loop again

wait_cmd:
		rcall	avail_rb				; check if we have 3 bytes
		cpi		buffer,3
		brlo	do_sleep				; if less, wait for more to arrive

		rcall	parsedata				; if so then go write it to the LCD

		rjmp	start


;----------------------------------------------------------------------------------
; parsing an escaped sequence
; 3 bytes should be available - equalling 1 command
; if escaped data, return after reading second byte

parsedata: 
		push	buffer
		push	buffer2
		push	temp1

read_esc:
		rcall	pop_rb					; 1. read: escape
		breq	read_esc

		cpi		buffer,CODE_ESCAPE		; is it _really_ escape?
		breq	read_icode

		rjmp	parsing_done

read_icode:
		rcall	pop_rb					; 2. read: instruction code
		breq	read_icode

		cpi		buffer,CODE_ESCAPE		; another escape? ist stuffed data?
		brne	parse_cmds				; 

		rcall	LCD_data				; write data and return
		rjmp	parsing_done

parse_cmds:
		mov		buffer2,buffer			; save command code to buffer2
read_param:
		rcall	pop_rb					; 3. read: parameter
		breq	read_param

		cpi 	buffer2,CODE_INSTR		; check if it is an escapecode for LCD-instructions
		brne	parse_bl				; if so then treat next received byte as an instruction

// write instruction
		
		mov		temp1,buffer			; Filter for unwanted instructions
		andi	temp1,$F0				;
		cpi		temp1,$30				; check if one tries to set 8-bit mode
		breq	force4bit				; if so then go put it in 4-bit mode anyway
		
		rcall	LCD_inst				; Go write the received byte as instruction to LCD
		rjmp	parsing_done

force4bit:
		andi	buffer,$2F				; set 4-bit mode, 1/16 (2line), 5x10 dots ((not good!?))
		rcall	LCD_inst				; write this byte as instruction
		rjmp	parsing_done

// done: instruction
		
parse_bl:
		cpi 	buffer2,CODE_BACKLIGHT	; check if it is an escapecode for the LCD-backlight
		brne	parse_contrast			; if so then treat next received byte as backlight control 

// set the brightness level
		out		OCR1BL,buffer
		rjmp	parsing_done
// done: contrast

parse_contrast:
		cpi 	buffer2,CODE_CONTRAST	; check if it is an escapecode for the LCD-backlight
		brne	parsing_done			; if so then treat next received byte as backlight control 

// set the contrast value
		out		OCR1AL,buffer
;		rjmp	parsing_done
// done: contrast

parsing_done:
		pop		temp1
		pop		buffer2
		pop		buffer

		ret

;------------------------------------------------------------------------------
; LCD routines

LCD_inst:
		rcall	wait_busy		; wait while LCD is busy
		cbi		PORTD,RS		; address 0 is instruction
		rjmp	LCD_write
				
LCD_data:
		rcall	wait_busy		; wait while LCD is busy
		sbi		PORTD,RS		; address 1 is data

LCD_write:
		push	buffer
		push	buffer2
			
		mov		buffer2,buffer	; save byte
		
		lsr		buffer			; get upper nibble in position
		lsr		buffer
		lsr		buffer
		lsr		buffer
						
		andi	buffer,$0F		; mask out data pins
		out		PORTB,buffer	; write to display

		; transfer upper nybble
										
		sbi		PORTD,E			; enable E
		rcall	delay_E			; wait
		cbi		PORTD,E			; data is latched on falling E
		rcall	delay_E			; keep cycle time
		
		rcall	wait_busy		; wait while LCD is busy

		; transfer lower nybble

		andi	buffer2,$0F		; mask out data pins
		out		PORTB,buffer2	; write to display
		
		sbi		PORTD,E			; enable E
		rcall	delay_E			; wait
		cbi		PORTD,E			; data is latched on falling E
		rcall	delay_E			; keep cycle time
		
		pop		buffer2
		pop		buffer

		ret						; return from this subroutine

;---------------------------------------------------------------------------
; read busy flag from display

wait_busy:

		push	temp
		in		temp,PORTD		; save control lines

		cbi		DDRB,DB7		; switch DB7 to input
		cbi		PORTD,RS		; adress 0: instruction
		sbi		PORTD,RD		; read

;busy_loop:
		sbi		PORTD,E			; enable

busy_loop:
		rcall	delay_E
		sbic	PINB,DB7		; busy bit cleared?

		rjmp	busy_loop		; 

		sbi		DDRB,DB7		; switch DB7 back to output
		out		PORTD,temp		; restore control lines
		pop		temp

		ret

;-------------------------------------------------------------------------
; assure E cycle time of 1us
; rcall + ret uses 7cycles (enough for 14MHz)
; add additional NOPs for every 2MHz

delay_E:
.IF		CLOCK > 14000000		; add another cycle
		nop
.ENDIF
.IF		CLOCK > 16000000
		nop
.ENDIF
.IF		CLOCK > 18000000		; works till 20MHz
		nop
.ENDIF

		ret

;--------------------------------------------------------------
; delay for multiples of 100us

delay_100u:
		push	XL
		push	XH

d100u_outer:
		ldi		XL, LOW(CLOCKS_100U)
		ldi		XH,HIGH(CLOCKS_100U)

d100u_inner:
		sbiw 	X,1			; 4 clocks for 1 loop
		brne	d100u_inner

		dec		buffer
		brne	d100u_outer

		pop		XH
		pop		XL

		ret


;====================================================================================
; Keyboard routine

keyread:
		push	temp20
		push	temp
		push	buffer

		ldi		buffer,100
		rcall	delay_100U

;		rcall	wait				; wait for debounce

		in		temp20,PINB			; read portd (rows of the keyboard)

		andi	temp20,KEYBITMASK	; use bits 4/5/6/7
		cpi		temp20,KEYBITMASK	;
		brne	key_pressed			; do a little debouncing

		rjmp	keyread_done

key_pressed:
		ldi		temp,$F0			; pressing pulls low
		eor		temp20,temp			; invert it to get presses as 1

		lds		temp,last_key
		cp		temp,temp20			; dont send a key twice
		brne	send_new_char

		rjmp	keyread_done

send_new_char:
		sts		last_key, temp20	; save key for repeat detection

		ldi		temp,$10			; get mask bit
		ldi		buffer,1			; preset row count

keycount_loop:
		mov		char,temp			; get mask
		and		char,temp20			; and with input
		breq	not_set				; skip if not set


		push	buffer				; save row counter
		ldi		buffer,$FE			; send escape
		rcall	uart_xmit
		pop		buffer				; restore row counter

		rcall	uart_xmit			; buffer is preset with row count

not_set:
		inc		buffer				; inc row counter
		lsl		temp				; shift row-mask
		brne	keycount_loop		; done if bit shifted out

keyread_done:
		pop		buffer
		pop		temp
		pop		temp20
		ret


;=========================================================================================
; usart int handlers

;---------------------------------------------------------------------------
; rx interrupt

int_uart_rx:
		push	buffer
		push	temp20
		in		temp20,SREG
		
;		sbis 	UCSRA,RXC		; check if rxbuf is full
;		jmp		spurious_rx		; no: spurious int

		in		buffer,UDR		; get the byte

		ldi		YL,LOW(RB_RX)
		call	push_rb

spurious_rx:
		out		SREG,temp20
		pop		temp20
		pop		buffer
		reti

;---------------------------------------------------------------------------
; tx interrupt

int_uart_tx:
		push	buffer
		push	temp20
		in		temp20,SREG
		
;		sbis 	UCSRA,UDRE		; check if rxbuf is full
;		jmp		tx_buffer_empty	; no: spurious int

		ldi		YL,LOW(RB_TX)	; set pointer to tx buffer
		call	pop_rb			; try to read ring-buffer

		breq	tx_buffer_empty	; if empty Z is set

		out		UDR,buffer		; get the byte

tx_buffer_empty:
		out		SREG,temp20
		pop		temp20
		pop		buffer
		reti

;----------------------------------------------------------------------------
; send char
; queue or send directly if queue empty

uart_xmit:
		ldi		YL,LOW(RB_TX)		; set pointer to tx rb

		call	isempty_rb			; anything in ringbuffer?
		brne	tx_not_empty		; no: queue char

		sbis 	UCSRA,UDRE			; is uart ready?
		rjmp	tx_not_empty		; no: queue char

		out		UDR, buffer			; buffer empty, send directly
		rjmp	xmit_done

tx_not_empty:
		call	push_rb

xmit_done:
		ret


;==============================================================================================
; ring buffer routines
; adress of rb-structure is expected in Y

;---------------------------------------------------------------------------
; return chars available in buffer

avail_rb:							; returns available chars in buffer
		push	temp

		ldd		buffer,Y+RB_IN		; get offset, lower byte from pointer
		ldd		temp, Y+RB_OUT			; get out-pointer

		cp		buffer,temp
		brsh	rb_no_inv			; same or higher

		com		temp				; wrap pointer round

rb_no_inv:
		sub		buffer,temp

		pop		temp

		ret

;---------------------------------------------------------------------------
; return chars available in buffer

isempty_rb:							; returns available chars in buffer
		push	temp
		push	buffer

		ldd		buffer,Y+RB_IN		; get offset, lower byte from pointer
		ldd		temp, Y+RB_OUT			; get out-pointer

		cp		buffer,temp			; if in==out it is empty

		pop		buffer
		pop		temp

		ret

;---------------------------------------------------------------------------
; return first char in buffer

peek_rb:							; return first char in buffer
		push	XL
		push	XH
		push	temp

		ldd		XL,Y+RB_OUT			; get pointer as low-byte
		ldd		XH,Y+RB_ADRH		; get high-byte

		ldd		temp,Y+RB_IN		; get in-pointer
		cp		temp,XL
		brne	rb_not_empty1		; are we filled?

		sez							; set zero if empty
		rjmp	rb_pop_quit1

rb_not_empty1:
		ld		buffer, X			; read data
		clz							; clear zero if full

rb_pop_quit1:
		pop		temp
		pop		XH
		pop		XL

		ret

;---------------------------------------------------------------------------
; push char in buffer

push_rb:							; pushes "char" into receive buffer
		push	XL
		push	XH
		push	temp

		ldd		XL,Y+RB_IN			; get offset, lower byte from pointer
		ldd		XH,Y+RB_ADRH		; higher byte from buffer offset

		st		X+,buffer			; write byte, postincrement pointer

		ldd		buffer,Y+RB_MASK	; wrap buffer with mask
		and		XL, buffer

		std		Y+RB_IN,XL			; store pointer

		ldd		temp,Y+RB_OUT			; get out-pointer
		cp		temp,XL
		brne	rb_no_over			; not equal: didn't fill up

;		sbi		PORTD,LED0			; signale overrun

		inc		temp				; increment out-pointer
		std		Y+RB_OUT,temp		; write

rb_no_over:
		pop		temp
		pop		XH
		pop		XL

		ret

;---------------------------------------------------------------------------
; pop first char off buffer

pop_rb:								; pops char from rx-buf, return in char
		push	XL
		push	XH
		push	temp

		ldd		XL,Y+RB_OUT			; get pointer as low-byte
		ldd		XH,Y+RB_ADRH		; get high-byte

		ldd		temp,Y+RB_IN			; get in-pointer
		cp		temp,XL
		brne	rb_not_empty			; are we filled?

		sez							; set zero if empty
;		cbi		PORTD,LED0			; signal empty
		rjmp	rb_pop_quit

rb_not_empty:
		ld		buffer, X+			; read data with postincrement

		ldd		temp,Y+RB_MASK	; wrap buffer with mask
		and		XL, temp

		std		Y+RB_OUT,XL			; write out-pointer
		clz							; clear zero if full

rb_pop_quit:
		pop		temp
		pop		XH
		pop		XL

		ret

;==================================================================================

;==================================================================================

--------------070907070901030500020802
Content-Type: text/plain;
 name="los2.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="los2.diff"

diff -Nur lcdproc_cvs/server/drivers/hd44780-drivers.h lcdproc_msite/server/drivers/hd44780-drivers.h
--- lcdproc_cvs/server/drivers/hd44780-drivers.h	2007-01-29 21:37:50.000000000 +0100
+++ lcdproc_msite/server/drivers/hd44780-drivers.h	2007-03-01 18:42:50.000000000 +0100
@@ -42,6 +42,7 @@
 	{"picanlcd", hd_init_serial, "\tnone\n"},
 	{"lcdserializer", hd_init_serial, "\tnone\n"},
 	{"los-panel", hd_init_serial, "\tnone\n"},
+	{"los2", hd_init_serial, "\tnone\n"},
 	{"vdr-lcd", hd_init_serial, "\tnone\n"},
 	{"vdr-wakeup", hd_init_serial, "\tnone\n"},
 	/* End serial connectiontypes */
diff -Nur lcdproc_cvs/server/drivers/hd44780-serial.c lcdproc_msite/server/drivers/hd44780-serial.c
--- lcdproc_cvs/server/drivers/hd44780-serial.c	2007-02-21 17:19:22.000000000 +0100
+++ lcdproc_msite/server/drivers/hd44780-serial.c	2007-03-01 18:08:26.000000000 +0100
@@ -37,6 +37,7 @@
 
 #include <stdio.h>
 #include <sys/types.h>
+#include <sys/time.h>
 #include <fcntl.h>
 #include <termios.h>
 
@@ -116,10 +117,12 @@
 	return 1;
 }
 
-static int lastdisplayID;
-
 void serial_HD44780_senddata (PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch);
 void serial_HD44780_backlight (PrivateData *p, unsigned char state);
+void serial_HD44780_set_brightness (PrivateData *p, unsigned char state, int promille);
+int serial_HD44780_get_brightness (PrivateData *p, unsigned char state);
+void serial_HD44780_set_contrast (PrivateData *p, int promille);
+int serial_HD44780_get_contrast (PrivateData *p);
 unsigned char serial_HD44780_scankeypad (PrivateData *p);
 void serial_HD44780_close (PrivateData *p);
 
@@ -139,18 +142,18 @@
 	char conf_serialif[SERIALIF_NAME_LENGTH];
 
 	strncpy(conf_serialif, drvthis->config_get_string(drvthis->name, "connectiontype", 0, ""), SERIALIF_NAME_LENGTH);
-	conf_serialif[SERIALIF_NAME_LENGTH-1] = '\0';
-	p->serial_type = 0;
-	for (counter = 0; counter < (sizeof(serial_interfaces)/sizeof(SerialInterface)); counter++) {
+	conf_serialif[SERIALIF_NAME_LENGTH-1]='\0';
+	p->serial_type=0;
+	for (counter=0; counter<(sizeof(serial_interfaces)/sizeof(SerialInterface)); counter++) {
 		if (strcasecmp(conf_serialif, serial_interfaces[counter].name) == 0) {
-			p->serial_type = counter;
+			p->serial_type=counter;
 			break;
 		}
 	}
 	if (p->serial_type != counter) {
 		report(RPT_ERR, "HD44780: serial: serial interface %s unknown", conf_serialif);
 		report(RPT_ERR, "HD44780: serial: available interfaces:");
-		for (counter = 0; counter < (sizeof(serial_interfaces)/sizeof(SerialInterface)); counter++)
+		for (counter=0; counter<(sizeof(serial_interfaces)/sizeof(SerialInterface)); counter++)
 			report(RPT_ERR, " %s", serial_interfaces[counter].name);
 		return -1;
 	}
@@ -163,19 +166,27 @@
 		report (RPT_ERR, "HD44780: serial: check your configuration file and disable it");
 		return -1;
 	}
-	if (p->have_backlight && !(SERIAL_IF.backlight)) {
-		report (RPT_ERR, "HD44780: serial: backlight control is not supported by %s", SERIAL_IF.name);
-		report (RPT_ERR, "HD44780: serial: check your configuration file and disable it");
-		return -1;
+	if (p->have_backlight) {
+		if ( !(SERIAL_IF.backlight)) {
+			report (RPT_ERR, "HD44780: serial: backlight control is not supported by %s", SERIAL_IF.name);
+			report (RPT_ERR, "HD44780: serial: check your configuration file and disable it");
+			return -1;
+		}
+		p->backbright_on = drvthis->config_get_int(drvthis->name, "OnBrightness", 0, 1000);
+		p->backbright_off = drvthis->config_get_int(drvthis->name, "OffBrightness", 0, 300);
+		report(RPT_ERR, "HD44780: serial: brightness on:%d off:%d.\n",p->backbright_on,p->backbright_off);
 	}
 
+	p->contrast = drvthis->config_get_int(drvthis->name, "Contrast", 0, 0);
+	report(RPT_ERR, "HD44780: serial: contrast:%d .\n",p->contrast);
+
 	/* Get bitrate */
 	unsigned int conf_bitrate;
 	size_t bitrate;
 
-	conf_bitrate = drvthis->config_get_int(drvthis->name, "Speed", 0, SERIAL_IF.default_bitrate);
-        if (conf_bitrate==0)
-                conf_bitrate = SERIAL_IF.default_bitrate;
+	conf_bitrate=atoi( drvthis->config_get_string(drvthis->name, "Speed", 0, "0") );
+	if (conf_bitrate==0)
+		conf_bitrate=SERIAL_IF.default_bitrate;
 	if (convert_bitrate(conf_bitrate, &bitrate)) {
 		report(RPT_ERR, "HD44780: serial: invalid configured bitrate speed");
 		return -1;
@@ -218,22 +229,31 @@
 	/* Set TCSANOW mode of serial device */
 	tcsetattr(p->fd, TCSANOW, &portset);
 
-        lastdisplayID = -1;
-
 	/* Assign functions */
 	p->hd44780_functions->senddata = serial_HD44780_senddata;
-	p->hd44780_functions->backlight = serial_HD44780_backlight;
+	
+	if ( p->have_backlight) {
+		p->hd44780_functions->backlight = serial_HD44780_backlight;
+
+		p->hd44780_functions->get_brightness = serial_HD44780_get_brightness;
+		p->hd44780_functions->set_brightness = serial_HD44780_set_brightness;
+	}
+
+	p->hd44780_functions->get_contrast = serial_HD44780_get_contrast;
+	p->hd44780_functions->set_contrast = serial_HD44780_set_contrast;
+
 	if (p->have_keypad)
 		p->hd44780_functions->scankeypad = serial_HD44780_scankeypad;
-        p->hd44780_functions->close = serial_HD44780_close;
-
+	report (RPT_INFO,"HD44780: serial: %d", p->have_keypad);
+	p->hd44780_functions->close = serial_HD44780_close;
+	
 	/* Do initialization */
 	if (SERIAL_IF.if_bits == 8) {
-		report(RPT_INFO,"HD44780: serial: initializing with 8 bits interface");
-		common_init(p, IF_8BIT);
+		report (RPT_INFO,"HD44780: serial: initializing with 8 bits interface");
+		common_init (p, IF_8BIT);
 	} else {
 		report (RPT_INFO,"HD44780: serial: initializing with 4 bits interface");
-		common_init(p, IF_4BIT);
+		common_init (p, IF_4BIT);
 	}
 
 	return 0;
@@ -243,67 +263,174 @@
 void
 serial_HD44780_senddata (PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch)
 {
-	/* Filter illegally sent escape characters (for interfaces without data escape) */
-	if (flags == RS_DATA && SERIAL_IF.data_escape == 0 && ch == SERIAL_IF.instruction_escape)
-		ch='?';
-
+	static int lastdisplayID = -1;
+	unsigned char sbuffer[3];
+	int i=0;
+	
 	if (flags == RS_DATA) {
-		/* Do we need a DATA indicator byte? */
-		if ((SERIAL_IF.data_escape != '\0') &&
-		    (ch >= SERIAL_IF.data_escape_min) &&
-		    (ch < SERIAL_IF.data_escape_max) ||
-                    (SERIAL_IF.multiple_displays && displayID != lastdisplayID)) {
-			write(p->fd, &SERIAL_IF.data_escape + displayID, 1);
+/*
+		// Filter illegally sent escape characters (for interfaces without data escape)
+		if ( SERIAL_IF.data_escape == 0) {
+			// stuff escape with escape
+			if ( ch == 0xfe ) write( p->fd, &ch, 1 );
+		} else {
+			// Do we need a DATA indicator byte?
+			if ( ch < SERIAL_IF.data_escape_max ) {
+				write( p->fd, &SERIAL_IF.data_escape, 1 );
+			}
 		}
-		write(p->fd, &ch, 1);
+*/
+                if ( ((SERIAL_IF.data_escape != '\0') &&
+			(ch >= SERIAL_IF.data_escape_min) &&
+			(ch < SERIAL_IF.data_escape_max)) ||
+		    (SERIAL_IF.multiple_displays && displayID != lastdisplayID)) {
+			sbuffer[ i++] = SERIAL_IF.data_escape + displayID;
+		} else {                                                                                            
+			// byte stuff escape in data
+			if ( ch == SERIAL_IF.instruction_escape ) {
+				if ( SERIAL_IF.byte_stuff) sbuffer[ i++] = ch;
+				else ch = '?';
+			}
+		}
+		// copy data to buffer
 	}
 	else {
-		write(p->fd, &SERIAL_IF.instruction_escape, 1);
-		write(p->fd, &ch, 1);
-	}
-        lastdisplayID = displayID;
+		// send escape
+		sbuffer[ i++] = SERIAL_IF.instruction_escape;
+		
+		// is there a instruction code?
+		if ( SERIAL_IF.instruction_code != 0) sbuffer[ i++] = SERIAL_IF.instruction_code;
+		
+		// send instruction
+	}
+	sbuffer[ i++] = ch;
+	write( p->fd, sbuffer, i);
+	
+	lastdisplayID = displayID;
 }
 
 void
-serial_HD44780_backlight (PrivateData *p, unsigned char state)
-{
-	if (p->have_backlight) {
-		if (SERIAL_IF.backlight_escape) {
-			unsigned char send[2];
-			send[0] = SERIAL_IF.backlight_escape;
-			send[1] = state ? 0 : 0xFF;
-			write(p->fd, &send, 2);
-		}
-		else {
-			unsigned char send[1];
-			send[0] = state ? SERIAL_IF.backlight_on : SERIAL_IF.backlight_off;
-			write(p->fd, &send, 1);
-		}
+serial_HD44780_backlight (PrivateData *p, unsigned char state) {
+
+	unsigned char sbuffer[3];
+	int i = 0;
+	
+	/* TODO */
+	if ( p->have_backlight) {
+                if (SERIAL_IF.backlight_escape) {
+                	// write escape
+			sbuffer[ i++] = SERIAL_IF.backlight_escape;
+			
+			// write function-code if appropriate
+			if ( SERIAL_IF.backlight_code != 0) sbuffer[ i++] = SERIAL_IF.backlight_code;
+			
+			// send scaled brightness
+			sbuffer[ i++] = (( state ? p->backbright_on : p->backbright_off) / 4) & 0xff;
+                }
+                else {
+                        sbuffer[ i++] = state ? SERIAL_IF.backlight_on : SERIAL_IF.backlight_off;
+                }
+
+		write( p->fd, sbuffer, i);
+//		report (RPT_INFO,"HD44780: serial: backlight @ %d", state);
+	} else {
+//		report (RPT_INFO,"HD44780: serial: can't set backlight @ %d", state);
 	}
+	// track backlight state, so we can do live-update for set_brightness
+	p->backlight_bit = state;
+}
+
+void 
+serial_HD44780_set_brightness (PrivateData *p, unsigned char state, int promille) {
+	// clip values to 0...1000
+	if ( promille > 1000) promille = 1000;
+	if ( promille < 0) promille = 0;
+	
+	if ( state) p->backbright_on = promille;
+	else p->backbright_off = promille;
+	
+	// do a live update
+	if ( p->backlight_bit == state) serial_HD44780_backlight( p, state);
+}
+
+int 
+serial_HD44780_get_brightness (PrivateData *p, unsigned char state) {
+	return ( state ? p->backbright_on : p->backbright_off);
+}
+
+void 
+serial_HD44780_set_contrast (PrivateData *p, int promille) {
+	unsigned char sbuffer[3];
+	int i = 0;
+	
+	// clip value
+	if ( promille > 1000) promille = 1000;
+	if ( promille < 0) promille = 0;
+	
+	p->contrast = promille;
+
+	// send escape
+	sbuffer[ i++] = SERIAL_IF.instruction_escape;
+	
+	// send function code CONTRAST
+	sbuffer[ i++] = SERIAL_IF.contrast_code;
+	
+	// send contrast value
+	sbuffer[ i++] = ( promille / 4) & 0xff;
+	write( p->fd, sbuffer, i);
+}
+
+int 
+serial_HD44780_get_contrast (PrivateData *p) {
+	return p->contrast;
 }
 
 unsigned char
 serial_HD44780_scankeypad (PrivateData *p)
 {
-	unsigned char buffer = 0;
-	char hangcheck = 100;
+	static int keyesc = 0;
+	char buffer = 0;
 
-	read(p->fd, &buffer, 1);
-	if (buffer == SERIAL_IF.keypad_escape) {
-		while (hangcheck > 0) {
-			/* Check if I can read another byte */
-			if (read(p->fd, &buffer, 1) == 1) {
-				return buffer;
-			}
-			hangcheck--;
-		}
+        fd_set rfds;
+        struct timeval timeout;
+        int retval;
+
+        // wait/sleep for 10000us = 10ms
+        FD_ZERO(&rfds);
+        FD_SET( p->fd, &rfds);
+        timeout.tv_sec = 0;
+        timeout.tv_usec = 10000;
+        retval = select( p->fd + 1, &rfds, NULL, NULL, &timeout);
+
+        // any descriptor ready?
+        if ( retval <= 0) return 0;
+
+        // get char
+	read( p->fd, &buffer, 1);
+	
+	// use key escape mode to skip power-on-message
+	
+	// was previous char key esc?
+	if ( keyesc == 1) {
+		// yes: buffer contains key-code, leave key-esc mode
+		keyesc = 0;
+	} else {
+		// no, received key-esc?
+		if ( buffer == SERIAL_IF.keypad_escape) keyesc = 1;
+		
+		// swallow escape, return "nothing"
+		buffer = 0;
 	}
-	return 0;
+	
+	return buffer;
 }
 
 void
 serial_HD44780_close (PrivateData *p)
 {
+        // switch backlight off
+        serial_HD44780_backlight( p, 0);
+        
         if (SERIAL_IF.end_code)
                 write(p->fd, &SERIAL_IF.end_code, 1);
         close(p->fd);
diff -Nur lcdproc_cvs/server/drivers/hd44780-serial.h lcdproc_msite/server/drivers/hd44780-serial.h
--- lcdproc_cvs/server/drivers/hd44780-serial.h	2007-02-02 17:46:07.000000000 +0100
+++ lcdproc_msite/server/drivers/hd44780-serial.h	2007-03-01 17:52:55.000000000 +0100
@@ -10,7 +10,9 @@
 typedef struct SerialInterface {
 	char         name[SERIALIF_NAME_LENGTH];
 	char         instruction_escape;
+	char         instruction_code;  /* code sent after instr-esc (if != 0) */
 	char         data_escape;
+	char	     byte_stuff;	/* byte stuff if instr-escape is in data (if != 0)*/
 	char         data_escape_min; /* escaped data lower limit */
 	char         data_escape_max; /* escaped data upper limit */
 	unsigned int default_bitrate;
@@ -19,23 +21,32 @@
 	char         keypad_escape;
 	char         backlight;
 	char         backlight_escape; /* leave to 0 is the interface uses on/off codes   */
+	char         backlight_code;   /* code sent after backlight-esc (if != 0) */
 	char         backlight_off;
 	char         backlight_on;     /* leave these two to 0 is backlight_escape is set */
+	char	     contrast_code;    /* code sent after instr-esc (if != 0) */
 	char         multiple_displays;
 	char         end_code;         /* code to send on shutdown */
 } SerialInterface;
 
 /* List of connectiontypes managed by this driver, if you change
    something here, remember also to change hd44780-drivers.h */
+   
+ /*
+  * needed to add "instruction codes" for intruction/backlight/contrast
+  * added byte_stuffed 
+  */
 static const SerialInterface serial_interfaces[] = {
-	/*    name         instr data     v     ^ bitrate bits  K   esc  B  Besc  Boff   Bon Multi  End */
-	{ "picanlcd",      0x11, 0x12, 0x00, 0x20,   9600,   8, 0, 0x00, 0,    0,    0,    0,   0,    0 },
-	{ "lcdserializer", 0xFE,    0, 0x00, 0x00,   9600,   8, 0, 0x00, 0,    0,    0,    0,   0,    0 },
-	{ "los-panel",     0xFE,    0, 0x00, 0x00,   9600,   4, 1, 0xFE, 1, 0xFF,    0,    0,   0,    0 },
-	{ "vdr-lcd",       0xFE,    0, 0x00, 0x00,   9600,   4, 0, 0x00, 0,    0,    0,    0,   0,    0 },
-	{ "vdr-wakeup",    0xC0, 0xC4, 0xC0, 0xD0,   9600,   4, 0, 0x00, 1,    0, 0xC9, 0xC8,   1, 0xCF }
+	/*    name         instr ic data  stuff    v     ^ bitrate bits  K   esc  B  Besc  Bcode Boff   Bon ctrst Multi  End */
+	{ "picanlcd",      0x11, 0, 0x12, 0,     0x00, 0x20,   9600,   8, 0, 0x00, 0,    0,    0,   0,    0,   0,    0,    0 },
+	{ "lcdserializer", 0xFE, 0,    0, 0,     0x00, 0x00,   9600,   8, 0, 0x00, 0,    0,    0,   0,    0,   0,    0,    0 },
+	{ "los-panel",     0xFE, 0,    0, 0,     0x00, 0x00,   9600,   4, 1, 0xFE, 1, 0xFF,    0,   0,    0,   0,    0,    0 },
+	{ "los2",          0xFE, 1,    0, 1,     0x00, 0x00,   9600,   4, 1, 0xFE, 1, 0xFE,    2,   0,    0,   3,    0,    0 },
+	{ "vdr-lcd",       0xFE, 0,    0, 0,     0x00, 0x00,   9600,   4, 0, 0x00, 0,    0,    0,   0,    0,   0,    0,    0 },
+	{ "vdr-wakeup",    0xC0, 0, 0xC4, 0,     0xC0, 0xD0,   9600,   4, 0, 0x00, 1,    0,    0,0xC9, 0xC8,   0,    1, 0xCF }
 };
 
+
 /* initialize this particular driver */
 int hd_init_serial (Driver *drvthis);
 
diff -Nur lcdproc_cvs/server/drivers/hd44780.c lcdproc_msite/server/drivers/hd44780.c
--- lcdproc_cvs/server/drivers/hd44780.c	2007-02-04 13:26:55.000000000 +0100
+++ lcdproc_msite/server/drivers/hd44780.c	2007-03-01 18:40:42.000000000 +0100
@@ -634,6 +633,46 @@
 }
 
 /////////////////////////////////////////////////////////////////
+// Sets the backlight brightness according to state
+//
+MODULE_EXPORT void
+HD44780_set_brightness(Driver *drvthis, int on, int promille)
+{
+	PrivateData *p = (PrivateData *) drvthis->private_data;
+	p->hd44780_functions->set_brightness(p, on, promille);
+}
+
+/////////////////////////////////////////////////////////////////
+// Retrieves backlight brightness according to state
+//
+MODULE_EXPORT int
+HD44780_get_brightness(Driver *drvthis, int on)
+{
+	PrivateData *p = (PrivateData *) drvthis->private_data;
+	return p->hd44780_functions->get_brightness(p, on);
+}
+
+/////////////////////////////////////////////////////////////////
+// Sets the contrast
+//
+MODULE_EXPORT void
+HD44780_set_contrast(Driver *drvthis, int promille)
+{
+	PrivateData *p = (PrivateData *) drvthis->private_data;
+	p->hd44780_functions->set_contrast(p, promille);
+}
+
+/////////////////////////////////////////////////////////////////
+// Retrieves contrast
+//
+MODULE_EXPORT int
+HD44780_get_contrast(Driver *drvthis)
+{
+	PrivateData *p = (PrivateData *) drvthis->private_data;
+	return p->hd44780_functions->get_contrast(p);
+}
+
+/////////////////////////////////////////////////////////////////
 // Draws a vertical bar...
 //
 MODULE_EXPORT void
@@ -737,10 +776,9 @@
 	}
 
 	// Lib_adv_bignum does everything needed to show the bignumbers.
-	lib_adv_bignum(drvthis, x, num, 0, do_init);
+	lib_adv_bignum(drvthis, x, num, do_init, NUM_CCs);
 }
 
-
 /**
  * Get number of custom characters available.
  * \param drvthis  Pointer to driver structure.
@@ -749,12 +787,10 @@
 MODULE_EXPORT int
 HD44780_get_free_chars(Driver *drvthis)
 {
-//PrivateData *p = drvthis->private_data;
-
-  return NUM_CCs;
+	//PrivateData *p = drvthis->private_data;
+	return NUM_CCs;
 }
-
-
+      
 /////////////////////////////////////////////////////////////////
 // Sets a custom character from 0-7...
 //
@@ -976,7 +1012,10 @@
 	gettimeofday(&curr_time,NULL);
 
 	scancode = p->hd44780_functions->scankeypad(p);
+	
 	if ( scancode ) {
+//		report(RPT_INFO, "HD44780_get_key: Key pressed: %02x (%d,%d)",
+//						scancode, scancode&0x0F, (scancode&0xF0)>>4 );
 		if ( scancode & 0xF0 ) {
 			keystr = p->keyMapMatrix[((scancode&0xF0)>>4)-1][(scancode&0x0F)-1];
 		}
diff -Nur lcdproc_cvs/server/drivers/hd44780.h lcdproc_msite/server/drivers/hd44780.h
--- lcdproc_cvs/server/drivers/hd44780.h	2006-10-15 12:45:47.000000000 +0200
+++ lcdproc_msite/server/drivers/hd44780.h	2007-03-01 17:48:16.000000000 +0100
@@ -35,9 +35,17 @@
 MODULE_EXPORT int  HD44780_icon (Driver *drvthis, int x, int y, int icon);
 
 MODULE_EXPORT void HD44780_set_char (Driver *drvthis, int n, unsigned char *dat);
 MODULE_EXPORT int  HD44780_get_free_chars(Driver *drvthis);
 
+// added for los2
 MODULE_EXPORT void HD44780_backlight (Driver *drvthis, int on);
+
+MODULE_EXPORT void HD44780_set_brightness (Driver *drvthis, int state, int level);
+MODULE_EXPORT int HD44780_get_brightness (Driver *drvthis, int state);
+
+MODULE_EXPORT void HD44780_set_contrast (Driver *drvthis, int level);
+MODULE_EXPORT int HD44780_get_contrast (Driver *drvthis);
+// end adding
+
 MODULE_EXPORT void HD44780_output (Driver *drvthis, int state);
 
 MODULE_EXPORT const char * HD44780_get_key (Driver *drvthis);

--------------070907070901030500020802--