;----------------------------------------------------------------------------- ; Full-duplex software UART ; ; This version is for the Scenix SX Microcontroller, and runs on the ; Parallax SX-Key Demo Board. Assemble with Microchip's MPASM assembler. ; ; Copyright 1996, 1998 Eric Smith ; ; Permission is granted to use this code or portions thereof for ; non-commercial purposes provided that the copyright notice is preserved ; intact and that credit is given to the author. For any other use, a ; license may be obtained from the author; send email to eric@brouhaha.com. ; ; $Id: fduart.asm,v 1.3 1998/11/05 06:35:07 eric Exp eric $ ;----------------------------------------------------------------------------- radix dec errorlevel -302 errorlevel -305 processor 16c57 ; really a Scenix SX, but MPASM doesn't ; know about those ;----------------------------------------------------------------------------- ; Scenix SX18AC/SX28AC register definitions ;----------------------------------------------------------------------------- indf equ 00h rtcc equ 01h pcl equ 02h status equ 03h fsr equ 04h porta equ 05h portb equ 06h portc equ 07h ; bits in status: c equ 0 z equ 2 ;----------------------------------------------------------------------------- ; ASCII characters ;----------------------------------------------------------------------------- lf equ 00ah cr equ 00dh ;----------------------------------------------------------------------------- ; Scenix instruction macros ;----------------------------------------------------------------------------- ret macro data 0ch ;RET ;return without destroying W register endm reti macro data 0eh endm retiw macro data 0fh endm pagex macro n ;(MPASM already uses "PAGE") data 10h|n ;PAGE ;write N into bits PA2:PA0 (N = 0-7) endm bankx macro n ;(MPASM already uses "BANK") data 18h|(n>>5) ;BANK ;write N into bits FSR7:FSR5 (N = 0-7) endm mode macro n data 50h|n ;MODE ;write N into MODE register (N = 0-F) endm ;----------------------------------------------------------------------------- ; target-specific definitions ;----------------------------------------------------------------------------- rx_ring_size equ 010h tx_ring_size equ 010h xtalfreq equ 50000000 b115200 equ -145 ; xtalfreq/(3*115200) rxport equ porta rxbit equ 2 rxinv equ 0 txport equ porta txbit equ 3 txinv equ 0 button_port equ portb maxbutton equ 4 porta_init equ 0ffh porta_tris equ (1<<rxbit) portb_init equ 0ffh portb_tris equ (1<<maxbutton)-1 ; button inputs portc_init equ 0ffh portc_tris equ 000h ;----------------------------------------------------------------------------- ; RAM usage ;----------------------------------------------------------------------------- org 008h ; globals rxbyte: res 1 ; this must be global rather than in ser_vars ; rxbyte may >only< be used in interrupt handler ser_temp: res 1 ; ser_temp must be global rather than in ser_vars ; ser_temp may not be used in interrupt handler msg_temp: res 1 ; this could be in main_vars temp: res 1 temp2: res 1 temp3: res 1 led_stat: res 1 ; this should be in button_vars, but I got lazy ; and referenced it directly from main org 010h main_vars = $ org 030h button_vars = $ but_press_cnt: res maxbutton but_mask: res 1 but_poll_cnt: res 1 but_stat: res 1 ; button status, 1 bit per button ; mainline code will clear bit to acknowledge org 050h ser_vars = $ uartspeed: res 1 uartstat: res 1 ; bits in uartstat: rbf equ 7 tbf equ 6 rxovr equ 5 rxfrm equ 4 rxphase equ 1 txphase equ 0 rx_ring_ip: res 1 rx_ring_op: res 1 rx_ring_cnt: res 1 tx_ring_ip: res 1 tx_ring_op: res 1 tx_ring_cnt: res 1 rxbits: res 1 rxbitcnt: res 1 rxmask: res 1 txbyte: res 1 txbitcnt: res 1 org 070h rx_ring: res rx_ring_size org 090h tx_ring: res tx_ring_size ;----------------------------------------------------------------------------- ; UART macros ;----------------------------------------------------------------------------- ringadv macro ptr,base,size local pow2,aligned,bit,val pow2 set !(size&(size-1)) aligned set pow2&&((base&(size-1))==0) if aligned val set size bit set 0 while val>1 bit set bit+1 val set val>>1 endw endif incf ptr if aligned&&!(base&(1<<bit)) bcf ptr,bit else movf ptr,w xorlw base+size movlw base btfsc status,z movwf ptr endif endm ;----------------------------------------------------------------------------- ; interrupt entry point ; all subroutine entry points must be in first page of ROM ;----------------------------------------------------------------------------- org 0 int call ser_int call but_int bankx ser_vars movf uartspeed,w retiw ;----------------------------------------------------------------------------- ; jump table ; all subroutine entry points must be in first page of ROM ;----------------------------------------------------------------------------- uartinit: goto uartinit_x xmit: goto xmit_x recv: goto recv_x rxavail: goto rxavail_x msgout: goto msgout_x buttoninit: goto buttoninit_x getbutstat: goto getbutstat_x clrbutstat: goto clrbutstat_x ;----------------------------------------------------------------------------- ; tables ;----------------------------------------------------------------------------- msg_table: addwf pcl banner equ $-(msg_table+1) dt cr,lf,"Hello, world!",cr,lf dt "This is a test to see if very long messages",cr,lf dt "lose characters. If they do, there must be",cr,lf dt "a problem with buffer management.",cr,lf,0 button_msg equ $-(msg_table+1) dt "button ",0 pressed_msg equ $-(msg_table+1) dt " pressed",cr,lf,0 ;----------------------------------------------------------------------------- ; button interrupt handler code ;----------------------------------------------------------------------------- but_int: bankx button_vars incf but_poll_cnt ; poll button every 256 ticks movf but_poll_cnt,w ; if but_poll_cnt > 3, return andlw 0fch btfss status,z ret movf but_poll_cnt,w ; must be between 0 and 3 addwf pcl goto but_set_input goto but_read_input goto led_set_output ret led_set_output: movf led_stat,w xorlw 0ffh movwf button_port movlw 0 tris button_port ret but_set_input: movlw (1<<maxbutton)-1 tris button_port ret but_read_input: movlw button_vars movwf fsr movlw 001h movwf but_mask butint1: movf button_port,w andwf but_mask,w btfss status,z goto butrel btfsc indf,5 ; already pressed? goto butint9 incf indf btfss indf,5 goto butint9 movf but_mask,w iorwf but_stat goto butint9 butrel: clrf indf butint9: incf fsr bcf status,c rlf but_mask btfss but_mask,maxbutton goto butint1 ret ;----------------------------------------------------------------------------- ; serial interrupt handler code ;----------------------------------------------------------------------------- ser_int: bankx ser_vars if rxbit==0 ; get current receive sample in one instruction rrf rxport,w ; if possible, otherwise three instructions else if rxbit==7 rlf rxport,w else bcf status,c btfsc rxport,rxbit bsf status,c endif endif rlf rxbits btfsc uartstat,rxphase goto rxint btfsc uartstat,txphase goto txint ; housekeeping phase bsf uartstat,txphase ; transmit phase next btfss uartstat,rbf ; has the receiver collected a byte? goto try_xmit_ring ; no, check transmitter movf rx_ring_cnt,w ; is the receive buffer full? xorlw rx_ring_size btfss status,z goto rx_ok ; no, go store the char bsf uartstat,rxovr ; yes, set the overrun flag goto try_xmit_ring ; and skip storing the char rx_ok: movf rx_ring_ip,w ; store character in receive buffer movwf fsr movf rxbyte,w ; rxbyte must be global!!! movwf indf bankx ser_vars ringadv rx_ring_ip,rx_ring,rx_ring_size incf rx_ring_cnt bcf uartstat,rbf try_xmit_ring: btfsc uartstat,tbf ; is the transmitter busy? ret ; yes, don't do anything movf tx_ring_cnt,w ; is there anything in the ring? btfsc status,z ret ; no, don't do anything movf tx_ring_op,w ; move one character from the ring to the movwf fsr ; transmitter movf indf,w bankx ser_vars movwf txbyte decf tx_ring_cnt ; decrement tx char count ringadv tx_ring_op,tx_ring,tx_ring_size ; advance pointer bsf uartstat,tbf ; now the transmitter will be busy ret rxint: bcf uartstat,rxphase ; housekeeping phase next if rxinv movlw 7 xorwf rxbits endif movf rxmask,w btfss status,z goto rxgetbit movlw 9 movwf rxbitcnt movlw 2 btfss rxbits,2 goto rxstart9 movlw 1 btfss rxbits,1 goto rxstart9 movlw 4 btfsc rxbits,0 ret rxstart10: incf rxbitcnt rxstart9: movwf rxmask ret rxgetbit: andwf rxbits,w ; extract the appropriate sample bit bcf status,c ; into the carry flag btfss status,z bsf status,c decfsz rxbitcnt ; is it a stop bit? goto rxdbit ; no, it's a data bit btfss status,c ; is the stop bit a 1 like it's supposed to be? bsf uartstat,rxfrm ; no, framing error! bsf uartstat,rbf btfss rxmask,2 ; could there be a start bit in this group? goto rxnost ; no btfsc rxbits,0 ; is there in fact a start bit? goto rxnost ; no movlw 4 ; yes, start up again movwf rxmask movlw 10 movwf rxbitcnt ret rxnost: clrf rxmask ret rxdbit: rrf rxbyte ret txint: bcf uartstat,txphase bsf uartstat,rxphase ; receive phase next movf txbitcnt ; are we transmitting? btfss status,z goto txputbit btfss uartstat,tbf ; is there a char awaiting transmission? ret if txinv bsf txport,txbit ; send start bit else bcf txport,txbit ; send start bit endif movlw 9 ; 8 data bits plus a stop bit movwf txbitcnt ret txputbit: bsf status,c rrf txbyte if txinv btfss status,c bsf txport,txbit btfsc status,c bcf txport,txbit else btfss status,c bcf txport,txbit btfsc status,c bsf txport,txbit endif decfsz txbitcnt ; any more bits to send? ret ; yes bcf uartstat,tbf ; no, the transmit buffer is now empty ret ;----------------------------------------------------------------------------- ; UART initialization ; caller must restore register bank ; returns with register bank 010h selected ;----------------------------------------------------------------------------- uartinit_x: bankx ser_vars clrf uartstat clrf rxmask clrf txbitcnt clrf rx_ring_cnt movlw rx_ring movwf rx_ring_ip movwf rx_ring_op clrf tx_ring_cnt movlw tx_ring movwf tx_ring_ip movwf tx_ring_op movlw b115200 movwf uartspeed clrf rtcc bankx 010h ret ;----------------------------------------------------------------------------- ; serial transmit character ; blocks until space if available in buffer ; returns with register bank 010h selected ;----------------------------------------------------------------------------- xmit_x: bankx ser_vars movwf ser_temp xmit0: movf tx_ring_cnt,w ; is there room for a character? xorlw tx_ring_size btfsc status,z goto xmit0 movf tx_ring_ip,w ; store char in buffer movwf fsr movf ser_temp,w ; must be global!!! movwf indf bankx ser_vars ringadv tx_ring_ip,tx_ring,tx_ring_size ; advance pointer incf tx_ring_cnt ; increment tx char count ; (this must not be done until after character ; has been stored in buffer) bankx 010h ret ;----------------------------------------------------------------------------- ; rxavail ; returns count of received characters available ; returns with register bank 010h selected ;----------------------------------------------------------------------------- rxavail_x: bankx ser_vars movf rx_ring_cnt,w bankx 010h ret ;----------------------------------------------------------------------------- ; serial receive character ; blocks until a character is available ; returns with register bank 010h selected ;----------------------------------------------------------------------------- recv_x: bankx ser_vars movf rx_ring_cnt btfsc status,z goto recv movf rx_ring_op,w movwf fsr movf indf,w ; get character from buffer movwf ser_temp ; must be global!!! bankx ser_vars ringadv rx_ring_op,rx_ring,rx_ring_size ; advance pointer decf rx_ring_cnt ; decrement rx char count ; (this must not be done until after character ; has been pulled from buffer) movf ser_temp,w bankx 010h ret ;----------------------------------------------------------------------------- ; message output ;----------------------------------------------------------------------------- msgout_x: movwf msg_temp msglp: movf msg_temp,w call msg_table xorlw 0 btfsc status,z ret call xmit incf msg_temp goto msglp ;----------------------------------------------------------------------------- ; button routines ;----------------------------------------------------------------------------- buttoninit_x: bankx button_vars clrf but_stat clrf led_stat bankx 010h ret ; return button state in W getbutstat_x: bankx button_vars movf but_stat,w bankx 010h ret ; clear buttons that are selected by bits set in W clrbutstat_x: bankx button_vars xorlw 0ffh andwf but_stat bankx 010h ret ;----------------------------------------------------------------------------- ; reset ;----------------------------------------------------------------------------- reset: clrf fsr movlw porta_init movwf porta movlw porta_tris tris porta movlw portb_init movwf portb movlw portb_tris tris portb movlw portc_init movwf portc movlw portc_tris tris portc movlw 0dfh option call uartinit call buttoninit movlw 09fh ; enable RTCC interrupt option ;----------------------------------------------------------------------------- ; main ;----------------------------------------------------------------------------- main: movlw banner call msgout loop: call rxavail btfsc status,z goto loop1 call recv call xmit loop1: call getbutstat btfsc status,z goto loop9 movwf temp call clrbutstat movf temp,w movlw 001h movwf temp2 movlw '0' movwf temp3 loop2: movf temp,w andwf temp2,w btfsc status,z goto loop3 movf temp,w xorwf led_stat movf ' ' ; don't know why, but this fixes the bug that call xmit ; loses the leading 'b' in the button message movlw button_msg call msgout movf temp3,w call xmit movlw pressed_msg call msgout loop3: incf temp3 bcf status,c rlf temp2 btfss temp2,maxbutton goto loop2 loop9: goto loop ;----------------------------------------------------------------------------- ; reset vector ;----------------------------------------------------------------------------- errorlevel -306 ; don't display "crossing page boundary" warning org 07ffh goto reset errorlevel +306 ;----------------------------------------------------------------------------- ; fuses ;----------------------------------------------------------------------------- _DEVICE equ 0x44e4fa errorlevel -220 ;don't display "address exceeds range" warning org 1010h data _DEVICE&0xfff ;configuration bits (TURBO, SYNC, OPTIONX, etc.) data _DEVICE>>12 ; (PINS, CARRYX, BOR40, BANKS, PAGES) errorlevel +220 ;restore warning message end
See: