A dsPIC-Based Laser Light Show Controller

Abstract

Presented is a laser light show utilizing a ds-PIC controller as an arbitrary waveform generator (ARB). The controller allows vector patterns to be either stored in flash (program) memory or uploaded from a PC in a vector format. Analog output, via two 14-bit DACs, is fed to a commercial analog driver board and two high-speed galvo scanners.

The scanner currently resides in the laser lab at Niagara College (in V15) where it is used for both student labs as well as lab demonstrations. A portable scanner, packaged with a HeNe laser, was also created for 'road show' use at promotional engagements - both use the same hardware and software.

V-30 Scanner Logo

The splash logo from the system. This image, composed of multiple, unretouched, photos of actual scanner output is made possible by resizing the image in the horizontal direction only. To mirror the image, X coordinates are simply multiplied by a negative number. All of this is made possible by the high-speed multiplier built into the dsPIC processor chip.

Laser Scanner Controller

The prototype controller is built onto a Microchip dsPICDEM Starter demonstration board featuring a 30F6012 processor chip. The board has a prototyping area which is populated with the DACs, two connectors (one for power and one for analog output), and voltage regulators. The board already contains basic I/O circuitry including an RS-232 interface, which simplifies the construction of the system.

Theory

The best background on laser scanning in general is the ELM Homebuilt Laser Projector Page which describes the theory of laser scanners as well as presents a homebuilt design. This site also features vector design software and example files.

In a basic light show, a beam from a laser is deflected by two galvanometer scanner heads, each of which moves a mirror to steer the beam in the horizontal and vertical direction independently. Given the high speeds at which these galvos are required to operate, they are quite pricey.

GSI Scanner Board As well as the actual galvanometer movements (in my case two Cambridge 6350 moving coil closed-loop units) a driver is required which usually has feedback from the sensors. The entire system is usually a PI or PID (proportional-integral-derivative) control loop with the output fed to the galvos and feedback supplied via a capacitive sensor inside the galvo scanners themselves - you can probably appreciate the expense of the scanners owing to the fact that they contain a variable-vane capacitor which changes at most a few pF during rotation. The driver/control-loop usually accepts analog signals: in my case the GSI-Lumonics scanner board (show to the left) employed gives full deflection in one axis with an applied signal varying from -3V to +3V (it is bipolar). This board contains a plethora of high-end AD OP-Amps as well as high-current drivers. Small plug-in daughterboards are included as well with notch filters to control resonances in the scanners.

Up to this point, what is basically described is a slow X/Y oscilloscope in which two analog inputs are used to direct the beam in either direction. In my specific case, I was fortunate to have the scanners and a matching driver board available. Next, a control system is required to generate waveforms.

The earliest laser light shows, in the 1970's, used analog technology. Signals were generated using analog generators and often stored on multi-track magnetic tape. Consider a simple Lissajous pattern generated with two sine waves (supplied by commercial lab-type signal generators) which are out-of-phase by 90 degrees:

Laser Show - Circle with Signals

This pattern, a circle, may be made to rotate by simply setting the frequency of one of the sine waves to be slightly ahead of the other axis - for example using a 100Hz signal for the X-axis and a 100.1Hz for the Y-axis. Many other patterns are possible by setting the frequencies of the axes to be multiples of one another as seen here in the Laser Light Show lab outline:

Laser Show - Lissajous Figures

Add colour, the ability to offset an image (by adding a DC offset to the signal) and variable gain amplifiers, and fairly complex shows can be made as seen in this open-shutter photograph of the classic light show Laser U2, as shot by the professor in the MacMillan Planetarium, Vancouver BC, in 1988:

Laser U2

While Lissajous patterns are interesting and smooth, the drawing of complex graphics (including text) requires digital technology to generate the complex waveforms required.

Complex patterns are generated by vectoring: breaking long line segments into smaller segments which are scanned out via DACs at fixed speeds, e.g. 8000 points per second - with each 'point' consisting of an (X,Y) pair. In this manner, a straight line can be drawn by sending successive (X,Y) values to the DACs which generate corresponding voltage levels to position the mirrors.

Laser Scanner Output

Here, the beam from an air-cooled argon laser is deflected by two galvanometer movements, one for each direction (X and Y). An ISOMET AO modulator (the gold box in the center of the photo) is used as a blanker allowing the beam to be switched on and off rapidly (it is not properly aligned in this photo since 'hidden' lines between graphics elements are still visible). The vectors, produced as analog signals from two DACs on the DSP board, direct the position of the galvanometer movements.

