'Laser Lock - Display Controller.SXB '------------------------------------------------------------------------------------- 'Overview: '------------------------------------------------------------------------------------- ' The Laser Lock display controller controls various LED bar graphs ' for status and property display. The user controls which property ' is being modified by adjusting an optical encoder, and adjusts ' that property by "selecting" it with a button, and using the ' same optical encoder. LED bar graphs change illumination intensity ' to indicate what is being modified: property or value of property. ' This chip communicates as slave device with the Laser Lock main ' controller via a bidirectional SPI connection '------------------------------------------------------------------------------------- 'Notes '------------------------------------------------------------------------------------- 'Inputs: '------ ' Serial IO with Laser Lock main controller ' User interface: Optical Encoder and momentary button 'Outputs: '------- ' Serial IO with Laser Lock main controller ' Several LED bar graph displays 'LED Bar graph display notes: '--------------------------- 'The LED bar graphs are controlled by a common bus, and are 'selected using 74245 tristate tranceivers. A timeslicing procedure 'serves to: ' -provide independent display control to all connected LED bar graphs using ' reduced pin requirements ' -reduce the overall power draw from LED bar graphs ' -provide differential intensities by varying duty cycles 'Optical encoder encoder waveform notes: '-------------------------------------- 'the optical encoder is a quadrature encoder, meaning the knob has four measurable 'position states given by two digital output values: 'position 0 1 2 3 'OA low high high low 'OB low low high high 'change and direction can be sensed by observing how these values change. '------------------------------------------------------------------------------------- 'Speed selection and device initialization '------------------------------------------------------------------------------------- DEVICE SX28, OSC4MHZ, TURBO, STACKX, OPTIONX '-using an SX28 device, with internal clock of 4 MHz '-TURBO refers to the operation mode, where one clock cycle per 'instruction. '-STACKX, OPTIONX required for SX28 only (apparently - P32 of Practical SXBasic ' programming guide) 'when programming SX48, TURBO, STACKX, and OPTIONX are assumed 'this is used for timing-specific parts of the code, like PAUSE 'this is also used by the SX-KEY, as it generates a clock for debugging 'purposes FREQ 4_000_000 'NOTE: the underscore can be used to separate number values to improve readability ' eg binary valu %0100_0010 versus %01000010 '------------------------------------------------------------------------------------- 'Pin Assignment '------------------------------------------------------------------------------------- 'pin assignment, just like in PBASIC for Basic Stamps, but can assign an entire bank! DispBus PIN RC Output 'display bus is connected directly to RC OA PIN RA.2 Input 'optical encoder inputs OB PIN RA.0 Input 'optical encoder inputs Butn PIN RA.1 Input 'optical encoder button press LED1 PIN RB.7 Output 'line for activating LED display 1 LED2 PIN RB.6 Output 'line for activating LED display 2 LED3 PIN RB.5 Output 'line for activating LED display 3 'chip select input - set low when main controller initiates communication with this CS PIN RB.0 Input 'master in, slave out - default input to allow bussed use of this line Miso PIN RB.1 Input Mosi PIN RB.2 Input 'Master out, slave in - SPI input for this device SPIClk PIN RB.3 Input 'SPI clock - sent from main controller 'attention acknowledge - default input to allow bussed use of this line by other ' controllers AttnAck PIN RB.4 Input Debug Pin RA.3 Output '------------------------------------------------------------------------------------- 'Variable Declaration '------------------------------------------------------------------------------------- 'these are used for determining the position and change of the 'optical encoder state var byte oldstate var byte diff var byte 'running indices for loops i var byte j var byte 'button debounce delay counter debounce var word 'stored values stats var byte 'status bitflags, which are specified by the ' main controller errmsg var byte 'error code, which is specified by the main ' controller (0 = no error) maxprops var byte 'maximum number of porperties to display property var byte value var byte(8) editprop var bit 'this flags whether the user is selecting a ' property or editing that property 'packet data for serial IO packet var byte(10) 'display value storage disp var byte(3) '------------------------------------------------------------------------------------- 'Subroutine Definitions '------------------------------------------------------------------------------------- 'checks the optical encoder for a change in position and returns the change GetEncoderChange sub 0 'performs the display operation on the LED bar graphs Display sub 0 'perform initial configuration and read from main controller ConfigureChip sub 0 'slave asynchronous serial packet read ASyncPacketRead sub 0 'slave aynchronous serial packet write ASyncPacketWrite sub 0 '------------------------------------------------------------------------------------- 'Main Program '------------------------------------------------------------------------------------- 'The PROGRAM directive specifies the label where the program begins. 'In this case, "Start" is the label specified. This also specifies 'where to put the internally generated SX initialization code. 'NOTE: to manually remove the initialization code, look into 'the NOSTARTUP option for the PROGRAM directive PROGRAM Start 'the Start label Start: 'POST of LED's, and delay while main controller boots DispBus = 1 for i = 1 to 8 pause 150 DispBus = DispBus << 1 next 'set all outputs off high LED1 high LED2 high LED3 'startup config: set the position to an intermediate spot 'main controller loads properties by serial communication ConfigureChip 'start the default selected property at property #1 property = 0 'calling this subroutine here initializes the encoder detection behaviour so the ' current position is considered the initial position GetEncoderChange 'startup condition: the property is the thing being selected, not the property value editprop = 0 'main program loop MainLoop: 'NOTE: at 4 MHz, this has an execution time of about 150us per loop. The majority of ' limit comes from the delays imposed to illuminate the displays. Communication with ' main controller will definately limit this further. 'update displays if errmsg = 0 then disp(0) = stats else disp(0) = errmsg endif disp(1) = 1 << property disp(2) = value(property) 'handle display timeslicing Display 'check for main controller io request if CS = 0 then ConfigureChip endif 'don't handle user interface if there is an error if errmsg > 0 then goto MainLoop endif 'determine if there has been a change in the encoder position diff = GetEncoderChange 'if you are selecting which property to edit if editprop = 0 then if diff = $01 then maxprops = maxprops - 1 if property < maxprops then property = property + 1 endif maxprops = maxprops + 1 elseif diff = $FF then if property > $00 then property = property - 1 endif endif else if diff = $01 then if value(property) < $FF then value(property) = value(property) + 1 endif elseif diff = $FF then if value(property) > $01 then value(property) = value(property) - 1 endif endif endif 'check for button use if debounce > 0 then 'dead zone for right after button press debounce = debounce - 1 elseif Butn = 1 then 'reset the debounce timer 'NOTE: this value depends on the decay constant of the button switch decoupler ' circuit, and the execution speed of the code. This works well at 4 MHz, ' using 2.2k resistors and 1 uF cap for decoupling (5V pulse with Vo/e at ' approximately 3 ms. Voltage drops below 1V in aprox 5 ms. debounce = $8F 'toggle the edit property flag if editprop = 1 then editprop = 0 else editprop = 1 endif endif GOTO MainLoop '------------------------------------------------------------------------------------- 'Subroutines '------------------------------------------------------------------------------------- 'This is called at the beginning of execution, and every time the CS is set low. ' This initiates communicaiton with the main controller, and reacts accordingly sub ConfigureChip 'read the packet in. Command # is stores in packet(1) ASyncPacketRead 'command $00: write number of properties, and write to all properties if packet(1) = $00 then 'write number of properties to maxprops maxprops = packet(2) 'load in all values into the property array for i = 0 to packet(2) if i < packet(2) then j = i + 3 value(i) = packet( j ) endif next errmsg = 0 'command $01: read selected property elseif packet(1) = $01 then 'determine which property is requested to be read j = packet(2) j = j - 1 'change index to be compatable with array indexing packet(0) = 2 packet(1) = value(j) 'send the response back ASyncPacketWrite 'command $02: set status value elseif packet(1) = $02 then stats = packet(2) 'command $03: read number of properties. Useful for checking if the chip ' is configured properly elseif packet(1) = $03 then packet(0) = 2 packet(1) = maxprops ASyncPacketWrite 'command $FF - set error message elseif packet(1) = $FF then maxprops = $1 errmsg = packet(2) endif endsub 'this subroutine determines the state of the optical encoder, knob, and if there has ' been a change sub GetEncoderChange state.1 = OB state.0 = OA 'if there's no change, diff will be 0 diff = state XOR oldstate 'if there is a change, then determine what the change is if diff > 0 then 'shift the old state left by one bit oldstate = oldstate << 1 diff = oldstate XOR state 'choose polarity here! if diff.1 = 0 then diff = $FF 'decrease else diff = $1 'increase endif endif oldstate = state return diff endsub 'this timeslices the display, with pauses depending on the current display state sub Display 'write the status to the display bus DispBus = disp(0) LOW LED1 'strobe in the first display if errmsg = 0 then pauseus 90 'normal display time for status else 'if there is an error, then flash the error on the status display pause 1800 endif HIGH LED1 'normal operation if errmsg = 0 then pauseus 5 'write the parameter number to the parameter display DispBus = disp(1) LOW LED2 'strobe in second display if editprop = 0 then pauseus 90 'timeslice: long if property is being selected else pauseus 25 'timeslice: short if property value is being ' edited endif HIGH LED2 'write the parameter value to the parameter display DispBus = disp(2) 'strobe in third delay LOW LED3 if editprop = 1 then pauseus 90 'timeslice: long if property value is being ' edited else pauseus 25 'timeslice: short if property is being ' selected endif HIGH LED3 'error message operation else 'if there is an error, then flash the error on the status display pause 200 endif endsub 'this performs asynchronous packet read operation sub ASyncPacketRead 'indicate that packet read sub has begun DispBus = $80 LOW LED1 'wait for CS to go low do while CS = 1 loop 'indicate that CS has gone low, and this is ready to begin receiving data DispBus = $C0 'take control of attention acknowledge line and set it low AttnAck = 0 output AttnAck 'read packet size header byte serin Mosi, N57600, packet(0) 'read the rest of the packet for i = 1 to packet(0) if i < packet(0) then serin Mosi, N57600, packet(i) endif next 'wait for CS to go high again do while CS = 0 loop 'release attention acknowledge line (pullup resistor will set it high) input AttnAck 'release the display bus HIGH LED1 endsub 'this performs asynchronous packet write operation sub ASyncPacketWrite 'flag Ack line to indicate that this is ready to begin transfer AttnAck = 0 output AttnAck 'wait for master controller to indicate that it is also ready do while CS = 1 loop for i = 0 to packet(0) 'send packet byte i on Miso line if i < packet(0) then serout Miso, N57600, packet(i) endif next 'release the Miso line so other devices can write to main controller input Miso 'reset attention acknowledge to an input, which is tied to high, indicating ' packet transfer complete input AttnAck 'wait for main controller to acknowledge packet transfer complete do while CS = 0 loop endsub