Asservissement en température numérique d'une cavité ultra-stable au LPL pour le Strontium
condition de stabilité +/- 10mK
Résultas obtenus : 1mk sur la journée
Compte tenu des constantes de temps d'intégration nous avons opté pour un lock numérique avec un uC 16 bits. ADC et DAC 12 bits
MSP430F169 16bits Texas instruments, and IAR Workbench for MSP430 or Code composer studio
Cahier des charges: La précision du lock en température devrait être de l'ordre de 10 mK, pour assurer une stabilité en fréquence du laser de l'ordre de quelques kHz. L'élément chauffant a une résistance de 6 Ohm. Un courant de l'ordre de 0.6 A devrait fournir la puissance pour nous amener au point de fonctionnement vers 27°C. Élément de mesure : thermistor MC65F103B
Ci-joint le code C du projet: gestion affiche LCD + calcul température + fonction standby + contrôle de la consigne de température + lock + timer + déclaration des variables + déclaration prototypes + fichier init système (ADC-DAC TIMER-PORT IN OUT)
Manuel descriptif de la cavité: vh6010-4_inst_manual.pdf
Function prototypes
void init_sys(void); // MSP430 Initialisation routine void tempo_loop(long loop_number); // wait void write_DAC12_0(int out_data); // write DAC12_0 routine void write_DAC12_1(unsigned short out_data); // write DAC12_1 routine int read_ADC12(int chanel); // read routine on ADC12 char lock(); void standby(); // init state of state machine unsigned short racine(unsigned long valIn); int Lcd_Cmd(int portP4); int lcd_display(char *disp); int lcd_display2(char *disp2); int lcd_print(char *s); int Lcd_Clear(); void Lcd_Init(void); int load_LCD_consigne(void); int calcul_Resistor_thermistance(void); void visu_etat_lock(void);
déclaration des variables
int consigne=2048; static unsigned int DAC0_out; static unsigned int DAC1_out; static int ADC_in; static int ADC_in_2; static float ADC_in_2_convert_in_volt; static long error_signal; static long accu_out; static long prop; static unsigned long valIn; static unsigned short valOut; static long control; static char next_state = 0; static char state = 0; static char s[20]; static char t[20]; static double RTH; static double Temperature_in_degre; static double calcul_ln; char i = 0; char j = 0;
main
void main(void) { init_sys(); // Initialise the MSP430 Lcd_Init(); tempo_loop(10); Lcd_Clear(); tempo_loop(10); Lcd_Cmd(0x80); lcd_display("Welcome to"); Lcd_Cmd(0xc0); lcd_display("CNRS-LPL-IG-UP13"); tempo_loop(10); Lcd_Clear(); tempo_loop(10); Lcd_Cmd(0x84); lcd_display("Atelier"); Lcd_Cmd(0xc2); lcd_display("Electronique"); tempo_loop(10); Lcd_Clear(); tempo_loop(10); sprintf(t,"T in Deg= %.3f",Temperature_in_degre); Lcd_Cmd(0x80); lcd_display(t); copy_RAM_to_consigne(); Lcd_Cmd(0xc0); sprintf(s,"consigne= %d",consigne); lcd_display(s); standby(); _BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt }
gestion affichage LCD
//Table 1: Character LCD pins with 1 Controller //**********************************************// //1 VSS Power supply (GND) //2 VCC Power supply (+5V) //3 VEE Contrast adjust //4 RS 0 = Instruction input //1 = Data input //5 R/W 0 = Write to LCD module //1 = Read from LCD module //6 EN Enable signal //7 D0 Data bus line 0 (LSB) //8 D1 Data bus line 1 //9 D2 Data bus line 2 //10 D3 Data bus line 3 //11 D4 Data bus line 4 //12 D5 Data bus line 5 //13 D6 Data bus line 6 //14 D7 Data bus line 7 (MSB) int Lcd_Port(int portP4) { P4OUT = portP4; return 0; } // Function for sending command to LCD int Lcd_Cmd(int portP4) { //RS = 0x00; // => RS = 0 P1OUT &= ~BIT7; /* Pin P1.6 = 0 */ Lcd_Port(portP4); //E = 0x40; // Enable = 1 P1OUT |= BIT6; /* Pin P1.7 = 1 */ tempo_loop(1000); //E = 0x00; // Enable = 0 P1OUT &= ~BIT6; /* Pin P1.7 = 0 */ return 0; } // Function for sending data to LCD int Lcd_data(int portP4) { Lcd_Port(portP4); //RS = 0x80; // RS = 1 P1OUT |= BIT7; /* Pin P1.6 = 1 */ //E = 0x40; //Enable = 1 P1OUT |= BIT6; /* Pin P1.7 = 1 */ tempo_loop(1000); //E = 0x00; // Enable = 0 P1OUT &= ~BIT6; /* Pin P1.7 = 0 */ return 0; } int Lcd_Clear() { Lcd_Cmd(0); Lcd_Cmd(1); return 0; } void Lcd_display_off() { Lcd_Cmd(0); Lcd_Cmd(0x0C); } void Lcd_Shift_Right() { Lcd_Cmd(0x01); Lcd_Cmd(0x18); } void Lcd_Shift_Left() { Lcd_Cmd(0x01); Lcd_Cmd(0x1C); } // Function for initializing LCD void Lcd_Init() { Lcd_Cmd(0x38); //Function set: 2 Line, 8-bit, 5x7 dots Lcd_Cmd(0x0c); Lcd_Cmd(0x01); //Clear LCD Lcd_Cmd(0x06); //Entry mode, auto increment with no shift Lcd_Cmd(0x83); // DDRAM addresses 0x80..0x8F + 0xC0..0xCF are used. } // Function for sending string to LCD int lcd_display(char *disp) { int x=0; while(disp[x]!=0) { Lcd_data(disp[x]); x++; } return 0; }
Calcul température + affichage
int calcul_Resistor_thermistance(void) { WDTCTL = WDTPW + WDTHOLD; // stop Watch Dog Timer ADC_in_2_convert_in_volt = (ADC_in_2*2.498)/4096; RTH = (ADC_in_2_convert_in_volt*9100)/(2.498 - ADC_in_2_convert_in_volt); float K = 298.15; // T(25°C) calcul_ln = log((RTH/10000)); //log népérien Temperature_in_degre = 1/(calcul_ln/3988 + 1/K) - 273.15; //température en degrès C sprintf(t,"T in Deg= %.3f",Temperature_in_degre); Lcd_Cmd(0x80); lcd_display(t); return 0; }
fonction standby
void standby() { WDTCTL = WDTPW + WDTHOLD; // stop WatchDog DAC0_out= consigne; // init DAC0 DAC1_out= 0; // init DAC1 accu_out= 0; P2OUT &= ~BIT1; /* Pin P2.1 = 0 */ P2OUT &= ~BIT2; /* Pin P2.2 = 0 */ P2OUT &= ~BIT3; /* Pin P2.3 = 0 */ P2OUT &= ~BIT4; /* Pin P2.4 = 0 */ P2OUT &= ~BIT5; /* Pin P2.5 = 0 */ }
fonction lock
char lock() { error_signal = (ADC_in - consigne); // error_signal => int (16 bits) (-32768 to 32767) prop = error_signal << 21; control = prop + accu_out ; // + (accu_out >> 8); visu_etat_lock(); if(P1IN & 0x20) { if (accu_out >= 2147000000 | accu_out <= -2147000000) { accu_out = accu_out; } else {accu_out = (error_signal << 7) + accu_out;} } if(control < 0) { valIn = 0;} else {valIn = (control << 1);} valOut = racine(valIn); DAC1_out = valOut >> 4; return 0; } unsigned short racine(unsigned long valIn) //on linéarise la réponse de la thermistance { unsigned short valOut=0; unsigned long diff=0L; for(int i=0; i<16; i++) { diff <<= 2; valOut <<=1; diff |= valIn >> 30; valIn <<=2; if(diff > 2*valOut) { diff -= (2*valOut)+1; valOut++; } } return valOut; }
fonction TIMER pour échantillonnage
#pragma vector = TIMERB0_VECTOR // timer pour échantillonner à 0.1s __interrupt void Timer_B (void) { WDTCTL = WDTPW + WDTCNTCL; // clear and start WatchDog write_DAC12_1(DAC1_out); // write DAC1_out to DAC12_1 write_DAC12_0(DAC0_out); // write DAC0_out to DAC12_0 ADC_in = read_ADC12(INCH_3); // read signal erreur ADC_in_2 = read_ADC12(INCH_4); // read tension thermistor calcul_Resistor_thermistance(); if((P2IN & 0x01)==0x01) //P2.0 modification de la valeur de consigne + { if(P3IN & 0x01) {consigne = consigne + 10;} //handle P1.7 switch if (P3IN & 0x02) {consigne = consigne + 100;} if(( P3IN & BIT0 ) == 0 && ( P3IN & BIT1 ) == 0 ) {consigne = consigne + 1;} }P1IFG &= ~BIT1; if((P1IN & 0x01)==0x01) //P1.0 modification de la valeur de consigne - { if(P3IN & 0x01) {consigne = consigne - 10;} //handle P1.7 switch if (P3IN & 0x02) {consigne = consigne - 100;} if(( P3IN & BIT0 ) == 0 && ( P3IN & BIT1 ) == 0 ) {consigne = consigne - 1;} }P1IFG &= ~BIT0; chargement_consigne(); DAC0_out = consigne; Lcd_Cmd(0xc0); sprintf(s,"consigne= %d",consigne); lcd_display(s); state = next_state; if(P1IN & 0x10) // P1.4 mode standby ou lock { state=0; } else {state=1;} switch (state) { case 0:{standby();}break; case 1:{lock();}break; } }
fonction init system
void init_sys(void) { P1SEL = 0x00; // P1 I/O select P2SEL = 0x00; // P2 I/O select P3SEL = 0x00; // P3 I/O select P4SEL = 0x00; // P4 I/O select P5SEL = 0x1B; // P5.1,3 SPI option select CS P5.0 P6SEL = 0x18; // P6.5 ADC_3 options select Enable A/D channel A3 channel A4 P1DIR = 0xC0; // P1 inputs direction P1.6 P1.7 outputs P2DIR = 0xFE; // P2 input direction P2.0 input P3DIR = 0xFC; // P3 output direction P3.0 P3.1 inputs P4DIR = 0xFF; // P4 output direction P5DIR = 0xFF; // P5 output direction P6DIR = 0xF7; // P6.3 input/other output //ADC12CTL0 = REF2_5V + REFON; // Internal 2.5V ref on ADC12CTL0 |= SHT0_4 + ADC12ON; // Set sampling time, turn on ADC12 ADC12CTL1 |= SHP + ADC12SSEL_3; // Use sampling timer & SMCLK clock //ADC12MCTL0 |= SREF_1; // Use Internal 2.5V for ADC ADC12MCTL0 = SREF_2; // Vr+ = VeREF+ (external) //DAC12_0CTL = DAC12IR + DAC12AMP_2 + DAC12RES; // 8bits, internal ref gain 1 (DAC_0 select with DAC12AMP_2) //DAC12_1CTL = DAC12IR + DAC12AMP_2 + DAC12RES; // 8bits, internal ref gain 1 (DAC_1 select with DAC12AMP_2) DAC12_0CTL = DAC12IR + DAC12SREF_3 + DAC12AMP_7 + DAC12ENC; //Ref = Veref+, Full-Speed, Enable Conv. DAC12_1CTL = DAC12IR + DAC12SREF_3 + DAC12AMP_7 + DAC12ENC; //Ref = Veref+, Full-Speed, Enable Conv. // DCO frequency ~200 kHz DCOCTL &= ~(DCO1 + DCO0); BCSCTL1 &= ~RSEL2; BCSCTL1 |= RSEL1; // + ~RSEL0; // timer B TBCCTL0 = CCIE; // CCR0 interrupt enabled TBCTL = TASSEL_2 + MC_1 + ID_3; // clk = SMCLK/8 (=DCO/8), Up mode //TBCCR0 = 2500; // timer count at 1/2500 clk : 0,1s TBCCR0 = 250; }