**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}} {{:fr:pcb_asservissement.png?600 |}} {{:fr:asservissement_de_temperature.png?800|}} ** 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; }