// // pulser.c : Raspberry pi utility that works with LabView program // AblationStation to serve as a pulse generator for the // MSC-101 EMG Lambda Physik excimer laser. // // author: brendan.pratt at uconn.edu // version: april 30, 2015 // // notes: // 1) Laser pulses are generated at a fixed rate, currently set to // fire at a rate of LASER_PULSE_RATE (Hz) defined below. // 2) The AblationStation software issues commands to the ablation // ablation state motor stages to move at a given speed across the // sample, and then sets one of the GPIO pins (GO) to high. This is // taken as a signal that the pulser program should start pulsing at // the LASER_PULSE_RATE until the fixed number of pulses have expired. // 3) The GO pin should go low around the time that the pulse pattern // is completed but this does not shorten the pulse pattern, nor // prolong it in case it stays high for a long time. // 4) After the preset pulse pattern is completed, the GO pin should // transition from low to high again to start the next row. // 5) The actual sequence of fired/skipped pulses during a single row // is encoded in an input file, one pattern per line. Each pattern // is executed at least once, and possibly multiple times as determined // by the AblationStation algorithm. // 6) Whenever it is time to advance to the next pattern (next line in // the input file), a second pin (ADVANCE) is asserted together with // the GO pin. // 7) The pattern on each line in the input sequence file may be written // either forward or reverse order, and it is up to pulser.c algorithm // to arrange them to maintain an order that is consistent with the // serpentine pattern of motor sweeps across the sample. The first row // is always left-to-right. // 8) If for some reason the AblationStation algorithm tells pulser.c to // advance past the last line in the sequence file, it should fall back // to the empty pattern where no laser pulses are generated. // 9) The pulser algorithm is designed not be interruptable in the middle // of one row pattern, but at the end of each row it pauses and must // see the GO pin go high again before any more pulses are generated. // 10) The pulser program needs to know which direction the motors are // moving in, left-to-right or right-to-left. This information is // communicated from AlbationStation by setting (right-to-left) or // reseting (left-to-right) a third pin (DIRECTION) before the GO edge. // // usage: // Two command-line arguments are required, as shown in the following example // $ sudo ./pulser input_file.seq 15 // where input_file.seq is the name of the input sequence file for this pass // that has prepared by the user, and "15" specifies at which line in the // sequence file the pulser algorithm should start. Sequence file line // numbers start at 0. // #include #include #include #include #include #include "rtc.h" #define LASER_PULSE_RATE 50 #define MAX_ROW_COUNT 2000 #define MAX_COLUMN_COUNT 2000 #define DEGLITCH_INTERVAL 6 // GPIO bus pin numbers #define PULSE_OUT 4 #define GO_IN 18 #define ADVANCE_IN 23 #define REVERSE_IN 24 typedef uint32_t bitarray_t; bitarray_t *parray; double xlim[2][MAX_ROW_COUNT]; double ylim[2][MAX_ROW_COUNT]; int pcount[MAX_ROW_COUNT]; int max_lineno = 0; int advance_pin = 0; int reverse_pin = 0; void usage() { printf("Usage: pulser \n"); exit(1); } bitarray_t *mallocbit(int size) { // Utility function to implement bit array semantics in c int w32 = ceil(size / 32.); return malloc(sizeof(uint32_t) * w32); } void setbit(bitarray_t *bitarray, const int index, const int value) { // Utility function to implement bit array semantics in c int w32 = index / 32; int bit = index % 32; if (value) bitarray[w32] |= (1 << bit); else bitarray[w32] &= ~(1 << bit); } int testbit(const bitarray_t *bitarray, const int index) { // Utility function to implement bit array semantics in c int w32 = index / 32; int bit = index % 32; return (bitarray[w32] >> bit) & 1; } void parse_input_file(FILE *fp) { // Reads pulse sequence information from the input file // and stores the information in global arrays: // parray[line * MAX_COLS + col] : 2d pulse on/off table, one per bit // xlim[n][line] : x-limits of this input line, low (n=0) and high (n=1) // ylim[n][line] : y-limits of this input line, low (n=0) and high (n=1) // pcount[line] : total number of pulses (bits) in this line char inbuff[1000]; int lineno = 0; while (fgets(inbuff, sizeof(inbuff), fp)) { int rowno; int w, wmax; int reverse; int p, pmax; bitarray_t *workarray = mallocbit(MAX_COLUMN_COUNT); sscanf(strtok(inbuff, " "), "%d", &rowno) ; sscanf(strtok(0, " "), "%f", &ylim[0][lineno]); sscanf(strtok(0, " "), "%f", &ylim[1][lineno]); sscanf(strtok(0, " "), "%f", &xlim[0][lineno]); sscanf(strtok(0, " "), "%f", &xlim[1][lineno]); sscanf(strtok(0, " "), "%d", &pcount[lineno]); reverse = (xlim[0][lineno] < xlim[1][lineno])? 0 : 1; if (reverse) { double xx = xlim[0][lineno]; xlim[0][lineno] = xlim[1][lineno]; xlim[1][lineno] = xx; } pmax = pcount[lineno]; wmax = ceil(pmax / 32.); for (w = 0; w < wmax; w++) { sscanf(strtok(0, " "), "%x", &workarray[w]); } for (p = 0; p < pmax; ++p) { int index = lineno * MAX_COLUMN_COUNT + p; if (reverse) setbit(parray, index, testbit(workarray, pmax - 1 - p)); else setbit(parray, index, testbit(workarray, p)); } ++lineno; } max_lineno = lineno; fclose(fp); } main(int argc, char **argv) { FILE *finp; char finname[999]; int startline; char inbuff[1000]; int lineno; volatile int *MHzClock; // parse command line if (argc != 3) { usage(); } else if (sscanf(argv[2], "%d", &startline) != 1) { usage(); } else if ((finp = fopen(argv[1], "r")) == 0) { fprintf(stderr, "Error - cannot open input file %s\n", argv[0]); exit(1); } // load input pulse pattern into parray parray = mallocbit(MAX_ROW_COUNT * MAX_COLUMN_COUNT); if (parray == 0) { fprintf(stderr, "Error - failed to allocate parray, please resize.\n"); exit(1); } parse_input_file(finp); // initialize real-time clock and gpio pins MHzClock = setup_rtc(); gpio_config_output(PULSE_OUT); gpio_config_input(GO_IN, 1); gpio_config_input(ADVANCE_IN, 1); gpio_config_input(REVERSE_IN, 1); // loop over all remaining lines in the input file lineno = startline; while (1) { int reverse; int advance; int repcount; // polling loop for GO edge, leave room for keyboard interrupts repcount = 0; while (repcount < DEGLITCH_INTERVAL) { if (gpio_read_input(GO_IN)) repcount = 0; else ++repcount; } printf("waiting for go at input line %d of %d...\n", lineno, max_lineno); repcount = 0; while (repcount < DEGLITCH_INTERVAL) { if (gpio_read_input(GO_IN)){ ++repcount; } else repcount = 0; } // advance to next line if requested advance = 0; if (gpio_read_input(ADVANCE_IN)) { advance = 1; ++lineno; } if (lineno >= max_lineno) { break; } // block interupts interrupts(0); // shift out the pulses at LASER_PULSE_RATE if (gpio_read_input(REVERSE_IN)) { int p; reverse = 1; for (p = pcount[lineno] -1; p >= 0; --p) { int value = testbit(parray, lineno * MAX_COLUMN_COUNT + p); int tstart = *MHzClock; while (*MHzClock < tstart + 100) gpio_write_output(PULSE_OUT, value); while (*MHzClock < tstart + 20000) gpio_write_output(PULSE_OUT, 0); } } else { int p; reverse = 0; for (p = 0; p < pcount[lineno]; ++p) { int value = testbit(parray, lineno * MAX_COLUMN_COUNT + p); int tstart = *MHzClock; while (*MHzClock < tstart + 100) gpio_write_output(PULSE_OUT, value); while (*MHzClock < tstart + 20000) gpio_write_output(PULSE_OUT, 0); } } // unblock interupts and continue interrupts(1); printf("finished line %d, advance=%d, reverse=%d\n", lineno, advance, reverse); } }