Subject: Version 0.21 of pic library Status: R Here is a copy of my pic library. Sorry I couldn't respond personally to each of your kind and encouraging notes, but there have been so many requests. All the best. ;*************************************** ; READ.ME FILE FOR ; ; LIBRARY OF UTILITIES FOR PIC ; ; EDWARD CHEUNG, PH.D. ; ; 14505 DOLBROOK LANE ; ; MITCHELLVILLE, MD 20721 ; ; ebc714@rs710.gsfc.nasa.gov ; ;*************************************** Introduction This library of utilities was written for the 16C71 microcontroller, but can be adapted for use on other PIC chips. The library is fully interrupt based: sending and receiving of data in various formats occurs in the background using interrupts. The 'main' program just sets some variables and calls the appropriate routine. The data will be sent in the background at the proper rate and time. The included demo.asm program runs a simple RS232 controlled Infra Red remote with LCD display. See the end if this file for more info. General Description The functions that interface to each type of hardware are arranged into modules. Each of these modules can be disabled or enabled at the top of the main program (see demo.asm) in order to include only those that you need for the particular PIC that you are using. Being a first time assembly programmer, I wrote the code in C, which is then hand compiled to PIC mnemonics. The C code is included at the end of each line, allowing me to quickly read a section to find out what the code does. It also made debugging a lot easier because I could follow the program flow (if - then - else - else etc.). My naming convention is to add three common characters to all labels and function names in a module. For example, all names in the RS232 module start with R2_, all of those in the IR module starts with IR_. This is in order to facilitate the reading of my code. As mentioned above when you want to send data (IR,RS232 etc) you call the appropriate routine in the associated module. For example, to send a character down the RS232 line, you call R2_SEND. This is then placed into a FIFO, and then sent according to the correct timing. If the FIFO is full, the function will wait until there is a spot open in the FIFO before returning to the main program. Whenever new data is received, the function XX_NEW is called (where XX is the module abbreviation). Look for these functions in the lib.h file. In these functions is where you add your code to do the processing of new incoming data. Currently incoming data is either sent to the LCD display or the RS232 line. Specifically, incoming data is processed as follows: Incoming data Displayed on Comments RS232 RS232 Typing characters to PIC causes simple echo RS422 RS422 Typing characters to PIC causes simple echo IR LCD Displays device and key code of IR command X-10 LCD Displays house and unit code of command By customizing R2_NEW_BYTE, IR_NEW, TW_NEW etc. you can tailor the response to incoming data. I have written some memory management routines that automatically assign register locations to variables. Here is an example. To allocate a register location to the variable TEMP_W, and TEMP_STAT, you use the syntax: ALLOC TEMP_W ALLOC TEMP_STAT Since this is the first ALLOC statement, TEMP_W is assigned the register 0CH, and TEMP_STAT is assigned the location 0DH. You can continue to do this for all your variables until you run out of memory. When this occurs, the compiler will hit the statement: CALL OUT_OF_MEMORY This is a dummy call, and will cause an error message with the words OUT_OF_MEMORY. When you see this, you know that you have run out of RAM. The above mechanism allows you to enable and disable modules and have the memory locations be allocated efficiently. In other words, you can have a PIC with just the RS232 module enabled, and expand the FIFO to use as much RAM as possible. Don't forget to set the variables FXTAL and INTSEC at the top of the lib.h file. The former is your clock frequency, the latter is the desired number of interrupts/second. Bugs As mentioned above, to send something, you just set some variables, and call the appropriate routine. If the send queue is full, the routine has to wait until the interrupt handler empties a spot. The problem occurs when the interrupt handler itself needs to send data. If the queue is full, the microprocessor will hang (since the queue will never empty). In that case, the watchdog timer will timeout, and the chip resets. The above problem can occur especially with the IR and X-10 modules. Their send queues are only one command deep (due to limited memory on PICs). You must be careful not to send too many commands per second. One way to prevent the watchdog from timing out is to write a second version of the send initiate routine--one that is called by the interrupt service routine only. This version does not wait until the queue has an open spot, but returns immediately. With or without this second version of the send routine, data is lost anyway. This situation can be properly remedied by allocated more memory to their send routines and writing larger FIFOs for them. Modification History. See lib.h for mod history. Circuit Connection for 16C71 I am working on a circuit schematic. In any case, the circuit is simple enough that you can wire it up with the info below. 1 (X-10) TW523 data output (see Note 1) 2 (IR) input (see Note 4) 3 (IR) output 4 +5 Volt (Mclr) 5 Ground 6 (LCD) DB4 (see Note 2) 7 (LCD) DB5 8 (LCD) DB6 9 (LCD) DB7 10 (LCD) ENABLE 11 (LCD) Register Select 12 (RS232) input (see Note 3 13 (RS232) output 14 +5 Volt 15 crystal 16 crystal 17 (X-10) TW523 zero crossing input 18 (X-10) TW523 data input Notes. 1) TW523 pin 2 goes to ground. 2) LCD is an Optrex DMC Series 16x1 line display from ALL ELECTRONICS. Supply power and ground according to spec, add a contrast control pot. Be sure R/W input of LCD is grounded. 3) Use an RS232 driver such as the Maxim MAX203. 4) IR input is from a 'cube' such as Radio Shack 276-137. IR output goes to a 40KHz gated IRED flasher. When the output goes 'high', the IRED should flash. I use a 555 oscillator (has high current sourcing capability) by applying the gate input to pin 4. Distribution Policy This software is intended for non commercial use and as shareware. Your contribution can be anything you wish, but I think that $15 to $30 is a common shareware fee. Please mail to the address at the top of this file. The files follow in text format. The top of each file is marked by a line of asterisks *******. The following files are in the package: read.me ;you are reading this file. irdemo.asm ;assemble this file - starts with some simple demos, and ;continues with an RS232 controlled IR remote. The mapping ;of RS232 characters to IR function is as follows: ;U - Volume Up. D - Volume Down. ;u - Channel Up. d - Channel Down. ;Send the above characters to the chip, and it sends out the ;corresponding IR command. The LCD display shows all received ;IR commands, and characters typed from the RS232 port. x10demo.asm ;assemble this file - has RS232 controlled X10 interface. ;format of commands is: XC_NN, where NN is the X10 ;command. Example 'Et' is Housecode E and Keycode ON. ;Response is CX_NN, format similar to commands. p16cxx.inc ;file from Microchip, contains defs for P16 family of registers. lib.h ;pic library. The last line in this message should be: ;***** END OF FILE ***** Let me know if it isn't. All the best! ;*************************************** TITLE "Library Demo Program" ; Edward Cheung, Ph.D. ; ; Compiled with MPASM 1.20 ; ; Loaded with PICSTART 4.02 ; ;*************************************** ;Select library modules. Use 1/0, TRUE/FALSE not defined yet. CONSTANT AD_ENABLE = 0 CONSTANT R2_ENABLE = 1 CONSTANT R4_ENABLE = 0 CONSTANT LCD_ENABLE = 1 CONSTANT IR_ENABLE = 1 CONSTANT TW_ENABLE = 0 INCLUDE "LIB.H" ;pic library MAIN ;Initializations CALL GEN_INIT ;simple module demos IF LCD_ENABLE == TRUE MOVLW 'H' ;Print 'Hi' on LCD CALL LCD_PRINT MOVLW 'i' CALL LCD_PRINT MOVLW H'10' ;Put cursor at start CALL LCD_PRINT CLRWDT ENDIF IF R2_ENABLE == TRUE ;Print 'Hi' on terminal MOVLW 'H' CALL R2_SEND MOVLW 'i' CALL R2_SEND CLRWDT ENDIF IF TW_ENABLE == TRUE MOVLW 'E' ; tw_o_house = E; MOVWF TW_T_HOUSE MOVLW '1' ; tw_o_key = 1; MOVWF TW_T_KEY CALL TW_SEND MOVLW 'E' ; tw_o_house = E; MOVWF TW_T_HOUSE MOVLW 't' ; tw_o_key = on; MOVWF TW_T_KEY CALL TW_SEND ; Send CLRWDT ENDIF IF IR_ENABLE == TRUE MOVLW D'1' ; TV = 1 MOVWF IR_T_DEV MOVLW D'19' ; Volume Down = 19 MOVWF IR_T_DATA CALL IR_SEND ; Send CLRWDT ENDIF ;Do some real work with the library ;vars for RS232->IR command interpreter ALLOC COM_BYTE ;int com_byte; //received byte to interpret MAIN_LOOP GOTO MAIN_LOOP ;//do foreground processing here IF R2_ENABLE == TRUE ;This gets called when there is a new byte from rs232 serial line. ;A 'U' causes a 'Volume Up' command to be sent, a 'D' causes a ;'Volume Down' to be sent, a 'u' causes a Channel Up, and a 'd' ;a Channel Down (in SONY SIRCS format). R2_NEW_BYTE MOVWF COM_BYTE ;com_byte = W; CALL R2_SEND ;//echo to serial out R2_TEST_VOUP ;if (com_byte == 'U') MOVFW COM_BYTE SUBLW 'U' SKPZ GOTO R2_TEST_VODN MOVLW D'1' ; TV = 1 MOVWF IR_T_DEV MOVLW D'18' ; Volume Up = 18 MOVWF IR_T_DATA CALL IR_SEND ; // Send IR GOTO R2_NEW_END R2_TEST_VODN ;else if (com_byte == 'D') MOVFW COM_BYTE SUBLW 'D' SKPZ GOTO R2_TEST_CHUP MOVLW D'1' ; TV = 1 MOVWF IR_T_DEV MOVLW D'19' ; Volume Down = 19 MOVWF IR_T_DATA CALL IR_SEND ; // Send IR GOTO R2_NEW_END R2_TEST_CHUP MOVFW COM_BYTE ;if (com_byte == 'u') SUBLW 'u' SKPZ GOTO R2_TEST_CHDN MOVLW D'1' ; TV = 1 MOVWF IR_T_DEV MOVLW D'16' ; Channel Up = 16 MOVWF IR_T_DATA CALL IR_SEND ; // Send IR GOTO R2_NEW_END R2_TEST_CHDN ;else if (com_byte == 'd') MOVFW COM_BYTE SUBLW 'd' SKPZ GOTO R2_PRINT MOVLW D'1' ; TV = 1 MOVWF IR_T_DEV MOVLW D'17' ; Channel Down = 17 MOVWF IR_T_DATA CALL IR_SEND ; // Send IR GOTO R2_NEW_END R2_PRINT ;else MOVFW COM_BYTE CALL LCD_PRINT ; //display on lcd R2_NEW_END RETURN ENDIF END ;*************************************** TITLE "Library Demo Program" ; Edward Cheung, Ph.D. ; ; Compiled with MPASM 1.20 ; ; Loaded with PICSTART 4.02 ; ;*************************************** ;Select library modules. Use 1/0, TRUE/FALSE not defined yet. CONSTANT AD_ENABLE = 0 CONSTANT R2_ENABLE = 1 CONSTANT R4_ENABLE = 0 CONSTANT LCD_ENABLE = 0 CONSTANT IR_ENABLE = 0 CONSTANT TW_ENABLE = 1 INCLUDE "LIB.H" ;pic library MAIN ;Initializations CALL GEN_INIT ;Do some real work with the library ;vars for RS232->IR command interpreter ALLOC COM_BYTE ;int com_byte; //received byte to interpret ALLOC COM_STATE ;int com_state; //state of command state machine CONSTANT ADDRESS = 'X' ALLOC TW_P_HOUSE ALLOC TW_P_KEY ;inits for main program CLRF COM_STATE ;com_state = 0; MAIN_LOOP GOTO MAIN_LOOP ;//do foreground processing here ;This gets called when there is a new byte from rs232 serial line. R2_NEW_BYTE MOVWF COM_BYTE ;com_byte = W; ; CALL R2_SEND ;//echo to serial out R2_STATE0 ;if (com_state == 0) MOVFW COM_STATE SUBLW D'0' SKPZ GOTO R2_STATE1 MOVFW COM_BYTE ; if (com_byte == address) SUBLW ADDRESS SKPNZ INCF COM_STATE,F ; com_state ++; GOTO R2_END_NEW R2_STATE1 ;else if (com_state == 1) MOVFW COM_STATE SUBLW D'1' SKPZ GOTO R2_STATE2 INCF COM_STATE,F ; com_state ++; GOTO R2_END_NEW R2_STATE2 ;else if (com_state == 2) MOVFW COM_STATE SUBLW D'2' SKPZ GOTO R2_STATE3 MOVFW COM_BYTE ; if (com_byte == '_') SUBLW '_' SKPZ GOTO R2_STATE2_ELSE INCF COM_STATE,F ; com_state ++; GOTO R2_END_NEW R2_STATE2_ELSE ; else CLRF COM_STATE ; com_state = 0; GOTO R2_END_NEW R2_STATE3 ;else if (com_state == 3) MOVFW COM_STATE SUBLW D'3' SKPZ GOTO R2_STATE4 MOVFW COM_BYTE ; tw_t_house = com_byte MOVWF TW_T_HOUSE INCF COM_STATE,F ; com_state ++; GOTO R2_END_NEW R2_STATE4 ;else if (com_state == 4) MOVFW COM_STATE SUBLW D'4' SKPZ GOTO R2_STATE5 MOVFW COM_BYTE ; tw_t_key = com_byte MOVWF TW_T_KEY INCF COM_STATE,F ; com_state ++; GOTO R2_END_NEW R2_STATE5 ;else MOVFW COM_BYTE ; if (com_byte == 13) SUBLW D'13' SKPZ GOTO R2_ABORT CALL TW_SEND ; // send x-10 CALL R2_ECHO ; // send status R2_ABORT CLRF COM_STATE ; com_state = 0; R2_END_NEW RETURN ;This is called when an X10 command is received. House code will be ;in tw_house, and key code in tw_key. TW_NEW MOVFW TW_HOUSE ;tw_p_house = converted(tw_house); CALL TW_TO_HOUSE MOVWF TW_P_HOUSE MOVFW TW_KEY ;tw_p_key = converted(tw_key); CALL TW_TO_KEY MOVWF TW_P_KEY RETURN R2_ECHO MOVLW 'C' ;Format of reply: CALL R2_SEND ;CX_E1 MOVLW 'X' ;^^^^^^ CALL R2_SEND ;|||||| MOVLW '_' ;|||||+- return character (0x13) CALL R2_SEND ;||||+-- 2nd response character MOVFW TW_P_HOUSE ;|||+--- 1st response character CALL R2_SEND ;||+---- underscore MOVFW TW_P_KEY ;|+----- from X-10 module CALL R2_SEND ;+------ to Computer MOVLW D'13' CALL R2_SEND MOVLW ' ' MOVWF TW_P_HOUSE ;//erase house and key code MOVWF TW_P_KEY RETURN END ;*************************************** LIST ; P16CXX.INC Standard Header File, Version 2.04 Microchip Technology, Inc. NOLIST ; This header file defines configurations, registers, and other useful bits of ; information for the 16CXX microcontrollers. These names are taken to match ; the data sheets as closely as possible. The microcontrollers included ; in this file are: ; 16C61 ; 16C62 ; 16C620 ; 16C621 ; 16C622 ; 16C63 ; 16C64 ; 16C65 ; 16C71 ; 16C73 ; 16C74 ; 16C84 ; There is one group of defines that is valid for all microcontrollers. ; Each microcontroller in this family also has its own section of special ; defines. Note that the processor must be selected before this file is ; included. The processor may be selected the following ways: ; 1. Command line switch: ; C:\ MPASM MYFILE.ASM /P16C71 ; 2. LIST directive in the source file ; LIST P=16C71 ; 3. Processor Type entry in the MPASM full-screen interface ;========================================================================== ; ; Generic Definitions ; ;========================================================================== W EQU H'0000' F EQU H'0001' ;----- Register Files------------------------------------------------------ INDF EQU H'0000' TMR0 EQU H'0001' PCL EQU H'0002' STATUS EQU H'0003' FSR EQU H'0004' PORTA EQU H'0005' PORTB EQU H'0006' PCLATH EQU H'000A' INTCON EQU H'000B' OPTION_REG EQU H'0081' TRISA EQU H'0085' TRISB EQU H'0086' ;----- INTCON Bits (except ADC/Periph) ------------------------------------ GIE EQU H'0007' T0IE EQU H'0005' INTE EQU H'0004' RBIE EQU H'0003' T0IF EQU H'0002' INTF EQU H'0001' RBIF EQU H'0000' ;----- OPTION Bits -------------------------------------------------------- NOT_RBPU EQU H'0007' INTEDG EQU H'0006' T0CS EQU H'0005' T0SE EQU H'0004' PSA EQU H'0003' PS2 EQU H'0002' PS1 EQU H'0001' PS0 EQU H'0000' ;----- STATUS Bits -------------------------------------------------------- IRP EQU H'0007' RP1 EQU H'0006' RP0 EQU H'0005' NOT_TO EQU H'0004' NOT_PD EQU H'0003' Z EQU H'0002' DC EQU H'0001' C EQU H'0000' ;========================================================================== ; ; Processor-dependent Definitions ; ;========================================================================== IFDEF __16C61 __MAXRAM H'0AF' __BADRAM H'07'-H'09', H'030'-H'07F', H'087'-H'089' #define __CONFIG_0 ENDIF IFDEF __16C62 PORTC EQU H'0007' __MAXRAM H'0BF' __BADRAM H'08'-H'09',H'0D',H'018'-H'01F',H'08D',H'08F'-H'091',H'095'-H'09F' #define __CONFIG_2 ENDIF IFDEF __16C620 ;----- Register Files -------------------------------------------------- PIR1 EQU H'000C' CMCON EQU H'001F' PIE1 EQU H'008C' PCON EQU H'008E' VRCON EQU H'009F' __MAXRAM H'09F' __BADRAM H'07'-H'09', H'0D'-H'01E', H'070'-H'07F', H'087'-H'089', H'08D', H'08F'-H'09E' #define __CONFIG_6 ENDIF IFDEF __16C621 ;----- Register Files -------------------------------------------------- PIR1 EQU H'000C' CMCON EQU H'001F' PIE1 EQU H'008C' PCON EQU H'008E' VRCON EQU H'009F' __MAXRAM H'09F' __BADRAM H'07'-H'09', H'0D'-H'01E', H'70'-H'07F', H'087'-H'089', H'08D', H'08F'-H'09E' #define __CONFIG_4 ENDIF IFDEF __16C622 ;----- Register Files -------------------------------------------------- PIR1 EQU H'000C' CMCON EQU H'001F' PIE1 EQU H'008C' PCON EQU H'008E' VRCON EQU H'009F' __MAXRAM H'0BF' __BADRAM H'07'-H'09', H'0D'-H'01E', H'087'-H'089', H'08D', H'08F'-H'09E' #define __CONFIG_5 ENDIF IFDEF __16C63 ;----- Register Files -------------------------------------------------- PORTC EQU H'0007' PIR1 EQU H'000C' TMR1L EQU H'000E' TMR1H EQU H'000F' T1CON EQU H'0010' TMR2 EQU H'0011' T2CON EQU H'0012' SSPBUF EQU H'0013' SSPCON EQU H'0014' CCPR1L EQU H'0015' CCPR1H EQU H'0016' CCP1CON EQU H'0017' TRISC EQU H'0087' PIE1 EQU H'008C' PCON EQU H'008E' PR2 EQU H'0092' SSPADD EQU H'0093' SSPSTAT EQU H'0094' __MAXRAM H'0BF' __BADRAM H'08'-H'09', H'0D', H'18'-H'1F', H'88', H'89', H'8D', H'8F'-H'91', H'95'-H'9F' #define __CONFIG_5 ENDIF IFDEF __16C64 ;----- Register Files -------------------------------------------------- PORTC EQU H'0007' PORTD EQU H'0008' PORTE EQU H'0009' PIR1 EQU H'000C' TMR1L EQU H'000E' TMR1H EQU H'000F' T1CON EQU H'0010' TMR2 EQU H'0011' T2CON EQU H'0012' SSPBUF EQU H'0013' SSPCON EQU H'0014' CCPR1L EQU H'0015' CCPR1H EQU H'0016' CCP1CON EQU H'0017' TRISC EQU H'0087' TRISD EQU H'0088' TRISE EQU H'0089' PIE1 EQU H'008C' PCON EQU H'008E' PR2 EQU H'0092' SSPADD EQU H'0093' SSPSTAT EQU H'0094' __MAXRAM H'0BF' __BADRAM H'0D', H'018'-H'01F', H'08D', H'08F'-H'091', H'095'-H'09F' #define __CONFIG_2 ENDIF IFDEF __16C65 ;----- Register Files -------------------------------------------------- PORTC EQU H'0007' PORTD EQU H'0008' PORTE EQU H'0009' PIR1 EQU H'000C' PIR2 EQU H'000D' TMR1L EQU H'000E' TMR1H EQU H'000F' T1CON EQU H'0010' TMR2 EQU H'0011' T2CON EQU H'0012' SSPBUF EQU H'0013' SSPCON EQU H'0014' CCPR1L EQU H'0015' CCPR1H EQU H'0016' CCP1CON EQU H'0017' RCSTA EQU H'0018' TXREG EQU H'0019' RCREG EQU H'001A' CCPR2L EQU H'001B' CCPR2H EQU H'001C' CCP2CON EQU H'001D' TRISC EQU H'0087' TRISD EQU H'0088' TRISE EQU H'0089' PIE1 EQU H'008C' PIE2 EQU H'008D' PCON EQU H'008E' PR2 EQU H'0092' SSPADD EQU H'0093' SSPSTAT EQU H'0094' TXSTA EQU H'0098' SPBRG EQU H'0099' __MAXRAM H'0FF' __BADRAM H'1E'-H'1F',H'08F'-H'091', H'095'-H'097', H'09A'-H'09F' #define __CONFIG_2 ENDIF IFDEF __16C71 __MAXRAM H'0AF' __BADRAM H'07', H'030'-H'07F', H'087' #define __ADC_CONFIG_0 #define __CONFIG_0 ENDIF IFDEF __16C73 ;----- Register Files -------------------------------------------------- PORTC EQU H'0007' PIR1 EQU H'000C' PIR2 EQU H'000D' TMR1L EQU H'000E' TMR1H EQU H'000F' T1CON EQU H'0010' TMR2 EQU H'0011' T2CON EQU H'0012' SSPBUF EQU H'0013' SSPCON EQU H'0014' CCPR1L EQU H'0015' CCPR1H EQU H'0016' CCP1CON EQU H'0017' RCSTA EQU H'0018' TXREG EQU H'0019' RCREG EQU H'001A' CCPR2L EQU H'001B' CCPR2H EQU H'001C' CCP2CON EQU H'001D' TRISC EQU H'0087' PIE1 EQU H'008C' PIE2 EQU H'008D' PCON EQU H'008E' PR2 EQU H'0092' SSPADD EQU H'0093' SSPSTAT EQU H'0094' TXSTA EQU H'0098' SPBRG EQU H'0099' __MAXRAM H'0FF' __BADRAM H'08F'-H'091', H'095'-H'097', H'09A'-H'09E' #define __ADC_CONFIG_1 #define __CONFIG_2 ENDIF IFDEF __16C74 ;----- Register Files -------------------------------------------------- PORTC EQU H'0007' PORTD EQU H'0008' PORTE EQU H'0009' PIR1 EQU H'000C' PIR2 EQU H'000D' TMR1L EQU H'000E' TMR1H EQU H'000F' T1CON EQU H'0010' TMR2 EQU H'0011' T2CON EQU H'0012' SSPBUF EQU H'0013' SSPCON EQU H'0014' CCPR1L EQU H'0015' CCPR1H EQU H'0016' CCP1CON EQU H'0017' RCSTA EQU H'0018' TXREG EQU H'0019' RCREG EQU H'001A' CCPR2L EQU H'001B' CCPR2H EQU H'001C' CCP2CON EQU H'001D' TRISC EQU H'0087' TRISD EQU H'0088' TRISE EQU H'0089' PIE1 EQU H'008C' PIE2 EQU H'008D' PCON EQU H'008E' PR2 EQU H'0092' SSPADD EQU H'0093' SSPSTAT EQU H'0094' TXSTA EQU H'0098' SPBRG EQU H'0099' __MAXRAM H'0FF' __BADRAM H'08F'-H'091', H'095'-H'097', H'09A'-H'09E' #define __ADC_CONFIG_1 #define __CONFIG_2 ENDIF IFDEF __16C84 ;----- Register Files -------------------------------------------------- EEDATA EQU H'0008' EEADR EQU H'0009' EECON1 EQU H'0088' EECON2 EQU H'0089' __MAXRAM H'0AF' __BADRAM H'07', H'030'-H'07F', H'087' #define __CONFIG_0 ENDIF ;========================================================================== ; ; Configuration Bits ; ;========================================================================== IFDEF __CONFIG_0 _CP_ON EQU H'3FEF' _CP_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FFF' _PWRTE_OFF EQU H'3FF7' _WDT_ON EQU H'3FFF' _WDT_OFF EQU H'3FFB' _LP_OSC EQU H'3FFC' _XT_OSC EQU H'3FFD' _HS_OSC EQU H'3FFE' _RC_OSC EQU H'3FFF' #undefine __CONFIG_0 ENDIF IFDEF __CONFIG_1 _BODEN_ON EQU H'3FFF' _BODEN_OFF EQU H'3FBF' _CP_ON EQU H'004F' _CP_OFF EQU H'3FFF' _PWRTE_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FF7' _WDT_ON EQU H'3FFF' _WDT_OFF EQU H'3FFB' _LP_OSC EQU H'3FFC' _XT_OSC EQU H'3FFD' _HS_OSC EQU H'3FFE' _RC_OSC EQU H'3FFF' #undefine __CONFIG_1 ENDIF IFDEF __CONFIG_2 _CP_ALL EQU H'3F8F' _CP_75 EQU H'3F9F' _CP_50 EQU H'3FAF' _CP_OFF EQU H'3FBF' _PWRTE_ON EQU H'3FBF' _PWRTE_OFF EQU H'3FB7' _WDT_ON EQU H'3FBF' _WDT_OFF EQU H'3FBB' _LP_OSC EQU H'3FBC' _XT_OSC EQU H'3FBD' _HS_OSC EQU H'3FBE' _RC_OSC EQU H'3FBF' #undefine __CONFIG_2 ENDIF IFDEF __CONFIG_3 _CP_ON EQU H'000F' _CP_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FFF' _PWRTE_OFF EQU H'3FF7' _WDT_ON EQU H'3FFF' _WDT_OFF EQU H'3FFB' _LP_OSC EQU H'3FFC' _XT_OSC EQU H'3FFD' _HS_OSC EQU H'3FFE' _RC_OSC EQU H'3FFF' #undefine __CONFIG_3 ENDIF IFDEF __CONFIG_4 _BODEN_ON EQU H'3FFF' _BODEN_OFF EQU H'3FBF' _CP_ALL EQU H'00CF' _CP_50 EQU H'15DF' _CP_OFF EQU H'3FFF' _PWRTE_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FF7' _WDT_ON EQU H'3FFF' _WDT_OFF EQU H'3FFB' _LP_OSC EQU H'3FFC' _XT_OSC EQU H'3FFD' _HS_OSC EQU H'3FFE' _RC_OSC EQU H'3FFF' #undefine __CONFIG_4 ENDIF IFDEF __CONFIG_5 _BODEN_ON EQU H'3FFF' _BODEN_OFF EQU H'3FBF' _CP_ALL EQU H'00CF' _CP_75 EQU H'15DF' _CP_50 EQU H'2AEF' _CP_OFF EQU H'3FFF' _PWRTE_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FF7' _WDT_ON EQU H'3FFF' _WDT_OFF EQU H'3FFB' _LP_OSC EQU H'3FFC' _XT_OSC EQU H'3FFD' _HS_OSC EQU H'3FFE' _RC_OSC EQU H'3FFF' #undefine __CONFIG_5 ENDIF IFDEF __CONFIG_6 _BODEN_ON EQU H'3FFF' _BODEN_OFF EQU H'3FBF' _CP_ON EQU H'00CF' _CP_OFF EQU H'3FFF' _PWRTE_OFF EQU H'3FFF' _PWRTE_ON EQU H'3FF7' _WDT_ON EQU H'3FFF' _WDT_OFF EQU H'3FFB' _LP_OSC EQU H'3FFC' _XT_OSC EQU H'3FFD' _HS_OSC EQU H'3FFE' _RC_OSC EQU H'3FFF' #undefine __CONFIG_6 ENDIF ;========================================================================== ; ; More Bit Definitions ; ;========================================================================== IFDEF __ADC_CONFIG_0 ;---- Register Files --------------------------------------------------- ADCON0 EQU H'0008' ADRES EQU H'0009' ADCON1 EQU H'0088' ;---- Finish INTCON Definition ----------------------------------------- ADIE EQU H'0006' ;----- ADCON0 Bits ----------------------------------------------------- ADCS1 EQU H'0007' ADCS0 EQU H'0006' CHS1 EQU H'0004' CHS0 EQU H'0003' GO EQU H'0002' NOT_DONE EQU H'0002' GO_DONE EQU H'0002' ADIF EQU H'0001' ADON EQU H'0000' ;----- ADCON1 Bits ----------------------------------------------------- PCFG1 EQU H'0001' PCFG0 EQU H'0000' #undefine __ADC_CONFIG_0 ELSE ;---- Finish INTCON Definition ----------------------------------------- PEIE EQU H'0006' ENDIF IFDEF __ADC_CONFIG_1 ;----- Register Files -------------------------------------------------- ADRES EQU H'001E' ADCON0 EQU H'001F' ADCON1 EQU H'009F' ;----- ADCON0 Bits ----------------------------------------------------- ADCS1 EQU H'0007' ADCS0 EQU H'0006' CHS2 EQU H'0005' CHS1 EQU H'0004' CHS0 EQU H'0003' GO EQU H'0002' NOT_DONE EQU H'0002' GO_DONE EQU H'0002' ADON EQU H'0000' ;----- ADCON1 Bits ----------------------------------------------------- PCFG2 EQU H'0002' PCFG1 EQU H'0001' PCFG0 EQU H'0000' ;----- PIE1 and PIR1 ADC Bits ------------------------------------------ ADIE EQU H'0006' ADIF EQU H'0006' #undefine __ADC_CONFIG_1 ENDIF IFDEF CCP1CON CCP1X EQU H'0005' CCP1Y EQU H'0004' CCP1M3 EQU H'0003' CCP1M2 EQU H'0002' CCP1M1 EQU H'0001' CCP1M0 EQU H'0000' ENDIF IFDEF CCP2CON CCP2X EQU H'0005' CCP2Y EQU H'0004' CCP2M3 EQU H'0003' CCP2M2 EQU H'0002' CCP2M1 EQU H'0001' CCP2M0 EQU H'0000' ENDIF IFDEF CMCON C2OUT EQU H'0007' C1OUT EQU H'0006' CIS EQU H'0003' CM2 EQU H'0002' CM1 EQU H'0001' CM0 EQU H'0000' ;----- PIE1 and PIR1 ADC Bits ------------------------------------------ CMIE EQU H'0006' CMIF EQU H'0006' ENDIF IFDEF EECON1 EEIF EQU H'0004' WRERR EQU H'0003' WREN EQU H'0002' WR EQU H'0001' RD EQU H'0000' ENDIF IFDEF PCON NOT_POR EQU H'0001' NOT_BO EQU H'0000' ENDIF IFDEF PIE1 PSPIE EQU H'0007' SSPIE EQU H'0003' CCP1IE EQU H'0002' TMR2IE EQU H'0001' TMR1IE EQU H'0000' ENDIF IFDEF PIR1 PSPIF EQU H'0007' SSPIF EQU H'0003' CCP1IF EQU H'0002' TMR2IF EQU H'0001' TMR1IF EQU H'0000' ENDIF IFDEF PIE2 ; Assumes PIE2 and PIR2 CCP2IE EQU H'0000' CCP2IF EQU H'0000' ENDIF IFDEF RCSTA SPEN EQU H'0007' RC9 EQU H'0006' NOT_RC8 EQU H'0006' RC8_9 EQU H'0006' SREN EQU H'0005' CREN EQU H'0004' FERR EQU H'0002' OERR EQU H'0001' RCD8 EQU H'0000' ;----- PIE1 and PIR1 RC Bits ------------------------------------------ RCIE EQU H'0005' RBFL EQU H'0005' ENDIF IFDEF SSPCON WCOL EQU H'0007' SSPOV EQU H'0006' SSPEN EQU H'0005' CKP EQU H'0004' SSPM3 EQU H'0003' SSPM2 EQU H'0002' SSPM1 EQU H'0001' SSPM0 EQU H'0000' ENDIF IFDEF SSPSTAT D EQU H'0005' I2C_DATA EQU H'0005' NOT_A EQU H'0005' NOT_ADDRESS EQU H'0005' D_A EQU H'0005' DATA_ADDRESS EQU H'0005' P EQU H'0004' I2C_STOP EQU H'0004' S EQU H'0003' I2C_START EQU H'0003' R EQU H'0002' I2C_READ EQU H'0002' NOT_W EQU H'0002' NOT_WRITE EQU H'0002' R_W EQU H'0002' READ_WRITE EQU H'0002' UA EQU H'0001' BF EQU H'0000' ENDIF IFDEF T1CON T1CKPS1 EQU H'0005' T1CKPS0 EQU H'0004' T1OSCEN EQU H'0003' T1INSYNC EQU H'0002' TMR1CS EQU H'0001' TMR1ON EQU H'0000' ENDIF IFDEF T2CON TOUTPS3 EQU H'0006' TOUTPS2 EQU H'0005' TOUTPS1 EQU H'0004' TOUTPS0 EQU H'0003' TMR2ON EQU H'0002' T2CKPS1 EQU H'0001' T2CKPS0 EQU H'0000' ENDIF IFDEF TRISE IBF EQU H'0007' OBF EQU H'0006' IBOV EQU H'0005' PSPMODE EQU H'0004' TRISE2 EQU H'0002' TRISE1 EQU H'0001' TRISE0 EQU H'0000' ENDIF IFDEF TXSTA CSRC EQU H'0007' TX9 EQU H'0006' NOT_TX8 EQU H'0006' TX8_9 EQU H'0006' TXEN EQU H'0005' SYNC EQU H'0004' BRGH EQU H'0002' TRMT EQU H'0001' TXD8 EQU H'0000' ;----- PIE1 and PIR1 TX Bits ------------------------------------------ TXIE EQU H'0004' TXIF EQU H'0004' ENDIF IFDEF VRCON VREN EQU H'0007' VROE EQU H'0006' VRR EQU H'0005' VR3 EQU H'0003' VR2 EQU H'0002' VR1 EQU H'0001' VR0 EQU H'0000' ENDIF LIST ;*************************************** ; LIBRARY OF UTILITIES FOR PIC ; ; EDWARD CHEUNG, PH.D. ; ; MITCHELLVILLE, MD ; ; ebc714@rs710.gsfc.nasa.gov ; ;*************************************** ; MODIFICATION HISTORY ; Version 0.1, July 1995: ; Compiles under MPASM 1.02.05. Loads with PICSTART 4.02. ; Currently available modules are A/D, LCD, X-10, IR, RS422 and RS232. ;The RS485 code remains to be tested, and is awaiting ;driver chips to test the transmit enable line (R4_TRANON). IR module ;supports SONY (aka SIRCS) format. Each module tested using LCD module. ; Version 0.2, July 1995: ; Compiles under mpasm 1.20. Moved interrupt functions into ;main library. Improved INT_HANDLER to call one module per ;interrupt. Previous version called all enabled modules, which caused timing ;problems. Each module was not guaranteed to be called at a stable frequency. ; Fixed bug with computed gotos. Added assembler code that will give a ;warning if code with computed gotos is placed in program memory above ;location 0xff. See Application Note AN556 in Embedded Control Handbook ;for more details. ; Tested with 14.7456Mhz crystal. This is a commonly available ;frequency that is divisible by 9600 and under 16Mhz. ;15.360Mhz and 15.9744Mhz are also good frequencies (not tested). ;At 14.x Mhz, 57600 interrupts/second is probably the fastest you can ;go. If you need more, use a faster crystal. ; Eliminated use of P16C71.INC file and used defs in P16CXX.INC. This ;should improve support for other processors by substituting the proper ;*.inc file. As far as I know, the only thing you will have to change ;for other processors in the MEM_FIRST and the MEM_LAST variables below, ;and don't enable the A/D if it doesn't exist. ; Version 0.21: ; Invalid commands to TW_SEND are rejected. GOTO MAIN ;start execution at 'main' #define __16C71 ;using PIC16C71 #INCLUDE "P16CXX.INC" ;defs for register location __FUSES _WDT_ON&_HS_OSC ;watch dog on, and hs oscillator ;***** General constants FXTAL EQU D'14745600' ;device clock freq INTSEC EQU D'28800' ;desired interrupts/sec. TRUE EQU 1H FALSE EQU 0H ;Set up desired interrupts/sec for chip. A smaller number means more ;operations between interrupts, and more time alotted to the main program. RTC_NUM EQU D'256' - ((FXTAL/(4*INTSEC)) - D'7') ;number of modules that use the interrupt mechanism NUM_MOD EQU R2_ENABLE + R4_ENABLE + IR_ENABLE + IR_ENABLE + TW_ENABLE ;number of times/sec each interrupt module gets control (is run) RUNSEC EQU INTSEC/NUM_MOD ;***** Memory Management ;Assign memory location to input variable 'name' ;Addresses will start at MEM_FIRST, and last one allowed is at MEM_LAST ;0CH to 2FH inclusive are available on '71. ;See .lst file for actual addresses. In that file, ;MEM_INDEX will be one address past the last one. ;Thus max for MEM_INDEX is MEM_LAST + 1 MEM_FIRST EQU 0CH MEM_LAST EQU 2FH MEM_INDEX SET MEM_FIRST ALLOC MACRO NAME NAME EQU MEM_INDEX IF MEM_INDEX > MEM_LAST CALL OUT_OF_MEMORY_ERROR ;Reduce the number of modules in use ELSE MEM_INDEX SET MEM_INDEX + 1 ENDIF ENDM ;Assign memory location to input variable 'name' ;Total number of storage locations is 'spacing', ;which includes memory allocated in previous ALLOC call, ;and the one occupied by 'name'. ALLOC_ARRAY MACRO NAME,SPACING IF SPACING < 2 CALL ARRAY_TOO_SMALL ;Increase size of array to be greater than 2 ENDIF MEM_INDEX SET (MEM_INDEX+SPACING)-2 NAME EQU MEM_INDEX IF MEM_INDEX > MEM_LAST CALL OUT_OF_MEMORY ;Reduce the number of modules in use ELSE MEM_INDEX SET MEM_INDEX + 1 ENDIF ENDM ;Memory location assignment ;register storage for interrupt ALLOC TEMP_W ALLOC TEMP_STAT ALLOC TASK_INDEX ;gen purpose for use in functions that interface to interrupt routines ALLOC SCRATCH_1 ALLOC SCRATCH_2 IF R2_ENABLE == TRUE ;rs232 uart ALLOC R2_OUT_TIMR ;output timer ALLOC R2_OUT_BIT ;index of current output bit ALLOC R2_IN_TIMER ;input timer ALLOC R2_IN_BIT ;index of current input bit ALLOC R2_IN_BYTE ;byte being received ALLOC R2_IN_PTR ;ring buffer pointer in ALLOC R2_OUT_PTR ;ring buffer pointer out ALLOC R2_FIRST_BUF ;ring buffer location ALLOC_ARRAY R2_LAST_BUF,D'07' ;ring buffer end ENDIF IF R4_ENABLE == TRUE ;rs422/485 uart ALLOC R4_OUT_TIMR ;current bit being sent ALLOC R4_OUT_BIT ;output data ALLOC R4_IN_TIMER ;bit counter ALLOC R4_IN_BIT ;data input bit ALLOC R4_IN_BYTE ;byte being received ALLOC R4_IN_PTR ;ring buffer pointer in ALLOC R4_OUT_PTR ;ring buffer pointer out ALLOC R4_FIRST_BUF ;ring buffer location ALLOC_ARRAY R4_LAST_BUF,D'08' ;ring buffer end ENDIF IF LCD_ENABLE == TRUE ;timer ALLOC TIMER_HI ALLOC TIMER_LO ;binary to bcd conversion ALLOC LSD ALLOC MSD ENDIF IF IR_ENABLE == TRUE ;ir uart ALLOC IR_DEV ;received ir device ALLOC IR_DATA ;received ir data ALLOC IR_PHASE ;current ir bit being received ALLOC IR_TIMER ;rx countdown timer ALLOC IR_T_DEV ;tx ir device ALLOC IR_T_DATA ;tx ir data ALLOC IR_O_COUNT ;number of times to send ir data ALLOC IR_O_DEV ;ir device being sent ALLOC IR_O_DATA ;ir data being sent ALLOC IR_O_PHASE ;ir bit being sent ALLOC IR_O_TIMER ;tx countdown timer ENDIF IF TW_ENABLE == TRUE ;x10 uart ;memory ALLOC TW_FLAGS ;for the following booleans: TW_PREV EQU 00H ;boolean, previous 60 hz status TW_STATE EQU 01H ;boolean, current 60 hz status TW_CARRIER EQU 02H ;boolean, data bit TW_O_CARR EQU 03H ;boolean, tw_o_carrier; TW_FIRST EQU 04H ;boolean, first packet of two ALLOC TW_SAMPLE ;countdown and control timer ALLOC TW_PHASE ;which bit current being sampled ALLOC TW_HOUSE ;house code data ALLOC TW_KEY ;key code data ALLOC TW_O_SAMPLE ;countdown and control timer ALLOC TW_O_PHASE ;bit being sent ALLOC TW_O_HOUSE ;house code being sent ALLOC TW_O_KEY ;key code being sent ALLOC TW_T_HOUSE ;next house code to be sent ALLOC TW_T_KEY ;next key code to be sent ALLOC TW_MATCH ;ascii of x10 code sought ALLOC TW_INDEX ;indexing counter ENDIF ;TW_ENABLE ;***** Interrupt related functions ;This is called every time an interrupt occurs. ;Due to computed GOTO, this function must reside in memory range 0-FF. ;One module is called everytime there is an interrupt. Note how the call ;of each module is written. There must be exactly 4 instructions between ;each IF...ENDIF statement. ;Note that IR functions are called in separate interrupts. That is because ;each takes so much time to run. INT_HANDLER MACRO MOVFW TASK_INDEX ;if (task_index == num_mod) SUBLW NUM_MOD SKPNZ CLRF TASK_INDEX ; task_index = 0; CLRC ;// clear carry RLF TASK_INDEX,F ;pc += (task_index++ * 4) RLF TASK_INDEX,W RRF TASK_INDEX,F INCF TASK_INDEX,F ADDWF PCL,F ;//computed goto, run one of the modules below IF TW_ENABLE == TRUE CALL TW_GET ;check x10 input CALL TW_PUT ;check x10 output GOTO INT_H_END GOTO INT_H_END ENDIF IF R2_ENABLE == TRUE CALL R2_SER_IN ;check serial input CALL R2_SER_OUT ;check serial output GOTO INT_H_END GOTO INT_H_END ENDIF IF R4_ENABLE == TRUE CALL R4_SER_IN ;check serial input CALL R4_SER_OUT ;check serial output GOTO INT_H_END GOTO INT_H_END ENDIF IF IR_ENABLE == TRUE CALL IR_GET ;check ir input GOTO INT_H_END GOTO INT_H_END GOTO INT_H_END CALL IR_PUT ;check ir output GOTO INT_H_END GOTO INT_H_END GOTO INT_H_END ENDIF INT_H_END IF INT_H_END > H'FE' CALL PAGE_ERROR ;Due to computed gotos, this should be in program memory below 0xFF ;See Application Note AN556, example 5 for more info. ENDIF ENDM ;Interrupt service routine. INT_VECT ORG H'04' ;Save W and STATUS registers MOVWF TEMP_W ;save W SWAPF STATUS,W ;get swapped status MOVWF TEMP_STAT ;save swapped status ;Reschedule next interrupt MOVLW RTC_NUM MOVWF TMR0 ;setup for next interrupt CLRWDT ;Do interrupt actions INT_HANDLER ;Clear interrupt sources ; BCF INTCON,RBIF ;clear interrupt from RB<7:4> ; BCF INTCON,INTF ;clear interrupt from RB0 BCF INTCON,T0IF ;clear interrupt from timer 0 ;Restore registers and return SWAPF TEMP_STAT,W ;get and unswap STATUS MOVWF STATUS ;restore STATUS SWAPF TEMP_W,F ;swap TEMP_W SWAPF TEMP_W,W ;unswap and restore W RETFIE ;return from interrupt ;***** General Macros and Functions ;Select page 1 PAGE_1 MACRO BSF STATUS,RP0 ENDM ;Select page 0 PAGE_0 MACRO BCF STATUS,RP0 ENDM ;Main Inits GEN_INIT ;Note! This also sets up general operation of Ports. If they ;are digital or analog, pullups enabled or not etc. ;Setup PORTA options IF AD_ENABLE == TRUE PAGE_1 MOVLW B'00000000' ;all pins analog MOVWF ADCON1^H'80' ;setup PORTA function PAGE_0 ELSE PAGE_1 MOVLW B'00000011' ;all pins digital MOVWF ADCON1^H'80' ;setup PORTA function PAGE_0 ENDIF ;PortB no pullup, Prescaler to WDT. See Page 2-355 '94 edition PAGE_1 CLRWDT MOVLW B'10001000' MOVWF OPTION_REG^H'80' PAGE_0 ;other variables CLRF TMR0 CLRF TASK_INDEX ;Call the init functions of modules that are needed IF LCD_ENABLE == TRUE CALL LCD_INIT ENDIF IF R2_ENABLE == TRUE CALL R2_INIT ENDIF IF R4_ENABLE == TRUE CALL R4_INIT ENDIF IF IR_ENABLE == TRUE CALL IR_INIT ENDIF IF TW_ENABLE == TRUE CALL TW_INIT ENDIF ;GIE enable, T0IE enable for interrupt mechanism MOVLW B'10100000' MOVWF INTCON CLRWDT RETURN ;***** X-10 FUNCTIONS. Due to computed gotos, this has to be ;below program memory location FF. Warnings are built in if the above is ;not met. IF TW_ENABLE == TRUE ;defs TW_PORT EQU PORTA TW_60 EQU 00H ;60Hz crossing input TW_FROM EQU 01H ;data from house input TW_TO EQU 02H ;data to house output TW_10 EQU (RUNSEC * D'11')/D'10000' TW_05 EQU (RUNSEC * D'05')/D'10000' ;Returns house code when given x10 code in W TW_TO_HOUSE ANDLW H'F' ;mask upper nibble ADDWF PCL,F ;computed goto RETLW 'M' ;returned if input = 0 RETLW 'N' RETLW 'O' RETLW 'P' RETLW 'C' RETLW 'D' RETLW 'A' RETLW 'B' RETLW 'E' RETLW 'F' RETLW 'G' RETLW 'H' RETLW 'K' RETLW 'L' RETLW 'I' RETLW 'J' ;returned if input = F TW_TOH_END IF TW_TOH_END > H'FE' CALL PAGE_ERROR ;Due to computed gotos, this should be in program memory below 0xFF ;See Application Note AN556, example 5 ENDIF ;Returns unit or function code when given x10 code in W TW_TO_KEY ANDLW H'1F' ;mask upper 3 bits ADDWF PCL,F ;computed goto RETLW 'D' ;returned if input = 0x0 RETLW 'E' RETLW 'F' RETLW 'G' RETLW '3' RETLW '4' RETLW '1' RETLW '2' RETLW '5' RETLW '6' RETLW '7' RETLW '8' RETLW 'B' RETLW 'C' RETLW '9' RETLW 'A' ;returned if input = 0xf RETLW 'u' ;all units off RETLW 'r' ;hail request RETLW 'd' ;dim RETLW 'n' ;extended data (analog) RETLW 't' ;on RETLW 'p' ;pre-set dim RETLW 'a' ;all lights off RETLW 'l' ;status = off RETLW 'o' ;all lights on RETLW 'h' ;hail acknowledge RETLW 'b' ;bright RETLW 's' ;status = on RETLW 'f' ;off RETLW 'p' ;pre-set dim RETLW 'x' ;extended code RETLW 'q' ;status request TW_TOK_END IF TW_TOK_END > H'FE' CALL PAGE_ERROR ;Due to computed gotos, this should be in program memory below 0xFF ;See Application Note AN556, example 5 ENDIF ;Given a key code (in ASCII), this returns the X-10 code TW_TO_XKEY MOVWF TW_MATCH ; match = w; CLRF TW_INDEX ; index = 0; TW_TEST_KEY ; while { MOVFW TW_INDEX CALL TW_TO_KEY ; if (w == match) { SUBWF TW_MATCH,W SKPZ GOTO TW_RETRY_KEY MOVFW TW_INDEX ; return index RETURN TW_RETRY_KEY ; } else { MOVLW D'32' ; if (index < 32) { SUBWF TW_INDEX,W SKPNC GOTO TW_NONE_KEY ; goto none_found INCF TW_INDEX,F ; index ++; GOTO TW_TEST_KEY ; } TW_NONE_KEY ; none_found RETLW H'80' ; return h'80' ;Given a house code (in ASCII), this returns the X-10 code TW_TO_XHOUSE MOVWF TW_MATCH ; match = w; CLRF TW_INDEX ; index = 0; TW_TEST ; while { MOVFW TW_INDEX CALL TW_TO_HOUSE ; if (w == match) { SUBWF TW_MATCH,W SKPZ GOTO TW_RETRY MOVFW TW_INDEX ; return index RETURN TW_RETRY ; } else { MOVLW D'16' ; if (index < 16) { SUBWF TW_INDEX,W SKPNC GOTO TW_NONE_FOUND ; goto none_found INCF TW_INDEX,F ; index ++; GOTO TW_TEST ; } TW_NONE_FOUND ; none_found RETLW H'80' ; return h'80' ;Initialize tw523 stuff TW_INIT ;tw_init() { ;Ports PAGE_1 BSF TW_PORT,TW_60 ; 1 IS INPUT BSF TW_PORT,TW_FROM; 0 IS OUTPUT BCF TW_PORT,TW_TO PAGE_0 CLRF TW_FLAGS ; tw_flags = 0; BCF TW_FLAGS,TW_PREV BTFSC TW_PORT,TW_60 ; tw_prev = input(tw_port,tw_60); BSF TW_FLAGS,TW_PREV CLRF TW_O_PHASE ; tw_o_phase = 0; CLRF TW_O_HOUSE ; tw_o_house = 0; CLRF TW_O_KEY ; tw_o_key = 0; ;Reset variables for start of x10 reception TW_RESET CLRF TW_SAMPLE ; tw_sample = 0; CLRF TW_PHASE ; tw_phase = 0; CLRF TW_HOUSE ; tw_house = 0; CLRF TW_KEY ; tw_key = 0; RETURN ;} ;Get data from X10 interface. New_tw() gets called if a valid ;message is received TW_GET ;tw_get() { TSTF TW_SAMPLE ; if (tw_sample == 0) { SKPZ GOTO TW_SAMPLE_DATA ; //check zero crossing BTFSS TW_PORT,TW_60 ; if (input(tw_port,tw_60) == 1) { GOTO TW_60_LO BTFSC TW_FLAGS,TW_PREV; if (tw_prev == 0) { RETURN MOVLW TW_05 ; tw_sample = tw_05; MOVWF TW_SAMPLE BSF TW_FLAGS,TW_PREV; tw_prev = 1; RETURN ; } TW_60_LO ; } else { BTFSS TW_FLAGS,TW_PREV; if (tw_prev == 1) { RETURN MOVLW TW_05 ; tw_sample = tw_05; MOVWF TW_SAMPLE BCF TW_FLAGS,TW_PREV; tw_prev = 0; RETURN ; } TW_SAMPLE_DATA ; } MOVLW D'1' ; } else if (tw_sample == 1) { SUBWF TW_SAMPLE,W SKPZ GOTO TW_WAIT ; // sample data CLRF TW_SAMPLE ; tw_sample = 0; BSF TW_FLAGS,TW_CARRIER BTFSC TW_PORT,TW_FROM; tw_carrier = ~input(tw_port,tw_from); BCF TW_FLAGS,TW_CARRIER MOVLW D'12' ; if (tw_phase >= 12) { SUBWF TW_PHASE,W SKPC GOTO TW_HOUSECODE ; // sample key code INCF TW_PHASE,F ; tw_phase ++; BTFSS TW_PHASE,W ; if (tw_phase,W == 1) { GOTO TW_KEY_HALF CLRC ; // first half bit RRF TW_KEY,F ; tw_key >> BTFSC TW_FLAGS,TW_CARRIER ; if (tw_carrier = 1) BSF TW_KEY,4 ; set tw_key,4; GOTO TW_SAMPLE_END TW_KEY_HALF ; } else { ; // second half bit BTFSS TW_FLAGS,TW_CARRIER ; if (tw_carrier == 1) { GOTO TW_KEY_ELSE BTFSC TW_KEY,4 ; if (tw_key,4 != 0) CALL TW_RESET ; tw_reset; GOTO TW_SAMPLE_END TW_KEY_ELSE ; } else { BTFSS TW_KEY,4 ; if (tw_key,4 != 1) CALL TW_RESET ; tw_reset; GOTO TW_SAMPLE_END ; } TW_HOUSECODE ; } MOVLW D'4' ; } else if (tw_phase >= 4) { SUBWF TW_PHASE,W ; // sample house code SKPC GOTO TW_SYNC_B INCF TW_PHASE,F ; tw_phase ++; BTFSS TW_PHASE,W ; if (tw_phase,W == 1) { GOTO TW_HOUSE_HALF CLRC ; // first half bit RRF TW_HOUSE,F ; tw_house >> BTFSC TW_FLAGS,TW_CARRIER; if (tw_carrier = 1) BSF TW_HOUSE,3 ; set tw_house,3; GOTO TW_SAMPLE_END TW_HOUSE_HALF ; } else { ; // second half bit BTFSS TW_FLAGS,TW_CARRIER ; if (tw_carrier == 1) { GOTO TW_HOUSE_ELSE BTFSC TW_HOUSE,3 ; if (tw_house,3 != 0) CALL TW_RESET ; tw_reset; GOTO TW_SAMPLE_END TW_HOUSE_ELSE ; } else { BTFSS TW_HOUSE,3 ; if (tw_house,3 != 1) CALL TW_RESET ; tw_reset; GOTO TW_SAMPLE_END ; } TW_SYNC_B MOVLW D'3' ; } else if tw_phase == 3) { SUBWF TW_PHASE,W SKPZ GOTO TW_SYNC_A INCF TW_PHASE,F ; tw_phase ++; BTFSC TW_FLAGS,TW_CARRIER; if (tw_carrier == 1) CLRF TW_PHASE ; tw_phase = 0; GOTO TW_SAMPLE_END ; } TW_SYNC_A ; } else { INCF TW_PHASE,F ; tw_phase ++; BTFSS TW_FLAGS,TW_CARRIER; if (tw_carrier == 0) CLRF TW_PHASE ; tw_phase = 0; GOTO TW_SAMPLE_END TW_SAMPLE_END ; } MOVLW D'22' ; if (tw_phase == 22) { SUBWF TW_PHASE,W SKPZ RETURN CALL TW_NEW ; new_tw(); CALL TW_RESET ; tw_reset(); RETURN ; } TW_WAIT ; } else { ; wait until sample time DECF TW_SAMPLE,F ; tw_sample --; ; } RETURN ;} ;trigger send. Does not restore W register TW_SEND ;tw_send() { ;Store data in converted form MOVFW TW_T_HOUSE ; tw_t_house = converted(tw_t_house); CALL TW_TO_XHOUSE MOVWF TW_T_HOUSE BTFSC TW_T_HOUSE,7 ; if (tw_t_house,7 == 1) RETURN ; return; // invalid command MOVFW TW_T_KEY ; tw_t_key = converted(tw_t_key); CALL TW_TO_XKEY MOVWF TW_T_KEY BTFSC TW_T_KEY,7 ; if (tw_t_key,7 == 1) RETURN ; return; // invalid command ;Wait till no transmissions TW_SEND_WAIT TSTF TW_O_PHASE ; while (tw_o_phase != 0) {} SKPZ GOTO TW_SEND_WAIT ;Put data into transmit queue MOVFW TW_T_HOUSE ; tw_o_house = tw_t_house; MOVWF TW_O_HOUSE MOVFW TW_T_KEY ; tw_o_key = tw_t_key; MOVWF TW_O_KEY BSF TW_FLAGS,TW_FIRST; tw_first = 1; MOVLW D'1' ; tw_o_phase = 1; MOVWF TW_O_PHASE CLRF TW_O_SAMPLE ; tw_o_sample = 0; RETURN ;} ;Interrupt based X10 send function ;Call tw_get before tw_put to get zero crossing TW_PUT ;tw_put(){ TSTF TW_O_PHASE ; if (tw_o_phase == 0) { SKPZ ; // no active transmission GOTO TW_TEST_ZERO BCF TW_PORT,TW_TO ; output (tw_port,tw_to) = 0; RETURN ; return TW_TEST_ZERO MOVLW TW_05 ; } else if (tw_sample == tw_05) { SUBWF TW_SAMPLE,W ; // just had zero crossing SKPZ GOTO TW_ENDBIT TW_ZEROWAIT MOVLW H'55' ; if (tw_o_phase > 0x55) { SUBWF TW_O_PHASE,W SKPNC ; // wait the req'd # of zero crossings GOTO TW_STARTBIT ; // between transmissions TW_SYNC MOVLW D'4' ; } else if (tw_o_phase <= 4) { SUBWF TW_O_PHASE,W ; // send carrier high (sync begin) SKPNC GOTO TW_SECOND BSF TW_PORT,TW_TO ; output (tw_port,tw_to) = 1; BSF TW_FLAGS,TW_O_CARR; tw_o_carr == 1; //for 2nd bit GOTO TW_STARTBIT TW_SECOND BTFSC TW_O_PHASE,W ; } else if (tw_o_phase,W == 0) { GOTO TW_SENDKEY ; // send second half bit BTFSS TW_FLAGS,TW_O_CARR; if (tw_o_carr != 1) BSF TW_PORT,TW_TO ; output (tw_port,tw_to) = 1; GOTO TW_STARTBIT TW_SENDKEY MOVLW D'13' ; } else if (tw_o_phase >= 13) { SUBWF TW_O_PHASE,W ; // send key code SKPC GOTO TW_SENDHOUSE CLRC ; clear carry BCF TW_FLAGS,TW_O_CARR; tw_o_carr = 0; RRF TW_O_KEY,F ; tw_o_key >> SKPC ; if (carry == 1) { GOTO TW_STARTBIT BSF TW_PORT,TW_TO ; output (tw_port,tw_to) = 1; BSF TW_FLAGS,TW_O_CARR; tw_o_carr = 1; BSF TW_O_KEY,4 ; tw_o_key,4 = 1; GOTO TW_STARTBIT ; } TW_SENDHOUSE ; } else { ; // send house code CLRC ; clear carry BCF TW_FLAGS,TW_O_CARR; tw_o_carr = 0; RRF TW_O_HOUSE,F ; tw_o_house >> SKPC ; if (carry == 1) { GOTO TW_STARTBIT BSF TW_PORT,TW_TO ; output (tw_port,tw_to) = 1; BSF TW_FLAGS,TW_O_CARR; tw_o_carr = 1; BSF TW_O_HOUSE,3 ; tw_o_house,3 = 1; ; } TW_STARTBIT ; } INCF TW_O_PHASE,F ; tw_o_phase ++; MOVLW TW_10 ; tw_o_sample = tw_10; MOVWF TW_O_SAMPLE RETURN TW_ENDBIT MOVLW D'1' ; } else if (tw_o_sample == 1) { SUBWF TW_O_SAMPLE,W ; // send carr low, end of bit SKPZ GOTO TW_WAIT_SEND BCF TW_PORT,TW_TO ; output (tw_port,tw_to) = 0; CLRF TW_O_SAMPLE ; tw_o_sample = 0; MOVLW D'23' ; if (tw_o_phase == 23) { SUBWF TW_O_PHASE,W ; // end of tx ? SKPZ RETURN BTFSS TW_FLAGS,TW_FIRST; if (tw_first == 1) { GOTO TW_ENDELSE MOVLW D'1' ; tw_o_phase = 1; MOVWF TW_O_PHASE BCF TW_FLAGS,TW_FIRST; tw_first = 0; RETURN TW_ENDELSE ; } else MOVLW H'FB' ; //end transmission, setup wait time MOVWF TW_O_PHASE ; tw_o_phase = -5; RETURN ; } TW_WAIT_SEND MOVLW D'1' ; } else if (tw_o_sample >= 1) { SUBWF TW_O_SAMPLE,W ; // wait time till end of bit SKPNC DECF TW_O_SAMPLE,F ; tw_o_sample --; TW_SENDEND ; } RETURN ;} ENDIF ;TW_ENABLE ;***** A/D ROUTINES IF AD_ENABLE == TRUE ;A/D CHANNELS CH0 EQU 00H CH1 EQU 08H CH2 EQU 10H CH3 EQU 18H ;Select CHANNEL as the desired A/D input ;Usage: AD_SELECT CH0 AD_SELECT MACRO CHANNEL MOVLW B'11000001' ;use internal clock, ad on IORLW CHANNEL ;program channel MOVWF ADCON0 ;setup ad BCF INTCON,ADIE ;disable A/D interrupt ENDM ;Read the currently selected A/D input into W AD_READ BSF ADCON0,2 ;start conversion AD_TEST BTFSC ADCON0,2 ;test ad done GOTO AD_TEST ;test again MOVF ADRES,W ;put result in W RETURN ENDIF ;AD_ENABLE ;***** RS232 ROUTINES ;Default parameters are no parity, eight bits, one stop bit IF R2_ENABLE == TRUE ;The quiescent state of the line is '1'. A start bit is '0', ;and the data bits follow uninverted. The stop bit is a '1'. ;Constants R2_BAUD EQU RUNSEC/D'2400' IF (RUNSEC != R2_BAUD*D'2400') CALL INTSEC_ERROR ;R2_BAUD must be a whole number - adjust INTSEC or ;the number of modules in use ENDIF R2_PORT EQU PORTB R2_IN EQU 06H R2_OUT EQU 07H R2_DUPLEX EQU TRUE ;True for regular rs232 ;Moves 'index' to next ;int advance_ptr(int *index) ;element in ring buffer ;{ R2_ADV_PTR MACRO INDEX LOCAL R2_END_ADV INCF INDEX,F ; index++ MOVF INDEX,W ; if (index > last_buffer) SUBLW R2_LAST_BUF SKPNC GOTO R2_END_ADV MOVLW R2_FIRST_BUF; index = first_buffer MOVWF INDEX R2_END_ADV ; return index ENDM ;} ;Inits for RS232 serial routines R2_INIT ;serial ring buffer and status variables MOVLW R2_FIRST_BUF ;out_ptr = first_buf; MOVWF R2_OUT_PTR MOVLW R2_FIRST_BUF ;in_ptr = first_buf; MOVWF R2_IN_PTR CLRF R2_OUT_BIT;out_bit = 0; CLRF R2_IN_BIT ;in_bit = 0; MOVLW H'1' ;out_timer = 1; MOVWF R2_OUT_TIMR ;setup serial port pins BSF R2_PORT,R2_OUT ;outp(1); PAGE_1 BSF TRISB^H'80',R2_IN ;1 is input BCF TRISB^H'80',R2_OUT ;0 is output PAGE_0 RETURN ;Serial output routines ;void serial_out() R2_SER_OUT ;{ TSTF R2_OUT_BIT ; if (out_bit == 0) { SKPZ GOTO R2_TIMER ; // idle, not sending IF R2_DUPLEX == FALSE ; // check if currently reading byte TSTF R2_IN_BIT ; if ((in_bit != 0)&&(r2_duplex == false)) SKPZ RETURN ; return; ENDIF ;R2_DUPLEX MOVF R2_OUT_PTR,W ; if (out_ptr != in_ptr) { SUBWF R2_IN_PTR,W SKPNZ RETURN ; // send next byte MOVLW D'1' ; out_bit = 1; MOVWF R2_OUT_BIT R2_ADV_PTR R2_OUT_PTR ; advance_ptr(out_ptr); RETURN ; } R2_TIMER ; } else { DECF R2_OUT_TIMR,F ; out_timer--; MOVFW R2_OUT_TIMR ; if (out_timer <= 0) { SUBLW D'0' SKPC RETURN MOVLW R2_BAUD ; out_timer = rn_baud; MOVWF R2_OUT_TIMR MOVFW R2_OUT_BIT ; if (out_bit == 1) { SUBLW D'1' SKPZ GOTO R2_TEST_1TO8 ; // start bit BCF R2_PORT,R2_OUT ;!0 outp(0); INCF R2_OUT_BIT,F ; out_bit++ RETURN R2_TEST_1TO8 MOVF R2_OUT_BIT,W ; } else if (out_bit <= 9) { SUBLW D'9' SKPC GOTO R2_STOP ; // send bit MOVF R2_OUT_PTR,W ; if (ring_buffer[out_ptr]&&0x01) MOVWF FSR BTFSC INDF,W BSF R2_PORT,R2_OUT ;!1 outp(1); BTFSS INDF,W ; else BCF R2_PORT,R2_OUT ;!0 outp(0); RRF INDF,F ; ring_buffer[out_ptr] = ring_buffer[out_ptr] >> 1; INCF R2_OUT_BIT,F ; out_bit++ RETURN R2_STOP MOVF R2_OUT_BIT,W ; } else if (out_bit <= 10) SUBLW D'10' SKPC GOTO R2_DONE ; // stop bit BSF R2_PORT,R2_OUT ;!1 outp(1); INCF R2_OUT_BIT,F ; out_bit++; RETURN R2_DONE ; } else { ; // done sending CLRF R2_OUT_BIT ; out_bit = 0; ; } ; } ; } RETURN ;} ;Send byte in W ;void byte_send(int data) R2_SEND ;{ MOVWF SCRATCH_1 ; SCRATCH_1 = W; R2_WHILE_SEND ; while (advance_ptr(in_ptr) == out_ptr) {} MOVF R2_IN_PTR,W ; // wait while buffer full MOVWF SCRATCH_2 R2_ADV_PTR SCRATCH_2 MOVF SCRATCH_2,W SUBWF R2_OUT_PTR,W SKPNZ GOTO R2_WHILE_SEND R2_ADV_PTR R2_IN_PTR MOVF R2_IN_PTR,W ; ring_buffer[in_ptr] = SCRATCH_1; MOVWF FSR MOVF SCRATCH_1,W ; W = SCRATCH_1; MOVWF INDF RETURN ;} ;Check serial input ;void ser_in(void) { R2_SER_IN ;{ MOVF R2_IN_BIT,W; if (in_bit == 0) { SKPZ GOTO R2_BUSY_IN ; // not currently receiving IF R2_DUPLEX == FALSE ; // check if currently sending byte TSTF R2_OUT_BIT; if ((out_bit != 0)&&(r2_duplex == false)) SKPZ RETURN ; return; ENDIF ;R2_DUPLEX BTFSC R2_PORT,R2_IN; if (inp == 1) { RETURN ; // start bit detected MOVLW R2_BAUD ; in_timer = r2_baud MOVWF R2_IN_TIMER MOVLW H'1' ; in_bit = 1 MOVWF R2_IN_BIT CLRF R2_IN_BYTE; in_byte = 0 RETURN ; } R2_BUSY_IN ; } else { // busy reading input DECF R2_IN_TIMER,F; in_timer -- MOVF R2_IN_TIMER,W; if (in_timer == 0) { SKPZ RETURN ; // sample input line MOVLW R2_BAUD ; in_timer = r2_baud MOVWF R2_IN_TIMER MOVF R2_IN_BIT,W; if (in_bit == 9) { SUBLW H'9' SKPZ GOTO R2_TEST ; // done sampling CLRF R2_IN_BIT ; in_bit = 0 MOVF R2_IN_BYTE,W CALL R2_NEW_BYTE ; new_byte() RETURN R2_TEST ; } else // sample byte CLRC ; clear carry BTFSC R2_PORT,R2_IN; if (inp == 1) SETC ; set carry RRF R2_IN_BYTE,F; >> data INCF R2_IN_BIT,F; in_bit ++ ; } ; } ; } RETURN ;} ;Gets called when there is a new byte from rs232 serial line ;R2_NEW_BYTE ; CALL R2_SEND ;echo to serial out ; CALL LCD_PRINT ;display ; RETURN ELSE ;R2_ENABLE R2_SEND ;dummies if module not enabled RETURN ENDIF ;R2_ENABLE ;***** RS422/485 ROUTINES ;Default parameters are no parity, eight bits, one stop bit IF R4_ENABLE == TRUE ;The quiescent state of the line is '1'. A start bit is '0', ;and the data bits follow uninverted. The stop bit is a '1'. ;Constants R4_BAUD EQU RUNSEC/D'9600' IF (RUNSEC != R4_BAUD*D'2400') CALL INTSEC_ERROR ;R4_BAUD must be a whole number - adjust INTSEC or ;adjust the number of modules in use ENDIF R4_PORT EQU PORTB R4_IN EQU 06H ;Data input R4_OUT EQU 07H ;Data output R4_TRANON EQU 05H ;Transmit enable line (for RS485 only) R4_DUPLEX EQU TRUE ;True for RS422, False for RS485 ;Moves 'index' to next ;int advance_ptr(int *index) ;element in ring buffer ;{ R4_ADV_PTR MACRO INDEX LOCAL R4_END_ADV INCF INDEX,F ; index++ MOVF INDEX,W ; if (index > last_buffer) SUBLW R4_LAST_BUF SKPNC GOTO R4_END_ADV MOVLW R4_FIRST_BUF ; index = first_buffer MOVWF INDEX R4_END_ADV ; return index ENDM ;} ;Inits for RS422/485 serial routines R4_INIT ;serial ring buffer and status variables MOVLW R4_FIRST_BUF ;out_ptr = first_buf; MOVWF R4_OUT_PTR MOVLW R4_FIRST_BUF ;in_ptr = first_buf; MOVWF R4_IN_PTR CLRF R4_OUT_BIT;out_bit = 0; CLRF R4_IN_BIT ;in_bit = 0; MOVLW H'1' ;out_timer = 1; MOVWF R4_OUT_TIMR ;setup serial port pins BSF R4_PORT,R4_OUT ;outp(1); PAGE_1 BSF TRISB^H'80',R4_IN ;1 is input BCF TRISB^H'80',R4_OUT ;0 is output PAGE_0 RETURN ;Serial output routines ;void serial_out() R4_SER_OUT ;{ TSTF R4_OUT_BIT ; if (out_bit == 0) { SKPZ GOTO R4_TIMER ; // idle, not sending IF R4_DUPLEX == FALSE ; // check if currently reading byte TSTF R4_IN_BIT ; if ((in_bit != 0)&&(r2_duplex == false)) SKPZ RETURN ; return; ENDIF ;R4_DUPLEX MOVF R4_OUT_PTR,W ; if (out_ptr != in_ptr) { SUBWF R4_IN_PTR,W SKPNZ RETURN ; // send next byte MOVLW D'1' ; out_bit = 1; MOVWF R4_OUT_BIT R4_ADV_PTR R4_OUT_PTR ; advance_ptr(out_ptr); RETURN ; } R4_TIMER ; } else { DECF R4_OUT_TIMR,F ; out_timer--; MOVFW R4_OUT_TIMR ; if (out_timer <= 0) { SUBLW D'0' SKPC RETURN MOVLW R4_BAUD ; out_timer = rn_baud; MOVWF R4_OUT_TIMR MOVFW R4_OUT_BIT ; if (out_bit == 1) { SUBLW D'1' SKPZ GOTO R4_TEST_1TO8 ; // start bit IF R4_DUPLEX == FALSE ; // set enable if needed BSF R4_PORT,R4_TRANON; tx_enable = 1; ENDIF ;R4_DUPLEX BCF R4_PORT,R4_OUT ;!0 outp(0); INCF R4_OUT_BIT,F ; out_bit++ RETURN R4_TEST_1TO8 MOVF R4_OUT_BIT,W ; } else if (out_bit <= 9) { SUBLW D'9' SKPC GOTO R4_STOP ; // send bit MOVF R4_OUT_PTR,W ; if (ring_buffer[out_ptr]&&0x01) MOVWF FSR BTFSC INDF,W BSF R4_PORT,R4_OUT ;!1 outp(1); BTFSS INDF,W ; else BCF R4_PORT,R4_OUT ;!0 outp(0); RRF INDF,F ; ring_buffer[out_ptr] = ring_buffer[out_ptr] >> 1; INCF R4_OUT_BIT,F ; out_bit++ RETURN R4_STOP MOVF R4_OUT_BIT,W ; } else if (out_bit <= 10) SUBLW D'10' SKPC GOTO R4_DONE ; // stop bit BSF R4_PORT,R4_OUT ;!1 outp(1); INCF R4_OUT_BIT,F ; out_bit++; RETURN R4_DONE ; } else { ; // done sending CLRF R4_OUT_BIT ; out_bit = 0; IF R4_DUPLEX == FALSE ; // clear enable if needed BCF R4_PORT,R4_TRANON; tx_enable = 0; ENDIF ;R4_DUPLEX ; } ; } ; } RETURN ;} ;Send byte in W ;void byte_send(int data) R4_SEND ;{ MOVWF SCRATCH_1 ; SCRATCH_1 = W; R4_WHILE_SEND ; while (advance_ptr(in_ptr) == out_ptr) {} MOVF R4_IN_PTR,W ; // wait while buffer full MOVWF SCRATCH_2 R4_ADV_PTR SCRATCH_2 MOVF SCRATCH_2,W SUBWF R4_OUT_PTR,W SKPNZ GOTO R4_WHILE_SEND R4_ADV_PTR R4_IN_PTR MOVF R4_IN_PTR,W ; ring_buffer[in_ptr] = SCRATCH_1; MOVWF FSR MOVF SCRATCH_1,W ; W = SCRATCH_1; MOVWF INDF RETURN ;} ;Check serial input ;void ser_in(void) { R4_SER_IN ;{ MOVF R4_IN_BIT,W; if (in_bit == 0) { SKPZ GOTO R4_BUSY_IN ; // not currently receiving IF R4_DUPLEX == FALSE ; // check if currently sending byte TSTF R4_OUT_BIT; if ((out_bit != 0)&&(r2_duplex == false)) SKPZ RETURN ; return; ENDIF ;R4_DUPLEX BTFSC R4_PORT,R4_IN; if (inp == 1) { RETURN ; // start bit detected MOVLW R4_BAUD ; in_timer = r2_baud MOVWF R4_IN_TIMER MOVLW H'1' ; in_bit = 1 MOVWF R4_IN_BIT CLRF R4_IN_BYTE; in_byte = 0 RETURN ; } R4_BUSY_IN ; } else { // busy reading input DECF R4_IN_TIMER,F; in_timer -- MOVF R4_IN_TIMER,W; if (in_timer == 0) { SKPZ RETURN ; // sample input line MOVLW R4_BAUD ; in_timer = r2_baud MOVWF R4_IN_TIMER MOVF R4_IN_BIT,W; if (in_bit == 9) { SUBLW H'9' SKPZ GOTO R4_TEST ; // done sampling CLRF R4_IN_BIT ; in_bit = 0 MOVF R4_IN_BYTE,W CALL R4_NEW_BYTE ; new_byte() RETURN R4_TEST ; } else // sample byte CLRC ; clear carry BTFSC R4_PORT,R4_IN; if (inp == 1) SETC ; set carry RRF R4_IN_BYTE,F; >> data INCF R4_IN_BIT,F; in_bit ++ ; } ; } ; } RETURN ;} ;Gets called when there is a new byte ;from serial line R4_NEW_BYTE CALL R4_SEND ;echo to serial out CALL LCD_PRINT ;display RETURN ELSE ;R4_ENABLE R4_SEND ;dummies if module not enabled RETURN ENDIF ;R4_ENABLE ;***** LCD ROUTINES IF LCD_ENABLE == TRUE ;Constants ; Connections for LCD: LCD_PORT EQU PORTB ;data is on lower nibble of this port LCD_CNTRL EQU PORTB ;control pins are on this port LCD_E EQU 04H ;Pin for Enable LCD_RS EQU 05H ;Pin for Register Select ; LCD_RW make sure this is grounded LCD_I_DELAY EQU 03H ;Delay time for LCD during init process LCD_T_DELAY EQU 01H ;Delay time for LCD between characters ;Initialize lcd port. This cannot be interrupted. ;Make sure General Interrupt Enable is clear. LCD_INIT ;Setup Port direction PAGE_1 BCF LCD_PORT,W ; 1 IS INPUT BCF LCD_PORT,F ; 0 IS OUTPUT BCF LCD_PORT,2 BCF LCD_PORT,3 BCF LCD_CNTRL,LCD_E BCF LCD_CNTRL,LCD_RS PAGE_0 BSF LCD_CNTRL,LCD_E ; E BCF LCD_CNTRL,LCD_RS ; RS ;Init LCD MOVLW B'00000011' ; 1 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000011' ; 2 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000011' ; 3 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000010' ; 4 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000010' ; 5 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000100' ; 5B system set ; 0000 also works CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000000' ; 6 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00001000' ; 6B CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000000' ; 7 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000001' ; 7B CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000000' ; 8 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00000110' ; 8B entry mode set CALL LCD_NIBBLE ; 101 cursor stays put, screen scrolls right MOVLW LCD_I_DELAY CALL LCD_DELAY ; 111 cursor stays put, screen scrolls left ; 110 cursor moves right, screen stays put ; 100 same as 111 MOVLW B'00000000' ; 9 CALL LCD_NIBBLE MOVLW LCD_I_DELAY CALL LCD_DELAY MOVLW B'00001101' ; 9B 1111 cursor on and blink CALL LCD_NIBBLE ; 1101 cursor on and blink MOVLW LCD_I_DELAY CALL LCD_DELAY ; 1110 cursor off ; 1100 cursor off RETURN ;Send command/data byte to lcd port LCD_PRINT ;lcd_print(W) MOVWF SCRATCH_1 ;SCRATCH_1 = W; ;Check if cursor position command ANDLW H'F0' ;if (msb == 1) { SUBLW H'10' SKPZ GOTO LCD_CHAR MOVFW SCRATCH_1 ; position cursor ANDLW H'0F' IORLW H'80' CALL LCD_INSTRUCT MOVFW SCRATCH_1 ; W = SCRATCH_1; RETURN ;} else { LCD_CHAR ;display character BSF LCD_CNTRL,LCD_RS ;data SWAPF SCRATCH_1,W ; get upper nibble CALL LCD_NIBBLE MOVFW SCRATCH_1 ; get lower nibble CALL LCD_NIBBLE MOVLW LCD_T_DELAY CALL LCD_DELAY MOVFW SCRATCH_1 ; W = SCRATCH_1; RETURN ;} ;Send instruction byte to lcd port LCD_INSTRUCT BCF LCD_CNTRL,LCD_RS ;instruction MOVWF SCRATCH_1 ;store byte SWAPF SCRATCH_1,W ;get upper nibble CALL LCD_NIBBLE MOVFW SCRATCH_1 ;get lower nibble CALL LCD_NIBBLE MOVFW SCRATCH_1 ;restore W RETURN ;Send nibble to lcd port LCD_NIBBLE BSF LCD_CNTRL,LCD_E ; latch control ANDLW 0FH MOVWF SCRATCH_2 MOVFW LCD_PORT ANDLW H'F0' IORWF SCRATCH_2,W MOVWF LCD_PORT MOVWF LCD_PORT ; extra delay BCF LCD_CNTRL,LCD_E ; latch data RETURN ;Delay time for lcd, delay constant in W register. 1 = minimum delay LCD_DELAY MOVWF TIMER_HI ; Use TIMER_HI and TIMER_LO CLRF TIMER_LO LCD_TIME_LOOP DECFSZ TIMER_LO,F; Delay time = TIMER_HI * ((3 * 256) + 3) * Tcy GOTO LCD_TIME_LOOP DECFSZ TIMER_HI,F GOTO LCD_TIME_LOOP RETURN ;Print number in W to LCD as three digit BCD LCD_BCD MOVWF SCRATCH_1 MOVWF LSD CLRF MSD MOVLW .200 SUBWF LSD,W SKPC GOTO LCD_BIN_1 MOVWF LSD ;save number<100 MOVLW '2' CALL LCD_PRINT ;print 100s GOTO LCD_TWO_DIGIT LCD_BIN_1 MOVLW .100 SUBWF LSD,W SKPC GOTO LCD_BIN_0 MOVWF LSD ;save number<100 MOVLW '1' CALL LCD_PRINT ;print 100s GOTO LCD_TWO_DIGIT LCD_BIN_0 MOVLW ' ' CALL LCD_PRINT ;print 100s LCD_TWO_DIGIT MOVLW .10 ;check how many 10s SUBWF LSD,W ; in the input SKPC GOTO LCD_DIGITS ;done MOVWF LSD ;move 10 from LSD INCF MSD,F ; to MSD GOTO LCD_TWO_DIGIT LCD_DIGITS MOVFW MSD ADDLW '0' CALL LCD_PRINT MOVFW LSD ADDLW '0' CALL LCD_PRINT MOVFW SCRATCH_1 RETURN ELSE ;LCD_ENABLE LCD_PRINT ;dummies if lcd module not enabled RETURN LCD_BCD RETURN ENDIF ;LCD_ENABLE ;***** INFRA RED ROUTINES IF IR_ENABLE == TRUE ;Constants IR_PORT EQU PORTA ;Port and pins IR_IN EQU 3H ;for IR port IR_OUT EQU 4H ;See GEN_INIT for more IR_DWELL EQU H'FF' ;longest delay IR_30 EQU (RUNSEC * D'30')/D'10000' IR_24 EQU (RUNSEC * D'24')/D'10000' IR_18 EQU (RUNSEC * D'18')/D'10000' IR_12 EQU (RUNSEC * D'12')/D'10000' IR_10 EQU (RUNSEC * D'10')/D'10000' IR_09 EQU (RUNSEC * D'09')/D'10000' IR_06 EQU (RUNSEC * D'06')/D'10000' IR_03 EQU (RUNSEC * D'03')/D'10000' IR_DEBUG EQU FALSE IR_INIT ;Registers CLRF IR_DEV ;ir_dev = 0; CLRF IR_DATA ;ir_data = 0; CLRF IR_PHASE ;ir_phase = 0; BSF IR_PHASE,7 ;ir_phase,7 = 1; MOVLW IR_10 ;ir_timer = IR_10; MOVWF IR_TIMER CLRF IR_O_DEV ;ir_o_dev = 0; CLRF IR_O_DATA ;ir_o_data = 0; CLRF IR_O_PHASE ;ir_o_phase = 0; CLRF IR_O_TIMER ;ir_o_timer = 0; CLRF IR_O_COUNT ;ir_o_count = 0; ;IO Port. See GEN_INIT for background operation of Port PAGE_1 BSF IR_PORT,IR_IN BCF IR_PORT,IR_OUT PAGE_0 BCF IR_PORT,IR_OUT ;output(0); RETURN ;Increment 'ir_timer' until 0xFF IR_INC_COUNT MACRO MOVLW H'FF' ;if(ir_timer != 0xff) SUBWF IR_TIMER,W SKPZ INCF IR_TIMER,F ; ir_timer++; ENDM ;Decode length of pulse IR_BIT MACRO MOVLW IR_30 ;if (ir_timer > IR_30) { SUBWF IR_TIMER,W ; // false trigger SKPC GOTO IR_ELSE_SY CLRF IR_PHASE ; ir_phase = 0; IF IR_DEBUG == TRUE MOVLW 'L' CALL LCD_PRINT ENDIF GOTO IR_ELSE_END IR_ELSE_SY MOVLW IR_18 ;} else if (ir_timer > IR_18) { SUBWF IR_TIMER,W ; // sync pulse is 2.4 msec nominal SKPC GOTO IR_ELSE_LONG CLRF IR_DEV ; ir_dev = 0; CLRF IR_DATA ; ir_data = 0; MOVLW D'1' ; ir_phase = 1; MOVWF IR_PHASE IF IR_DEBUG == TRUE MOVLW 'S' CALL LCD_PRINT ENDIF GOTO IR_ELSE_END IR_ELSE_LONG MOVLW IR_09 ;} else if (ir_timer > IR_09) { SUBWF IR_TIMER,W ; // long hi, logic 1 SKPC GOTO IR_ELSE_SHORT ;Store '1' MOVLW D'8' ; if (ir_phase > 8) { SUBWF IR_PHASE,W SKPC GOTO IR_1_ELSE SETC ; carry set RRF IR_DEV,F ; ir_dev >> GOTO IR_1_END ; } IR_1_ELSE MOVLW D'0' ; else if (ir_phase > 0) { SUBWF IR_PHASE,W SKPC GOTO IR_1_END BSF IR_DATA,7 ; ir_data,7 = 1 CLRC RRF IR_DATA,F IR_1_END ; } INCF IR_PHASE,F ; ir_phase++; IF IR_DEBUG == TRUE MOVLW '1' CALL LCD_PRINT ENDIF GOTO IR_ELSE_END IR_ELSE_SHORT MOVLW IR_03 ;} else if (ir_timer > IR_03) { SUBWF IR_TIMER,W ; // short hi, logic 0 SKPC GOTO IR_ELSE_BAD ;Store '0' MOVLW D'8' ; if (ir_phase > 8) { SUBWF IR_PHASE,W SKPC GOTO IR_0_ELSE CLRC RRF IR_DEV,F ; ir_dev >> with carry clear GOTO IR_0_END ; } IR_0_ELSE MOVLW D'0' ; else if (ir_phase > 0) { SUBWF IR_PHASE,W SKPC GOTO IR_0_END CLRC ; ir_data >> with carry clear RRF IR_DATA,F IR_0_END ; } INCF IR_PHASE,F ; ir_phase++; IF IR_DEBUG == TRUE MOVLW '0' CALL LCD_PRINT ENDIF GOTO IR_ELSE_END IR_ELSE_BAD ;} else { // false trigger CLRF IR_PHASE ; ir_phase = 0; IF IR_DEBUG == TRUE MOVLW 'B' CALL LCD_PRINT ENDIF IR_ELSE_END ;} ENDM ;Long IR off elapsed, check for valid stream IR_CHECK_NEW MACRO MOVLW IR_06 ;if (ir_timer > IR_06) { SUBWF IR_TIMER,W SKPC GOTO IR_C_END MOVLW H'90' ; if (ir_phase == 16) { SUBWF IR_PHASE,W ; // 8 bit device code SKPZ GOTO IR_C_ELSE ; // no further processing needed CALL IR_NEW ; new_ir(); GOTO IR_C_CLEAN IR_C_ELSE MOVLW H'8D' ; else if (ir_phase == 12) SUBWF IR_PHASE,W ; // 5 bit device code SKPZ GOTO IR_C_CLEAN ; // justify properly CLRC ; ir_dev >> 3 RRF IR_DEV,F RRF IR_DEV,F RRF IR_DEV,F CALL IR_NEW ; new_ir(); IR_C_CLEAN ; } MOVLW H'80' MOVWF IR_PHASE ; ir_phase = 0; IR_C_END ENDM ;} ;Sample IR IR_GET BTFSS IR_PORT,IR_IN ;if (ir_input == 1) GOTO IR_ON ;{ // ir off BTFSC IR_PHASE,7 ; if (ir_phase,7 = 0) GOTO IR_WAS_OFF ; { // was on before IR_BIT ; check_bit(ir_timer); BSF IR_PHASE,7 ; ir_phase,7 = 1; CLRF IR_TIMER ; ir_timer = 0; IR_WAS_OFF ; } IR_CHECK_NEW ; check_new(); IR_INC_COUNT ; increment ir_timer if needed GOTO IR_END ;} else IR_ON ;{ // ir on BTFSS IR_PHASE,7 ; if (ir_phase,7 = 1) GOTO IR_WAS_ON ; { // was off before BCF IR_PHASE,7 ; ir_phase,7 = 0; CLRF IR_TIMER ; ir_timer = 0; IR_WAS_ON ; } IR_INC_COUNT ; increment ir_timer if needed IR_END ;} RETURN ;Act on new IR command. Device is in IR_DEV and data is in IR_DATA IR_NEW MOVLW 'D' CALL LCD_PRINT MOVLW '=' CALL LCD_PRINT MOVFW IR_DEV CALL LCD_BCD MOVLW ' ' CALL LCD_PRINT MOVLW 'C' CALL LCD_PRINT MOVLW '=' CALL LCD_PRINT MOVFW IR_DATA CALL LCD_BCD MOVLW H'10' CALL LCD_PRINT RETURN ;Send data in ir_dev and ir_data. Does not restore W register IR_SEND ;// wait until interrupt routine is done TSTF IR_O_PHASE ;while (ir_o_phase != 0) {//wait} SKPZ GOTO IR_SEND TSTF IR_O_TIMER ;while (ir_o_timer != 0) {//wait} SKPZ GOTO IR_SEND TSTF IR_O_COUNT ;while (ir_o_count != 0) {//wait} SKPZ GOTO IR_SEND MOVLW D'2' ;// reload for new tx MOVWF IR_O_COUNT ;ir_o_count = 2; // send twice RETURN ;Use interrupts to transmit IR command IR_PUT MOVLW D'1' ;if (ir_o_timer >= 1) { SUBWF IR_O_TIMER,W ; // wait time in effect SKPC GOTO IR_QUIET DECF IR_O_TIMER,F ; ir_o_timer --; GOTO IR_O_END IR_QUIET ;} else if (ir_o_phase == 0) { TSTF IR_O_PHASE ; // done transmitting packet SKPZ GOTO IR_SYNC ; // check if any more DEBUG_IR BCF IR_PORT,IR_OUT ; output(0); MOVLW D'1' ; if ( ir_o_count >= 1 ) { SUBWF IR_O_COUNT,W SKPC GOTO IR_O_END ; // reload for new tx DECF IR_O_COUNT,F ; ir_t_cout --; MOVFW IR_T_DEV ; ir_o_dev = ir_t_dev; MOVWF IR_O_DEV MOVFW IR_T_DATA ; ir_o_data = ir_t_data; MOVWF IR_O_DATA MOVLW D'1' ; ir_o_phase = 1; MOVWF IR_O_PHASE GOTO IR_O_END ; } IR_SYNC ;} else if (ir_o_phase == 1) { MOVLW D'1' ; // send sync SUBWF IR_O_PHASE,W SKPZ GOTO IR_LOW INCF IR_O_PHASE,F ; ir_o_phase++; BSF IR_PORT,IR_OUT ; output(1); MOVLW IR_24 ; ir_o_timer = IR_24; MOVWF IR_O_TIMER GOTO IR_O_END IR_LOW ;} else if (ir_o_phase,W == 0) { BTFSC IR_O_PHASE,W ; // send low GOTO IR_HIGH ; // ir_phase == 2,4,6, etc. BCF IR_PORT,IR_OUT ; output(0); ;check if transmission over MOVLW D'26' ; if (ir_o_phase == 26) { SUBWF IR_O_PHASE,W SKPZ GOTO IR_DONE15 ; // setup to continue transmission INCF IR_O_PHASE,F ; ir_o_phase++; MOVLW IR_06 ; ir_o_timer = IR_06; TSTF IR_O_DEV ; if (ir_o_dev == 0) { // test if continue tx SKPZ GOTO IR_O_END CLRF IR_O_PHASE ; ir_o_phase = 0; // stop transmission MOVLW IR_DWELL ; ir_o_timer = IR_DWELL; // space out messages MOVWF IR_O_TIMER ; } GOTO IR_O_END ; } IR_DONE15 ; else if (ir_o_phase == 32) { MOVLW D'32' SUBWF IR_O_PHASE,W SKPZ GOTO IR_PROCEED CLRF IR_O_PHASE ; ir_o_phase = 0; // stop transmission MOVLW IR_24 ; ir_o_timer = IR_24; // space out messages MOVWF IR_O_TIMER GOTO IR_O_END IR_PROCEED ; } else { // continue transmission INCF IR_O_PHASE,F ; ir_o_phase++; MOVLW IR_06 ; ir_o_timer = IR_06; MOVWF IR_O_TIMER GOTO IR_O_END ; } IR_HIGH ;} else ;send high BSF IR_PORT,IR_OUT ; output(1); MOVLW IR_06 ; ir_o_timer = IR_06; MOVWF IR_O_TIMER MOVLW D'17' ; if (ir_o_phase >= 17) { SUBWF IR_O_PHASE,W SKPC GOTO IR_SEND_DEV ; // send device bit CLRC ; clear carry RRF IR_O_DEV,F ; ir_o_dev >> GOTO IR_SELECT_OK IR_SEND_DEV ; } else { ; // send data bit CLRC ; clear carry RRF IR_O_DATA,F ; ir_o_data >> IR_SELECT_OK ; } ;select length of pulse SKPC ; if (carry) { GOTO IR_OUTDONE ;output long high (1) MOVLW IR_12 ; ir_o_timer = IR_12; MOVWF IR_O_TIMER IR_OUTDONE ; } INCF IR_O_PHASE,F ; ir_o_phase++; IR_O_END ;} RETURN ENDIF ;IR_ENABLE ;***** END OF FILE *****