LIBS Spectrometer - Build 3

I don't quite get the signal to noise ratio out of the existing build that I was hoping for. One possible reason is the synchronization between the firing of the laser and the acquisition in the spectrometer. Right now, a single button press starts both. I want to measure the delay between sending the firing signal and the firing of the laser. And I also want to have control over a delay between the firing and the acquisition signals.

To implement that, I am using a STM32F746G-DISCO discovery kit from ST:

http://www.st.com/en/evaluation-tools/32f746gdiscovery.html

It has a very fast STM32F746NGH6 microcontroller at 200+ Mhz. That is far more than we need here, but it also has a nice touch screen, which will help me adjust delays and display data. Finally, it has an Arduino compatible header, which makes it easy to add screw terminals etc. with off the shelf shields.

The first step is to install the development environment, System Workbench for STM32.

http://www.openstm32.org/HomePage

Then start a new C Project:

And select the options to match the current board. Notable is that the first time the wizard is run, the STMCube library will not yet be installed. Luckily there is a convenient button to download it.

This results in an empty project with a main.c file that just contains an infinite empty loop. Interestingly, there are utility files available for all the hardware on the board under Utilities/STM32746G-Discovery.

That makes it very easy to use the touchscreen and other devices. To use the touchscreen, all that is necessary is to add an include at the beginning of main.c - #include "stm32746g_discovery_lcd.h"

and immediately the LCD can be used with the functions provided in stm32746g_discovery_lcd.c

The LCD expects a certain range for the system clock, so the clock needs to be configured first. Lets define a function, SystemClock_Config:

/*
    System Clock Configuration
      System Clock source            = PLL (HSE)
      SYSCLK(Hz)                     = 216000000
      HCLK(Hz)                       = 216000000
      AHB Prescaler                  = 1
      APB1 Prescaler                 = 4
      APB2 Prescaler                 = 2
      HSE Frequency(Hz)              = 25000000
      PLL_M                          = 25
      PLL_N                          = 432
      PLL_P                          = 2
      PLL_Q                          = 9
      VDD(V)                         = 3.3
      Main regulator output voltage  = Scale1 mode
      Flash Latency(WS)              = 7
  */
static void SystemClock_Config (void) {
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_OscInitTypeDef RCC_OscInitStruct;
  /* Enable HSE Oscillator and activate PLL with HSE as source */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 432;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 9;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);
  /* Activate the OverDrive to reach the 216 MHz Frequency */
  HAL_PWREx_EnableOverDrive();
  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
}

Then we can drive the LCD with very little code at all:

#include "stm32f7xx.h"
#include "stm32746g_discovery.h"
#include "stm32746g_discovery_lcd.h"
static void SystemClock_Config (void) {
.
.
.
.
}
int main(void)
{
     SystemInit();
     HAL_Init();
     SystemClock_Config();
     BSP_LCD_Init();
     BSP_LCD_LayerDefaultInit(0, LCD_FB_START_ADDRESS);
     BSP_LCD_DisplayOn();
     BSP_LCD_SelectLayer(0);
     BSP_LCD_Clear(LCD_COLOR_GREEN);               // draw color over whole screen
     BSP_LCD_SetTextColor(LCD_COLOR_WHITE);        // set color of text
     BSP_LCD_SetBackColor(LCD_COLOR_BLUE);         // set background color of text
     BSP_LCD_DisplayStringAt(0, LINE(0),(uint8_t*)"LCD", CENTER_MODE);    // draw text
     while(1)
      {
      }
}

That results in a simple display:

The above is all the framework needed to build a GUI. What is missing is a way to detect touches. This is also surprisingly easy:

#include "stm32746g_discovery_ts.h"
.
.
.
//Configure touch screen
    TS_StateTypeDef ts;
    BSP_TS_Init(BSP_LCD_GetXSize(), BSP_LCD_GetYSize());
    //uint8_t theResult = BSP_TS_ITConfig();  /* Touch screen interrupt configuration and enable */
    while(1)
    {
        uint8_t result = BSP_TS_GetState(&ts);
        if((ts.touchDetected)&& (TS_OK == result))
        {
            if (GEST_ID_NO_GESTURE == ts.gestureId)
            {
                for(int index = 0; index < TS_MAX_NB_TOUCH; index++)
                {
                    if (ts.touchEventId[index]==TOUCH_EVENT_PRESS_DOWN)
                    {
                        if ((130 > ts.touchX[index])&&(100 > ts.touchY[index]))
                        {
                            // respond to press
                        }
                    }
                }
            }
            //Debouncing
            while(ts.touchDetected)
            {
                BSP_TS_GetState(&ts);
                HAL_Delay(50);
            }
        }
    }

With that a simple application with functional touch-GUI can be built:

The board uses 400 mA of current. That means I will likely power it separately rather than hoping the spectrometer can source it on the 5V line.

The horizontal and vertical lines in the middle are a placeholder to put a graph. Here, I plan to display the delay between sending the trigger signal and the actual output of the laser - probably with a photo-diode of some sort. To test this approach, I set up a reverse bias photodiode (OP906) in this configuration:

The test used a 9V battery for convenience and a 680 Ohm resistor to keep the current limit in the 10-20 mA range. I put up a piece of tape on the back end (there is an air gap so the tape does not touch the optics). It has a pinhole and I cradled the diode to it.

It survived multiple firings without apparent degradation, and the shot is clearly identifiable in the scope trace:

The pulse is <100 us, so clearly the controller will have to sample faster than that...

Some experimentation showed that the photo-diode does not have to be in the exact center. That means that I can use a regular LED in the center to assist with aiming and focus, while placing the photo-diode off-center and still be able to sense the xenon flash.

I made an adapter in Fusion360 that allows me to mount the photo-diode in a more permanent fashion:

To monitor the Xenon flash driving the laser, we need an A/D channel. There is a good reference on how to use these here:

https://visualgdb.com/tutorials/arm/stm32/adc/

Here is the list of STM32 to Arduino pin assignments:

Because I am using a breakout board that I have on hand, not all the pins are easily usable with the headers I have. I decided on the following convenient pins

Then, with the use of a number of custom parts made in Fusion360, 3D printer and laser cutter, build #3 is starting to take shape:

The next step is aligning the optics for the light capture.