The project is a fast lock in amplifier (LIA) which can update its output at rate of 1000 measurements/s. It performs digital dual mixing and filtering to obtain a DC value proportional to the AC input signal.

Dependencies:   N5110 mbed

main.cpp

Committer:
Nikollao
Date:
2017-09-22
Revision:
4:2264789de7b2
Parent:
3:dd4eb355f8d9

File content as of revision 4:2264789de7b2:

#include "main.h"

int main()
{
    lcd_intro();
    calibrateJoystick(); ///calibrate joystick
    settings_menu();
    lcd.clear();
    dref.rise(&voltageRise); /// set interrupt to calculate reference frequency
    setupK64Fclocks();
    //settings_menu();
    /// initialise DAC output dac0_out
    while (ref_freq < 1e2) {
        sleep();
    }
    double freq_check = ref_freq*0.001;
    double out_speed = 2*var_speed;
    
    if (freq_check < out_speed) {
        lcd.printString("RefFreq Low",0,0);
        lcd.printString("OR: ",0,1);
        lcd.printString("AnOut too Fast",0,2);
        
        while (freq_check < out_speed) {
            sleep();
        }
    }
    /// make sure frequency is read before we go to the program
    /// cancel event-triggered rise interrupt, not to interfere with program
    dref.rise(NULL);
    pc.printf("Ref_Freq is:%.2f kHz\n\r",ref_freq*0.001);
    lcd.clear();
    char refFreq_char[20]; ///create an array of chars
    sprintf(refFreq_char,"%.3f",ref_freq); ///create string
    
    lcd.printString("Ref Freq: ",0,0);        
    lcd.printString(refFreq_char,35,1);
    /// constant 6 for correct sampling time 
    /// compensates for delay caused by computations
    sample_freq = 6*samples16*ref_freq;
    sample_time = 1/sample_freq;

    initDAC();
    delay_freq = ref_freq*amp_points;
    amplitude_delay = 1/delay_freq;
    
    wait(1);
    lcd.clear();
    lcd.printString("LIA ",0,0);
    lcd.printString("In process...",0,1);
    lcd.refresh();
    /// find the offset of the signal
    offset_ticker.attach(&offset_isr,0.001);
    
    double update_value = 0.0;
    
    while (offset == 0) {
        if (g_offset_flag == 1) {
            g_offset_flag = 0;
            offset = mavg_filter(filter_points);
        }
        sleep();
    }
    offset_ticker.detach();
    /// once the offset is calculated detach the offset ticker
    /// attach the output ticker to update every x ms
    update_value = var_speed*0.001;
    output_ticker.attach(&output_isr,update_value);
    
    while (true) {
        // gpo = !gpo;
        digitalMix(offset); /// perform digital mixing 
        while (g_output_flag == 0) {sleep();} /// sleep until flag is set
        /// update output
        if (g_output_flag == 1) {
            g_output_flag = 0;
            //aout = max(samples16);
            aout = var_gain*max(samples16); 
            /// DC output by taking the maximum value of the mixed signal (R)
        }
    }
}

double max(int points)
{
    double amp = 0;

    for (int i = 0; i < points; i++) {
        if (amp < R[i])
            amp = R[i]; /// find max of R
        //wait(amplitude_delay);
    }
    return amp;
}

double mavg_filter(int filt_points)
{
    double avg = 0, signal = 0;
    //double delay = 0.9/(1*ref_freq*filter_points);
    for (int i = 0; i < filter_points; i++) {
        signal = ain.read();
        avg = avg + signal;
         wait((float)(5e-5));
    }
    avg = avg/filter_points; /// find offset of input signal
    return avg;
}

void digitalMix(double remove_offset) {
    /// perform mixing of input and reference signals
    double input = 0;
    for (int i = 0; i < samples16;i++) {
        /// remove the offset before doing the multiplication of signals
        input = ain.read()-remove_offset;
        /// find the X component by multiplying with sine 17 values array
        double refX = input*sin_array16[i];
        /// find the Y component by multiplying with cosine 17 values array
        double refY = input*cos_array16[i];
        //double XY = exp(2*log(refX))+exp(2*log(refY));
        double XY = (refX*refX)+(refY*refY); /// R square
        //double R = exp(0.5*log(XY))/4;
        R[i] = pow(XY,0.5); /// R
        //aout = (1+sin_array16[i])/4;
        //aout = R[i]/2;
        wait(sample_time); /// sample time
    }
}

