; uart.asm
;
; Copyright (C) 2006,2007 Nuno Sucena Almeida
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
;

; support for serial rs232 uart

	include	<proc.inc>

	udata
uart_digit	res	1

;	extern	setup

	code
;--------------------------------------------------------------------
; setup uart for rs232 asynchronous communication
; 16f88 is running at 20MHz
; RS232 is operating at 19.2 kbps
; see page 97 of 16f88 manual
;
; baud rate generator formula for asynchronous operation
; BRGH = 0	baud rate = Fosc/(64*(X+1))
; BRGH = 1	baud rate = Fosc/(16*(X+1))

uart_setup:
	; clear transmission byte register:
;	banksel	TXREG
;	clrf	TXREG
	; clear receive byte register:
;	clrf	RCREG
	; set baud rate generator:
	; this assumes that BRGH='1' and 20MHz operation
	; (see table 11-4, page 100)
	; all values in decimal
	;	 9.6k	-	129
	;	19.2k	-	 64
	;	28.8k	-	 42
	;	33.6k	-	 36
	;	57.6k	-	 20
	movlw	D'129'
	banksel	SPBRG
	movwf	SPBRG
	; setup transmit register:
	; 7 -
	; 6 - 8bit transmission
	; 5 - transmitt enabled
	; 4 - asynchronous
	; 3 - ignored
	; 2 - high speed (BRGH)
	; 1 - tsr empty
	; 0 - 9th bit of transmit data
	movlw	B'00100110'
	banksel	TXSTA
	movwf	TXSTA
	; setup receive register:
	; 7 - serial port enabled
	; 6 - 8bit reception
	; 5 -
	; 4 - continuous receive mode
	; 3 - address detection
	; 2 - framing error
	; 1 - overrun error
	; 0 - 9th bit of received data
	movlw	B'10001000'
	banksel	RCSTA
	movwf	RCSTA

	return

;--------------------------------------------------------------------
; send byte through the rs232 port
; the user must provide it through 'W'
; it waits for the buffer to be empty before sending the character
uart_send:
	banksel	PIR1
uart_send_loop:
	clrwdt
	btfss	PIR1,TXIF
	goto	uart_send_loop
	banksel	TXREG
	movwf	TXREG
	return

;--------------------------------------------------------------------
; receive byte through the rs232 port and give it to the user
; through 'W'. Firt it checks for overflow and then waits for the
; receive buffer to be ready before returning to the user
uart_receive:
	;; check if we have overflow
	banksel	RCSTA
	btfsc	RCSTA,OERR
	;; we have overflow, so reset receive logic
	;; (see page 104 and 98 of 16F88)
	bcf	RCSTA,CREN
	bsf	RCSTA,CREN
	banksel	PIR1
uart_receive_loop:
	clrwdt
	btfss	PIR1,RCIF
	goto	uart_receive_loop
	banksel	RCREG
	movf	RCREG,W
	return


; -------------------------------------------------------------------
; this routine sends the digit provided in 'W'
; the number must be between 0x00 and 0x0f
uart_show_digit:
        call    uart_hex_table     ; call the table to get the mapping
        call    uart_send
        return

; -------------------------------------------------------------------
; the ascii table is not continuous for hex numbers, so
; an easy way is to use the following:
uart_hex_table:
	; add the uart_hex_table address to the provided 'W'
        addwf   PCL,F
        dt      "0123456789ABCDEF"

; -------------------------------------------------------------------
; show hex number between 0x00 and 0xff
uart_show_hex:
        banksel uart_digit
        movwf   uart_digit       ; save it
        swapf   uart_digit,W     ; get higher nibble
        andlw   B'00001111'
        call    uart_show_digit
	banksel	uart_digit
        movf    uart_digit,W     ; get lower nibble
        andlw   B'00001111'
        call    uart_show_digit
        return

;--------------------------------------------------------------------
; this routine sends a string to the uart using indirect addressing.
; the address should be provided by 'W' and the user might need to
; set the bank with banksel before calling this.
; BUT this is for RAM addressing not FLASH...
uart_send_string:
	movwf	FSR
uart_send_string_loop:
	movf	INDF,W
        addlw   0               ; dummy instruction so that we
				; can get the STATUS register...
        btfsc   STATUS,Z        ; check if the previous result is not 0
                                ; sec 4.2.2.1 (page 22 of the manual).
        goto    uart_send_string_end
        call    uart_send
        incf    FSR,F         ; increment FSR
        goto    uart_send_string_loop
uart_send_string_end:
	return

;--------------------------------------------------------------------
	global	uart_setup
	global	uart_send
	global	uart_receive
	global	uart_send_string
	global	uart_show_hex
	end