Laser Scanner Output - Scope Shot The vector nature of the output becomes evident in this shot of the output as seen on an analog oscilloscope. When in X/Y mode, an analog scope, as opposed to a digital, shows areas where the beam stays longer as bright dots - in this case the endpoints of each vector. On the scope screen, movement between endpoints is so fast that the beam is not clearly visible between endpoints (at 8000pps, the beam dwells at individual points for 125ms). Some points visible here would not be visible in the projected image since the blanker would turn off the beam between these points.

Hardware

This laser light show controller is built on a dsPICDEM Starter demonstration board which features a 30F6012 MCU and a connector for an ICD-2 debugger module. Additional required circuitry includes two 14-bit DACs - Analog Devices AD7840 parallel DACs were chosen - which are mounted on the prototyping area on the board. These DACs require bipolar (-5V and +5V) power supplies and have a full-range output swing from -3V to +3V which is a good match to the GSI-Lumonics driver board used for the galvanometers.

In order to simplify the wiring of the DACs, the fourteen data lines (D0 through D13) are wired to pins RB2 through RB15 on the 30F6012. RB0 and RB1 were unavailable since they are pre-wired on the board to the ICD-II debug connector (this was also the reasoning to use a 14-bit DAC ... aside from the fact that 14 bits of resolution are more than enough for the task). Data lines on both DACs were wired in parallel with the control wiring for each DAC (/CS, /WR, and /LDAC) wired to unique I/O pins on the chip. Independent control lines allow both DACs to be updated simultaneously if required.

The DACs also require two supply voltages. +5V is derived from the on-board 7805 regulator while -5V is provided via a 7905 regulator added to the prototyping area of the demo board. The DACs themselves provide an internal 3V reference which is used for both DACs. The reference voltage is divided via a potentiometer which serves as a gain control to allow adjustment of the image size.

Aside from the DAC wiring, additional lines from the dsPIC chip are made available for blanking as well as a PCAOM (Poly-Chromatic AOM) used as a colour selector.

Laser Scanner Hardware

Two dual-inline ten-pin headers connect the board to the outside world. One is used for power - to supply the board with +12V and -12V as required for the DACs and the DSP chip - and the second connector is used for I/O. I/O includes two analog outputs from the DACs, and several digital outputs (connected to I/O pins on the chip) for blanking and PCAOM use. Grounds were separated into analog (AGND) and digital (DGND) to enhance noise immunity.

Software

First and foremost, a storage format is required for vectors in memory. With a 16-bit word size, a single (X,Y) vector can easily be stored in two consecutive words. The format chosen was designed to allow easy playback of the stored vector via the DACs, which accept data in a two's complement signed form - in other words the most-significant bit is the sign bit. Since the stored number is only 14-bits (13 plus the sign bit), two "unused" bits, D14 and D13, are used to store blanking and PCAOM data. To convert a 14-bit number for storage bit D14 and D13 are first cleared: the number now consists of the sign bit in D15 and the mantissa in bits D0 through D12. The two remaining bits assigned as follows: For the X value (the first word) D14 is used to indicate BLANKING (active high), while D13 (X only) and D14 and D13 (Y only) are used for PCAOM selection bits.

Most vector formats use 16-bit values (between -32768 and +32767) which are divided by four to convert to a 14-bit value. The original format chosen is a generic CSV (comma separated value) format. Utilities available from the ELM site (and a few other sites) allow conversion of ILDA frames to this format. The format is as follows:

VECTOR,[pps],[delay]
Frame Name
[v],[x],[y]
[v],[x],[y]
....
[v],[x],[y]

The first line, starting with the keyword VECTOR, is the header. In the header, is the scan rate in PPS - it is ignored here and is set by the system - only the keyword VECTOR is used to start a vector upload. The frame name is a text string (max 16 chars) to identify the frame - it is also ignored. Point data is in the form [v],[x],[y] where [v] is the visibility flag (1=visible, 0=blank), and [x] and [y] are X-Y vectors as 16-bit signed numbers. When converting data to a format suitable for program memory (i.e. storage in a table), only point data is used.

Now, consider the position (3666,995) from the original CSV data file which is converted to the 14-bit numbers (917,-249). The X value would be 0x394. If the vector was a blank, D14 would be set high and so the X value would be stored as 0x4394 in memory (a visible vector would have a value of 0x0394). Similarly, a position of -474 would be stored as 0x9E26 (with the beam being visible since the blanking bit, D14, is clear).