void voltageRise() {
    if (g_counter == 1) { 
        /// first time function is called is the first rise
        /// start timer
        period_timer.start();
        /// increase counter so next time function is called we calculate freq.
        g_counter++; 
    }
    else if (g_counter == 2) {
        /// second time function is called is the second rise 
        /// stop timer
        period_timer.stop();  
        /// calculate the time taken between the two rises to find period
        ref_period = period_timer.read();
        /// frequency is the inverse of the signal period
        ref_freq = 1/ref_period;
        /// reset timer
        period_timer.reset();
        /// increase counter because we only want to calculate once per cycle
        /// if we want to actively track the ref_freq we should decrease counter
        g_counter++;
    }
}

void setupK64Fclocks() {
    if(1) {
        uint32_t div1=0,div2=0,busClk=0,adcClk=0;
        SystemCoreClockUpdate();
        pc.printf("SystemCoreClock= %u \r\n",SystemCoreClock);
        /// System Core Clock: 120 MHz
        div1=( (SIM->CLKDIV1) & SIM_CLKDIV1_OUTDIV1_MASK)>>SIM_CLKDIV1_OUTDIV1_SHIFT;
        div1=1+div1;
        div2=1+( (SIM->CLKDIV1) &    SIM_CLKDIV1_OUTDIV2_MASK)>>SIM_CLKDIV1_OUTDIV2_SHIFT;
        busClk=SystemCoreClock*div1/div2;
        pc.printf("Divider1== %u div2=%u \r\n",div1,div2);
        pc.printf("MCGOUTCLK= %u,  busClk = %u \r\n",SystemCoreClock*div1,busClk);
        /// MCGOUTCLK 120 MHz, Bus Clock = 120 MHz
        ADC1->SC3 &= ~ADC_SC3_AVGE_MASK;//disable averages
        ADC1->CFG1 &= ~ADC_CFG1_ADLPC_MASK;//high-power mode
        ADC1->CFG1 &= ~0x0063 ; //clears ADICLK and ADIV
        ADC1->CFG1 |= ADC_CFG1_ADIV(2); //divide clock 0=/1, 1=/2, 2=/4, 3=/8
        //ADC0->SC3 |= 0x0007;//enable 32 averages

        if (((ADC1->CFG1)& 0x03) == 0) adcClk = busClk/(0x01<<(((ADC1->CFG1)&0x60)>>5));
        if (((ADC1->SC3)& 0x04) != 0) adcClk = adcClk/(0x01<<(((ADC1->SC3)&0x03)+2));
        pc.printf("adcCLK= %u  \r\n",adcClk);
        /// ADC Clock: 60 MHz
    }   
}

void offset_isr() {
    g_offset_flag = 1;   
}

void output_isr() {
    g_output_flag = 1;   
}

void initDAC() {
        DAC0->C0 = 0;
        DAC0->C1 = 0; //reset DAC state
        DAC0->C0 = DAC_C0_DACEN_MASK | DAC_C0_DACSWTRG_MASK| DAC_C0_DACRFS_MASK;
}   
void lcd_intro()
{
    pc.baud(115200);
    joyButton.rise(&joyButton_isr); ///assign rise with ISR
    joyButton.mode(PullDown); ///use PullDown mode
    swButton.rise(&swButton_isr); ///assign rise with ISR
    swButton.mode(PullDown); ///use PullDown mode
    lcd.init();
    lcd.setBrightness(0.7); // put LED backlight on 50%
    lcd.printString("THE CIRCUIT IS",1,1);
    lcd.printString("A FAST LIA!",7,3);
    lcd.refresh();
    Timeout timeout;
    timeout.attach(&timeout_isr,3);
    sleep();
    lcd.clear();
}

void settings_menu()
{
    lcd.setBrightness(0.7); // put LED backlight on 50%
    menu_ticker.attach(&menu_isr,0.2);

    while (exit_menu == 0) {
        if (g_menu_flag == 1) {
            g_menu_flag = 0;
            
            updateJoystick();
            init_LIA_menu();
            setup_selector();
            
            lcd.refresh();
            
            
        }
        sleep();
    }
    menu_ticker.detach();
}

void init_LIA_menu() {
       lcd.clear();
            
            lcd.printString("Settings:",0,0);
            
            char gain_char[20]; ///create an array of chars
            
            sprintf(gain_char,"%.2f",var_gain); ///create string
            
             char speed_char[20]; ///create an array of chars
            sprintf(speed_char,"%.2f",var_speed); ///create string
            
            lcd.printString("Gain: ",0,1);
            lcd.printString(gain_char,35,2);
            lcd.printString("Speed (ms)",0,3);
            lcd.printString(speed_char,35,4);
            lcd.printString("Confirm",0,5);
}

