ECE4760 - Laser Projector (ipb7, jcc384, pfc38)  1
Raster Laser Projection
projector.c
Go to the documentation of this file.
1 
26 #include "projector.h"
27 
28 /*******************************/
29 /* LOCAL Macro Definitions */
30 /*******************************/
32 
33 // DAC Configs
34 #define DAC_A (0b0 << 15)
35 #define DAC_B (0b1 << 15)
36 #define DAC_GAIN_VREF (0b1 << 13)
37 #define DAC_GAIN_2VREF (0b0 << 13)
38 #define DAC_ACTIVE (0b1 << 12)
39 #define DAC_INACTIVE (0b0 << 12)
40 
41 #define PIXEL_DMA_CHN (0)
42 #define Y_MIRROR_SPI_CHN (1)
43 #define Y_MIRROR_SPI_CONFIG (DAC_A | DAC_GAIN_2VREF | DAC_ACTIVE)
44 
48 #define PIXEL_ON_TIME (400)
49 
50 
51 /********************************/
52 /* LOCAL Type(def) Declarations */
53 /********************************/
55 
57 
58 /*******************************/
59 /* LOCAL Variable Definitions */
60 /*******************************/
62 
67 static volatile uint8_t current_row;
68 
70 
71 /*******************************/
72 /* LOCAL Function Declarations */
73 /*******************************/
75 
82 static void configure_dma_for_row(uint8_t row_number);
83 
89 static void trigger_row(void);
90 
97 static void update_y_axis_position(uint8_t row_number);
98 
104 static void write_pixel(struct color const pixel);
105 
107 
108 /*******************************/
109 /* GLOBAL Variable Definitions */
110 /*******************************/
112 
115 
117 
118 /*******************************/
119 /* GLOBAL Function Definitions */
120 /*******************************/
122 
125  /* Set Up Pixel CLock */
127 
128  // Open Timer1 with the following configuration:
129  // - T1_ON :: Timer is turned on
130  // - T1_SOURCE_INT :: Clock source is internal
131  // - T1_PS_1_1 :: Prescalar is 1 to 1 (timer sees PBCLK)
132  //
133  // The timer's period is used to fine-tune how long each pixel lasts;
134  // one pixel displayed every time the timer wraps around.
135  OpenTimer1(T1_ON | T1_SOURCE_INT | T1_PS_1_1, PIXEL_ON_TIME);
136 
137  // set up the pin to reset the Gate Latch, set initially high
138  // (the signal is active low)
139  PORTSetPinsDigitalOut(IOPORT_A, BIT_3);
140  mPORTASetBits(BIT_3);
141 
143  /* Set Up CN */
145 
146  // see reference manual 12.3.3.1
147  CNCONASET = BIT_15; // enable CN on Port A (set bit 15 of CNCON to 1)
148  PORTSetPinsDigitalIn(IOPORT_A, BIT_4); // RA4 is input
149  unsigned int ignore = PORTA; // clear interrupt
150  IPC8SET = ((unsigned int) 5) << 18; // set CN interrupt priority
151  // (IPC6<20:18>=5, defaults to 0 at POR)
152  IFS1CLR = BIT_13; // clear CN interrupt flag (set bit 0 of IFS1 to 0)
153  CNENASET = BIT_4; // enable for RA4 (set bit 4 of CNENA to 1)
154  // see family data sheet 11.1.4
155  IEC1SET = BIT_13; // enable CN interrupt (set bit 0 of IEC1 to 1)
156 
158  /* Set Up DMA */
160 
161  // Set up the pins as an output
162  PORTSetPinsDigitalOut(IOPORT_B, BIT_0 | BIT_1 // red
163  | BIT_2 | BIT_3 // blue
164  | BIT_4 | BIT_5); // green
165 
166  // Configure DMA interrupt vector and enable DMA interrupts on CPU
167  INTSetVectorPriority(INT_VECTOR_DMA(PIXEL_DMA_CHN), INT_PRIORITY_LEVEL_5);
168  INTSetVectorSubPriority(INT_VECTOR_DMA(PIXEL_DMA_CHN),
169  INT_SUB_PRIORITY_LEVEL_3);
170  INTEnable(INT_SOURCE_DMA(PIXEL_DMA_CHN), INT_ENABLED);
171 
172  configure_dma_for_row(0);
173 
175  /* Set Up SPI */
177 
178  // SPI Slave Sync output on pin 26
179  PPSOutput(1, RPB15, SS1);
180 
181  // SPI1 data output on pin 17
182  PPSOutput(2, RPB8, SDO1);
183 
184  // SPI channel 1 channel A drives the y axis control mirror.
185  // The messages we send are 16 bits long (4 control bits, 12 signal bits).
186  // The config flags do the following:
187  // - SPICON_MSTEN :: Set to master mode
188  // - SPICON_MODE16 :: Sets the word size to 16 bits
189  // - SPICON_ON :: Module ON Control
190  // - SPICON_FRMPOL :: Polarity of frame signal
191  // - SPICON_CKP :: Reverse clock edges
192  // - SPICON_FRMEN :: Use automatic framing: Serial clock is always on
193  // even when no data is being sent and Slave Select/CS
194  // pulses before each word
195  // The fpbDiv = 2 sets baud rate to Fpb / fpbDiv.
196  SpiChnOpen(Y_MIRROR_SPI_CHN, SPICON_MSTEN | SPICON_MODE16 | SPICON_ON
197  | SPICON_FRMPOL | SPICON_CKP | SPICON_FRMEN, 2);
198 }
199 
200 static void trigger_row(void) {
201  WriteTimer1(0);
202  DmaChnEnable(PIXEL_DMA_CHN);
203 }
204 
205 void projector_set_pixel(struct color const color,
206  unsigned int x, unsigned int y) {
207  projector_framebuffer[y][x + RED_PHASE_SHIFT ].red = color.red;
210 }
211 
213 
214 /*******************************/
215 /* ISR Definitions */
216 /*******************************/
218 
229 void __ISR(_DMA0_VECTOR, IPL5SOFT) EndOfRowHandler(void) {
230  // acknowledge the INT controller, we're servicing int
231  INTClearFlag(INT_SOURCE_DMA(DMA_CHANNEL0));
232 
233  // turn off the lasers
234  write_pixel(color_blank);
235 
236  current_row = (current_row + 1) % IMAGE_HEIGHT;
237 
238  // move the y-axis mirror to the next row
239  update_y_axis_position(current_row);
240 
241  // reset the Timer Gate Latch
242  mPORTAClearBits(BIT_3); // set the active low reset signal
243  Nop(); Nop(); Nop(); Nop(); // wait for reset to occur - 4 nops is 100ns, an
244  // upper bound on the time the signal has to be
245  // low for the latch to be happy
246  mPORTASetBits(BIT_3); // unset the active low reset signal
247 
248  // prepare the DMA to output the next row
249  configure_dma_for_row(current_row);
250 }
251 
252 void __ISR(_CHANGE_NOTICE_VECTOR, IPL5SOFT) LightArrival(void) {
253  if ((PORTA & BIT_4) == BIT_4) {
254  // if the bit is high, the light just went off. The light disappearing is a
255  // low-to-high edge.
256  trigger_row();
257  }
258 
259  // clear the interrupt flag
260  IFS1CLR = BIT_13;
261 }
262 
264 
265 /*******************************/
266 /* LOCAL Function Definitions */
267 /*******************************/
269 
270 static void configure_dma_for_row(uint8_t row_number) {
271 
272  // Open dma channel, priority 3, in default mode
273  // (transfer the block and stop)
274  DmaChnOpen(PIXEL_DMA_CHN, 3, DMA_OPEN_DEFAULT);
275 
276  // Set up the DMA transfer
277  DmaChnSetTxfer(PIXEL_DMA_CHN, // channel number
278  (void *) projector_framebuffer[row_number], // Source
279  (void *) &LATB, // Destination
280  IMAGE_WIDTH, // source size
281  1, // dest size
282  1); // cell size
283 
284  // Start DMA channel transfer on IRQ from timer 1
285  // i.e. transfer a pixel every time the timer hits its period
286  DmaChnSetEventControl(PIXEL_DMA_CHN, DMA_EV_START_IRQ(_TIMER_1_IRQ));
287 
288  // Create an interrupt when the transfer completes and
289  // Enable the channel interrupt in the MCU INT controller.
290  //
291  // This interrupt indicates that the entire row of pixels has been displayed
292  // and that the next row should be configured.
293  DmaChnSetEvEnableFlags(PIXEL_DMA_CHN, DMA_EV_BLOCK_DONE);
294  DmaChnIntEnable(PIXEL_DMA_CHN);
295 }
296 
297 static void update_y_axis_position(uint8_t row_number) {
298  // Convert the row number into the correct DAC output
299  // float avoided by rewrite:
300  // (row_number / IMAGE_HEIGHT) * (MAX_COMMAND - MIN_COMMAND) + MIN_COMMAND
301  uint16_t mirror_position =
304 
305  // construct the DAC message by adding the DAC config bits on to the message
306  uint16_t dac_word = Y_MIRROR_SPI_CONFIG | ((mirror_position << 2) & 0x0FFF);
307 
308  // wait for spi to empty
309  while (TxBufFullSPI1());
310  // put the message in the DAC FIFO
311  WriteSPI1(dac_word);
312  while (SPI1STATbits.SPIBUSY); // wait for end of transaction
313 }
314 
315 static void write_pixel(struct color const pixel) {
316  // pixel needs to be accessed as raw bits which is achieved using this
317  // disgusting hack
318  union {
319  struct color pixel;
320  uint8_t pixel_as_int;
321  } pixel_to_int_converter;
322  pixel_to_int_converter.pixel = pixel;
323  uint8_t pixel_int = pixel_to_int_converter.pixel_as_int;
324 
325  // set/clear the bits appropriately
326  // The masking is unecessary in the first case and could be avoided in the
327  // second case but is included in both because it is more explicit and more
328  // clear.
329  mPORTBSetBits ( ((unsigned int) pixel_int) & 0XFF);
330  mPORTBClearBits(~((unsigned int) pixel_int) & 0XFF);
331 }
332 
#define BLUE_PHASE_SHIFT
Shift to the right of the blue laser.
Definition: parameters.h:51
#define IMAGE_HEIGHT
Definition: parameters.h:18
char blue
Definition: color.h:43
char red
Definition: color.h:42
struct color const color_blank
Definition: color.c:30
Definition: color.h:41
#define PIXEL_ON_TIME
How many clock ticks the pixel is on.
Definition: projector.c:48
#define PHASE_SHIFT_PADDING
Definition: parameters.h:56
void projector_set_pixel(struct color const color, unsigned int x, unsigned int y)
Set the color of a pixel at the specified location.
Definition: projector.c:205
struct color projector_framebuffer[IMAGE_HEIGHT][IMAGE_WIDTH+PHASE_SHIFT_PADDING]
Definition: projector.c:114
#define Y_AXIS_MIN_COMMAND
Min voltage to output to drive the y-axis mirror.
Definition: parameters.h:67
#define Y_MIRROR_SPI_CONFIG
Definition: projector.c:43
char green
Definition: color.h:44
void projector_init()
Initialize projector peripherals, variables etc. MUST be called before calling any other projector fu...
Definition: projector.c:123
#define Y_MIRROR_SPI_CHN
Definition: projector.c:42
#define IMAGE_WIDTH
Definition: parameters.h:19
void __ISR(_DMA0_VECTOR, IPL5SOFT)
Definition: projector.c:229
#define Y_AXIS_MAX_COMMAND
Max voltage to output to drive the y-axis mirror.
Definition: parameters.h:74
#define GREEN_PHASE_SHIFT
Shift to the right of the green laser.
Definition: parameters.h:43
#define PIXEL_DMA_CHN
Definition: projector.c:41
#define RED_PHASE_SHIFT
Shift to the right of the red laser.
Definition: parameters.h:35