One 'feature' of this particular scanner is that the Y-axis scanner is mounted upside-down so that positive numbers cause the beam to deflect downward and negative numbers, upwards. Y-values, therefore, must be inverted by multiplying by -1 to invert the image. Finally, a value of (0xFFFF,0xFFFF) is used to indicate the end of a frame. To be safe, unitinialized memory can be set to this value to ensure an 'overrun' pointer will not read an invalid value and produce bizarre results.

A spreadsheet was used to process values. The spreadsheet divides X/Y values by four (converting to 14-bits), multiplies the Y-axis by -1 to invert it, masks the unused blanking bits (D13/14), and generates code in the form of "hword" directives for that code produced by the spreadsheet may be cut and pasted directly into the data segment of a dsPIC program in program flash memory.

For example, consider the original point (from the CSV file for the 'Radiation' symbol above):

The assembly code may be pasted directly into the data segment of the program where it will be stored in program (flash) memory and may be accessed using table pointers [e.g. mov #tbloffset(LaserRadiationSign),w5]. Several images used as 'splash' screens were converted in this manner and are stored permanently in program memory.

Aside from the preprogrammed frames in flash program memory, vector files may be uploaded into chip SRAM via the serial port. Upload begins with the command 'VECTOR' (part of the CSV file) followed by (b,x,y) pairs where b=0/1 (blanking), and x & y coordinates as per above. PSV is used to map all frames into the same memory space for easy access - only the initial pointer need be reassigned in order to change the image scanned. From the RAM frame, the data may be copied into the program flash memory using the RTSP feature which allows writes to flash memory under program control (usually used to update a program via a bootloader). Flash memory is divided into 4K blocks, each holding one frame.

The actual code to produce the image is quite simple since the values in memory are already processed. The 16-bit number (representing X/Y position) is processed to make it a true 14-bit number with the LSB in bit position 2 and the sign bit in position 15. The value is now output directly to the DAC via port lines RB2-RB15, data is latched, and the DAC is updated. Other elements of the program (which is completely interrupt-driven) include the command interpreter (which understands a few basic commands such as VECTOR to upload a frame to RAM memory, SPEED to change the scan rate, and RUN to select a frame to display), storage of incoming characters to string memory (essentially a 'get-string' function), and a conversion function which converts a string to a binary number (string-to-binary) to parse arguments (including comma-separated X/Y values).

The conversion function ConvertString accepts the argument of a pointer to the end of a string of ASCII numbers and returns a 16-bit number. It uses the dsp 'mac' instruction and the 40-bit accumulator to add each digit. The last digit (least-significant) is simply converted from ASCII to binary (subtract 0x30) and stored in the accumulator. The next digit is converted similarly and multiplied by ten before storage. the process continues with each digit multiplied by 100, 1000, etc. until a delimiter is found such as a space, comma, or negative sign prefix. The converted number, now in the accumulator, is returned from the function as a signed 2's complement 16-bit value. This function is used extensively to convert incoming desimal values into binary numbers for storage and/or argument parsing.

The command-line interpreter accepts user commands via the serial port. Strings for comparison are stored in program memory and compared to the string in the incoming string buffer. When a string matches, a function is called to process the buffer and extract arguments as required using the ConvertString function described above.

An accurate time delay is required for the scanner since patterns are scanned-out at fixed rates of 8000, 10000, or 12000 points-per-second. The Chip is clocked at 4MHz with the PLL (*16) active for an internal clock frequency of 64MHz. For a scan rate of 8000 pps, for example, a delay of 125ms is required between points. By loading the timer with a value of 2000, a delay of 2000 times the internal instruction cycle time of 62.5ns is effected - the scan rate is changed simply by loading a new value into the PR1 register for timer 1. The scan rate may be changed using the 'SPEED' command via the serial port.

Enhancements: Image Manipulation

In a traditional 1970's and 1980's light show, many images were produced by analog oscillators and computers composed of many op-amps. Op-Amps can be used to accomplish many effects: for example, a simple lissajous pattern can be made to move in any direction by adding a DC offset by using a summing amplifier. Image size can be adjusted by using variable-gain amplifiers or attentuators. Inverting amplifiers can be used to invert an entire image about an axis .... the list is somewhat endless. It was desired to give this laser scanner similar functionality however in the digital domain.

In order to produce "analog-type" effects, several functions were added to allow the manipulation of image size, position, and rotation. Image position is changed simply by adding a number (positive or negative) to the coordinates before output by the DACs.

The basic system operates by first generating the waveform via an arbitrary-waveform generator which simply accesses stored 16-bit values in memory for the X and Y coordinates. These numbers are then multiplied by a size variable - size is multiplied by the coordinates then the final value is divided by 100 so that '100' represents full size. Rotation is then accomplished by using the standard transforms X'=X*cos(Theta)-Y*sin(Theta) and Y'=X*sin(Theta)+Y*cos(Theta). Sine and Cosine coefficents are provided by 16-bit tables generated using a spreadsheet and pasted into program memory. These tables have a resolution of one degree. An offset is then added to the X and Y values to accomplish a position shift. Finally these values are sent to the 14-bit DACs.

Immediate commands (available via the console port) include many of these image manipulations as follows:

VECTOR - to upload a CSV file into the RAM frame
SPEED - to set the scanner speed in pps
RUN - to select a frame to output
SAVE - Write the RAM frame into flash memory
SIZEX / SIZEY - Image scale in percent
RESET - Resets all scaling values to default (100%), halts scripts
ROTATE - Rotates the image, argument is in degrees
POSX / POSY - The image position offset
PHASE - The shift of the Y term ahead of the X term
SCRIPT - To upload a script to the script RAM (see below)
SRUN - To start a script in memory running

Scripting

One of the most useful features for displaying a complete show is the ability to run a stored script consisting of commands which affect the image. The script commands consist of 16-bit values of which six bits are the command (op-code) and ten bits are the argument (with a possible range of -512 to +511).

Scripts are loaded into a reserved area of memory in a manner similar to how the vector patterns are uploaded. To run a script, the 'SRUN' (Script RUN) command is issued on the console (serial) port which sets a global flag. The script interpreter then begins to fetch commands from the script memory area and uses, essentially, a large 'case' switch to select a function. There are simple functions to set the position and size variables as well as support for a time delay and use of variables (four, labelled 'A' through 'D') which allows loops to be constructed in a script. A typical script looks like this:

RESET
FRAME       1
SIZEX       1   Start at size 1%
SIZEY       1	
LOADA      36   Number of iterations
LABEL       1	
DELAY      10   Delay 10ms between frame changes
ROTATEINC  10   Rotate by 10 degrees each time (*36 = 360 degrees)
SIZEXINC    3   Size grows by 3%
SIZEYINC    3	
INCA       -1   Variable A-=1
BRNZA       1   Branch if A>0 to label 1
DELAY     500	
LOADA      36	
LABEL       2	
DELAY      10	
ROTATEINC -10	
SIZEXINC   -3	
SIZEYINC   -3	
INCA       -1	
BRNZA       2	
RESET		
LOOP		

This script loads frame #1 from flash memory and displays it at 1% of it's size. The size of the image grows at the same time the image rotates. After 360 degrees rotation, the image is 90% of it's original size. Scripts are written as per above and translated by a script compiler written in Excel. Each instruction is translated into a 16-bit value which is the uploaded to the scan engine.

It should be noted that once invoked, the script interpreter on the dsPIC runs until a DISPLAY, DELAY, or WAIT command is found at which point control is returned to the main scan routine to display a frame (displaying only one frame, after which the script interpreter is called again). The DISPLAY command displays only one frame, DELAY will continue displaying frames until a timer is complete (allowing, for example, an image to be displayed for 500ms), and WAIT will display frames until an external input goes high. WAIT is used to synchronize a script with an external event (for example, a sync signal embedded in a show - one could use the video stream from a DVD player to do this).

The full list of script commands can be found in the script compiler written in Excel.

Conclusion

This project is a continuing work. Future plans include the addition of a vector-bisection algorithm which will break-up long vectors for successive scanning - in this manner it would be unnecessary to divide long lines into multiple vectors as the scanning system will do this automatically. It will be possible, then, to draw a square simply by defining four points (the corners) ... the algorithm will evaluate the length of each vector and, assuming it exceeds a threshold value, begin disecting it into 2,3,4, or more parts as necessary to keep each vector element small enough to scan properly (i.e. without overshoot). The use of a DSP processor will be appreciated here since the chip has features allowing simple scaling, squaring, and square-root determination. This will also overcome a current limitation which may be seen when frames are switched: when the display of one frame is complete and the display of a second frame begins the beam is seen to suddenly jump from one area (the end-of-frame position of the first image) to another (the beginning-of-frame position of the second image). The lack of proper vectoring of this 'frame jump' trace also causes 'tearing' of the second image.


Download the firmware, version 1.00 in MPLAB 7.5x assembler format. This version is basic and supports only basic output.

Download the firmware, version 1.50 in MPLAB 7.5x assembler format. This version is the latest and most advanced version, supporting scripts and image manipulation commands. It also features the ability to store an entire script into flash memory so upload of an entire show script is not required.

Download the script compiler in Microsoft Excel format.