User Tools

Site Tools


Writing /var/lib/dokuwiki/data/meta/teaching/ie0624/proyectos/control_motores_sincronicos/codigo.meta failed
teaching:ie0624:proyectos:control_motores_sincronicos:codigo

Código Fuente Utilizado para la Implementación del Sistema de Control

Diagrama de Flujo

  • A continuación se muestra el código fuente utilizado para la implementación de Sistema de Control, incluyendo la comunicación a través de USART, y las señales enviadas al puente H para el control de potencia.

Para una mejor comprensión de la lógica general, ver el diagrama de flujo del sistema implementado.

/*
   * Copyright (C) 2018 ARCOS-Lab Universidad de Costa Rica
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
 
  #include <libopencm3/stm32/rcc.h>
  #include <libopencm3/stm32/gpio.h>
  #include <libopencm3/stm32/adc.h> //analog digital converter
  #include <libopencm3/stm32/usart.h>
  #include <libopencm3/stm32/timer.h>
  #include <libopencm3/stm32/f3/nvic.h>
  #include "proyecto.h"
  #include <limits.h>
  #include <stdbool.h>
  #include <stdio.h>
  #include <stdlib.h>
 
  //pwm-related timer configuration
  #define SYSFREQ     64000000 //168MHz
  #define PWMFREQ      8000  //32000
  #define PWMFREQ_F       ((float )(PWMFREQ)) //32000.0f
  #define PRESCALE        1                           //freq_CK_CNT=freq_CK_PSC/(PSC[15:0]+1)
  #define PWM_PERIOD_ARR   (SYSFREQ/(PWMFREQ*PRESCALE))
 
  #define LBLUE GPIOE, GPIO8
  #define LRED GPIOE, GPIO9
  #define LORANGE GPIOE, GPIO10
  #define LGREEN GPIOE, GPIO11
  #define LBLUE2 GPIOE, GPIO12
  #define LRED2 GPIOE, GPIO13
  #define LORANGE2 GPIOE, GPIO14
  #define LGREEN2 GPIOE, GPIO15
 
  #define LD4 GPIOE, GPIO8
  #define LD3 GPIOE, GPIO9
  #define LD5 GPIOE, GPIO10
  #define LD7 GPIOE, GPIO11
  #define LD9 GPIOE, GPIO12
  #define LD10 GPIOE, GPIO13
  #define LD8 GPIOE, GPIO14
  #define LD6 GPIOE, GPIO15
 
 
  void leds_init(void) {
  	rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_IOPEEN);
  	gpio_mode_setup(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO8| GPIO9| GPIO10| GPIO11| GPIO12| GPIO13| GPIO14| GPIO15);//PE8-15
  }
 
  void system_init(void) {
    rcc_clock_setup_hsi(&rcc_hsi_configs[RCC_CLOCK_HSI_64MHZ]);
 
    leds_init();
  }
 
 
 
//timer  ########################################################################################
 
 
  void tim_init(void)
  {
    /* Enable TIM1 clock. and Port E clock (for outputs) */
    rcc_periph_clock_enable(RCC_TIM1);
    rcc_periph_clock_enable(RCC_GPIOE);
 
    //Set TIM1 channel (and complementary) output to alternate function push-pull'.
    //f4 TIM1=> GIO9: CH1, GPIO11: CH2, GPIO13: CH3
    //f4 TIM1=> GIO8: CH1N, GPIO10: CH2N, GPIO12: CH3N
    gpio_mode_setup(GPIOE, GPIO_MODE_AF,GPIO_PUPD_NONE,GPIO9);
    gpio_set_af(GPIOE, GPIO_AF2, GPIO9);
 
    /* Reset TIM1 pepheral. */
    rcc_periph_reset_pulse(RST_TIM1);
 
    /* Timer global mode:
     * - No divider
     * - Alignment edge
     * - Direction up
     */
    timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, //For dead time and filter sampling, not important for now.
                   TIM_CR1_CMS_CENTER_3,  //TIM_CR1_CMS_EDGE
                   //TIM_CR1_CMS_CENTER_1
                   //TIM_CR1_CMS_CENTER_2
                   //TIM_CR1_CMS_CENTER_3 la frequencia del pwm se divide a la mitad.
                   TIM_CR1_DIR_UP);
 
    timer_set_prescaler(TIM1, PRESCALE); //1 = disabled (max speed)
    timer_set_repetition_counter(TIM1, 0); //disabled
    timer_enable_preload(TIM1);
    timer_continuous_mode(TIM1);
 
    /* Period (32kHz). */
    timer_set_period(TIM1, PWM_PERIOD_ARR); //ARR (value compared against main counter to reload counter aka: period of counter)
 
    /* Configure break and deadtime. */
    timer_set_enabled_off_state_in_idle_mode(TIM1);
    timer_set_enabled_off_state_in_run_mode(TIM1);
    timer_disable_break(TIM1);
    timer_set_break_polarity_high(TIM1);
    timer_disable_break_automatic_output(TIM1);
    timer_set_break_lock(TIM1, TIM_BDTR_LOCK_OFF);
 
    /* Disable outputs. */
    timer_disable_oc_output(TIM1, TIM_OC1);
    timer_disable_oc_output(TIM1, TIM_OC1N);
    timer_disable_oc_output(TIM1, TIM_OC2);
    timer_disable_oc_output(TIM1, TIM_OC2N);
    timer_disable_oc_output(TIM1, TIM_OC3);
    timer_disable_oc_output(TIM1, TIM_OC3N);
 
    /* -- OC1 and OC1N configuration -- */
    /* Configure global mode of line 1. */
    timer_enable_oc_preload(TIM1, TIM_OC1);
    timer_set_oc_mode(TIM1, TIM_OC1, TIM_OCM_PWM1);
    /* Configure OC1. */
    timer_set_oc_polarity_high(TIM1, TIM_OC1);
    timer_set_oc_idle_state_unset(TIM1, TIM_OC1); //When idle (braked) put 0 on output
    /* Configure OC1N. */
    timer_set_oc_polarity_high(TIM1, TIM_OC1N);
    timer_set_oc_idle_state_unset(TIM1, TIM_OC1N);
 
 
    //#######
    /* Set the capture compare value for OC1. */
    //#########
 
 
 
    /* ARR reload enable. */
    timer_enable_preload(TIM1);
 
    /*
     * Enable preload of complementary channel configurations and
     * update on COM event.
     */
    timer_disable_preload_complementry_enable_bits(TIM1);
 
    /* Enable outputs in the break subsystem. */
    timer_enable_break_main_output(TIM1);
 
    /* Generate update event to reload all registers before starting*/
    timer_generate_event(TIM1, TIM_EGR_UG);
 
    timer_set_oc_mode(TIM1, TIM_OC1, TIM_OCM_PWM1);
    timer_enable_oc_output(TIM1, TIM_OC1 );
    timer_disable_oc_output (TIM1, TIM_OC1N);
 
    /* Counter enable. */
    timer_enable_counter(TIM1);
 
    //enable capture compare interrupt
    timer_enable_update_event(TIM1);
 
    timer_enable_irq(TIM1, TIM_DIER_UIE);
    nvic_enable_irq(NVIC_TIM1_UP_TIM16_IRQ);
  }
 
 
 
