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.
main.cpp@0:4e20939af8bb, 2017-08-21 (annotated)
- Committer:
- Nikollao
- Date:
- Mon Aug 21 11:22:14 2017 +0000
- Revision:
- 0:4e20939af8bb
- Child:
- 1:bf693859586c
LIA FAST DONE!
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Nikollao | 0:4e20939af8bb | 1 | #include "main.h" |
Nikollao | 0:4e20939af8bb | 2 | |
Nikollao | 0:4e20939af8bb | 3 | int main() |
Nikollao | 0:4e20939af8bb | 4 | { |
Nikollao | 0:4e20939af8bb | 5 | pc.baud(115200); |
Nikollao | 0:4e20939af8bb | 6 | dref.rise(&voltageRise); /// set interrupt to calculate reference frequency |
Nikollao | 0:4e20939af8bb | 7 | setupK64Fclocks(); |
Nikollao | 0:4e20939af8bb | 8 | /// initialise DAC output dac0_out |
Nikollao | 0:4e20939af8bb | 9 | while (ref_freq < 1e2) { |
Nikollao | 0:4e20939af8bb | 10 | sleep(); |
Nikollao | 0:4e20939af8bb | 11 | } |
Nikollao | 0:4e20939af8bb | 12 | //ref_freq = 5e3; |
Nikollao | 0:4e20939af8bb | 13 | /// make sure frequency is read before we go to the program |
Nikollao | 0:4e20939af8bb | 14 | /// cancel event-triggered rise interrupt, not to interfere with program |
Nikollao | 0:4e20939af8bb | 15 | dref.rise(NULL); |
Nikollao | 0:4e20939af8bb | 16 | pc.printf("Ref_Freq is:%.2f kHz\n\r",ref_freq*0.001); |
Nikollao | 0:4e20939af8bb | 17 | //constant |
Nikollao | 0:4e20939af8bb | 18 | sample_freq = 6*samples16*ref_freq; |
Nikollao | 0:4e20939af8bb | 19 | sample_time = 1/sample_freq; |
Nikollao | 0:4e20939af8bb | 20 | |
Nikollao | 0:4e20939af8bb | 21 | initDAC(); |
Nikollao | 0:4e20939af8bb | 22 | delay_freq = ref_freq*amp_points; |
Nikollao | 0:4e20939af8bb | 23 | amplitude_delay = 1/delay_freq; |
Nikollao | 0:4e20939af8bb | 24 | offset_ticker.attach(&offset_isr,0.001); |
Nikollao | 0:4e20939af8bb | 25 | |
Nikollao | 0:4e20939af8bb | 26 | while (offset == 0) { |
Nikollao | 0:4e20939af8bb | 27 | if (g_offset_flag == 1) { |
Nikollao | 0:4e20939af8bb | 28 | g_offset_flag = 0; |
Nikollao | 0:4e20939af8bb | 29 | offset = mavg_filter(filter_points); |
Nikollao | 0:4e20939af8bb | 30 | } |
Nikollao | 0:4e20939af8bb | 31 | sleep(); |
Nikollao | 0:4e20939af8bb | 32 | } |
Nikollao | 0:4e20939af8bb | 33 | offset_ticker.detach(); |
Nikollao | 0:4e20939af8bb | 34 | |
Nikollao | 0:4e20939af8bb | 35 | output_ticker.attach(&output_isr,0.00099); |
Nikollao | 0:4e20939af8bb | 36 | |
Nikollao | 0:4e20939af8bb | 37 | while (true) { |
Nikollao | 0:4e20939af8bb | 38 | // gpo = !gpo; |
Nikollao | 0:4e20939af8bb | 39 | digitalMix(offset); |
Nikollao | 0:4e20939af8bb | 40 | while (g_output_flag == 0) {sleep();} |
Nikollao | 0:4e20939af8bb | 41 | |
Nikollao | 0:4e20939af8bb | 42 | if (g_output_flag == 1) { |
Nikollao | 0:4e20939af8bb | 43 | g_output_flag = 0; |
Nikollao | 0:4e20939af8bb | 44 | //aout = max(samples16); |
Nikollao | 0:4e20939af8bb | 45 | aout = 2*max(samples16); |
Nikollao | 0:4e20939af8bb | 46 | } |
Nikollao | 0:4e20939af8bb | 47 | } |
Nikollao | 0:4e20939af8bb | 48 | } |
Nikollao | 0:4e20939af8bb | 49 | |
Nikollao | 0:4e20939af8bb | 50 | double max(int points) |
Nikollao | 0:4e20939af8bb | 51 | { |
Nikollao | 0:4e20939af8bb | 52 | double amp = 0; |
Nikollao | 0:4e20939af8bb | 53 | |
Nikollao | 0:4e20939af8bb | 54 | for (int i = 0; i < points; i++) { |
Nikollao | 0:4e20939af8bb | 55 | if (amp < R[i]) |
Nikollao | 0:4e20939af8bb | 56 | amp = R[i]; |
Nikollao | 0:4e20939af8bb | 57 | //wait(amplitude_delay); |
Nikollao | 0:4e20939af8bb | 58 | } |
Nikollao | 0:4e20939af8bb | 59 | return amp; |
Nikollao | 0:4e20939af8bb | 60 | } |
Nikollao | 0:4e20939af8bb | 61 | |
Nikollao | 0:4e20939af8bb | 62 | double mavg_filter(int filt_points) |
Nikollao | 0:4e20939af8bb | 63 | { |
Nikollao | 0:4e20939af8bb | 64 | double avg = 0, signal = 0; |
Nikollao | 0:4e20939af8bb | 65 | double delay = 0.9/(1*ref_freq*filter_points); |
Nikollao | 0:4e20939af8bb | 66 | for (int i = 0; i < filter_points; i++) { |
Nikollao | 0:4e20939af8bb | 67 | signal = ain.read(); |
Nikollao | 0:4e20939af8bb | 68 | avg = avg + signal; |
Nikollao | 0:4e20939af8bb | 69 | wait((float)(5e-5)); |
Nikollao | 0:4e20939af8bb | 70 | } |
Nikollao | 0:4e20939af8bb | 71 | avg = avg/filter_points; |
Nikollao | 0:4e20939af8bb | 72 | return avg; |
Nikollao | 0:4e20939af8bb | 73 | } |
Nikollao | 0:4e20939af8bb | 74 | |
Nikollao | 0:4e20939af8bb | 75 | void digitalMix(double remove_offset) { |
Nikollao | 0:4e20939af8bb | 76 | /// perform mixing of input and reference signals |
Nikollao | 0:4e20939af8bb | 77 | double input = 0; |
Nikollao | 0:4e20939af8bb | 78 | for (int i = 0; i < samples16;i++) { |
Nikollao | 0:4e20939af8bb | 79 | /// remove the offset before doing the multiplication of signals |
Nikollao | 0:4e20939af8bb | 80 | input = ain.read()-remove_offset; |
Nikollao | 0:4e20939af8bb | 81 | /// find the X component by multiplying with sine 17 values array |
Nikollao | 0:4e20939af8bb | 82 | double refX = input*sin_array16[i]; |
Nikollao | 0:4e20939af8bb | 83 | /// find the Y component by multiplying with cosine 17 values array |
Nikollao | 0:4e20939af8bb | 84 | double refY = input*cos_array16[i]; |
Nikollao | 0:4e20939af8bb | 85 | //double XY = exp(2*log(refX))+exp(2*log(refY)); |
Nikollao | 0:4e20939af8bb | 86 | double XY = (refX*refX)+(refY*refY); /// R square |
Nikollao | 0:4e20939af8bb | 87 | //double R = exp(0.5*log(XY))/4; |
Nikollao | 0:4e20939af8bb | 88 | R[i] = pow(XY,0.5); /// R |
Nikollao | 0:4e20939af8bb | 89 | //aout = (1+sin_array16[i])/4; |
Nikollao | 0:4e20939af8bb | 90 | //aout = R[i]/2; |
Nikollao | 0:4e20939af8bb | 91 | wait(sample_time); /// sample time |
Nikollao | 0:4e20939af8bb | 92 | } |
Nikollao | 0:4e20939af8bb | 93 | } |
Nikollao | 0:4e20939af8bb | 94 | |
Nikollao | 0:4e20939af8bb | 95 | void voltageRise() { |
Nikollao | 0:4e20939af8bb | 96 | if (g_counter == 1) { |
Nikollao | 0:4e20939af8bb | 97 | /// first time function is called is the first rise |
Nikollao | 0:4e20939af8bb | 98 | /// start timer |
Nikollao | 0:4e20939af8bb | 99 | period_timer.start(); |
Nikollao | 0:4e20939af8bb | 100 | /// increase counter so next time function is called we calculate freq. |
Nikollao | 0:4e20939af8bb | 101 | g_counter++; |
Nikollao | 0:4e20939af8bb | 102 | } |
Nikollao | 0:4e20939af8bb | 103 | else if (g_counter == 2) { |
Nikollao | 0:4e20939af8bb | 104 | /// second time function is called is the second rise |
Nikollao | 0:4e20939af8bb | 105 | /// stop timer |
Nikollao | 0:4e20939af8bb | 106 | period_timer.stop(); |
Nikollao | 0:4e20939af8bb | 107 | /// calculate the time taken between the two rises to find period |
Nikollao | 0:4e20939af8bb | 108 | ref_period = period_timer.read(); |
Nikollao | 0:4e20939af8bb | 109 | /// frequency is the inverse of the signal period |
Nikollao | 0:4e20939af8bb | 110 | ref_freq = 1/ref_period; |
Nikollao | 0:4e20939af8bb | 111 | /// reset timer |
Nikollao | 0:4e20939af8bb | 112 | period_timer.reset(); |
Nikollao | 0:4e20939af8bb | 113 | /// increase counter because we only want to calculate once per cycle |
Nikollao | 0:4e20939af8bb | 114 | /// if we want to actively track the ref_freq we should decrease counter |
Nikollao | 0:4e20939af8bb | 115 | g_counter++; |
Nikollao | 0:4e20939af8bb | 116 | } |
Nikollao | 0:4e20939af8bb | 117 | } |
Nikollao | 0:4e20939af8bb | 118 | |
Nikollao | 0:4e20939af8bb | 119 | void setupK64Fclocks() { |
Nikollao | 0:4e20939af8bb | 120 | if(1) { |
Nikollao | 0:4e20939af8bb | 121 | uint32_t div1=0,div2=0,busClk=0,adcClk=0; |
Nikollao | 0:4e20939af8bb | 122 | SystemCoreClockUpdate(); |
Nikollao | 0:4e20939af8bb | 123 | pc.printf("SystemCoreClock= %u \r\n",SystemCoreClock); |
Nikollao | 0:4e20939af8bb | 124 | /// System Core Clock: 120 MHz |
Nikollao | 0:4e20939af8bb | 125 | div1=( (SIM->CLKDIV1) & SIM_CLKDIV1_OUTDIV1_MASK)>>SIM_CLKDIV1_OUTDIV1_SHIFT; |
Nikollao | 0:4e20939af8bb | 126 | div1=1+div1; |
Nikollao | 0:4e20939af8bb | 127 | div2=1+( (SIM->CLKDIV1) & SIM_CLKDIV1_OUTDIV2_MASK)>>SIM_CLKDIV1_OUTDIV2_SHIFT; |
Nikollao | 0:4e20939af8bb | 128 | busClk=SystemCoreClock*div1/div2; |
Nikollao | 0:4e20939af8bb | 129 | pc.printf("Divider1== %u div2=%u \r\n",div1,div2); |
Nikollao | 0:4e20939af8bb | 130 | pc.printf("MCGOUTCLK= %u, busClk = %u \r\n",SystemCoreClock*div1,busClk); |
Nikollao | 0:4e20939af8bb | 131 | /// MCGOUTCLK 120 MHz, Bus Clock = 120 MHz |
Nikollao | 0:4e20939af8bb | 132 | ADC0->SC3 &= ~ADC_SC3_AVGE_MASK;//disable averages |
Nikollao | 0:4e20939af8bb | 133 | ADC0->CFG1 &= ~ADC_CFG1_ADLPC_MASK;//high-power mode |
Nikollao | 0:4e20939af8bb | 134 | ADC0->CFG1 &= ~0x0063 ; //clears ADICLK and ADIV |
Nikollao | 0:4e20939af8bb | 135 | ADC0->CFG1 |= ADC_CFG1_ADIV(2); //divide clock 0=/1, 1=/2, 2=/4, 3=/8 |
Nikollao | 0:4e20939af8bb | 136 | //ADC0->SC3 |= 0x0007;//enable 32 averages |
Nikollao | 0:4e20939af8bb | 137 | |
Nikollao | 0:4e20939af8bb | 138 | if (((ADC0->CFG1)& 0x03) == 0) adcClk = busClk/(0x01<<(((ADC0->CFG1)&0x60)>>5)); |
Nikollao | 0:4e20939af8bb | 139 | if (((ADC0->SC3)& 0x04) != 0) adcClk = adcClk/(0x01<<(((ADC0->SC3)&0x03)+2)); |
Nikollao | 0:4e20939af8bb | 140 | pc.printf("adcCLK= %u \r\n",adcClk); |
Nikollao | 0:4e20939af8bb | 141 | /// ADC Clock: 60 MHz |
Nikollao | 0:4e20939af8bb | 142 | } |
Nikollao | 0:4e20939af8bb | 143 | } |
Nikollao | 0:4e20939af8bb | 144 | |
Nikollao | 0:4e20939af8bb | 145 | void offset_isr() { |
Nikollao | 0:4e20939af8bb | 146 | g_offset_flag = 1; |
Nikollao | 0:4e20939af8bb | 147 | } |
Nikollao | 0:4e20939af8bb | 148 | |
Nikollao | 0:4e20939af8bb | 149 | void output_isr() { |
Nikollao | 0:4e20939af8bb | 150 | g_output_flag = 1; |
Nikollao | 0:4e20939af8bb | 151 | } |
Nikollao | 0:4e20939af8bb | 152 | |
Nikollao | 0:4e20939af8bb | 153 | void initDAC() { |
Nikollao | 0:4e20939af8bb | 154 | DAC0->C0 = 0; |
Nikollao | 0:4e20939af8bb | 155 | DAC0->C1 = 0; //reset DAC state |
Nikollao | 0:4e20939af8bb | 156 | DAC0->C0 = DAC_C0_DACEN_MASK | DAC_C0_DACSWTRG_MASK| DAC_C0_DACRFS_MASK; |
Nikollao | 0:4e20939af8bb | 157 | } |