firmware  v0.1.2
Chromation Spectrometer Dev-Kit
VisCmd.h
Go to the documentation of this file.
1 // ---API (Go to the Doxygen documentation of this file)---
16 #ifndef _VISCMD_H
17 #define _VISCMD_H
18 // Pick a sensor
19 #ifdef LIS // <------------- $ make sensor=LIS
20 #include "Lis.h"
21 #include "LisConfig.h"
22 #endif
23 #ifdef S13131 // <---------- $ make sensor=S13131
24 #include "S13131.h"
25 #endif
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include "SpiSlave.h"
29 #include "StatusCode.h"
30 #include "Queue.h"
31 #include "BiColorLed.h"
32 #include "UartSpi.h"
33 #include "AutoExpose.h"
34 
35 /* ------------------------------------------------------- */
36 /* | Only use these headers when building unit tests | */
37 /* ------------------------------------------------------- */
38 #ifdef USE_FAKES
39 #include "SpiSlave_faked.h" // declare fakes
40 #include "UartSpi_faked.h" // declare fakes
41 #endif
42 
43 /* ------------------------------------------------------- */
44 /* | Only use these headers when building for AVR target | */
45 /* ------------------------------------------------------- */
46 #ifndef USE_FAKES
47 #include <util/delay_basic.h> // use _delay_loop_1 to count µs
48 #endif
49 
50 #ifdef USE_FAKES
51 #define WaitForS13131ClkLow WaitForS13131ClkLow_fake
52 #define StartAdcConversion StartAdcConversion_fake
53 #endif
54 
55 extern volatile Queue_s * SpiFifo; // definition in translation unit using SpiFifo
56 
58 extern uint8_t frame[];
59 
60 /* ---------------------------------------- */
61 /* | ---Command helpers (not commands)--- | */
62 /* ---------------------------------------- */
63 inline bool LedNumIsValid(bicolorled_num led_num) // -> is_valid
64 {
71  return ( (led_num == 0) || (led_num == 1) );
72 }
73 
74 inline uint8_t ReadLedState(bicolorled_num led_num) // -> led_state
75 {
83  if (BitIsClear(BiColorLed_ddr, led_num))
84  return OFF;
85  else // LED is on
86  return BitIsClear(BiColorLed_port, led_num)
87  ? GREEN : RED;
88 }
89 
90 inline bool AutoExposeConfigIsValid(
91  uint8_t new_max_tries,
92  uint16_t new_start_pixel,
93  uint16_t new_stop_pixel,
94  uint16_t new_target
95  )
96 {
97  // Check max_tries is valid
98  if (new_max_tries == 0) return false;
99 
100  // Check target is valid
101  if (new_target < 4500) return false;
102 
103  // Check stop_pixel is not less than start_pixel
104  if (new_stop_pixel < new_start_pixel) return false;
105 
106 #ifdef LIS
107  // Check start_pixel and stop_pixel are in range
108  if (binning == BINNING_ON)
109  {
110  if ((new_start_pixel < 7) || (new_start_pixel > 392)) return false;
111  if ((new_stop_pixel < 7) || (new_stop_pixel > 392)) return false;
112  }
113  else // (binning == BINNING_OFF)
114  {
115  if ((new_start_pixel < 14) || (new_start_pixel > 784)) return false;
116  if ((new_stop_pixel < 14) || (new_stop_pixel > 784)) return false;
117  }
118 #endif
119 #ifdef S13131
120  if ((new_start_pixel < 1) || (new_start_pixel > 512)) return false;
121  if ((new_stop_pixel < 1) || (new_stop_pixel > 512)) return false;
122 #endif
123 
124  // Config is valid
125  return true;
126 }
127 
128 #ifdef LIS
129 // "static" because it calls static _delay_loop_1
130 static inline void LisReadout(uint16_t num_pixels)
131 {
145  // wait for SYNC pulse to signify readout starts
146  while( BitIsClear(Lis_pin1, Lis_Sync) ); // wait for SYNC HIGH
147  while( BitIsSet(Lis_pin1, Lis_Sync) ); // wait for SYNC LOW
148 
149  /* --------------------------------------------------------- */
150  /* | LOOP: read one pixel on each rising edge of Lis clock | */
151  /* --------------------------------------------------------- */
152  uint16_t pixel_count = 0;
153  uint8_t *pframe = frame;
154  while( pixel_count++ < num_pixels)
155  {
156  // wait for rising edge of Lis clock
158 
159  // start ADC conversion
160  StartAdcConversion();
161 
162  /* --------------------------------------------------- */
163  /* | wait at least 4.66µs for conversion to complete | */
164  /* --------------------------------------------------- */
165  // use _delay_loop_1 to count 45 ticks of the 10MHz osc
166  // each loop iteration is 3 ticks
167 #ifndef USE_FAKES
168  _delay_loop_1(15); // 15 * 3 = 45 -> 4.5µs plus overhead
169 #endif
170 
171  // start 16-bit ADC readout
172  StartAdcReadout();
173 
174  // wait for MSB of ADC readout
175  while (BitIsClear(UartSpi_UCSR0A, UartSpi_RXC0));
176 
177  // save MSB to frame buffer
178  *(pframe++) = *UartSpi_UDR0;
179 
180  // wait for LSB of ADC readout
181  while (BitIsClear(UartSpi_UCSR0A, UartSpi_RXC0));
182 
183  // save LSB to frame buffer
184  *(pframe++) = *UartSpi_UDR0;
185  }
186 }
187 #endif
188 #ifdef S13131
189 // "static" because it calls static _delay_loop_1
190 static inline void S13131Readout(void)
191 { //TODO(sustainablelab): Use EOS
213  // Store 16-bit ADC readings in `frame`.
214  uint8_t *pframe = frame;
215 
216  // STATE: ST has been LOW for 13 falling edges of CLK (expose just finished)
217  // STATE: Readout starts with pixel 1 on the very next falling-edge of CLK
218 
219  // ---For loop: expected behavior---
220  // p_count (falling edge count)
221  // 0 < 512: Wait for CLK falling-edge // (f_count 13 -> 14)
222  // Read the pixel: p_count 0 -> 1
223  // 1 < 512: Wait for CLK falling-edge // (f_count 14 -> 15)
224  // Read the pixel: p_count 1 -> 2
225  // ...
226  // 511 < 512: Wait for CLK falling-edge // (f_count 524 -> 525)
227  // Read the pixel: p_count 511 -> 512
228  // All 512 pixels are now stored! Yay!
229  // 512 !< 512: Exit loop
230 
231  for(uint16_t p_count = 0; p_count < 512; p_count++)
232  {
233  WaitForS13131ClkLow();
234 
235  // Read Pixel
236  StartAdcConversion();
237 
238  /* --------------------------------------------------- */
239  /* | wait at least 4.66µs for conversion to complete | */
240  /* --------------------------------------------------- */
241  // use _delay_loop_1 to count 45 ticks of the 10MHz osc
242  // each loop iteration is 3 ticks
243 #ifndef USE_FAKES
244  _delay_loop_1(15); // 15 * 3 = 45 -> 4.5µs plus overhead
245 #endif
246 
247  // start 16-bit ADC readout
248  StartAdcReadout();
249 
250  // wait for MSB of ADC readout
251  while (BitIsClear(UartSpi_UCSR0A, UartSpi_RXC0));
252 
253  // save MSB to frame buffer
254  *(pframe++) = *UartSpi_UDR0;
255 
256  // wait for LSB of ADC readout
257  while (BitIsClear(UartSpi_UCSR0A, UartSpi_RXC0));
258 
259  // save LSB to frame buffer
260  *(pframe++) = *UartSpi_UDR0;
261  }
262 }
263 #endif
264 
265 uint16_t GetPeak(uint16_t const, uint16_t const);
266 uint16_t AutoExpose(void);
267 
268 /* ---------------------- */
269 /* | ---Commands :( --- | */ // (these NEED proper unit tests)
270 /* ---------------------- */
271 inline void NullCommand(void)
272 {
273 }
274 
275 inline void GetSensorLED(void)
276 {
284  // wait for led_num
285  while (QueueIsEmpty(SpiFifo));
286 
287  // read led_num
288  uint8_t led_num = QueuePop(SpiFifo);
289 
290  if (LedNumIsValid(led_num))
291  {
292  // send OK and LED_SETTING
293  SpiSlaveTxByte(OK);
294  SpiSlaveTxByte(ReadLedState(led_num));
295  }
296  else // led_num is invalid
297  {
298  // send ERROR and pad second byte
299  SpiSlaveTxByte(ERROR);
301  }
302 }
303 
304 inline void SetSensorLED(void)
305 {
317  // wait for led_num
318  while (QueueIsEmpty(SpiFifo));
319 
320  // read led_num
321  uint8_t led_num = QueuePop(SpiFifo);
322 
323  // wait for led_setting
324  while (QueueIsEmpty(SpiFifo));
325 
326  // read led_setting
327  uint8_t led_setting = QueuePop(SpiFifo);
328 
329  if (!LedNumIsValid(led_num)) // led_num is invalid
330  {
331  // send ERROR
332  SpiSlaveTxByte(ERROR);
333  }
334  else if (!led_setting_is_valid(led_setting)) // led_setting is invalid
335  {
336  // send ERROR
337  SpiSlaveTxByte(ERROR);
338  }
339  else // led_num and led_setting are valid
340  {
341  // lookup led_num
342  uint8_t led;
343  switch (led_num)
344  {
345  case 0: led = led_0; break;
346  case 1: led = led_1; break;
347  }
348 
349  // apply led_setting
350  switch (led_setting)
351  {
352  case 0: BiColorLedOff(led); break;
353  case 1: BiColorLedOn(led); BiColorLedGreen(led); break;
354  case 2: BiColorLedOn(led); BiColorLedRed(led); break;
355  }
356 
357  // send OK
358  SpiSlaveTxByte(OK);
359  }
360 }
361 
362 #ifdef LIS
363 inline void GetSensorConfig(void)
364 {
372  SpiSlaveTxByte(OK);
373  SpiSlaveTxByte(binning);
374  SpiSlaveTxByte(gain);
375  SpiSlaveTxByte(active_rows);
376 }
377 #endif
378 
379 inline void GetExposure(void)
380 {
387  SpiSlaveTxByte(OK);
390 }
391 
392 inline void SetExposure(void)
393 {
403  // wait for exposure_MSB
404  while (QueueIsEmpty(SpiFifo));
405 
406  // read exposure_MSB
407  uint8_t exposure_MSB = QueuePop(SpiFifo);
408 
409  // wait for exposure_LSB
410  while (QueueIsEmpty(SpiFifo));
411 
412  // read exposure_LSB
413  uint8_t exposure_LSB = QueuePop(SpiFifo);
414 
415  // update global exposure_ticks
416  exposure_ticks = (exposure_MSB << 8) | exposure_LSB;
417 
418  // send OK
419  SpiSlaveTxByte(OK);
420 }
421 
422 // "static" because it calls static LisReadout
423 static inline void CaptureFrame(void)
424 {
435  // send OK
436  SpiSlaveTxByte(OK);
437 
438 #ifdef LIS
439  // determine number of pixels
440  uint16_t num_pixels;
441  if (binning == BINNING_OFF) num_pixels = MAX_NUM_PIXELS;
442  else num_pixels = MAX_NUM_PIXELS/2;
443 #endif
444 #ifdef S13131
445  const uint16_t num_pixels = MAX_NUM_PIXELS;
446 #endif
447 
448  // send num_pixels
449  SpiSlaveTxByte(MSB(num_pixels));
450  SpiSlaveTxByte(LSB(num_pixels));
451 
452  /* -------------- */
453  /* | READ FRAME | */
454  /* -------------- */
455 
456 #ifdef LIS
457  // expose the LIS-770i pixels
458  LisExpose();
459 
460  // readout the LIS-770i pixels into global frame buffer
461  LisReadout(num_pixels);
462 #endif // LIS
463 #ifdef S13131
464  // expose the S13131-512 pixels
465  S13131Expose();
466 
467  // readout the S13131-512 pixels into global frame buffer
468  S13131Readout();
469 #endif
470  /* --------------- */
471  /* | WRITE FRAME | */
472  /* --------------- */
473 
474  // disable SPI interrupt while writing the frame
476 
477  // start at MSB of first pixel
478  uint8_t *pframe = frame;
479 
480  uint16_t byte_count = 0;
481  uint16_t nbytes = 2*num_pixels;
482  while( byte_count++ < nbytes )
483  {
484  // load the SPI data register with the next byte
485  *Spi_SPDR = *(pframe++);
486 
488 
489  // Wait for a byte from the SPI Master.
490  while ( !_SpiTransferIsDone() ); // Check SPI interrupt flag
491 
493  }
494 
495  // Re-enable interrupt and reset (clear) SPI interrupt flag
497 }
498 
499 inline void AutoExposure(void)
500 {
501 
502  // auto-expose, report result:
503  uint16_t result = AutoExpose();
504 
505  // send OK
506  SpiSlaveTxByte(OK);
507 
508  // send final_peak
509  SpiSlaveTxByte(MSB(result)); // success true/false
510  SpiSlaveTxByte(LSB(result)); // iterations to hit target
511 }
512 
513 inline void GetAutoExposeConfig(void)
514 {
515  SpiSlaveTxByte(OK);
527 }
528 
529 inline void SetAutoExposeConfig(void)
530 {
531 
532  /* ------------------------------------------- */
533  /* | Get 6 config values, a total of 11 bytes | */
534  /* ------------------------------------------- */
535 
536  // get max_tries
537  while (QueueIsEmpty(SpiFifo));
538  uint8_t new_max_tries = QueuePop(SpiFifo);
539 
540  // remaining 5 config values are each 16 bits, sent MSB first
541  uint8_t msb; uint8_t lsb;
542 
543  // get start pixel
544  while (QueueIsEmpty(SpiFifo));
545  msb = QueuePop(SpiFifo);
546  while (QueueIsEmpty(SpiFifo));
547  lsb = QueuePop(SpiFifo);
548  uint16_t new_start_pixel = (uint16_t)(msb << 8) | lsb;
549 
550  // get stop pixel
551  while (QueueIsEmpty(SpiFifo));
552  msb = QueuePop(SpiFifo);
553  while (QueueIsEmpty(SpiFifo));
554  lsb = QueuePop(SpiFifo);
555  uint16_t new_stop_pixel = (uint16_t)(msb << 8) | lsb;
556 
557  // get target
558  while (QueueIsEmpty(SpiFifo));
559  msb = QueuePop(SpiFifo);
560  while (QueueIsEmpty(SpiFifo));
561  lsb = QueuePop(SpiFifo);
562  uint16_t new_target = (uint16_t)(msb << 8) | lsb;
563 
564  // get target_tolerance
565  while (QueueIsEmpty(SpiFifo));
566  msb = QueuePop(SpiFifo);
567  while (QueueIsEmpty(SpiFifo));
568  lsb = QueuePop(SpiFifo);
569  uint16_t new_target_tolerance = (uint16_t)(msb << 8) | lsb;
570 
571  // get max_exposure
572  while (QueueIsEmpty(SpiFifo));
573  msb = QueuePop(SpiFifo);
574  while (QueueIsEmpty(SpiFifo));
575  lsb = QueuePop(SpiFifo);
576  uint16_t new_max_exposure = (uint16_t)(msb << 8) | lsb;
577 
578  /* ----------------------- */
579  /* | Validate New Config | */
580  /* ----------------------- */
581 
582  /* TODO: add check that new_max_exposure is valid */
583  if ( !AutoExposeConfigIsValid(
584  new_max_tries,
585  new_start_pixel, new_stop_pixel,
586  new_target
587  ) ) // any new_target_tolerance is valid
588  { // at least one value in new config is invalid
589  // reply with ERROR
590  SpiSlaveTxByte(ERROR);
591  }
592  else // new config is valid
593  {
594  // update global config
595  max_tries = new_max_tries;
596  start_pixel = new_start_pixel;
597  stop_pixel = new_stop_pixel;
598  target = new_target;
599  target_tolerance = new_target_tolerance;
600  max_exposure = new_max_exposure;
601 
602  // reply with OK
603  SpiSlaveTxByte(OK);
604  }
605 }
606 
607 /* ---------------------- */
608 /* | ---Commands :) --- | */ // (these HAVE proper unit tests)
609 /* ---------------------- */
610 
611 #ifdef LIS
612 #ifdef USE_FAKES
613 #define SpiSlaveTxByte SpiSlaveTxByte_fake
614 #define LisWriteConfig LisWriteConfig_fake
615 #endif
616 inline void SetSensorConfig(void)
617 {
631  // Send OK (OK for usb-bridge to send the config)
632  SpiSlaveTxByte(OK);
633 
634  // Get config values
635  while (QueueIsEmpty(SpiFifo)); // 5 cycles
636  uint8_t new_binning = QueuePop(SpiFifo);
637  while (QueueIsEmpty(SpiFifo)); // 5 cycles
638  uint8_t new_gain = QueuePop(SpiFifo);
639  while (QueueIsEmpty(SpiFifo)); // 5 cycles
640  uint8_t new_active_rows = QueuePop(SpiFifo);
641  if ( !LisConfigIsValid(new_binning, new_gain, new_active_rows) )
642  {
643  // Reply with error if any config value is invalid
644  SpiSlaveTxByte(ERROR);
645  return;
646  }
647  // New config is valid, update global config
648  binning = new_binning;
649  gain = new_gain;
650  active_rows = new_active_rows;
651  // Program the LIS-770i
652  LisWriteConfig();
653  // Report OK to SPI Master to indicate programming is done
654  SpiSlaveTxByte(OK);
655 }
656 #ifdef USE_FAKES
657 #undef SpiSlaveTxByte
658 #undef LisWriteConfig
659 #endif
660 #endif // ifdef LIS
661 #ifdef S13131
662 // invalid command
663 #endif
664 
665 #ifdef USE_FAKES
666 #define SpiSlaveTxByte SpiSlaveTxByte_fake
667 #endif
668 #ifdef LIS
669 #define SENSOR LIS
670 #endif
671 #ifdef S13131
672 #define SENSOR S13131
673 #endif
674 inline void GetSensorHash(void)
675 {
676  SpiSlaveTxByte(OK);
677  uint8_t first_byte = SENSOR >> 16;
678  uint8_t second_byte = (SENSOR >> 8) & 0xff;
679  uint8_t third_byte = SENSOR & 0xff;
680  SpiSlaveTxByte(first_byte);
681  SpiSlaveTxByte(second_byte);
682  SpiSlaveTxByte(third_byte);
683 }
684 #ifdef USE_FAKES
685 #undef SpiSlaveTxByte
686 #undef SENSOR
687 #endif
688 
689 #endif // _VISCMD_H
uint16_t max_exposure
max_exposure is 1.31 seconds (65535 20µs-cycles) This is the 16-bit limit, UINT16_MAX,...
Definition: AutoExpose.c:12
uint16_t start_pixel
AutoExpose() ignores pixels below start_pixel.
Definition: AutoExpose.c:6
uint8_t max_tries
maximum number of tries before AutoExpose() gives up
Definition: AutoExpose.c:5
uint16_t stop_pixel
AutoExpose() ignores pixels above stop_pixel.
Definition: AutoExpose.c:7
uint16_t target_tolerance
target ± target_tolerance is the target peak counts range for AutoExpose().
Definition: AutoExpose.c:9
uint16_t target
target peak counts for AutoExpose().
Definition: AutoExpose.c:8
void BiColorLedOff(bicolorled_num led)
Definition: BiColorLed.h:28
void BiColorLedGreen(bicolorled_num led)
Definition: BiColorLed.h:35
void BiColorLedRed(bicolorled_num led)
Definition: BiColorLed.h:42
void BiColorLedOn(bicolorled_num led)
Definition: BiColorLed.h:21
uint8_t const bicolorled_num
Indicator LED numbers in the JSON file are not necessarily the same as the corresponding bit number i...
Definition: BiColorLed.h:10
LIS-770i configuration
bool LisConfigIsValid(uint8_t binning, uint8_t gain, uint8_t active_rows)
Definition: Lis.h:441
uint8_t MSB(uint16_t msb_lsb)
Definition: Lis.h:401
void _WaitForLisClkHigh(void)
Definition: Lis.h:268
void LisWriteConfig(void)
Definition: Lis.h:475
uint16_t exposure_ticks
LIS-770i exposure time.
Definition: Lis.c:32
void LisExpose(void)
Definition: Lis.h:498
uint8_t LSB(uint16_t msb_lsb)
Definition: Lis.h:408
#define MAX_NUM_PIXELS
LIS-770i maximum number of pixels.
Definition: Lis.h:400
uint8_t QueuePop(volatile Queue_s *SpiFifo)
Definition: Queue.h:139
bool QueueIsEmpty(volatile Queue_s *SpiFifo)
Definition: Queue.h:95
void S13131Expose(void)
Definition: S13131.h:191
spi_ptr Spi_SPDR
SPI Data Register.
void _SignalDataNotReady(void)
Definition: SpiSlave.h:31
void EnableSpiInterrupt(void)
Definition: SpiSlave.h:77
void DisableSpiInterrupt(void)
Definition: SpiSlave.h:48
void SpiSlaveTxByte(uint8_t input_byte)
Definition: SpiSlave.h:150
void _SignalDataReady(void)
Definition: SpiSlave.h:14
bool _SpiTransferIsDone(void)
Definition: Spi.h:88
See cfg/microspec.json in the Python API repository.
bool led_setting_is_valid(led_state setting)
Definition: StatusCode.h:53
uint8_t PADDING
When status_code is ERROR, pad responses to send expected number of bytes.
Definition: StatusCodes.h:21
void GetSensorConfig(void)
Definition: UsbCmd.h:229
void SetSensorConfig(void)
Definition: UsbCmd.h:307
uint8_t frame[]
One frame of pixel data is, at most, 1568 bytes.
Definition: VisCmd.c:2
volatile Queue_s * SpiFifo
Allocate static memory for the SPI Rx Queue.
Definition: test_Queue.c:25
void GetSensorLED(void)
Definition: VisCmd.h:275
uint8_t ReadLedState(bicolorled_num led_num)
Definition: VisCmd.h:74
void SetExposure(void)
Definition: VisCmd.h:392
void SetSensorLED(void)
Definition: VisCmd.h:304
uint16_t GetPeak(uint16_t const, uint16_t const)
Definition: VisCmd.c:29
void NullCommand(void)
Definition: VisCmd.h:271
bool LedNumIsValid(bicolorled_num led_num)
Definition: VisCmd.h:63
uint16_t AutoExpose(void)
Definition: VisCmd.c:81
static void CaptureFrame(void)
Definition: VisCmd.h:423
void GetExposure(void)
Definition: VisCmd.h:379
Queue uses a byte array as a circular buffer.
Definition: Queue.h:27