//ADC  ########################################################################################
 
static void adc_setup(void)
{
	//ADC
	rcc_periph_clock_enable(RCC_ADC12);
	rcc_periph_clock_enable(RCC_GPIOA);
	//ADC
	gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO0);//PA0 config to work with USER botton
	gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO1);
	adc_power_off(ADC1);
	adc_set_clk_prescale(ADC1, ADC_CCR_CKMODE_DIV2);
	adc_set_single_conversion_mode(ADC1);
	adc_disable_external_trigger_regular(ADC1);
	adc_set_right_aligned(ADC1);
	/* We want to read the temperature sensor, so we have to enable it. */
	adc_enable_temperature_sensor();
	adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_61DOT5CYC);
	uint8_t channel_array[] = { 2 }; /* ADC1_IN2 (PA1) */
	adc_set_regular_sequence(ADC1, 1, channel_array);
	adc_set_resolution(ADC1, ADC_CFGR1_RES_12_BIT);
	adc_power_on(ADC1);
 
	/* Wait for ADC starting up. */
	int i;
	for (i = 0; i < 800000; i++)
		__asm__("nop");
}
 
  void tim1_up_tim16_isr(void) { //Timer overflow interrupt, PWM frecuency
    // Clear the update interrupt flag
    timer_clear_flag(TIM1,  TIM_SR_UIF);
    gpio_toggle(GPIOE, GPIO12);	/* LED on/off */
  }
 
 
 
//usart  ########################################################################################
 
 
static void usart_setup(void)
{
	/* Enable clocks for GPIO port A (for GPIO_USART2_TX) and USART2. */
	rcc_periph_clock_enable(RCC_USART2);
	rcc_periph_clock_enable(RCC_GPIOA);
 
	/* Setup GPIO pin GPIO_USART2_TX/GPIO9 on GPIO port A for transmit. */
	gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3);
	gpio_set_af(GPIOA, GPIO_AF7, GPIO2| GPIO3);
 
	/* Setup UART parameters. */
	usart_set_baudrate(USART2, 115200);
	usart_set_databits(USART2, 8);
	usart_set_stopbits(USART2, USART_STOPBITS_1);
	usart_set_mode(USART2, USART_MODE_TX_RX);
	usart_set_parity(USART2, USART_PARITY_NONE);
	usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
 
	/* Finally enable the USART. */
	usart_enable(USART2);
}
 
 
 
 
//print numbers func. with USART
 
