io serial x10 x10
Subject: here is the file for X10 (to put on your site)
Date: Friday, June 04, 1999 6:25 AM
OK....here is the rather unorganized, but in chronological order of the X10
stuff I have kept.
You might want to clean up the formatting some...?
I appreciate your offer to put on your site, as I know that many are not
interested and would
hate to have to download.
------------------------------------------ cut here
--------------------------------------------
I'm sure hoping code is available. This seems like such a natural
thing to do for a microcontroller project (given the prices of much
of the more versatile home automation stuff) that I hadn't really
considered the possibility that it hadn't been done yet.
A quick web search finds at least one page selling PIC X10 code
and power line interfaces:
http://users1.ee.net/web_surf/Mchip.htm
Also the PIC C compiler from CCS for the MicroChip PIC16 processors
includes software drivers for X10 (and a whole load of other stuff) for $99
http://ccsinfo.com/picc.html
So, apparently this is not an uncommon application. Anybody know of
any free X10 code or power line interfaces?
You may want to look at http://www.x10.com/products/x10_tw523.htm
The TW523 module connects to the wall - all you need to do is
provide
the data with the correct timing (TX and RX). This device frees
you from
having to deal with the AC line (hazardous). Somewhere on that
page there is
link to a PDF file that describes the device in detail, including
timing, etc.
As I recall, there may have also been a schematic.
Try using ST7537 or TDA5051 (better) instead of X10. It´s safer,
allows
bigger systems, and cheaper.
have now added a PIC resources page to my home page. Most of it is
directly related to home automation.
Look at http://www.xs4all.nl/~falstaff/ihome.html or
http://www.xs4all.nl/~falstaff/picres.html
The basic stamp 2 has built in BSR module controller commands, I See in
home automation catalogs a module with a 4 pin phone type connector made to
interface the signals to the power lines. look at
http://www.micromint.com/plix.html they sell a custom IC that does it all
for you....
don't know if this was mentioned before but a new protocol is
emerging called CEBus. This is supposed to handle things like home
automation using many transmission mediums including power lines. I
don't know if anything is available yet.
Try
http://www.intellon.com
http://www.cebus.org
http://www.hometoys.com
I have uploaded to SimTel, the Coast to Coast Software Repository (tm),
(available by anonymous ftp from ftp.coast.net and other SimTel mirrors):
http://www.coast.net/SimTel/msdos/x_10.html
irdc240.zip Send commands to a ONE FOR ALL Remote Control
IRDC - INFRA-RED Direct Control-Version 2.40. Control your ONE-FOR-ALL
(Supported Models: URC2005, URC4000, URC4005, URC4050, URC5000, URC6050)
remote control from your PC. Full screen Point & Click DOS character
interface. Your PC screen becomes a large remote keypad. This shareware
version will run with or without a ONE-FOR-ALL remote control & special
serial cable (the cable can be ordered from Home Automation Dealers).
IRDC when used with an I/R extender(eg.X-10 Powermid) is an inexpensive
way to automate I/R signal distribution. Includes IRDQ, a command line
version. CIS Reg. ID# 1449. PsL Program #14461. Uploaded by Author.
Special requirements: ONE FOR ALL Remote Control & Special Serial Cable
Changes: Support for upgraded URC-4050 chipsets added.
Replaces: irdc220.zip
ShareWare. Uploaded by the author.
David Huras
davidhuras@inforamp.net
>If you just need to send a simple RUT or on/off via the
>powerline - no data, etc, why not just modulate the 60cycle
>with some tone. Use a tone decoder at the other end. You'll
>need a transformer and associated parts for isolation and
>filtering.
Best way is to use LM1893 from National Semiconductor. It include Power
stage, FM modulator / Demodulator, limiter and auto-level setup to receive.
This circuit is specially design to do such transmission through AC power
line. It's old enought to be available anywhere.
>I have looked at the PLIX controller and TW-523 interface. They seem
>to solve my problem, yet they are very expensive. Over $100 for
>2 of each device.
The list price for a TW523 is $30 US.
Contact Worthington Distribution
Talk to Richard (Tell him I sent you) and he will sell you one for $18
The unit comes with a good description of the X10 protocol.
It is quite slow. About 1 second to send each message.
The TW523 does not allow arbitrary messages to be received, they must
conform to the protocol exactly.
I tried the NE5050N a couple of years ago and it worked just fine to
transmitt serial data on the mains (managed to go up to 1200 bps) but
I also suggest you take a closer look at SGS-Thomson's ST7536 and the
newer ST7537HS1 Power Line Modems, ST7537HS1 is capable to transmitt
at 2400 bps.
Have you concidered using a Power Line Modem like the NE5050N. This
device cost about 10 dollars (from a Swedish pricelist). You can
transmit any binary serial datastream up to 300 kbit/s over a
twisted pair cable and typical 1 kbit/s over a mains wire (with the
suggested circuit). To make it work, you have to buy the NE5050N
and a few discrete components like coils, a simple mains separating
transformer, a current generator (matched transistor pair) and
the usual components, resitors, capacitors ...
Before you get lulled into the novelty of homebrew device control,
Please take a long and healthy look at the strategy for 'Networking'
your PIC nodes. How will you manage the address of each node? Will
they be grouped or segmented? How will they get
'broadcast-to-all/group' system updates from host? How will you handle
the shifting priorities of arising emergency conditions? How will the
system & network handle accidental collisions of data on the network.
Each PIC node unit will need to do the device control and manage
network comm protocol. You may want to use an LSI chip to handle
network overhead. How will you load sfwr changes to the node MCUs and
call/schedule their data feeds to the host? Can nodes
exchange/share/control devices by interacting between themselves? How
will the host moderate/assign such 'distributed processing'?
Good reading is the Byte & Circuit Cellar Ink magazine articles on the
development of Home Control System by Steve Ciarcia. His ongoing
discussion of path choices will save you from reinventing the wheel.
Also get info on LON-Local Operating Network and CEBus. Then you can
better establish the architecture of your system
;***************************************
; 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<return>, where NN is the X10
;command. Example 'Et' is Housecode E and Keycode ON.
;Response is CX_NN<return>, 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<ret>
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 *****
Date: Sat, 1 Apr 1995 18:34:20
I developped a home automation program in Visual Basic : James 1.0
You can think of James as a butler. He is capable of controling electrical
and IR controled apparatus. James can be controled by voice, by a ædiaryÆ
database, by mouse or by keyboard.
James gives information via the Pcscreen or speeks to you : he can say
ascii or play wav files.
For the voice recognition, I use a Tandy Voice recognition card and the
accompanied software Voicekey. The IR module is based on a Siemens
SFH50630 and is made according the guidelines of Chris Dodge
(See :
http://alfred1.u.washington.edu:8080/~pfloyd/ee/circuits/PCIR/Welcome
.html)
To control the electrical apparatus, you can use the Velleman K8000 kit
or use X10 modules and control them via IR. No need for a x10 controler.
I use the X10 Powermid transmitters, they are capable of transmitting IR
from one room to another.
I use a audiopro soundcard but any soundblaster compatible card will do
Almost everyting is database controled. I use the MS access 2.0 database
engine.
When you start the program, James shows you a number of rooms.
You can define, create, change the rooms yourself. Each room is
represented by a bitmap (changeable via the standard windows software
Paintbrush) A mouseclick on a room shows all the apparatus
(also represented by bitmaps, two actually, one for the on status and one
for the off status) you defined for that room. A mouseclick on an apparatus
( or a a push on a keyboard key you attached to the apparatus) , changes
its
status. You can define groups of apparatus (for example all the lights in
the
living room) and attach a key to the group so the status of all the members
of the group changes when you press the key. (You can even define groups
within groups)
In the same way you can group apparatus, you can group IR signals an attach
a key to the group. Pushing the key ( or generating it by voicecontrol)
results
in sending all the IR signals in the group.
Every minute, the program checks the ædiaryÆ database.(Ms access 2.0).
In this diary, you can tell James to play a wav file, say an ascci tekst,
put an apparatus (or a group) on or off, send an ir signal (or a group of ir
signals), say the time, start any external program..
To make it short a few practial examples of what is possible with James 1.0
I call James, he says æYesÆ , and then I have 5 seconds to say a voice
command.
fi When I say dollar, James puts on the television ( if it was already
on,
the television shows the selected channel, goes to BRT 1 (the wanted
channel), puts on teletext mode, and selects page 540 where I
can find
the wanted valuta information. None of this is hard coded, James
gets
all
the information out of the Msaccess database.
When my little daughter says æSamsonÆ ( its a childrens program like
big
bird), James puts on the televison, selects the video channel, and
starts
playing the samson video
Why this information ?
To check the overall interest in a program like James, to see if it is worth
putting
a shareware version on the internet. To find some place where I can put
html-pages about James.
I hope to get a lot of reactions .
Benny Hooyberghs
email : bhooyber@innet.be
Date: Tue, 7 Feb 1995 09:00:30 -0700
To all X-10 hackers:
I recently posted my intention of uploading, to an FTP site, drawing files
of the component placement, PC board layout and schematics for the X-10
2-way and 3-way wallswitches. I have been trying to find out how to get
them to the comp.home.automation WWW site, but I haven't heard from the
WWW keeper there. However, I just finished uploading them to another FTP
site (ftp://ilces.ag.uiuc.edu/tmp/) which is an interim location until they
get moved to the ASRE archives at ftp://mrcnext.cso.uiuc.edu/asre/.
I don't know when the transfer will take place, but I suppose you can look
in both locations. The seven files all have the form: "wall_*.*" and
includes a descriptive text file. A cursory inspection reveals that there
are
two L1s on the component layout drawing. I believe the lower one should be
L2.
I replaced the triac with Digi-Key's part no.L4008L6-ND. This part is
rated for 8 Amps@400V. I refrained from using the often quoted part from
Radio Shack (#276-1000) because a Mr. Module article described them as not
having an isolated tab. That is VERY important for this application. The
unit from Digi-Key has an isolated tab and costs $2.09
I would like to make a few observations.
1) I have heard of and measured (using my cap. meter) a 0.1uF capacitor
between the red and blue wire of the companion switch.
2) The -VDC is -15Volts.
3) CR6 should be a 15 Volt Zener, it regulates the -VDC.
4) The fusible link was omitted, it is in series with SW1.
5) There are two L1s in the 'Component Side View'. The smaller one is
probably L2.
6) If one considers the black wire to be common (instead of +VDC) analysis
of the circuit becomes easier.
Repair tips
1) Definitely use a GFCI outlet.
2) Connect Black wire to neutral (wire in lamp according to your
preference).
The neutral is the wider of the two blades on a plug.
3) For waveform or voltage measurements connect between black wire and node.
4) A convenient point for power supply voltage measurement is the wire '
loop' of R5. This should be at least -15V, if not check R8.
5) Connect to R3 and push the wall switch button to check for low-duty-cycle
square wave. This confirms the chip is trying to switch on the load.
If the lamp is still not lit, suspect triac.
6) If no square wave at (5), check for 60Hz at pin 6. If none suspect
1/2 watt R7.
6) To reinsert the PC board, remove the metal front, then snap out the
slide shutoff switch.
7) While you have the whole thing disassembled, you might as well install
the local dimming mod.
>Trying to X-10 in my house, I have a number of places where controllers on
>on one side can't control a module on the other side - a classic case of
>the signal not being able to cross phases correctly. (I've tested this by
>
>But, an idea occurred to me. I've got an unused 220v dryer outlet (we
>switched to gas). Could I put something together, plug it into the
>dryer outlet, and Viola! my phases are coupled? Cheap, easy to install,
>easy to deinstall if I decide to move. Sounds perfect!
Yup, been there, done that.
I plugged in a 0.05uF, 600 Volt ceramic capacitor in my dryer outlet to
use as my signal bridge. It is supposedly not as good as the real
Leviton signal bridge, but it was also nearly free, and has worked for
several years.
Obdisclaimer: when working near 220V, turn off the breaker, cut off all
power to the house, stand on rubber sheets, wear a rubber suit, and hire
someone to do the electrical work.
An even easier solution worked for me. First, you should use a capacitor
that is UL listed for across-the-line connection. Radio Shack sells one
that is .01uf, 2kv I think. Next, you can put it across the outputs of any
220v circuit breaker (turn it off first) if you are careful working inside
the box there. I found the easiest way was to simply wedge the leads in,
and position the capacitor in a stable position where it can't short
anything.
X10 FAQ version 1.08 (8 Jan 95)
CHANGES SINCE LAST VERSION
Global FAQ structure changed to aid automatic parsing. Details posted to
comp.home.automation or available from address above. This may evolve in
future versions of the FAQ. Suggestions and comments welcome.
Q107. How do I solve common X10 problems? Rewritten.
Q110. Where do I get X10 software for my computer? Added another source.
Q113. How do I control fluorescent and halogen lights with X10?
Completely rewritten.
Q116. Can I use X10 components outside? New question.
Q117. What are the various combinations of X10 wireless receivers and
transmitters that work together? New question.
Q118. How do I make the motion detector floodlight unit work properly? New
question.
Q508. How do I repair a "blown" lamp module? Minor changes, alternate
source for triac.
Section 2: added some Radio Shack part numbers for existing descriptions
</FAQ CHANGES>
<FAQ OUTLINE>
OUTLINE
SECTION 1: General Information
Q101. What is X10?
Q102. What sort of X10 transmitters exist?
Q103. What sort of X10 receivers exist?
Q104. How many different units can X10 handle?
Q105. Who makes X10 components?
Q106. Who sells X10 components?
Q107. How do I solve common X10 problems?
Q108. Will X10 work on 220/240V?
Q109. How do I send and receive X10 signals with my computer?
Q110. Where do I get X10 software for my computer?
Q111. Where do I look for more information on X10?
Q112. How should I design the wiring of my new home to accommodate X10?
Q113. How do I control fluorescent and halogen lights with X10?
Q114. Can I use X10 in a three-way light switching application?
Q115. What is PLIX?
Q116. Can I use X10 components outside?
Q117. What are the various combinations of X10 wireless receivers and
transmitters that work together?
Q118. How do I make the motion detector floodlight unit work properly?
SECTION 2: Information on X10 Components
SECTION 3: Details on X10 Protocol
SECTION 4: Programming details for CP290 Home Control Interface
SECTION 5: Modifications to X10 hardware
Q501. How do I modify appliance modules for momentary operation?
Q502. How do I add local dimming capability to wall switch modules?
Q503. How do I modify the maxi-controller to accommodate more than 16
units?
Q504. How do I modify the mini-controller to control more units?
Q505. How do I modify the mini-controller to control all units for a
single housecode?
Q506. How do I modify the mini-controller to control only units 9-12 or
13-16?
Q507. How do I modify the mini-controller for momentary operation?
Q508. How do I repair a "blown" lamp module?
Q509. How do I defeat local control of lights and appliances?
Q510. How do I add a relay output to the power horn?
</FAQ OUTLINE>
<FAQ BODY>
<FAQ SECTION 1>
SECTION 1: GENERAL INFORMATION
===============================
Q101. What is X10?
A101. X10 is a communications protocol for remote control of electrical
devices. It is designed for communications between X10 transmitters and
X10 receivers which communicate on standard household wiring. Transmitters
and receivers generally plug into standard electrical outlets although some
must be hardwired into electrical boxes. Transmitters send commands such
as "turn on", "turn off" or "dim" preceded by the identification of the
receiver unit to be controlled. This broadcast goes out over the
electrical wiring in a building. Each receiver is set to a certain unit
ID, and reacts only to commands addressed to it. Receivers ignore commands
not addressed to them.
Note that "X-10" is a trademark of X-10 (USA) Incorporated an possibly of
X-10 Home Controls Incorporated (in Canada) as well. This FAQ uses "X10"
unless referring specifically to a product of the holder of the "X-10"
trademark.
Q102. What sort of X10 transmitters exist?
A102. The simplest X10 transmitter is a small control box with buttons.
The buttons select which unit is to be controlled, and which control
function is to be sent to the selected units (e.g. "turn on", "all units
off", etc). There are also clock timer transmitters which can be
programmed to send X10 commands at certain times. Some of these can be
programmed with buttons on the timer; some must be connected to a computer
to select the times. There are other special purpose transmitters that
send certain X10 commands at sunup or sundown, upon detecting movement, or
as commanded by tones over a telephone. This is not an all inclusive list,
and more detail on specific transmitters is given in Section 2.
Q103. What sort of X10 receivers exist?
A103. The simplest X10 receiver is a small module with an electrical plug
(to connect to a standard wall outlet), an electrical outlet (to provide
controlled power to the device it's controlling) and two dials (to set the
unit ID code) on it. An appliance module has relay inside which switches
power to its outlet on or off in response to X10 commands directed to it. A
lamp module is similar, but has a triac instead of a relay and will respond
to dimming commands as well as on or off commands. Other receivers can be
wired into wall outlets or into lamp fixtures. Note that the standard wall
switch (X10:WS467) is a receiver, not a transmitter; it does not transmit
X10 commands, and only takes action when it receives the appropriate
X10 command or local button-push.
Q104. How many different units can X10 handle?
A104. X10 specifies a total of 256 different addresses: 16 unit codes (1-
16) for each of 16 house codes (A-P). Normally a transmitter is set to a
certain house code (generally selectable by means of a dial) and so can
control at most 16 unit codes. There is no restriction on using multiple
transmitters each set to a different house code on the same wiring. Also,
several receivers could be set to the same house code and unit code so a
single command issued by an X10 transmitter could control multiple
receivers in parallel.
Q105. Who makes X10 components?
A105. Many different companies either make and/or distribute X10
components under different names. Some types are sold by more than one
company (probably made by same OEM). Some are specific to only one
company. Not all companies handle the complete range of components. Some
companies selling X10 components and their associated product names are:
- Radio Shack: Plug 'N Power
- Leviton: Decora Electronic Controls
Leviton Mfg. Co. Inc. Leviton Manufacturing of Canada
59-25 Little Neck Pkwy 165 Hymus Blvd
Little Neck, NY 11362-2591 Point Claire, QC H9R 1G2
(718) 229-4040
(800) 824-3005
- Stanley: Light Minder
- X-10: Powerhouse
X-10 (USA) Inc. X-10 Home Controls Inc.
91 Ruckman Road, Box 420 1200 Aerowood Drive, Unit 20
Closter, NJ 07624-0420 Mississauga, Ont L4W 2S7
(201) 784-9700 (416) 624-4446
(800) 526-0027 (800) 387-3346
x10usa@aol.com
Q106. Who sells X10 components?
A106. The following companies are alleged to sell X10 components in North
America. See Q108 for outside North America. Listing in this FAQ is not
an endorsement or recommendation of any kind:
Baran-Harper Group Inc.
77 Drakefield Road
Markham, ON L3P 1G9
Help/Info: (905) 294-6473
Orders only: (800) 661-6508
Fax: (905) 471-3730
BBS1: (905) 471-9574
BBS2: (905) 471-6776
Canadian Control and Automation Ltd
7 Wincanton Rd.
Markham, Ontario CANADA
L3S-3H3
Phone: (905) 470-9121
FAX: (905) 568-3658
Complete Home Automation
Phone: (800) 766-4226 (doesn't work in Canada)
Home Automation, Inc.
2709 Ridgelake Dr.
Metairie, LA 70002
Phone: (504) 833-7256
Fax: (504) 833-7258
Home Automation Laboratories
5500 Highlands Pkwy, Suite 450
Smyrna, GA 30082-5141
Orders: (800) 466-3522
Catalog: (800) 935-4425
Help: (404) 319-6000
Fax: (404) 438-2835 (is this the right number?)
(404) 410-1122 (is this the right number?)
BBS: (404) 319-6227 (300-14.4,8,N,1)
Home Automation and Security
286 Ridgedale Ave.
East Hanover, NJ 07936
Orders: (800) 254-5950
Help: (201) 887-1117
Fax: (201) 887-5170
Home Automation Systems, Inc.
151 Kalmus Drive, Suite M6
Costa Mesa, CA 92626
Orders: (800) 762-7846 (doesn't work in Canada)
(800) 367-9836 (supposedly works in Canada, but doesn't really)
Help: