votre temps local: :: ()
heure officielle (en France) : ::(TZ:)
Heure UTC client: :: Offset client: s - delai: ms - - timeZone: Heure UTC: ::Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
fr:asservissement_numerique_de_temperature_pour_cavite_laser_ultra-stable [2018/03/23 13:34] fwiotte |
fr:asservissement_numerique_de_temperature_pour_cavite_laser_ultra-stable [2018/04/06 11:01] (Version actuelle) fwiotte |
||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | **Asservissement en température d'une cavité ultra-sable au LPL pour le Strontium** | + | **Asservissement en température numérique d'une cavité ultra-stable au LPL pour le Strontium** |
- | //condition de stabilité// | + | |
+ | **//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: | 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. | 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'é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 | É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}} | {{ 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; | ||
+ | } | ||
+ | |||