REM Slave Network Stack V3.0 REM ======================== REM for PICAXE-08M REM Dataframe format used here: 4 bytes: REM [IDcalledProcess, IDcallerInfo, dataByteH, dataByteLow] REM Note that two dummy user processes have been implemented as well REM (userDefinedProcess and userDefinedSendingProcess) REM which can be extended with functionality REM Dataframe format used here: 4 bytes: REM [IDcalledProcess, IDcallerInfo, dataByteH, dataByteLow] REM -------------------------------------------- REM STATEMENT OF COPYRIGHT REM Copyright (C) 2008, Jurjen Kranenborg REM This work is licensed under the Creative Commons REM Attribution-Noncommercial-Share Alike 3.0 (Swedish port). REM To view a copy of this license, visit REM http://creativecommons.org/licenses/by-nc-sa/3.0/deed.se REM To view a translation of this license in English, visit: REM http://creativecommons.org/licenses/by-nc-sa/3.0 REM or send a letter to REM Creative Commons, REM 171 Second Street, Suite 300, REM San Francisco, California, 94105, USA. REM In short form (me, my = holder of copyright): REM * You may use, adapt, and distribute this work. REM * You must give credit to me when using this code in your application. REM * If you use my code commercially, I want my share. REM * You may share your adapted code (now with you as holder of copyright) REM on the condition that the same license conditions are applied. REM In order to acknowledge Jurjen Kranenborg (i.e. not the copyright holder of any derived work) REM as the architect of the original network concepts, REM please include the following statement in all derived works (code, webpages, docs, etc.): REM The network architecture of this work was originally developed by Jurjen Kranenborg. REM -------------------------------------------- REM This application example was tailored to the PICAXE-08M #picaxe-08m REM Definition of network input & output communication pins SYMBOL netSerialIN = 3 SYMBOL netSerialINPUT = pin3 SYMBOL netSerialOUT = 4 REM Registers used as resources by the Network Stack SYMBOL commandIndex = b9 SYMBOL IDsendingProcess = b9 SYMBOL IDdestinationProcess = b10 SYMBOL IDcalledProcess = b10 SYMBOL IDcallerInfo = b11 SYMBOL dataByteH = b12 SYMBOL dataByteL = b13 SYMBOL rises = b13 SYMBOL IDcandidateSendingProcess=b8 REM Definition of default index for process selection SYMBOL noProcessIndex = $FF REM Boolean constants definition: SYMBOL FALSE = 0 SYMBOL TRUE = 1 REM RAM MTLs (Message Transfer Locations) for storing message frame information that needs REM to be sent to the network as a message later on REM Each time the master node sends a timeslot message, the network stack will inspect the contents REM of RAMIDsendingProcessInfo (i.e. the ID of the process that wants this message to be transmitted) REM and if this ID and the timeslot ID match the message will be composed from the RAM locations REM and put on the network. No user intervention is required. After the message has been transmitted REM the network software will write IDnoProcess is written to RAMIDsendingProcessInfo, indicating to REM slave processes on this node that a new message may be composed. SYMBOL RAMIDdestinationProcess = $50 'dst process ID SYMBOL RAMIDsendingProcessInfo = RAMIDdestinationProcess + 1 'src process ID SYMBOL RAMdataByteLocationH = RAMIDsendingProcessInfo + 1 'dataByteH location SYMBOL RAMdataByteLocationL = RAMdataByteLocationH + 1 'dataByteL location REM RAM location for storing network configuration info as sent by the master node at start-up SYMBOL RAMnetworkConfigurationByte = RAMdataByteLocationL + 1 'storage of Network Configuration information REM (Optional use) RAM locations that contain the most recent network message received REM These locations are updated automatically after each network message received (i.e. after each interrupt) REM and can be used to synchronize the process(es) in the body program of this slave REM with processes running elsewhere SYMBOL RAMMessage_DestinationProcess = RAMnetworkConfigurationByte + 1 SYMBOL RAMMessage_SendingProcess = RAMMessage_DestinationProcess + 1 SYMBOL RAMMessage_dataByteH = RAMMessage_SendingProcess + 1 SYMBOL RAMMessage_dataByteL = RAMMessage_dataByteH + 1 REM Time intervals for network synchronization REM - in milliseconds REM - assuming 2400 baud data rate: REM - 1 bit takes 1/2400 = 0.42 ms REM - 1 byte takes 8/2400 = 1/300 sec = 3.3 ms = approx. 4 ms REM - 1 message takes 4 bytes -> 4 * 3.3 ms = 13.2 = approx. 14 ms REM Note that these defs should be the same as those for the master node! SYMBOL t_startUp = 1000 SYMBOL t_byte = 4 SYMBOL t_INTmaster = 2*t_byte SYMBOL t_INTslave = t_INTmaster SYMBOL t_dataFrame = 14 SYMBOL t_respondDelay = 2*t_byte SYMBOL t_testTimeSlot = 2*t_byte SYMBOL t_calc1 = t_respondDelay + t_INTslave SYMBOL t_slaveTimeSlot = t_calc1 + t_dataFrame SYMBOL t_masterTimeSlot = t_INTmaster + t_dataFrame SYMBOL t_comms = t_masterTimeSlot + t_slaveTimeSlot REM Interrupt mask and level definitions REM Note that mask definition must correspond with INT on netSerialIN! SYMBOL INTinputs = %00000000 SYMBOL INTmaskENABLED = %00001000 SYMBOL INTmaskDISABLED = %00000000 REM Network Status definitions SYMBOL NOT_READY = %00000000 SYMBOL READY_FREQ_4MHZ = %00000001 SYMBOL READY_FREQ_8MHZ = %00000011 REM Process Identifiers of network stack related processes: SYMBOL IDallProcesses = 255 SYMBOL IDnoProcess = 0 SYMBOL IDroamSendingProcess = 1 'Slave process SYMBOL IDregisterSendingProcess = 2 SYMBOL IDunRegisterSendingProcess = 3 SYMBOL IDavailableTimeSlot = 4 'Slave process SYMBOL IDroamPlugNplayNode = 5 'Slave process (plugNplay node) SYMBOL IDnetworkManager = 6 'Slave process (special external Network Manager Node; not currently implemented) SYMBOL IDflashLEDmasterNode = 7 SYMBOL IDnetworkConfigProcess = 8 'Allows to configure slaves at start-up (clock speed etc.) SYMBOL IDspecialProcess9 = 9 'Reserved ID for special system purposes (do not use for user processes) REM Data on allowed user-defined process IDs REM (used for roaming sending slave processes) REM Note that ID=255 is reserved (IDallProcesses) and should NOT be used as a user process! SYMBOL firstUserProcessID = 10 SYMBOL lastUserProcessID = 254 REM *********************** REM User programmable area: REM *********************** REM NOTE: registers b0 - b7 freely available REM registers b8 - b13 in use by network stack REM Allowable IDs for user processes: 10 - 254 REM Specify here if a simple diode-mixing network is used (separate power and serial comms lines) REM NOTE: It is very important to choose the right option here, otherwise the fet REM at the output gets destroyed! REM - - - - - - - - - - - REM #define simpleDiodeMixing REM - - - - - - - - - - - REM Specify here if all slave network messages have to be catched and stored in the assigned RAM locations. REM This feature allows synchronization with other processes, but consumes considerable resources REM - - - - - - - - - - - - - REM #define storeAllSlaveMessages REM - - - - - - - - - - - - - REM =============================== REM START of Main Program execution REM =============================== REM --------------------------- REM Network initialization code REM Do not change this part! REM --------------------------- #ifdef simpleDiodeMixing REM Put line high as default HIGH netSerialOut #else REM Guarantee that the output mosfet is closed by setting as output + Low! LOW netSerialOut #endif REM Allow local capacitor to charge up for some time REM note that the master node must have a longer startup time Pause t_startup REM Now the node is almost ready for network operation SETINT INTinputs, INTmaskENABLED REM First await the network information as sent by the master node (clock frequency to be used) REM Since this is the first message received, the network status will be available in dataByteL REM (as well as stored in RAMnetworkConfigurationByte for later use) REM Note that currently this part can be removed if the network is always run at a fixed frequency DO LOOP WHILE dataByteL = NOT_READY REM --------------------------- REM End of REM Network initialization code REM --------------------------- REM ======================================================= REM USER PROGRAM BODY REM Main location for processes to do something useful here REM If they have data they whish to send to other processes REM they should set the RAM message transfer locations, REM the network stack will inspect this information during REM every interrupt to see if action is needed. REM ======================================================= REM User-defined process IDs: SYMBOL IDuserDefinedProcess = 10 REM Dummy, not further defined currently, SYMBOL IDuserDefinedSendingProcess = 11 REM Dummy, not further defined currently, but gets registered and gets timeslots REM Obligatory administrative definitions (See User Programming Guide): REM Highest user ID available on this slave SYMBOL highestSlaveID = 11 REM -------------- DO REM Main user code here REM "Dummy" message sent for network testing purposes REM Note that this is the main body part of REM userDefinedSendingprocess REM Has RAM information already been composed into a message and sent? PEEK RAMIDsendingProcessInfo, IDcallerInfo REM Only compose information if this has not yet been done, REM otherways just sit and wait for indication that message has been sent IF IDcallerInfo = IDnoProcess THEN GOSUB deSynch POKE RAMIDdestinationProcess, IDnoProcess POKE RAMIDsendingProcessInfo, IDuserDefinedSendingProcess POKE RAMdataByteLocationH, 0 POKE RAMdataByteLocationL, 0 GOSUB reSynch ENDIF LOOP REM ======================== REM END OF USER PROGRAM BODY REM ======================== REM -------------------------------- REM Slave Network Stack subroutines: REM -------------------------------- Interrupt: REM Network message frame reception and handling routine REM ==================================================== REM Line has been brought low by master or slave, REM Read the command frame (4 bytes) and determine if REM the process addressed is actually present on this slave LET commandIndex = noProcessIndex SERIN netSerialIN, N2400, IDcalledProcess, IDcallerInfo, dataByteH, dataByteL LOOKDOWN IDcalledProcess, (IDroamSendingProcess, IDavailableTimeSlot, IDnetworkConfigProcess, IDuserDefinedProcess, IDuserDefinedSendingProcess), commandIndex REM Jump to proper command handling routine, and then REM return to normal execution BRANCH commandIndex, (roamSendingProcess, availableTimeSlot, networkConfigProcess, userDefinedProcess, userDefinedSendingProcess) REM If command is not recognised then return to normal execution: REM however, optionally the message just received may be stored so that REM processes in the body may use this information for synchronization with other processes. #ifdef storeAllSlaveMessages then POKE RAMMessage_DestinationProcess, IDcalledProcess POKE RAMMessage_SendingProcess, IDcallerInfo POKE RAMMessage_dataByteH, dataByteH POKE RAMMessage_dataByteL, dataByteL #endif exitFromSession: REM Network issues dealt with, REM prepare to return to normal background execution mode REM with Interrupts ENABLED SETINT INTinputs, INTmaskENABLED RETURN REM ------------------- availableTimeSlot: REM Here is a new message created and put on the network REM if requested by some process in the body: PEEK RAMIDsendingProcessInfo, IDsendingProcess IF IDcallerInfo = IDsendingProcess THEN sendSlaveData: REM first get the proper data bytes PEEK RAMdataByteLocationH, dataByteH PEEK RAMdataByteLocationL, dataByteL PEEK RAMIDdestinationProcess, IDdestinationProcess REM Use the granted timeslot to send the message GOSUB takeNetworkSlot ENDIF REM then prepare to normal execution mode GOTO exitFromSession REM ------------------- takeNetworkSlot: REM Helper subroutine to send a message; may be used by user processes as well REM See the SEROUT command below to see which variables are used for contructing the message #ifdef simpleDiodeMixing REM bring down the transmission line to take the network timeslot LOW netSerialOUT REM take a while for other slaves/master to get into receiving mode PAUSE t_INTslave REM ... and send out the message! SEROUT netSerialOUT, N2400, (IDdestinationProcess, IDsendingProcess, dataByteH, dataByteL) REM make the network line high again HIGH netSerialOUT #else REM bring down the transmission line to take the network timeslot HIGH netSerialOUT REM take a while for other slaves/master to get into receiving mode PAUSE t_INTslave REM ... and send out the message! SEROUT netSerialOUT, T2400, (IDdestinationProcess, IDsendingProcess, dataByteH, dataByteL) REM make the network line high again LOW netSerialOUT #endif REM All has been sent, so allow other processes to send as well POKE RAMIDsendingProcessInfo, IDnoProcess RETURN REM ------------------- roamSendingProcess: REM This process is called by the master node at power-up to see REM whether this slave node implements one or more sending processes REM REM If so, this subroutine returns the ID of the sending process REM to the registerSendingProcess at the master. REM REM The information in the message frame to be sent (see takeNetworkSlot) is defined as follows: REM - IDdestinationProcess is the ID of the registering process at the master node REM - IDsendingProcess is the ID of the current process (IDroamSendingProces) REM that requests for timeslots REM - dataByteH indicates the largest ID of all IDs implemented at the node REM (sending as well as listening-only processes) REM - dataByteL indicates the IDs of the processes that request a timeslot REM Note that a slave may implement several sending processes REM so this subroutine should deliver all related IDs REM This can be realized simply by extending the next line through ORing with multiple processes REM (IF IDcallerInfo = IDuserproc1 OR IDcallerInfo = IDuserproc2 ... THEN ) IF IDcallerInfo = IDuserDefinedSendingProcess THEN IDdestinationProcess = IDregisterSendingProcess IDsendingProcess = IDroamSendingProcess REM In databyteH the user should indicate the maximum ID of all processes REM (sending as well as non-sending) on this slave databyteH = highestSlaveID REM Do not change this line databyteL = IDcallerInfo REM Now use the same code as in availableTimeSlot to send the message frame GOSUB takeNetworkSlot ENDIF GOTO exitFromSession REM ------------------- networkConfigProcess: REM Set the slave node clock frequency as indicated by the master node REM at network start-up (Bit 0, i.e. LSB of network config byte REM residing currently in dataByteL) REM Firstly, store the Network Configuration byte POKE RAMnetworkConfigurationByte, dataByteL REM Extract clock frequency information LET dataByteH = dataByteL AND %00000010 IF dataByteH <> 0 THEN SETFREQ m8 ENDIF GOTO exitFromSession REM ------------------- REM Synchronization routines for slow instructions / code parts REM which should not be interrupted. REM See documentation for detailed explanation deSynch: SETINT INTinputs, INTmaskDISABLED RETURN reSynch: IF netSerialINPUT = 0 THEN waitAWhile COUNT netSerialIN, t_byte , rises IF rises > 0 THEN waitAWhile GOTO exitFromSession waitAWhile: PAUSE t_byte GOTO reSynch REM ------------------------ checkReadyToPrepareNewMessage: REM Monitor the RAM message transfer locations to see whether previous information still waiting to be sent REM has actually been transmitted as a message to the network (the RAM location RAMIDsendingProcessInfo then REM contains IDnoProcess ("empty" process) to indicate that new message information can be stored). REM If not, wait until the previous message has been sent REM REM An important advantage of the application of this routine is that while the slave node is REM waiting for a previous message to be sent, it is waiting in an interruptable loop, REM allowing it to catch any network messages that arrive before its own timeslot arrives! DO PEEK RAMIDsendingProcessInfo, IDcandidateSendingProcess LOOP UNTIL IDcandidateSendingProcess = IDnoProcess RETURN REM --------------------------------------------- REM END of Slave Network Stack subroutines. REM User subroutines/processes may be added REM here below: REM IMPORTANT: Do not conclude with "RETURN" but REM with "GOTO exitFromSession" instead, as interrupts have to be enabled again first REM --------------------------------------------- userDefinedProcess: REM Dummy listening-only process (Not currently implemented) REM If this process is not needed, then remove this routine here as well as REM the corresponding ID and address in the interrupt routine to save memory space REM [Body here ...] GOTO exitFromSession REM Note: not an ordinary RETURN! userDefinedSendingprocess: REM Parameter-passing part of sending process (Not currently implemented) REM You may use this section to pass parameters to the main part of REM userDefinedSendingprocess in the body REM If this is not needed, then remove this routine here as well as REM the corresponding ID and address in the interrupt routine to save memory space REM [Body here ...] GOTO exitFromSession REM Note: not an ordinary RETURN! REM ================================= REM END of SLAVE NODE implementation REM =================================