Example: Simple I/O expansion using Xwire
Let’s consider a simple example of of just using a CC18 as an I/O expander for an MS120 (or for another CC18 for that matter). We will make it so all 16 I/O points are available both as inputs and as outputs. That means we have to send 2 bytes to the CC18 and receive 2 bytes back. Let’s give the slave an address that is simply set by the Xwire jumpers. For demonstration purposes some of the CC18 I/O will be reflected on the user interface (buttons and LEDs) of an MS120.
Slave program
Here’s a program for the slave. Here’s what it does:
- At startup, set the slave’s bus address from the two address jumpers.
- At startup, initiate Xwire communications using a slave configuration table in NVEM0
- Repeatedly copy 16 inputs into 2 bytes of RAM, where they can be sent via Xwire to the master
- Repeatedly copy 2 bytes of RAM, which have been “filled” by the master via Xwire, to the 16 outputs.
;=====================================================================
;======== Slave program for making a CC18 into an I/O expander =======
;=====================================================================
;Define RAM locations for the exchange. Here I have used "absolute" (mEQU)
;allocations. I could just as easily use defBYTE allocations, as long as
;I also use defBLOCK to make sure the bytes get allocated to contiguous RAM
;locations.
XwRx0 mEQU 0 ;1st byte from master
XwRx1 mEQU 1 ;2nd byte from master
XwTx0 mEQU 2 ;1st byte to master
XwTx1 mEQU 3 ;2nd byte to master
Start: XwireGetJumpers ;Read the 2 address jumpers.
; For address 0 both jumpers should be out
XwireSetAddr ;That sets the board address
XwireSlave XwTab ;Start Xwire with its configuration
Loop: Recall XwRx0 ;1st byte of data from the master
LoadX 'FF ;Enable all bits
OutputM 0 ;Output all bits
Recall XwRx1 ;2nd byte from master
LoadX 'FF ;Enable all bits
OutputM 8 ;Output all bits
InputFM 0 ;Read lower 8 input bits
Store XwTx0 ;1st byte to master
InputFM 8 ;Read upper 8 input bits
Store XwTx1 ;2nd byte to master
GoTo Loop ;Repeat
;-------------------------------------------------------------------
NVEM0 ;Must appear after all executable code!
XwTab:
NV0Byte XwTx0,2,XwRx0,2
; Address of 1st RAM byte to be transmitted ___^ ^ ^ ^
; Number of RAM bytes to be transmitted ___________| | |
; Address of 1st RAM byte received from master _________| |
; Number of receive bytes to expect from master ___________|
;Note: If the byte counts each end do not match, it will not work.
Note: Xwire is not supported during simulation in SPLat/PC. That means it is not possible to be debugging one board and see it interacting with another board while you are using the Run menu. You need to run it in the actual hardware (Translate, Download and then Run button on Module window)
Master program
Here is a fully working program for the master. This will work with the slave program above, that has turned a CC18 into a 16 I/O peripheral expansion board. What the program will do is have the 5 front panel push buttons of the MS120 appear on outputs 0 through 4 of the CC18. At the same time inputs 8-11 of the CC18 will appear on the 4 front panel LEDs on the MS120 (on the MMI202 3 LEDs plus the beeper). I have provided equivalent MMi202 button/LED addresses as well.
I am going to use semaphore addressing in the master. What this means is:
- Each slave input is read (
RecallS) as a semaphore. The semaphore is counted relative to the start of the two byte RAM block that receives the slave input data. The appropriate instruction isRecallS x,Slv0Inputs, where x is the input number required andSlv0Inputsis the first of the two bytes that receive Xwire data from the slave. You could also useGoIfST ... orGoIfSF ...orWaitForST ...etc - Each slave output is written (
StoreS) as a semaphore. The semaphore is counted relative to the start of the two byte RAM block that transmits the slave output data. The appropriate instruction isStoreS x,Slv0Outputs, where x is the input number required. You could also useSetS ...orClrS ...
What the program has to do is:
- Get the Xwire communications going.
- Do whatever it has to do to interact with the slave. In this case it will be simply reflecting slave I/O to the MS120 (or MMi202) front panel. In this simple example I will just do it as a “tight loop” program, with nothing else to do. In a practical program the various accesses to the slave would probably be spread out across several tasks in a MultiTrack program.
- I will also include a display of the Xwire error counter. This is a very useful tool to use during system test, to monitor that Xwire is working.
Tip: If you are trying to get several slaves going, do one at a time. Write a simple test program that uses the error counter, and extend it as you add each slave to your system. If you try and get several slaves all working at once, you are likely to be overwhelmed if any one of them is incorrect. Divide and conquer!
Note: I am not going to define named (symbolic) values for all the slave I/O and MS120/MMi202 front panel stuff. I will use simple numeric values. Normally that would be naughty. In this case it is better not to clutter the program with 20+ lines of EQUates.
;=====================================================================
;======== Master program for using a CC18 as an I/O expander =========
;=====================================================================
;Define RAM locations for the exchange.
Slv0Inputs defBYTE 2 ;Remote inputs stored in 2 bytes as 16 semaphores
;Each semaphore will correspond to one slave input
Slv0Outputs defBYTE 2 ;Remote outputs stored in 2 bytes as 16 semaphores
;Each semaphore will correspond to one slave output
Slv0Addr EQU 0 ;Define the slave address.
Start:
XwireMaster XwTab ;Start Xwire with its configuration
;For MS120, as I want to use the LCD for error counts, I must turn on the backlight
On 20 ;MS120 only
;Once the startup stuff is done Xwire will be running
;(if everything is correctly set up and wired together!).
;I now go into a "tight loop" updating I/O and the error count display...
Loop:
;---------- Do the slave inputs: Copy to front panel resources
RecallS 8,Slv0Inputs ;Reflecting input 8 on the CC18
;Notice ^_________________________________^
Output 8 ;One front panel LED (Beeper on MMi)
RecallS 9,Slv0Inputs ;Reflecting input 9 on the CC18
Output 9 ;One front panel LED
RecallS 10,Slv0Inputs ;Reflecting input 10 on the CC18
; ^
;Coincidence ------------------- |
; v
Output 10 ;One front panel LED
RecallS 11,Slv0Inputs ;Reflecting input 11 on the CC18
Output 11 ;One front panel LED
;---------- Do the slave outputs: Set from front panel resources
Input 12 ;(8 on MMi) ;One front panel button
StoreS 0,Slv0Outputs ;Reflects in slave output 0
;Notice ^_________________________________________^
Input 13 ;(9 on MMi) ;One front panel button
StoreS 1,Slv0Outputs ;Reflects in slave output 1
Input 14 ;(10 on MMi) ;One front panel button
StoreS 2,Slv0Outputs ;Reflects in slave output 2
Input 15 ;(11 on MMi) ;One front panel button
StoreS 3,Slv0Outputs ;Reflects in slave output 3
Input 16 ;(12 on MMi) ;One front panel button
StoreS 4,Slv0Outputs ;Reflects in slave output 4
;----------- Display the error count. The count displayed will increment
;whenever there is an Xwire error.
fErrors defFLOAT
XwireGetErrCount ;Error count -> X, reset error count
float ;Convert to floating point
fRecallQ fErrors ;Previous running total
fAdd ;Add in new counts, if any
fStore fErrors ;Save
OBLCD_SetCur 0,0 ;Position LCD cursor
OBLCD_fDispW 5,0 ;Display running total
;----------- All done
GoTo Loop
;-------------------------------------------------------------------
;The NVEM0 directive must come after all executable code.
NVEM0
XwTab:
NV0Byte Slv0Addr,Slv0Outputs,2,Slv0Inputs,2
NV0Byte 'FF ;Sentinel