void setup_selector()
{
    switch (joystick.direction) { ///check the direction of joystick
        case UP:
            menu_option--;
            break;
        case DOWN:
            menu_option++;
            break;
        case RIGHT:
            switch (menu_option) {
                case 0:
                    var_gain += 0.1;
                    if (var_gain > 3)
                        var_gain = 0.1;
                    break;
                case 1:
                    var_speed++;
                    if (var_speed > 10)
                        var_speed = 1;
                    break;
            }
            break;
        case LEFT:
            switch (menu_option) {
                case 0:
                    var_gain -= 0.1;
                    if (var_gain < 0.1)
                        var_gain = 3;
                    break;
                case 1:
                    var_speed--;
                    if (var_speed < 1)
                        var_speed = 10;
                    break;
            }
            break;
    }
    if (menu_option < 0) { /// if the last (down) option is set for selection and user presses joystick down, selector moves at the top
        menu_option = 2;
    }
    if (menu_option > 2) { /// if the first (up) option is set for selection and user presses joystick up, selector moves at the bottom
        menu_option = 0;
    }

    if (menu_option == 0) { ///selection in menu depends on the value of int option
        lcd.drawCircle(75,10,3,1);
    } else if (menu_option == 1) {
        lcd.drawCircle(75,27,3,1);
    } else if (menu_option == 2) {
        lcd.drawCircle(55,43,3,1);
        if (g_joyButton_flag == 1) {
            g_joyButton_flag = 0;
            confirmationJoyButton();   
        }
    }
    g_joyButton_flag = 0;
}

void confirmationJoyButton () {
    
    bool confirm = 1;
    while (confirm) {
        lcd.clear();
        lcd.printString("Continue ?",0,0);
        lcd.printString("Yes",0,2);
        lcd.printString("No",0,3);
        updateJoystick();
        switch (joystick.direction) { ///check the direction of joystick
            case UP:
                save_option--;
                break;
            case DOWN:
                save_option++;
                break;
        }

        if (save_option < 0) { /// if the last (down) option is set for selection and user presses joystick down, selector moves at the top
            save_option = 1;
        }
        if (save_option > 1) { /// if the first (up) option is set for selection and user presses joystick up, selector moves at the bottom
            save_option = 0;
        }

        if (save_option == 0) {
            lcd.drawCircle(30,19,3,1);
        }
        else if (save_option == 1) { 
            lcd.drawCircle(30,28,3,1);
        }
        if (g_swButton_flag == 1) {
            g_swButton_flag = 0;
            if (save_option == 0) {
                confirm = 0; // exit continue
                exit_menu = 1; // exit menu   
            }
            else if (save_option == 1) {
                confirm = 0; // just exit continue   
            }
        }
        lcd.refresh();
        sleep();
    }
}

void menu_isr() {
    g_menu_flag = 1;   
}

void timeout_isr() {}

void joyButton_isr() {
    g_joyButton_flag = 1;   
}


void swButton_isr() {
    g_swButton_flag = 1;   
}

void calibrateJoystick()
{
    // must not move during calibration
    joystick.x0 = xPot;  // initial positions in the range 0.0 to 1.0 (0.5 if centred exactly)
    joystick.y0 = yPot;
}

void updateJoystick()
{
    // read current joystick values relative to calibrated values (in range -0.5 to 0.5, 0.0 is centred)
    joystick.x = xPot - joystick.x0;
    joystick.y = yPot - joystick.y0;
    // read button state
    joystick.button = joyButton;

    // calculate direction depending on x,y values
    // tolerance allows a little lee-way in case joystick not exactly in the stated direction
    if ( fabs(joystick.y) < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = CENTRE;
    } else if ( joystick.y > DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = UP;
    } else if ( joystick.y < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = DOWN;
    } else if ( joystick.x > DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = LEFT;
    } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = RIGHT;
    }/*
     else if (joystick.y > DIRECTION_TOLERANCE  && joystick.x  < DIRECTION_TOLERANCE) {
        joystick.direction = UP_LEFT;
    } else if (joystick.y > DIRECTION_TOLERANCE && joystick.x > DIRECTION_TOLERANCE) {
        joystick.direction = UP_RIGHT;
    } else if (joystick.y < DIRECTION_TOLERANCE && joystick.x < DIRECTION_TOLERANCE) {
        joystick.direction = DOWN_LEFT;
    }   else if (joystick.y < DIRECTION_TOLERANCE && joystick.x > DIRECTION_TOLERANCE) {
        joystick.direction = DOWN_RIGHT;
    }
    */
}