Laser Sensing Display for UI interfaces in the real world
Fork of skinGames_forktest by
Diff: hardwareIO/hardwareIO.cpp
- Revision:
- 41:74e24a0e6e50
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hardwareIO/hardwareIO.cpp Wed Oct 16 17:26:13 2013 +0000 @@ -0,0 +1,445 @@ +#include "hardwareIO.h" + +HardwareIO IO; // preintantiation of cross-file global object IO + +// -------------------------------------- (0) SETUP ALL IO (call this in the setup() function in main program) + +Serial pc(USBTX, USBRX); // tx, rx +LocalFileSystem local("local"); // Create the local filesystem under the name "local" + +SPI spiDAC(MOSI_PIN, MISO_PIN, SCK_PIN); // mosi, miso, sclk +DigitalOut csDAC(CS_DAC_MIRRORS); + +DigitalOut Laser_Red(LASER_RED_PIN); // NOTE: this is NOT the lock in sensing laser (actually, not used yet) +DigitalOut Laser_Green(LASER_GREEN_PIN); +DigitalOut Laser_Blue(LASER_BLUE_PIN); + +// Some manual controls over the hardware function: +InterruptIn switchOne(SWITCH_ONE); +DigitalOut ledSwitchOne(LED_SWITCH_ONE); +InterruptIn switchTwo(SWITCH_TWO); +AnalogIn ainPot(POT_ANALOG_INPUT); // I cannot use this directly because the adc is being used by the locking. We have to re-setup it (see updatePotValue()) + +// Testing leds (for debugging): +DigitalOut myLed1(LED1); // note: LED1/2/3/4 are defined in mbed.h, and correspond to mbed pins 32, 34, 35 and 37 +DigitalOut myLed2(LED2); +DigitalOut myLed3(LED3); +DigitalOut myLed4(LED4); + +void HardwareIO::init(void) +{ + setLaserLockinPower(1); // this may be always ON (the IR laser). But we may want to switch it off sometimes... + Laser_Red = 0; // note: this is not the lockin-laser! (in the future, the lock in laser will be infrared...) + Laser_Green = 0; + Laser_Blue = 0; + + // Test leds: + myLed1=0; + myLed2=0; + myLed3=0; + myLed4=0; + + //Serial Communication setup: + pc.baud(SERIAL_SPEED); + + // Setup for lock-in amplifier and pwm references: + setLaserLockinPower(1);// actually this is the Red laser in the hardware + lockin.init(); + + // Setup for DAC control to move the mirrors: + // Set spi for 8 bit data, high steady state clock, + // second edge capture, with a 10MHz clock rate: + csDAC = 1; + spiDAC.format(16,0); + spiDAC.frequency(16000000); + + // default initial mirror position: + writeOutX(CENTER_AD_MIRROR_X); + writeOutY(CENTER_AD_MIRROR_Y); + + // Load LUT table: + setLUT(); + + // Set interrupts on RAISING edge for button-switch functions: + // Note: The pin input will be logic '0' for any voltage on the pin below 0.8v, and '1' for any voltage above 2.0v. + // By default, the InterruptIn is setup with an internal pull-down resistor, but for security and clarity I will do it explicitly here: + switchOne.mode(PullUp); // pull down seems not very good + switchTwo.mode(PullUp); + switchOne.fall(this, &HardwareIO::switchOneInterrupt); // attach the address of the flip function to the falling edge + switchTwo.fall(this, &HardwareIO::switchTwoInterrupt); // attach the address of the flip function to the falling edge + switchOneState=true; + switchTwoState=false; + switchOneChange=false; + switchTwoChange=false; + + setSwitchOneState(true); //equal to switchOneState=true, plus set led value. False means fixed threshold, true AUTO THRESHOLD (will be the default mode) + + // Read and update pot value: + // updatePotValue(); // the value will be ajusted in the range 0-255 +} + +void HardwareIO::setSwitchOneState(bool newstate) +{ + switchOneState=newstate; + ledSwitchOne=(switchOneState? 1 :0); +} + +// these ISR could do more (like debouncing). +// For the time being, I will debounce electrically with a small capacitor. +void HardwareIO::switchOneInterrupt() +{ + switchOneState=!switchOneState; + ledSwitchOne=(switchOneState? 1 :0); // this switch has a built-in led + switchOneChange=true; +} +void HardwareIO::switchTwoInterrupt() +{ + switchTwoState=!switchTwoState; + switchTwoChange=true; +} + +bool HardwareIO::switchOneCheck(bool& state) +{ + if (switchOneChange) { + switchOneChange=false; + state=switchOneState; + return(true); + } else + return(false); +} + +bool HardwareIO::switchTwoCheck(bool& state) +{ + if (switchTwoChange) { + switchTwoChange=false; + state=switchTwoState; + return(true); + } else return(false); +} + +unsigned char HardwareIO::updatePotValue() // this will update the pot value, and return it too. +{ + //The value will be ajusted in the range 0-255 + //The 0.0v to 3.3v range of the AnalogIn is represented in software as a normalised floating point number from 0.0 to 1.0. + potValue=(unsigned char )(ainPot*255); + +/* USING the adc library: + // unset fast adc for lockin, and set normal adc for conversion from analog input pin: + lockin.setADC_forLockin(0); + adc.setup(POT_ANALOG_INPUT,1); + + wait(1); + + //Measure pin POT_ANALOG_INPUT + adc.select(POT_ANALOG_INPUT); + //Start ADC conversion + adc.start(); + //Wait for it to complete + while(!adc.done(POT_ANALOG_INPUT)); + potValue=adc.read(POT_ANALOG_INPUT); + + //Unset pin POT_ANALOG_INPUT + adc.setup(POT_ANALOG_INPUT,0); + + lockin.setADC_forLockin(1); + // wait(1); +*/ + + // Attention! reading using the AnalogIn library seems to break my burst reading mode. So, we need to re-set it: + lockin.setADC_forLockin(1); + + return(potValue); +} + +//write on the first DAC, output A (mirror X) +void HardwareIO::writeOutX(unsigned short value) +{ + if(value > MAX_AD_MIRRORS) value = MAX_AD_MIRRORS; + if(value < MIN_AD_MIRRORS) value = MIN_AD_MIRRORS; + + value |= 0x7000; + value &= 0x7FFF; + + csDAC = 0; + spiDAC.write(value); + csDAC = 1; +} + +//write on the first DAC, output B (mirror Y) +void HardwareIO::writeOutY(unsigned short value) +{ + if(value > MAX_AD_MIRRORS) value = MAX_AD_MIRRORS; + if(value < MIN_AD_MIRRORS) value = MIN_AD_MIRRORS; + + value |= 0xF000; + value &= 0xFFFF; + + csDAC = 0; + spiDAC.write(value); + csDAC = 1; +} + +void HardwareIO::setLaserLockinPower(int powerValue) +{ + if(powerValue > 0) { + lockin.setLaserPower(true); + } else { + lockin.setLaserPower(false); + } +} + +void HardwareIO::setRedPower(int powerValue) +{ + if(powerValue > 0) { + Laser_Red = 1; + } else { + Laser_Red = 0; + } +} +void HardwareIO::setGreenPower(int powerValue) +{ + if(powerValue > 0) { + Laser_Green = 1; + } else { + Laser_Green = 0; + } +} +void HardwareIO::setBluePower(int powerValue) +{ + if(powerValue > 0) { + Laser_Blue = 1; + } else { + Laser_Blue = 0; + } +} + + +void HardwareIO::setRGBPower(unsigned char color) // NOTE: this do NOT affect the power of the lockin laser... +{ + // lockin.setLaserPower((color&0x04)>0? true : false); + Laser_Red=(color&0x04)>>2; + Laser_Green=(color&0x02)>>1; + Laser_Blue =color&0x01; +} + +// Attention: we should stop the displaying engine lsd before calling this (if we want, otherwise it will continue showing the +// displayed objects/scene, but it will be weird) - this is done in the WRAPPERS methods. +void HardwareIO::showLimitsMirrors(unsigned short pointsPerLine, unsigned short durationSecs) +{ + //unsigned short pointsPerLine=150; + int shiftX = (MAX_AD_MIRRORS - MIN_AD_MIRRORS) / pointsPerLine; + int shiftY = (MAX_AD_MIRRORS - MIN_AD_MIRRORS) / pointsPerLine; + + setRGBPower(0x07);// all displaying lasers ON + + //for (int repeat=0; repeat<times; repeat++) { + + Timer t; + t.start(); + while(t.read_ms()<durationSecs*1000) { + + writeOutX(MIN_AD_MIRRORS); + writeOutY(MIN_AD_MIRRORS); + + for(int j=0; j<pointsPerLine; j++) { + wait_us(200);//delay between each points + writeOutY(j*shiftY + MIN_AD_MIRRORS); + } + + writeOutX(MIN_AD_MIRRORS); + writeOutY(MAX_AD_MIRRORS); + for(int j=0; j<pointsPerLine; j++) { + wait_us(200);//delay between each points + writeOutX(j*shiftX + MIN_AD_MIRRORS); + } + + writeOutX(MAX_AD_MIRRORS); + writeOutY(MAX_AD_MIRRORS); + for(int j=0; j<pointsPerLine; j++) { + wait_us(200);//delay between each points + writeOutY(-j*shiftX + MAX_AD_MIRRORS); + } + + writeOutX(MAX_AD_MIRRORS); + writeOutY(MIN_AD_MIRRORS); + for(int j=0; j<pointsPerLine; j++) { + wait_us(200);//delay between each points + writeOutX(-j*shiftX + MAX_AD_MIRRORS); + } + + } + t.stop(); + Laser_Green=0; +} + +void HardwareIO::scan_serial(unsigned short pointsPerLine) +{ + //scan the total surface with a custom resolution + //send the lockin value for each point as a byte on the serial port to the PC + //use "scanSLP_save" to see the data on processing + + int shiftX = (MAX_AD_MIRRORS - MIN_AD_MIRRORS) / pointsPerLine; + int shiftY = (MAX_AD_MIRRORS - MIN_AD_MIRRORS) / pointsPerLine; + + for(int j=0; j<pointsPerLine; j++) { + writeOutX(MIN_AD_MIRRORS); + writeOutY(j*shiftY + MIN_AD_MIRRORS); + + wait_us(300);//begining of line delay + for(int i=0; i<pointsPerLine; i++) { + writeOutX(i*shiftX + MIN_AD_MIRRORS); + + wait_us(200);//delay between each points + + // SEND A VALUE BETWEEN 0 and 255: + pc.putc(int(255.0*lockin.getMedianValue()/4095));//printf("%dL",int(valueLockin*255));//pc.putc(int(lockin*255));// + } + } +} + +//load Look-up Table from LUT.TXT file +//or create the file with scanLUT() if not existing. +void HardwareIO::setLUT() +{ + + FILE *fp = fopen(LUT_FILENAME, "r"); // Open file on the local file system for writing + if(fp) { + //load the file into the lut table; keep the SAME resolution! + fread(lut,sizeof(uint16),LUT_RESOLUTION*LUT_RESOLUTION,fp); + fclose(fp); + } else { + //fclose(fp); + //if the file "LUT.TXT" doesn't exist, create one with scanLUT() + lockin.setLaserPower(true); + scanLUT(); + } + +} + +//scan the total surface with a fixed 2^x resolution +//create the Look-Up Table used to "flatten" the scan according to the position +// +//To Do: maybe detect high frequency to be sure the area is clean and empty? +void HardwareIO::scanLUT() +{ + + //reset lut table + for(int j=0; j<LUT_RESOLUTION; j++) { + for(int i=0; i<LUT_RESOLUTION; i++) { + lut[i][j] =0; + } + } + + int delayScanning = 300; //in us + + //define the distance between each points (from 0 to 4096) and the offset (here 0) + float shiftX = 1.0*(MAX_AD_MIRRORS - MIN_AD_MIRRORS) / (LUT_RESOLUTION-1); + float shiftY = 1.0*(MAX_AD_MIRRORS - MIN_AD_MIRRORS) / (LUT_RESOLUTION-1); + float offsetX = MIN_AD_MIRRORS; + float offsetY = MIN_AD_MIRRORS; + + //move the mirrors to the first position + writeOutX(MAX_AD_MIRRORS); + writeOutY(MIN_AD_MIRRORS); + wait_us(500); + + float x, y; + + //scan the surface NB_SCANS times + //the total value in lut[i][j] shouldn't exceed uint16 !!! + for(int loop=0; loop<NB_SCANS; loop++) { + for(int j=0; j<LUT_RESOLUTION; j++) { + y = shiftY*j + offsetY ; + writeOutY(int(y)); + //scan from right to left + for(int i=LUT_RESOLUTION-1; i>=0; i--) { + x = shiftX*i + offsetX; + writeOutX(int(x)); + wait_us(delayScanning); + lut[i][j] += lockin_read(); + } + //re-scan from left to right + for(int i=0; i<LUT_RESOLUTION; i++) { + x = shiftX*i + offsetX; + writeOutX(int(x)); + wait_us(delayScanning); + lut[i][j] += lockin_read(); + } + } + } + + + //save tab in file + FILE *fp; +#ifdef LUT_FILENAME + fp = fopen(LUT_FILENAME, "w"); // Open file on the local file system for writing + fwrite(lut,sizeof(uint16),LUT_RESOLUTION*LUT_RESOLUTION,fp); + fclose(fp); //close the file (the mBed will appear connected again) +#endif + +#ifdef LUT_H_FILENAME + //save tab in Human readable file (not used by the program, this is just for checking) + // NOTE: we divide the content of the lut table by NB_SCANS, for easy reading (values should be between 0-4095) + fp = fopen(LUT_H_FILENAME, "w"); // Open file on the local file system for writing + fprintf(fp, "scan resolution: %d x %d\r\n",LUT_RESOLUTION, LUT_RESOLUTION); + for(int j=0; j<LUT_RESOLUTION; j++) { + for(int i=0; i<LUT_RESOLUTION; i++) { + fprintf(fp, "X=%d,\tY=%d,\tI=%d\t \r\n", int(shiftX*i + offsetX), int(shiftY*j + offsetY), int(1.0*lut[i][j]/NB_SCANS) ); + } + } + fclose(fp); //close the file (the mBed will appear connected again) +#endif + +} + + +//Return the lockin value "corrected with the Look-UpTable" - this means a RATIO between two reflectivities (and normally, this is <1). +float HardwareIO::lockInCorrectedValue(unsigned short x, unsigned short y) +{ +//*******Correction using DIRECT approximation +#ifdef LUT_DIRECT + return 2.0* NB_SCANS * lockin_read() / (lut[x >> LUT_BITS_SHIFT][y >> LUT_BITS_SHIFT]); // 2 * NB_SCANS is the number of recorded sample added to one position of the LUT (scan is performed twice: left-right and right-left) +#endif + +//*******Correction using BILINEAR approximation +#ifdef LUT_BILINEAR + unsigned short X = x >> LUT_BITS_SHIFT; //mirror "x" is 12bits, LUT "X" needs 4bits when lut is 17x17 + unsigned short Y = y >> LUT_BITS_SHIFT; //mirror "y" is 12bits, LUT "Y" needs 4bits when lut is 17x17 + float dx = 1.0*(x & LUT_BITS_MASK)/(LUT_BITS_MASK+1); //weight to apply on X (mask with 255 and norm) + float dy = 1.0*(y & LUT_BITS_MASK)/(LUT_BITS_MASK+1); //weight to apply on Y (mask with 255 and norm) + + //Wheighted mean approximation of the Look-Up Table at the position (x,y): + float wmLUT = (1-dy)*( (1-dx)*lut[X][Y] + dx*lut[X+1][Y] ) + dy*( (1-dx)*lut[X][Y+1] + dx*lut[X+1][Y+1] ); + + return 2.0* NB_SCANS * lockin_read() / wmLUT;// 2 * NB_SCANS is the number of recorded sample added to one position of the LUT (scan is performed twice: left-right and right-left) +#endif + +//*******Correction using LINEAR approximation +#ifdef LUT_LINEAR + unsigned short X = x >> LUT_BITS_SHIFT; //mirror "x" is 12bits, LUT "X" needs 4bits when lut is 17x17 + unsigned short Y = y >> LUT_BITS_SHIFT; //mirror "y" is 12bits, LUT "Y" needs 4bits when lut is 17x17 + float dx = 1.0*(x & LUT_BITS_MASK)/(LUT_BITS_MASK+1); //weight to apply on X (mask with 255 and norm) + float dy = 1.0*(y & LUT_BITS_MASK)/(LUT_BITS_MASK+1); //weight to apply on Y (mask with 255 and norm) + float linearLUT, dzx, dzy; + + if(dx>dy) { //if the position is on the "top-right" triangle + dzx = (lut[X+1][Y] - lut[X][Y]) * dx; + dzy = (lut[X+1][Y+1] - lut[X+1][Y]) * dy; + } else { //if the position is on the "bottom-left" triangle + dzy = (lut[X][Y+1] - lut[X][Y]) * dy; + dzx = (lut[X+1][Y+1] - lut[X][Y+1]) * dx; + } + + //linear approximation of the Look-Up Table at the position (x,y): + linearLUT = lut[X][Y] + dzx + dzy; + return 2.0* NB_SCANS * lockin_read() / linearLUT; // 2 * NB_SCANS is the number of recorded sample added to one position of the LUT (scan is performed twice: left-right and right-left) + +#endif + +//*******No corrections, just return the value divided by 4096 (this means we are assuming that the surface is everywhere perfectly reflective - we supposedly get the max value always) +#ifdef NO_LUT + return 1.0* lockin_read()/4096; +#endif + +} +