Hardware IO control: mirrors, lock in
Revision 0:c19dc1d8b225, committed 2011-10-17
- Comitter:
- mbedalvaro
- Date:
- Mon Oct 17 13:23:06 2011 +0000
- Commit message:
- I wonder if perhaps it would be better to avoid a hardwareIO object, and instead have a set of global hardware functions... I think I will do this in the next revisions of this library
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/adc.cpp Mon Oct 17 13:23:06 2011 +0000 @@ -0,0 +1,441 @@ +/* mbed Library - ADC + * Copyright (c) 2010, sblandford + * released under MIT license http://mbed.org/licence/mit + */ +#include "mbed.h" +#include "adc.h" + + +ADC adc(ADC_SAMPLE_RATE, 1); + +ADC *ADC::instance; + +ADC::ADC(int sample_rate, int cclk_div) + { + + int i, adc_clk_freq, pclk, clock_div, max_div=1; + + //Work out CCLK + adc_clk_freq=CLKS_PER_SAMPLE*sample_rate; + int m = (LPC_SC->PLL0CFG & 0xFFFF) + 1; + int n = (LPC_SC->PLL0CFG >> 16) + 1; + int cclkdiv = LPC_SC->CCLKCFG + 1; + int Fcco = (2 * m * XTAL_FREQ) / n; + int cclk = Fcco / cclkdiv; + + //Power up the ADC + LPC_SC->PCONP |= (1 << 12); + //Set clock at cclk / 1. + LPC_SC->PCLKSEL0 &= ~(0x3 << 24); + switch (cclk_div) { + case 1: + LPC_SC->PCLKSEL0 |= 0x1 << 24; + break; + case 2: + LPC_SC->PCLKSEL0 |= 0x2 << 24; + break; + case 4: + LPC_SC->PCLKSEL0 |= 0x0 << 24; + break; + case 8: + LPC_SC->PCLKSEL0 |= 0x3 << 24; + break; + default: + fprintf(stderr, "Warning: ADC CCLK clock divider must be 1, 2, 4 or 8. %u supplied.\n", + cclk_div); + fprintf(stderr, "Defaulting to 1.\n"); + LPC_SC->PCLKSEL0 |= 0x1 << 24; + break; + } + pclk = cclk / cclk_div; + clock_div=pclk / adc_clk_freq; + + if (clock_div > 0xFF) { + fprintf(stderr, "Warning: Clock division is %u which is above 255 limit. Re-Setting at limit.\n", + clock_div); + clock_div=0xFF; + } + if (clock_div == 0) { + fprintf(stderr, "Warning: Clock division is 0. Re-Setting to 1.\n"); + clock_div=1; + } + + _adc_clk_freq=pclk / clock_div; + if (_adc_clk_freq > MAX_ADC_CLOCK) { + fprintf(stderr, "Warning: Actual ADC sample rate of %u which is above %u limit\n", + _adc_clk_freq / CLKS_PER_SAMPLE, MAX_ADC_CLOCK / CLKS_PER_SAMPLE); + while ((pclk / max_div) > MAX_ADC_CLOCK) max_div++; + fprintf(stderr, "Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE); + } + + LPC_ADC->ADCR = + ((clock_div - 1 ) << 8 ) | //Clkdiv + ( 1 << 21 ); //A/D operational + + //Default no channels enabled + LPC_ADC->ADCR &= ~0xFF; + //Default NULL global custom isr + _adc_g_isr = NULL; + //Initialize arrays + for (i=7; i>=0; i--) { + _adc_data[i] = 0; + _adc_isr[i] = NULL; + } + + + //* Attach IRQ + instance = this; + NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr); + + //Disable global interrupt + LPC_ADC->ADINTEN &= ~0x100; + +}; + +void ADC::_adcisr(void) +{ + instance->adcisr(); +} + + +void ADC::adcisr(void) +{ + uint32_t stat; + int chan; + + // Read status + stat = LPC_ADC->ADSTAT; + //Scan channels for over-run or done and update array + if (stat & 0x0101) _adc_data[0] = LPC_ADC->ADDR0; + if (stat & 0x0202) _adc_data[1] = LPC_ADC->ADDR1; + if (stat & 0x0404) _adc_data[2] = LPC_ADC->ADDR2; + if (stat & 0x0808) _adc_data[3] = LPC_ADC->ADDR3; + if (stat & 0x1010) _adc_data[4] = LPC_ADC->ADDR4; + if (stat & 0x2020) _adc_data[5] = LPC_ADC->ADDR5; + if (stat & 0x4040) _adc_data[6] = LPC_ADC->ADDR6; + if (stat & 0x8080) _adc_data[7] = LPC_ADC->ADDR7; + + // Channel that triggered interrupt + chan = (LPC_ADC->ADGDR >> 24) & 0x07; + //User defined interrupt handlers + if (_adc_isr[chan] != NULL) + _adc_isr[chan](_adc_data[chan]); + if (_adc_g_isr != NULL) + _adc_g_isr(chan, _adc_data[chan]); + return; +} + +int ADC::_pin_to_channel(PinName pin) { + int chan; + switch (pin) { + case p15://=p0.23 of LPC1768 + default: + chan=0; + break; + case p16://=p0.24 of LPC1768 + chan=1; + break; + case p17://=p0.25 of LPC1768 + chan=2; + break; + case p18://=p0.26 of LPC1768 + chan=3; + break; + case p19://=p1.30 of LPC1768 + chan=4; + break; + case p20://=p1.31 of LPC1768 + chan=5; + break; + } + return(chan); +} + +PinName ADC::channel_to_pin(int chan) { + const PinName pin[8]={p15, p16, p17, p18, p19, p20, p15, p15}; + + if ((chan < 0) || (chan > 5)) + fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan); + return(pin[chan & 0x07]); +} + + +int ADC::channel_to_pin_number(int chan) { + const int pin[8]={15, 16, 17, 18, 19, 20, 0, 0}; + + if ((chan < 0) || (chan > 5)) + fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan); + return(pin[chan & 0x07]); +} + + +uint32_t ADC::_data_of_pin(PinName pin) { + //If in burst mode and at least one interrupt enabled then + //take all values from _adc_data + if (burst() && (LPC_ADC->ADINTEN & 0x3F)) { + return(_adc_data[_pin_to_channel(pin)]); + } else { + //Return current register value or last value from interrupt + switch (pin) { + case p15://=p0.23 of LPC1768 + default: + return(LPC_ADC->ADINTEN & 0x01?_adc_data[0]:LPC_ADC->ADDR0); + case p16://=p0.24 of LPC1768 + return(LPC_ADC->ADINTEN & 0x02?_adc_data[1]:LPC_ADC->ADDR1); + case p17://=p0.25 of LPC1768 + return(LPC_ADC->ADINTEN & 0x04?_adc_data[2]:LPC_ADC->ADDR2); + case p18://=p0.26 of LPC1768: + return(LPC_ADC->ADINTEN & 0x08?_adc_data[3]:LPC_ADC->ADDR3); + case p19://=p1.30 of LPC1768 + return(LPC_ADC->ADINTEN & 0x10?_adc_data[4]:LPC_ADC->ADDR4); + case p20://=p1.31 of LPC1768 + return(LPC_ADC->ADINTEN & 0x20?_adc_data[5]:LPC_ADC->ADDR5); + } + } +} + +//Enable or disable an ADC pin +void ADC::setup(PinName pin, int state) { + int chan; + chan=_pin_to_channel(pin); + if ((state & 1) == 1) { + switch(pin) { + case p15://=p0.23 of LPC1768 + default: + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14); + LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 14; + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14); + LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 14; + break; + case p16://=p0.24 of LPC1768 + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16); + LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 16; + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16); + LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 16; + break; + case p17://=p0.25 of LPC1768 + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18); + LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 18; + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18); + LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 18; + break; + case p18://=p0.26 of LPC1768: + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20); + LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 20; + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20); + LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 20; + break; + case p19://=p1.30 of LPC1768 + LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28); + LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 28; + LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28); + LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 28; + break; + case p20://=p1.31 of LPC1768 + LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30); + LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 30; + LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30); + LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 30; + break; + } + //Only one channel can be selected at a time if not in burst mode + if (!burst()) LPC_ADC->ADCR &= ~0xFF; + //Select channel + LPC_ADC->ADCR |= (1 << chan); + } + else { + switch(pin) { + case p15://=p0.23 of LPC1768 + default: + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14); + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14); + break; + case p16://=p0.24 of LPC1768 + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16); + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16); + break; + case p17://=p0.25 of LPC1768 + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18); + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18); + break; + case p18://=p0.26 of LPC1768: + LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20); + LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20); + break; + case p19://=p1.30 of LPC1768 + LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28); + LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28); + break; + case p20://=p1.31 of LPC1768 + LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30); + LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30); + break; + } + LPC_ADC->ADCR &= ~(1 << chan); + } +} +//Return channel enabled/disabled state +int ADC::setup(PinName pin) { + int chan; + + chan = _pin_to_channel(pin); + return((LPC_ADC->ADCR & (1 << chan)) >> chan); +} + +//Select channel already setup +void ADC::select(PinName pin) { + int chan; + + //Only one channel can be selected at a time if not in burst mode + if (!burst()) LPC_ADC->ADCR &= ~0xFF; + //Select channel + chan = _pin_to_channel(pin); + LPC_ADC->ADCR |= (1 << chan); +} + +//Enable or disable burst mode +void ADC::burst(int state) { + if ((state & 1) == 1) { + if (startmode(0) != 0) + fprintf(stderr, "Warning. startmode is %u. Must be 0 for burst mode.\n", startmode(0)); + LPC_ADC->ADCR |= (1 << 16); + } + else + LPC_ADC->ADCR &= ~(1 << 16); +} +//Return burst mode state +int ADC::burst(void) { + return((LPC_ADC->ADCR & (1 << 16)) >> 16); +} + +//Set startmode and edge +void ADC::startmode(int mode, int edge) { + int lpc_adc_temp; + + //Reset start mode and edge bit, + lpc_adc_temp = LPC_ADC->ADCR & ~(0x0F << 24); + //Write with new values + lpc_adc_temp |= ((mode & 7) << 24) | ((edge & 1) << 27); + LPC_ADC->ADCR = lpc_adc_temp; +} + +//Return startmode state according to mode_edge=0: mode and mode_edge=1: edge +int ADC::startmode(int mode_edge){ + switch (mode_edge) { + case 0: + default: + return((LPC_ADC->ADCR >> 24) & 0x07); + case 1: + return((LPC_ADC->ADCR >> 27) & 0x01); + } +} + +//Start ADC conversion +void ADC::start(void) { + startmode(1,0); +} + + +//Set interrupt enable/disable for pin to state +void ADC::interrupt_state(PinName pin, int state) { + int chan; + + chan = _pin_to_channel(pin); + if (state == 1) { + LPC_ADC->ADINTEN &= ~0x100; + LPC_ADC->ADINTEN |= 1 << chan; + /* Enable the ADC Interrupt */ + NVIC_EnableIRQ(ADC_IRQn); + } else { + LPC_ADC->ADINTEN &= ~( 1 << chan ); + //Disable interrrupt if no active pins left + if ((LPC_ADC->ADINTEN & 0xFF) == 0) + NVIC_DisableIRQ(ADC_IRQn); + } +} + +//Return enable/disable state of interrupt for pin +int ADC::interrupt_state(PinName pin) { + int chan; + + chan = _pin_to_channel(pin); + return((LPC_ADC->ADINTEN >> chan) & 0x01); +} + + +//Attach custom interrupt handler replacing default +void ADC::attach(void(*fptr)(void)) { + //* Attach IRQ + NVIC_SetVector(ADC_IRQn, (uint32_t)fptr); +} + +//Restore default interrupt handler +void ADC::detach(void) { + //* Attach IRQ + instance = this; + NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr); +} + + +//Append interrupt handler for pin to function isr +void ADC::append(PinName pin, void(*fptr)(uint32_t value)) { + int chan; + + chan = _pin_to_channel(pin); + _adc_isr[chan] = fptr; +} + +//Append interrupt handler for pin to function isr +void ADC::unappend(PinName pin) { + int chan; + + chan = _pin_to_channel(pin); + _adc_isr[chan] = NULL; +} + +//Unappend global interrupt handler to function isr +void ADC::append(void(*fptr)(int chan, uint32_t value)) { + _adc_g_isr = fptr; +} + +//Detach global interrupt handler to function isr +void ADC::unappend() { + _adc_g_isr = NULL; +} + +//Set ADC offset +void offset(int offset) { + LPC_ADC->ADTRM &= ~(0x07 << 4); + LPC_ADC->ADTRM |= (offset & 0x07) << 4; +} + +//Return current ADC offset +int offset(void) { + return((LPC_ADC->ADTRM >> 4) & 0x07); +} + +//Return value of ADC on pin +int ADC::read(PinName pin) { + //Reset DONE and OVERRUN flags of interrupt handled ADC data + _adc_data[_pin_to_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30)); + //Return value + return((_data_of_pin(pin) >> 4) & 0xFFF); +} + +//Return DONE flag of ADC on pin +int ADC::done(PinName pin) { + return((_data_of_pin(pin) >> 31) & 0x01); +} + +//Return OVERRUN flag of ADC on pin +int ADC::overrun(PinName pin) { + return((_data_of_pin(pin) >> 30) & 0x01); +} + +int ADC::actual_adc_clock(void) { + return(_adc_clk_freq); +} + +int ADC::actual_sample_rate(void) { + return(_adc_clk_freq / CLKS_PER_SAMPLE); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/adc.h Mon Oct 17 13:23:06 2011 +0000 @@ -0,0 +1,137 @@ +/* mbed Library - ADC + * Copyright (c) 2010, sblandford + * released under MIT license http://mbed.org/licence/mit + */ + +#ifndef MBED_ADC_H +#define MBED_ADC_H + +#include "mbed.h" + +#define XTAL_FREQ 12000000 +#define MAX_ADC_CLOCK 13000000 +#define CLKS_PER_SAMPLE 64 + +#define ADC_SAMPLE_RATE 150000 + + +class ADC { +public: + + //Initialize ADC with ADC maximum sample rate of + //sample_rate and system clock divider of cclk_div + //Maximum recommened sample rate is 184000 + ADC(int sample_rate, int cclk_div); + + //Enable/disable ADC on pin according to state + //and also select/de-select for next conversion + void setup(PinName pin, int state); + + //Return enabled/disabled state of ADC on pin + int setup(PinName pin); + + //Enable/disable burst mode according to state + void burst(int state); + + //Select channel already setup + void select(PinName pin); + + //Return burst mode enabled/disabled + int burst(void); + + /*Set start condition and edge according to mode: + 0 - No start (this value should be used when clearing PDN to 0). + 1 - Start conversion now. + 2 - Start conversion when the edge selected by bit 27 occurs on the P2.10 / EINT0 / NMI pin. + 3 - Start conversion when the edge selected by bit 27 occurs on the P1.27 / CLKOUT / + USB_OVRCRn / CAP0.1 pin. + 4 - Start conversion when the edge selected by bit 27 occurs on MAT0.1. Note that this does + not require that the MAT0.1 function appear on a device pin. + 5 - Start conversion when the edge selected by bit 27 occurs on MAT0.3. Note that it is not + possible to cause the MAT0.3 function to appear on a device pin. + 6 - Start conversion when the edge selected by bit 27 occurs on MAT1.0. Note that this does + not require that the MAT1.0 function appear on a device pin. + 7 - Start conversion when the edge selected by bit 27 occurs on MAT1.1. Note that this does + not require that the MAT1.1 function appear on a device pin. + When mode >= 2, conversion is triggered by edge: + 0 - Rising edge + 1 - Falling edge + */ + void startmode(int mode, int edge); + + //Return startmode state according to mode_edge=0: mode and mode_edge=1: edge + int startmode(int mode_edge); + + //Start ADC conversion + void start(void); + + //Set interrupt enable/disable for pin to state + void interrupt_state(PinName pin, int state); + + //Return enable/disable state of interrupt for pin + int interrupt_state(PinName pin); + + //Attach custom interrupt handler replacing default + void attach(void(*fptr)(void)); + + //Restore default interrupt handler + void detach(void); + + //Append custom interrupt handler for pin + void append(PinName pin, void(*fptr)(uint32_t value)); + + //Unappend custom interrupt handler for pin + void unappend(PinName pin); + + //Append custom global interrupt handler + void append(void(*fptr)(int chan, uint32_t value)); + + //Unappend custom global interrupt handler + void unappend(void); + + //Set ADC offset to a value 0-7 + void offset(int offset); + + //Return current ADC offset + int offset(void); + + //Return value of ADC on pin + int read(PinName pin); + + //Return DONE flag of ADC on pin + int done(PinName pin); + + //Return OVERRUN flag of ADC on pin + int overrun(PinName pin); + + //Return actual ADC clock + int actual_adc_clock(void); + + //Return actual maximum sample rate + int actual_sample_rate(void); + + //Return pin ID of ADC channel + PinName channel_to_pin(int chan); + + //Return pin number of ADC channel + int channel_to_pin_number(int chan); + + +private: + int _pin_to_channel(PinName pin); + uint32_t _data_of_pin(PinName pin); + + int _adc_clk_freq; + void adcisr(void); + static void _adcisr(void); + static ADC *instance; + + uint32_t _adc_data[8]; + void(*_adc_isr[8])(uint32_t value); + void(*_adc_g_isr)(int chan, uint32_t value); + void(*_adc_m_isr)(void); +}; + +extern ADC adc; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hardwareIO.cpp Mon Oct 17 13:23:06 2011 +0000 @@ -0,0 +1,87 @@ +#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 + + SPI spiDAC(MOSI_PIN, MISO_PIN, SCK_PIN); // mosi, miso, sclk + DigitalOut csDAC(CS_DAC_MIRRORS); + + DigitalOut Laser_Red(LASER_RED_PIN); + DigitalOut Laser_Green(LASER_GREEN_PIN); + DigitalOut Laser_Blue(LASER_BLUE_PIN); + +void HardwareIO::init(void) { + Laser_Red = 1; + Laser_Green = 0; + Laser_Blue = 0; + + //Serial Communication setup: + pc.baud(115200);//921600); + + // Setup for lock-in amplifier and pwm references: + lockin.init(); + + // Setup the 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); +} + +//write on the first DAC, output A (mirror X) +void HardwareIO::writeOutX(int 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(int 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::setRedPower(int powerValue){ + if(powerValue > 0){ + lockin.setLaserPower(true); + } + else{ + lockin.setLaserPower(false); + } +} +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; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hardwareIO.h Mon Oct 17 13:23:06 2011 +0000 @@ -0,0 +1,105 @@ + +#ifndef hardwareIO_h +#define hardwareIO_h + +#include "mbed.h" +#include "lockin.h" + + +// NOTE: the SPI library uses the following pins, but we don't have to set them and inputs or outputs (this is done when the library is initialized, which is done by pre-instantiation. +#define SCK_PIN p7 //SPI Clock +#define MISO_PIN p6 //SPI input (data comming from DAC, here not connected) +#define MOSI_PIN p5 //SPI output (data going to DAC) + +//**** CHIP SELECT pins for MP4922 DAC (mirrors and red laser) +// VERY IMPORTANT: the chip select for the DACs should be different from the default SPI "SS" pin (Slave Select), which is 53 by default (and will be used by the Ethernet Shield). +#define CS_DAC_MIRRORS p8 //Chip Select of the first DAC (mirrors) + +//**** LASERS pins: +#define LASER_RED_PIN p28 +#define LASER_GREEN_PIN p29 +#define LASER_BLUE_PIN p30 + +//**** MIRRORS: +//The DAC is 12 bytes capable (Max=4096), but we will limit this a little. +#define MAX_AD_MIRRORS 4095 // Absolute maximum for the SPI voltage (5V). This is for checking hardware compliance, but max and min angles can be defined for X and Y in each LivingSpot instance. +#define MIN_AD_MIRRORS 0 +// We assume that the center of the mirror is at MAX_AD_MIRRORS/2 = 2000: +#define CENTER_AD_MIRROR_X 2047 // This MUST BE the direction of the photodetector. +#define CENTER_AD_MIRROR_Y 2047 // This MUST BE the direction of the photodetector. + + +// **** REFERENCE SIGNAL: +/* +#define testPin_OC1A 11 // this is, output compare pin OC1A //connected to CK2 = lockIn clock +#define testPin_OC1B 12 // this is, output compare pin OC1B //connected to CK1 = laser clock +#define testPin_OC1C 13 +*/ + +// ================================================================================================================================================================== + +class HardwareIO { +public: + + + void init(void); + + float LockInRead_Volts(); + int LockInRead_AD(); + float LockInAD_to_Volts(int); + + // SPI control for DAC for mirrors and red laser power (low level): + void writeOutX(int value); + void writeOutY(int value); + + // mirror degree-to-AD units conversion factors: + //float AD_to_Deg_MIRROR_X;//=1.0*(MAX_DEG_MIRROR_X-MIN_DEG_MIRROR_X)/(MAX_AD_MIRRORS-MIN_AD_MIRRORS); + //float AD_to_Deg_MIRROR_Y;//=1.0*(MAX_DEG_MIRROR_Y-MIN_DEG_MIRROR_Y)/(MAX_AD_MIRRORS-MIN_AD_MIRRORS); + + /* + // Mirror position: + void setMirrorX_Deg(float _Az); + void setMirrorY_Deg(float _El); + void setMirrorX_AD(int _AzAD); + void setMirrorY_AD(int _ElAD); + void setMirrorsXY_AD(int _xAD, int _yAD); + void setMirrorsCenter(); + void getAnglesFromAD(float &Az, float &El, int _xAD, int _yAD); + //void setZoneDelimiters(...) // this could be here, instead on the LivingSpot class + */ + + + //Laser Power, for the moment laser are TTL but we can use int: + //if powerValue > 0 ==> 'true'; else 'false' + // Red laser: + void setRedPower(int powerRed); + // Green laser: + void setGreenPower(int powerGreen); + // Blue laser: + void setBluePower(int powerBlue); + + + //void setupPWM(); + /* IN ADVANCED HARDWARE: + // init PWM for reference generation: + void initPWM(); + // reference signal: + void setRefFreq(int); + void incRefFreq(int inc=1); + void decRefFreq(int dec=1); + */ + + //float refFreq; // could be private + + +private: + +}; + + +extern HardwareIO IO; // allows the object IO to be used in other .cpp files (IO is pre-instantiated in hardwareIO.cpp) +// NOTE: IO encapsulates many IO functions, but perhaps it is better not to have an IO object - just use each IO function separatedly (as with pc object for instance) +extern Serial pc; // allows pc to be manipulated by other .cpp files, even if pc is defined in the hardwareIO.cpp + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lockin.cpp Mon Oct 17 13:23:06 2011 +0000 @@ -0,0 +1,148 @@ +#include "lockin.h" + +Lockin lockin=Lockin();//pre-instanciation of object lockin with inter-file scope (declared extern in .h file) + +void catchInterupt(uint32_t value){ + lockin.buffer_pos=(lockin.buffer_pos+1)%BUFFER_SIZE; + lockin.buffer[lockin.buffer_pos] = (value>>4)&0xFFF; // this is 12 bit precision ADC (0 to 4095) +} + +// PWM generation is configure as double edge +// MR0 (Match Register 0) control the frequency +// 'pwm2' uses MR1 and MR2 (rising and falling edges) +// 'pwm4' uses MR3 and MR4 (rising and falling edges) +// 'pwm1' and 'pwm3' cannot be used since they share the same Match Register +// for the moment, all PWM pin are set as output: +PwmOut pwm1(p26); +PwmOut pwm2(p25); //also defined as LOCKIN_LASER_PIN +PwmOut pwm3(p24); +PwmOut pwm4(p23); //also defined as LOCKIN_REF_PIN +PwmOut pwm5(p22); +PwmOut pwm6(p21); + +//Lockin::Lockin(){} + +void Lockin::init(){ + + //configure PWM for the laser and the Lockin + refFreq = 147; + offsetRef = 40; + halfRefFreq = refFreq / 2; + + refFrequency = 653; //init the lock-in frequency at 653 kHz + phaseShiftLaser = 0.546; //offset of 54% for the laser signal + phaseShiftLockin = 0; //no offset for the lock-in reference + initPWM(); + + //configure ADC: + clearBuffer(); + + adc.startmode(0,0); + adc.burst(1); + adc.setup(LOCKIN_ADC_PIN, 1); + adc.select(LOCKIN_ADC_PIN); + adc.interrupt_state(LOCKIN_ADC_PIN, 1); + adc.append(LOCKIN_ADC_PIN, catchInterupt); +} + + +void Lockin::initPWM(){ + + float halfPeriod = 0.5 * MBEDFREQUENCY / refFrequency; // half shared periof + _currentMR[0] = int(1.0 * MBEDFREQUENCY / refFrequency); //save the current value of MR0 (shared periof) //147 + _currentMR[1] = int(phaseShiftLaser * halfPeriod); //save the current value of MR1 //40 + _currentMR[2] = int(_currentMR[1] + halfPeriod); //save the current value of MR2 //40+73 + _currentMR[3] = int(phaseShiftLockin * halfPeriod); //save the current value of MR1 //0 + _currentMR[4] = int(_currentMR[3] + halfPeriod); //save the current value of MR2 //73 + + + // set PWM: + LPC_PWM1->TCR = (1 << 1); // Reset counter, disable PWM + LPC_SC->PCLKSEL0 &= ~(0x3 << 12); + LPC_SC->PCLKSEL0 |= (1 << 12); // Set peripheral clock divider to /1, i.e. system clock + + LPC_PWM1->PCR |= 0x0014; // Double edge PWM for PWM2,4 + + LPC_PWM1->MR0 = _currentMR[0]; // Match Register 0 is shared period counter for all PWM1 + + LPC_PWM1->MR1 = _currentMR[1]; // Match Register 1 is laser rising edge counter + LPC_PWM1->MR2 = _currentMR[2]; // Match Register 2 is laser falling edge counter + LPC_PWM1->MR3 = _currentMR[3]; // Match Register 3 is lock-in rising edge counter + LPC_PWM1->MR4 = _currentMR[4]; // Match Register 4 is lock-in falling edge counter + + LPC_PWM1->LER |= 1; // Start updating at next period start + LPC_PWM1->TCR = (1 << 0) || (1 << 3); // Enable counter and PWM +} + +//change the frequency of the PWM after initPWM() +void Lockin::setPWMFrequency(float freq){ + refFrequency = freq; + _currentMR[0] = int(MBEDFREQUENCY / refFrequency); //save the current value of MR0 + LPC_PWM1->MR0 = _currentMR[0]; //update PWM shared period register + LPC_PWM1->LER |= 1; //update PWM +} + +//change the phase shift of the sensing laser after initPWM() +void Lockin::setLaserPhaseShift(float phaseShift){ + phaseShiftLaser = phaseShift; + float halfPeriod = 0.5 * MBEDFREQUENCY / refFrequency; + _currentMR[1] = int(phaseShiftLaser * halfPeriod); //save the current value of MR1 + _currentMR[2] = _currentMR[1] + halfPeriod; //save the current value of MR2 + + LPC_PWM1->MR1 = _currentMR[1]; //update Laser rising edge match register + LPC_PWM1->MR2 = _currentMR[2]; //update Laser faling edge match register +} + +//change the phase shift of the lock-in after initPWM() +void Lockin::setLockinPhaseShift(float phaseShift){ + phaseShiftLockin = phaseShift; + float halfPeriod = 0.5 * MBEDFREQUENCY / refFrequency; + _currentMR[3] = int(phaseShiftLockin * halfPeriod); //save the current value of MR1 + _currentMR[4] = _currentMR[3] + halfPeriod; //save the current value of MR2 + + LPC_PWM1->MR3 = _currentMR[3]; //update lock-in rising edge match register + LPC_PWM1->MR4 = _currentMR[4]; //update lock-in faling edge match register +} + + +void Lockin::setLaserPower(bool power){ + if(power){ + LPC_PWM1->MR1 = _currentMR[1]; + LPC_PWM1->MR2 = _currentMR[2]; + LPC_PWM1->LER |= 1; // update PWM at the next period + } + else{ + LPC_PWM1->MR1 = 0; //set rising edge at 0 + LPC_PWM1->MR2 = 0; //set falling edge at 0 + LPC_PWM1->LER |= 1; // update PWM at the next period + } +} + +void Lockin::clearBuffer(){ + for(int i=0; i<BUFFER_SIZE; i++){ + buffer[i] = 0; + } + buffer_pos = BUFFER_SIZE; +} + +/* +void Lockin::catchInterupt(uint32_t value){ + buffer_pos++; + buffer_pos%=BUFFER_SIZE; + buffer[buffer_pos] = value; +} +*/ + +float Lockin::getSmoothValue(){ + float smoothValue = buffer[0]; + for(int i=1; i<BUFFER_SIZE; i++){ + smoothValue += buffer[i]; + } + smoothValue /= BUFFER_SIZE; + + return smoothValue; +} + +float Lockin::getLastValue(){ + return buffer[buffer_pos]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lockin.h Mon Oct 17 13:23:06 2011 +0000 @@ -0,0 +1,73 @@ +#ifndef MBED_LOCKIN_H +#define MBED_LOCKIN_H + +#include "mbed.h" +#include "adc.h" + +#define MBEDFREQUENCY 96000 //frequency of the mBed in KHz + +//#define I_Volts_SAT 5 // this is 5V (the saturation voltage for the APD+LockIn+ADC, being it the arduino (when in normal mode) or the SPI. This saturation voltage could be different for each mode!? + +#define LOCKIN_ADC_PIN p18 //ADC pin : connect to lockin output +#define LOCKIN_LASER_PIN p22 //PWM pin : connect to laser input +#define LOCKIN_REF_PIN p25 //PWM pin : connect to lockin input +// p21 to p25 are used by the PWM timers even if only 2 pin are connected to the circuit + +#define BUFFER_SIZE 3 + + +class Lockin { +public: + // default constructor (untouched!) + // Lockin(); + + // initialization of object modes (could have parameters): + void init(); + + void initPWM(); //setup the laser and reference signal used by the lockin + void setPWMFrequency(float freq); //change the shared period of the pwm signals + void setLaserPhaseShift(float phaseShift); //change the phase shift of the laser (from 0 to 1) + void setLockinPhaseShift(float phaseShift); //change the phase shift of the lock-in (from 0 to 1) + //future works: + //add function to change frequency or offset with a potentiometer for exemple + //or scan the best frequency... + + void setLaserPower(bool power); //change PWM settings to turn on/off the laser + + + float getSmoothValue(); //return the average of the value stored on the buffer + float getLastValue(); //return the last conversion of the ADC + //it is just a begining; we can add more complex method for denoising for exemple + //maybe, needs function to start and stop the lockin + + void clearBuffer(); + + //can be private + //void catchInterupt(uint32_t value); + + //variables + float buffer[BUFFER_SIZE]; + int buffer_pos; + + float refFrequency; // frequency of sensing laser and lock-in (in KHz) + //Phase shift of the pwm signals (from 0 to 1): + //it corresponds to a percentage of the half of a period + //(no phase shift ==> 0% / 90deg phase shift ==> 100%) + float phaseShiftLaser; // phase shift of the sensing laser + float phaseShiftLockin; // phase shift of the lock-in reference + + int refFreq; //frequency of the lockin + int offsetRef; //offset between the laser signal and the lockin reference signal + int halfRefFreq; + +private: + // PWM match register value are saved as private + // to avoid computation when turn on/off for exemple + int _currentMR[6]; + + +}; + +extern Lockin lockin; + +#endif \ No newline at end of file