REM ===================================================================== REM SerialPower I V4.0 (M2 Part implememntation) MASTER NODE REM ===================================================================== REM "INTELLIGENT" NETWORK-ROAMING MASTER NODE for M2 & X2 newer parts, REM with following implementation details: REM - Implements same 4-byte protocol (i.e. original SerialPower) REM as for older M/X parts REM - PICAXE-08M2 implementation (but can be changed to other M2 parts REM without significant code changes) REM - Master node code supports both 2-wire power+data netwrok and 3-wire diode-mizing netwroks REM without any code changes REM - Timing relaxed (T_dataFrame, T_respinddelay) for network stability improvement REM - Master-selectable network frequency (128 KHz - 32 MHz) for slave and master node freq REM REM Provides for implementation of the following activities and processes: REM REM - Network power-up REM - Configuration of (optional) Network Manager Node REM - Roaming and registration of sending processes on slaves REM - Creation of timeslots for registered processes in an endless loop REM - Support for plugNplay nodes with dynamic process ID assignment REM REM --------------------------------------------------------------------- REM REM Implements following processes (can be addressed by slave nodes): REM REM - registerSendingprocess REM - unRegisterSendingprocess REM - flashLEDmasterNode REM REM See code and website for detailed documentation of process parameters REM REM --------------------------------------------------------------------- REM REM Network message frame format used: 4 bytes: REM [IDcalledProcess, IDcallerInfo, dataByteH, dataByteLow] REM REM --------------------------------------------------------------------- REM STATEMENT OF COPYRIGHT REM Copyright (C) 2018, Jurjen Kranenborg REM This work is licensed under the Creative Commons REM Attribution-Noncommercial-Share Alike 4.0. REM To view a copy of this license, visit REM http://creativecommons.org/licenses/by-nc-sa/4.0/deed.se REM To view a translation of this license in English, visit: REM http://creativecommons.org/licenses/by-nc-sa/4.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 Obligatory Base Frequency to start with for any picaxe type (M2/X2) REM since X2 and M2 parts have different default frequencies (8 vs. 4 MHz) SETFREQ m4 #no_data REM ********************************************************************* REM User Programmable Area 1: REM - PICAXE chip definition REM ************************* REM Picaxe type used #Picaxe 08m2 REM No further changes needed here, unless a diiferent M2-type is used ... REM Definitions for network signal comditioner (Comparator + FVR) REM IMPORTANT !!! Only change the pin definitions below in case use use a diferent M2-type PICAXE from the 08M2 REM (which likely will have different comparator pin assignments) REM input & output communications pins, fixed (not user configurable) since comparator is used for signal conditioning REM Note: The SerialIn input is connected to the output of the comparator (through a 1K resistor) REM and thus is used to improve the lower inout signal limit REM Comparator needs 2.048V as a reference: network input signal below this value is cut off to 0.0V bij comparator FVRSETUP FVR2048 REM Comparator has an output at C.2, required to set this pin as output first. OUTPUT C.2 REM Define comparator settiongs: SYMBOL CM1CON0_address = %1010001 'Address of control register 0 SYMBOL CM1CON1_address = %1010010 'Address of control register 1 SYMBOL valCM1CON0 = %10110100 'Comparator Control register 0 values: input (-) at C.1, inverted output at C.2 SYMBOL valCM1CON1 = %00100000 'Comparator Control register 1 values: use FVR output (2.048V) as (+)input of comparator POKESFR CM1CON0_address, valCM1CON0 POKESFR CM1CON1_address, valCM1CON1 SYMBOL netSerialIN = C.3 'Input of conditioned signal from comparator output (C.2) SYMBOL netSerialOUT = C.0 SYMBOL indicatorLEDoutput = C.4 REM interrupt definitions (change INTmask as well if NetSerialIn is changed) SYMBOL INTmaskENABLED = %00001000 'Definition of which pins are to generate an interrupt SYMBOL INTmaskDISABLED = %00000000 SYMBOL INTinputs = %00000000 'Pin Logic levels to generate interrupt REM ******************************* REM END of User Programmable Area 1 REM (see also Area 2 further below) REM ********************************************************************* REM Further definitons REM Serial data registers: SYMBOL commandIndex = b9 SYMBOL IDcalledProcess = b10 SYMBOL IDcallerInfo = b11 SYMBOL dataByteH = b12 SYMBOL dataByteL = b13 SYMBOL NetworkFrequency = b13 SYMBOL noProcessIndex = $FF REM Boolean constants definition: SYMBOL FALSE = 0 SYMBOL TRUE = 1 REM Definition of RAM table (and pointers) to store IDs of sending processes in network: SYMBOL RAMtablePointer = $50 SYMBOL RAMtableLimit = $7F SYMBOL RAMtableIndex = b8 SYMBOL currentSendingProcessPointer = b7 SYMBOL RAMsearchedIDpointer = b3 SYMBOL RAMnextElementPointer = b2 REM time intervals for message frame 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. 4ms SYMBOL t_startUp = 1000 SYMBOL t_startUp_masterNode = 2*t_startUp SYMBOL t_byte = 4 SYMBOL t_INTmaster = 2*t_byte SYMBOL t_INTslave = t_INTmaster SYMBOL t_dataFrame = 16 SYMBOL t_respondDelay = 3*t_byte SYMBOL t_testTimeSlot = 3*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 Network Status definitions SYMBOL NETWORK_NOT_READY = %00000000 REM Network Frequency definitions SYMBOL FREQ_BASE = %00000001 REM Corressponds to 4MHz (M2) or 8 MHz (X2) but same base timing SYMBOL FREQ_BASEx2 = %00000010 REM 8MHz (M2) SYMBOL FREQ_BASEx4 = %00000011 REM 16MHz SYMBOL FREQ_BASEx8 = %00000100 REM 32MHz REM Freqs lower than Base (d# = divide Base by #): SYMBOL FREQ_BASEd2 = %00001000 REM 2MHz SYMBOL FREQ_BASEd4 = %00001001 REM 1MHz SYMBOL FREQ_BASEd8 = %00001010 REM 512KHz SYMBOL FREQ_BASEd16 = %00001011 REM 256KHz SYMBOL FREQ_BASEd32 = %00001100 REM not supported on M2 series since 128 KHz not available REM 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 (IDnoprocess) and should NOT be used as a user process! SYMBOL firstUserProcessID = 10 SYMBOL IDcurrentProcess = b6 SYMBOL IDdestinationProcess = b5 SYMBOL maxCurrentID = b4 SYMBOL LEDlightPeriod = w5 REM ********************************************************************* REM User Programmable Area 2: REM - Newtrok & CPU frequency choice, only required at master node, slaves REM are informed and shortly after network power-up ! REM - Network behaviour REM - Highest userID that will be used in the network (10 - 254) REM ************************* REM - - - - - - - - - - - - REM Select network speed (and consequently CPU speed) here, by selecting the proper one REM from the "Network Frequency definitions" list above REM BASE implies 4MHZ for M2 parts amd 8MHZ for X2 parts so that pause commands etc. have the same duration REM and therefore M2 and X2 parts can be freely mixed in one system. REM Higher freqs mean more perfomance but also more susceptibility REM towards external noise and large capacitances due to larger networks, both breaking operation, REM so one should test this appriopriately. LET NetworkFrequency = FREQ_BASE REM - - - - - - - - - - - - REM plugNlay usage, define here whether plugNplay option is used (if so, following line should be de-commented out) REM ++++++++++++++++++ REM #define plugNplay REM ++++++++++++++++++ REM - - - - - - - - - - - - REM Guarantee a certain time with active power provision between timeslots REM Note: shorter period implies higher network bandwitdh but also more interruptions of slaves! REM Do not take a shorter period than t_comms REM ++++++++++++++++++++++++++++++++++++ SYMBOL t_pauseBetweenTimeSlots = t_comms REM ++++++++++++++++++++++++++++++++++++ REM - - - - - - - - - - - - REM Highest User-selectable process ID number (min = 10, max = 254 as id=255 is reserved) REM A larger number accomiodates more (sending) processes but also requires more process-id roaming REM after network powerup ... SYMBOL lastUserProcessID = 50 REM ******************************* REM END of User Programmable Area 2 REM ********************************************************************* REM =============================== REM START of Main Program execution REM =============================== Initialize: SETINT INTinputs, INTmaskDISABLED REM Current maximum ID in use is initialized here (gets updated automatically during process registration) LET maxCurrentID = IDspecialProcess9 REM Network Power-up: Power-up all slaves for a while REM Power-up the network firmly by enabling the MOSfet push-pull driver to deliver full power for a while REM Note that this is particularly necessary for slaves with an 18X (which does not have its brown-out bit set) ... HIGH netSerialOUT REM Full power for a while (twice as long as the power-up time of the slave nodes) PAUSE t_startUp_masterNode REM Master node sends configuration parameters to all slave nodes REM This allows network configuration without having to reprogram all slave nodes GOSUB configureNetwork PAUSE t_pauseBetweenTimeSlots REM Search all slaves for sending process IDs, and store all IDs thus collected in RAM memory (RAMtable) REM The indicator LED is lit constantly to indicate that the master node is busy roaming HIGH indicatorLEDoutput GOSUB roamForSendingProcesses LOW indicatorLEDoutput REM - - - - - - - - - - - - - - - - REM Endless loop to provide network timeslots to slaves REM REM NOTE: The master also listens to slave process responses via an interrupt, REM allowing it to implement user functionality itself as well! REM - - - - - - - - - - - - - - - - loopCreateTimeSlots: DO REM Endless loop for continuous timeslot & power provision REM REM Create timeslots for all processes on the network that potentially can send. REM All registered IDs are already available in RAMtable (due to roamForSendingProcesses) with: REM - RAMtablePointer as pointer to first registered ID REM - RAMtableIndex as pointer to last registered ID LET currentSendingProcessPointer = RAMtablePointer DO WHILE currentSendingProcessPointer <= RAMtableIndex REM Fetch the process ID from RAMtable PEEK currentSendingProcessPointer, IDcurrentProcess REM Guarantee a certain period with active power provision between timeslots PAUSE t_pauseBetweenTimeSlots REM Now create a timeslot for process with identifier IDcurrentProcess LET IDdestinationProcess = IDavailableTimeSlot LET dataByteH = maxCurrentID LET dataByteL = currentSendingProcessPointer GOSUB createSingleTimeSlot INC currentSendingProcessPointer LOOP REM Insert a plugNplay timeslot if this has been specified in user programmable area (default off) REM REM This command also informs the node on the largest ID currently registered (in dataByteH), allowing REM it to add extra processes with new IDs without risk of conflict with existing processes. REM Consequently, the plugNplay node need not know the currently registered IDs if it does not intend to use them. #IFDEF plugNplay IDdestinationProcess = IDroamPlugNplayNode dataByteH = maxCurrentID REM Guarantee a certain period with active power provision between timeslots PAUSE t_pauseBetweenTimeSlots GOSUB createSingleTimeSlot #ENDIF LOOP REM =================== REM Program subroutines REM =================== configureNetwork: REM Configure all nodes in the network by sending a configuration byte REM Currently only the node clock frequency can be changed REM After this message is sent by the master, the master node itself switches to the chosen frequency as well REM Indicate to slaves that network frequency info is coming LET IDdestinationProcess = IDnetworkConfigProcess LET IDcurrentProcess = IDnetworkConfigProcess REM Assumes that the NetworkFrequency register has its value properly defined! LET dataByteL = NetworkFrequency REM First, inform all nodes on network frequency GOSUB createSingleTimeSlot REM Then switch to appropriate frequency as well CALL applyNetworkFrequency RETURN REM ------------------- roamForSendingProcesses: REM This subroutine roams the network for potentially sending process IDs REM and stores them in the RAM table. This process is called after network power-up. REM The network slaves are roamed for sending processes by cycling through all possible process IDs. REM A special slave process is used for this: roamSendingProcess REM A slave that implements the particular sending process will then respond directly REM by addressing the master's registerSendingProcess (see Interrupt routine) and providing REM its ID and additional parameters. See subroutine registerSendingProcess for the REM description of these parameters. REM Note that we use RAMtableIndex in the interrupt routine REM as the index to the lastly added entry in the RAMtable; REM it is incremented there if there has been a registered process REM Start with empty table LET RAMtableIndex = RAMtablePointer - 1 REM Indicate to slaves that they need to provide IDs of their sending processes. LET IDdestinationProcess = IDroamSendingProcess REM Include information on network configuration, in particular the required REM slave node frequency that will be set after roaming is finished LET dataByteH = maxCurrentID REM Cycle through all possible IDs and allow any sending processes at slave nodes to respond FOR IDcurrentProcess = firstUserProcessID TO lastUserProcessID PAUSE t_comms LET dataByteH = maxCurrentID GOSUB createSingleTimeSlot NEXT IDcurrentProcess RETURN REM ------------------- createSingleTimeslot: REM send out the command informing slave processes that a timeslot is created REM first bring down the line a while to force all slaves to listen: LOW netSerialOUT REM take a while for slaves to get into receiving mode PAUSE t_INTmaster REM ... and send the timeslot command out for the IDcurrentProcess process on a slave! SEROUT netSerialOUT, N2400_4, (IDdestinationProcess, IDcurrentProcess, dataByteH, dataByteL) REM Bring the line high again for a short while (but a little bit shorter than t_responddelay) HIGH netSerialOut PAUSE 1 REM Now the slave timeslot needs to be created by having a pull-up instead of active push-pull INPUT netSerialOut REM Enable interrupts during timeslots to allow for master listening and action as well SETINT INTinputs, INTmaskEnabled REM Now sit and wait (unless interrupted) for a while to implement the timeslot PAUSE t_slaveTimeSlot REM Timeslot is over ... disable interrupts and reinforce powermode SETINT INTinputs, INTmaskDisabled HIGH netSerialOut RETURN REM ==================================================== REM Interrupt Routine: REM Handles Network message frame reception and handling REM ==================================================== interrupt: REM Line has been brought low by some slave process, REM read the command frame (4 bytes), then restore power supply to slaves REM and determine if the master needs to take action on the message LET commandIndex = noProcessIndex SERIN netSerialIN, N2400_4, IDcalledProcess, IDcallerInfo, dataByteH, dataByteL REM data frame from slave received, so master node interrupts can be disabled now ... SETINT INTinputs, INTmaskDisabled REM .. and active power provision can start again HIGH netSerialOut REM Flash the LED very shortly to indicate bus activity by slaves PULSOUT indicatorLEDoutput, 100 REM Decode process ID LOOKDOWN IDcalledProcess,(IDregisterSendingProcess,IDunRegisterSendingProcess,IDflashLEDmasterNode), commandIndex REM Jump to proper command handling routine (if available), and then REM return to normal execution BRANCH commandIndex, (registerSendingProcess, unRegisterSendingProcess, flashLEDmasterNode) REM if command is not recognised then return to main program (timeslot provision) directly RETURN REM ================================== REM Master Node processes follow below REM REM Implemented processes: REM - registerSendingprocess REM - unRegisterSendingprocess REM - flashLEDmasterNode REM REM ================================== registerSendingprocess: REM Module to register a sending process as indicated by a slave node. REM The ID of the process gets stored in RAM memory in RAMtable. REM REM The information in the received message frame is interpreted as follows: REM - IDcalledProcess is the ID of this registering process. REM - IDcallerInfo contains the ID of the process that requested this activity. REM - dataByteH indicates the largest ID (sendng & nonsending) of all IDs implemented at the node that sent REM the current message frame REM - dataByteL contains the ID of the process to be registered as a sending process REM that requests for timeslots REM Note that RAMtableIndex is adjusted to correctly indicate the end REM of the list of registered processes. REM REM This process can be used by slave nodes to register their sending processes REM at network start-up as well as later on ,for example for forking processes because REM some condition has been reached. In this way timeslots need only be reserved REM when needed. REM First check if this process has already been registered REM (multiple registration of same ID is not allowed): GOSUB searchIDinRAMtable IF RAMsearchedIDpointer > RAMtableIndex THEN REM OK, the ID has not been registered before, so it may be added. REM Store the ID in the RAMtable (if space is available) IF RAMtableIndex < RAMtableLimit THEN REM First increment the pointer to point at the new location INC RAMtableIndex REM write the ID to the RAMtable POKE RAMtableIndex, dataByteL REM Determine new maximum of all known IDs in the network IF dataByteH > maxCurrentID THEN maxCurrentID = dataByteH ENDIF ENDIF ENDIF RETURN REM ------------------- unRegisterSendingprocess: REM A process ID has to be removed from RAMtable. This can be done in two ways: REM - Through the indication of the process ID (as contained in dataByteL, databyte <> IDnoProcess) REM - Through the position of the RAMtable entry (through dataByteH when dataByteL contains IDnoProcess) REM The latter option is used by the optinal Network Manager node. REM This means: REM - look up the particular ID in the RAMtable (not needed for Network Manager) REM - remove it by "copying down" all IDs after this location REM Thus the size of the RAMtable shrinks; new elements can be added REM at the end (no "holes" within the table are allowed) REM REM The information in the received message frame is interpreted as follows: REM - IDcalledProcess is the ID of this unregistering process. REM - IDcallerInfo contains the ID of the calling process that requested the registery. REM - dataByteH (slave nodes): indicates the largest ID of all IDs implemented at the node REM that sent the current message frame. REM - dataByteH (IDcallerinfo = Network Manager): contains new (conservative) estimate REM of maximum ID active in the network (both sending and listening-only) REM - dataByteL contains the ID of the process to be removed as a sending process REM that requests for timeslots. IF IDcallerInfo <> IDnetworkManager THEN REM The ID has to be searched in the RAMtable GOSUB searchIDinRAMtable REM If ID is found then remove it, else return without action IF RAMsearchedIDpointer <= RAMtableIndex THEN GOSUB shiftDownRAMtableElements ELSE REM The position of the ID to be removed is known by the Network Manager REM and it has recalculated (conservatively re-estimated) maxCurrentID as well LET RAMsearchedIDpointer = dataByteL GOSUB shiftDownRAMtableElements maxCurrentID = dataByteH ENDIF RETURN REM ------------------- shiftDownRAMtableElements: REM Helper routine to unregistering of sending process REM Overwrite the found location by shifting down all remaining table elements one step, REM thus reducing the size of RAMtable DO WHILE RAMsearchedIDpointer < RAMtableIndex LET RAMnextElementPointer = RAMsearchedIDpointer + 1 PEEK RAMnextElementPointer, IDcurrentProcess POKE RAMsearchedIDpointer, IDcurrentProcess INC RAMsearchedIDpointer LOOP DEC RAMtableIndex RETURN REM ------------------- searchIDinRAMtable: REM Helper routine to registering/unregistering of sending process REM The ID in dataByteL is searched in the RAMtable REM If found then: REM - RAMsearchedIDpointer <= RAMtableIndex REM - RAMsearchedIDpointer points to the particular ID REM If not found then the condition RAMsearchedIDpointer > RAMtableIndex holds LET RAMsearchedIDpointer = RAMtablePointer DO WHILE RAMsearchedIDpointer <= RAMtableIndex REM Fetch a registered ID from the RAMtable into IDcurrentProcess PEEK RAMsearchedIDpointer, IDcurrentProcess REM If the fetched ID is the same as the requested one then exit this loop REM because it should not be registered again. REM Then RAMsearchedIDpointer will always be <= RAMtableIndex REM which is used as a test condition directly after the execution of this subroutine IF IDcurrentProcess = dataByteL THEN EXIT INC RAMsearchedIDpointer LOOP RETURN REM ------------------- flashLEDmasterNode: REM Master process, can be used by slaves to indicate network activity REM Parameter: REM - databyteL: contains the flash duration (in 2ms units) LET LEDlightPeriod = 200*dataByteL PULSOUT indicatorLEDoutput, LEDlightPeriod RETURN REM ------------------- applyNetworkFrequency: REM Set the node frequency in accordance with the REM network frequency definitions. This routine is needed REM since the M2 and X2 have different base frequencies REM (M2: 4MHz, X2: 8MHz) for the same (network) timing REM information on netwrok frequency is assumed to be stored in RAMnetworkFrequency REM based on the base frequency definition, select the correct CPU frequency SELECT NetworkFrequency CASE FREQ_BASE SETFREQ m4 CASE FREQ_BASEx2 SETFREQ m8 CASE FREQ_BASEx4 SETFREQ m16 CASE FREQ_BASEx8 SETFREQ m32 CASE FREQ_BASEd2 SETFREQ m2 CASE FREQ_BASEd4 SETFREQ m1 CASE FREQ_BASEd8 SETFREQ k500 CASE FREQ_BASEd16 SETFREQ k250 ELSE SETFREQ m4 ENDSELECT RETURN REM ================================= REM END of MASTER NODE implementation REM =================================