static void my_usart_print_int(uint32_t usart, int16_t value)//print sent values
{
	int8_t i;
	int8_t nr_digits = 0;
	char buffer[25];
 
	if (value < 0){
		value = value*(-1);
	}
	if (value == 0) {
		usart_send_blocking(usart, '0');
	}
 
	while (value > 0) {
		buffer[nr_digits++] = "0123456789"[value % 10];
		value /= 10;
	}
 
	for (i = nr_digits-1; i >= 0; i--) {
		usart_send_blocking(usart, buffer[i]);
	}
 
	usart_send_blocking(usart, '\r');
	usart_send_blocking(usart, '\n');	 
}
 
//funcion para esperar tiempos
static void delay(int t){
	for(int i= 0; i<10000*t; i++)
	__asm__("nop");
}
 
//funcion que recibe un valor entre -99% y 99% para el valor de referencia
static valor_de_referencia(){
	uint16_t ret=0;
	int16_t in_data[4]={0};
	uint8_t i=0;
	int new_data=0;
 
	while(i<4){
 
		switch (new_data){
			case 0: //Si la primera tecla no es - o +, no avanza
				in_data[0]=usart_recv_blocking(USART2);
				if(in_data[0]== 0x2B || in_data[0]== 0x2D){
					usart_send_blocking(USART2, in_data[i]);
					i++;
					new_data=1;
				}
				break;
			case 1: //Si la segunda tecla no es un numero no avanza
				in_data[1]=usart_recv_blocking(USART2);		
				if(in_data[1]>= 0x30 && in_data[1]<= 0x39){
					usart_send_blocking(USART2, in_data[i]);
					i++;
					new_data=2;
				}
				break;
			case 2://Si la tercer tecla no es un numero no avanza
 
				in_data[2]=usart_recv_blocking(USART2);		
				if(in_data[2]>= 0x30 && in_data[2]<= 0x39){
					usart_send_blocking(USART2, in_data[i]);
					i++;
					new_data=3;
				}
				break;
			case 3: //Si la cuarta tecla no es un enter no finaliza
				in_data[3]=usart_recv_blocking(USART2);			
				if(in_data[3]==0x0D){
					i++;
				}
				break;
		}
 
	}
	if(in_data[0]==0x2B){ret=100;} //si es +
	ret=ret+(in_data[1]-48)*10+(in_data[2]-48); //si es + queda 1XX, si es - queda 0XX
 
	//regresa el valor convertido a un int, si es mayor a 100 es positivo, y si es menor a 100 es negativo. +50 = 150; -50 = 050
	return ret;
 
 
}
 
 
//main  ########################################################################################
 
  int main(void)
  {
    double duty;
    double kp=0.3;
    double Ti=40;
    double rango=4096;
    double e;
    double I=0.0;
    double Inew=0.0;
    double Iant=0.0;
    double control;
    double vel;
    double P;
    int16_t temp;
    bool sentido;
    int16_t ref;//% del valor deseado,
    int cambio_sentido;
 
    system_init();
    tim_init();
    adc_setup();
    usart_setup();
 
 
    ref=valor_de_referencia();
    if(ref>100){
	sentido=true;//+
	ref=ref-100;//Para que quede un numero entre 0 y 100. El signo se almacena con la variable 'sentido'
    } else {
	sentido=false;//-
	ref=ref*(-1);
    }
    cambio_sentido=ref;
 
    while (1){
 
		//Imprimir ref
	    usart_send_blocking(USART2, '\n');
	    usart_send_blocking(USART2, 'R');
	    usart_send_blocking(USART2, 'e');
	    usart_send_blocking(USART2, 'f');
	    usart_send_blocking(USART2, ':');
	    usart_send_blocking(USART2, ' ');
	    if(sentido==true){
	 	usart_send_blocking(USART2, '+');
	    } else {
		usart_send_blocking(USART2, '-');
	    }
	    my_usart_print_int(USART2, ref); 
 
 
	    //ADC-USART
		//Conversion analogica a digital en el pin PA1
	    adc_start_conversion_regular(ADC1);
	    while (!(adc_eoc(ADC1)));
	    temp=adc_read_regular(ADC1);
 
	    if (gpio_get(GPIOA, GPIO0)) {//Si se presiona el boton de user
		ref=valor_de_referencia(); //Se va a la funcion para introducir un nuevo valor de referencia
    		if(ref>100){
			sentido=true;//+
			ref=ref-100;
   		 } else {
			sentido=false;//-
			ref=ref*(-1);
    		}
 
		if (cambio_sentido*ref<0){//si entre el ultimo valor de referencia y el actual hay un cambio de signo, apaga las senales para que el motor se detenga, luego de un tiempo vuelve a funcionar
	       	 	gpio_clear(GPIOE, GPIO14);
	        	gpio_clear(GPIOE, GPIO11);
			delay(5000);
		}
		cambio_sentido=ref;
	    }
 
		//Imprimer el valor actual de la tension del tacometro pero entre el rango de 0 a 4096 (conversion analogica)
	    usart_send_blocking(USART2, '\n');
	    usart_send_blocking(USART2, 'T');
	    usart_send_blocking(USART2, 'e');
	    usart_send_blocking(USART2, 'm');
	    usart_send_blocking(USART2, 'p');
	    usart_send_blocking(USART2, ':');
	    usart_send_blocking(USART2, ' ');
	    my_usart_print_int(USART2, temp);
 
	    //Control PI
	    //Calculo de error normalizado
	    e=((double)ref/(double)100) - ((double)temp/(double)4096);//se convierte result a double de las divisiones
 
	    usart_send_blocking(USART2, 'e');
	    if (e>0){
		usart_send_blocking(USART2, '+');
	    } else {
		usart_send_blocking(USART2, '-');
	    }
	    my_usart_print_int(USART2, (int)(e*10000));
	    //Control proporcional
	    P = e*kp;
	    usart_send_blocking(USART2, 'P');
	    my_usart_print_int(USART2, (int)(P*10000));
 
        //Anti windup
	    if (control > 1.0){
	        control=1.0;
		I=Iant;
	    }
	    if (control < -1.0){
		control=-1.0;
		I=Iant;
	    }
 
	   if(control>0){//el duty siempre tiene que ser positivo
	   	duty=control;
	   } else {
		duty=control*(-1);
	   }
 
	    // Control Integral
	    usart_send_blocking(USART2, 'I');
	    my_usart_print_int(USART2, (int)(I)*10000);
 
	    //Senal de control PI
	    control = P + I;
	    usart_send_blocking(USART2, 'c');
	    if (control>0){
		usart_send_blocking(USART2, '+');
	    } else {
		usart_send_blocking(USART2, '-');
	    }
	    my_usart_print_int(USART2, (int)(control*10000));
 
 
 
 	   //PWM
	   timer_set_oc_value(TIM1, TIM_OC1, PWM_PERIOD_ARR*duty);//duty_cycle*pwm_period_ARR);
 
 
	   //habilitar out1 e out2 segun el signo de control
	   if((control)>0.001){
	     gpio_clear(GPIOE, GPIO14);
	     gpio_set(GPIOE, GPIO11);//LD7
	   } 
	   if((control)<-0.001){
	     gpio_clear(GPIOE, GPIO11);
	     gpio_set(GPIOE, GPIO14);
	   }
	   if ((control)<0.00001 && (control)>-0.00001){
	     gpio_clear(GPIOE, GPIO11);
	     gpio_clear(GPIOE, GPIO14);
	     gpio_set(GPIOE, GPIO15); //indica control cerca de cero
	   }
 
	   //Actualizar los valores Iant, I, Inew
	   Iant = I;
	   Inew = I + (kp/Ti)*e;
	   I=Inew;
 
	   vel=(temp/rango)*(3.25)*(680.0/5.0); 
	   //velocidad del motor calculado con el factor de conversion 680rpm/5V
 
 
	    //Se imprime el valor de la velocidad
	    usart_send_blocking(USART2, '\n');
	    usart_send_blocking(USART2, 'V');
	    usart_send_blocking(USART2, 'e');
	    usart_send_blocking(USART2, 'l');
	    usart_send_blocking(USART2, ':');
	    usart_send_blocking(USART2, ' ');
	    my_usart_print_int(USART2, vel);
 
 
	    }
  }
  • NOTA: El código fuente utilizado para la implementación del sistema de control, está diseñado para una alimentación de 24V por lo que en caso de querer ejecutarse a 12V y que se realice una lectura correctar por parte del microcontrolador, se debe de ir a la sección en donde se calcula el error normalizado
 //Calculo de error normalizado
	    e=((double)ref/(double)100) - ((double)temp/(double)4096);

y cambiar el valor 4096 por 2048. Para valores distintos de tensión, se debe de escalar este valor y realizar los ajustes necesarios al divisor de tensiones de manera que la entrada analógica sólo reciba valores de entrada entre los 0V y 3,3V.

Tabla de Contenidos

teaching/ie0624/proyectos/control_motores_sincronicos/codigo.txt · Last modified: 2022/09/20 00:08 (external edit)