Control led RGB usando TIM17

Una de las primeras versiones que hice de LibreServo, sólo en esquemas y mirando posibles configuraciones con CubeMX, el led RGB se hubiera controlado mediante el timer 2, TIM2, directamente con sus salidas PWM. Es la forma más sencilla y directa de hacerlo. Pero más adelante y por petición de varias personas, decidí añadir cierta compatibilidad con un posible futuro encóder magnético. Eso me forzaba a reservar TIM2 para una posible lectura del encóder y por tanto dejaba al led RGB sin la salida directa de TIM2. Finalmente lo tuve que instalar en salidas digitales "normales" y dejar recaer en un futuro software el control del led RGB con un sólo timer, en éste caso, el timer 17.

La idea de este programa es además de configurar y usar el timer 17, controlar el usa el led RGB Asmb-mtb0-0A3a2 mediante interrupciones del timer 17. Es un sencillo programa que nos obligará a utilizar y configurar más características de nuestro LibreServo de cara a futuras características.

  • Centrándonos en el programa, el programa tiene varias partes. Una parte es la función que se llama para dar valor al led RGB al cual se le pasan tres valores, uno por cada color, que pueden tomar valores de 0, apagado, a 255 brillo máximo:

/* USER CODE BEGIN 4 */
[...]

static void F_LED_RGB(uint8_t Z_RED, uint8_t Z_GREEN, uint8_t Z_BLUE)
{
	led_rgb_temp = (struct s_led_rgb){		//estructura que guardará el brillo del led RGB, se ha declarado anteriormente como variable global y VOLATILE
		{LED_RED,LED_GREEN,LED_BLUE} ,
		{Z_RED,Z_GREEN,Z_BLUE} };
	uint8_t z_brillo;
	uint32_t z_pin;

	for(int a=0;a<=1;a++)
	{
		for(int b=a+1;b<=2;b++)
		{
			if(led_rgb_temp.brillo[a]>led_rgb_temp.brillo[b])		//Order from less to more brightness
			{
				z_brillo=led_rgb_temp.brillo[a];
				z_pin=led_rgb_temp.pin[a];
				led_rgb_temp.brillo[a]=led_rgb_temp.brillo[b];
				led_rgb_temp.pin[a]=led_rgb_temp.pin[b];
				led_rgb_temp.brillo[b]=z_brillo;
				led_rgb_temp.pin[b]=z_pin;
			}
		}
	}


	led_rgb_temp.brillo[1]=led_rgb_temp.brillo[1]-led_rgb_temp.brillo[0];	//Se restarán los valores para así obtener sólo la diferencia entre ellos
	led_rgb_temp.brillo[2]=led_rgb_temp.brillo[2]-led_rgb_temp.brillo[0];	//Resto a los led de brillo más intenso, el valor del menor
	led_rgb_temp.brillo[2]=led_rgb_temp.brillo[2]-led_rgb_temp.brillo[1];	//Resto al led más brillante, el resto quedado del segundo más brillante

	new_led_rgb=TRUE;			//Indico que hay un nuevo valor para el led RGB
}
/* USER CODE END 4 */
  • Por otro lado, hay que configurar TIM17 para que funcione a la frecuencia requerida y para que genere interrupciones. El código que configura de manera básica TIM17 está generado directamente por CubeMX según los parámetros pasados que hemos escogido para que funcione a 120Hz:

/* TIM17 init function */
static void MX_TIM17_Init(void)
{

  LL_TIM_InitTypeDef TIM_InitStruct;

  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM17);

  TIM_InitStruct.Prescaler = 586;                                            //El valor de prescaler será 586
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 255;                                       //Al llegar a 255 desbordará y empezará a contar de nuevo desde cero.
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4;       //El reloj del contador estará dividido entre cuatro
  TIM_InitStruct.RepetitionCounter = 0;
  LL_TIM_Init(TIM17, &TIM_InitStruct);                             //La velocidad final de TIM17 es: 72000000/(586*255*4)= 120Hz

  LL_TIM_DisableARRPreload(TIM17);

}
  • Tenemos que activar el TIM17 y que va a generar interrupciones cuando el contador desborde:

/* USER CODE BEGIN 2 */
LL_TIM_EnableCounter(TIM17);           //Timer17 starts running (TIM17->CR1.CEN)
LL_TIM_EnableIT_UPDATE(TIM17);       //Enable the Update interrupt in TIM17 (TIM17->DIER.UIE ---> TIM17->SR.UIF)

/* USER CODE END 2 */
  • El código que gestiona la interrupción de TIM17 está en el fichero stm32f3xx_it.c. El truco está en que los valores de intensidad del led se han ordenado y luego se han restado, con lo que hacemos que TIM17 sólo cuente el brillo de cada led que irá apagando en orden contando al final el tiempo restante hasta llegar a 255... Por ejemplo, si los valores introducidos para el led RGB son 100, 125 y 130 respectivamente. En la estructura quedarán guardados como, 100, 25, 5. El contador 17 encenderá los tres colores y contará primero 100, apagará el led rojo. Contará 25 y apagará el led verde. Contará 5 y apagará el led azul. Una vez hecho esto, contará 125 con los tres leds apagados (el resto de 255-130) y volverá a empezar de nuevo.

/**
* @brief This function handles TIM1 trigger, commutation and TIM17 interrupts.
*/
void TIM1_TRG_COM_TIM17_IRQHandler(void)
{
  /* USER CODE BEGIN TIM1_TRG_COM_TIM17_IRQn 0 */
	if(LL_TIM_IsActiveFlag_UPDATE(TIM17)==TRUE)
	{
		LL_TIM_ClearFlag_UPDATE(TIM17);

		if(estado_led==2||brillado_led_rgb>=255)
		{
			GPIOA->BSRR=LED_GREEN;			//Set_pin
			GPIOA->BSRR=LED_RED;			//Set_pin
			GPIOA->BSRR=LED_BLUE;			//Set_pin
			if(brillado_led_rgb>=255)
			{
				estado_led=0;
				brillado_led_rgb=0;

				if(new_led_rgb==TRUE)			//Copy the new LED configuration
				{
					new_led_rgb=FALSE;
					for(int i_a=0;i_a<3;i_a++)
					{
						led_rgb.pin[i_a]=led_rgb_temp.pin[i_a];
						led_rgb.brillo[i_a]=led_rgb_temp.brillo[i_a];
					}
				}
			}
			else
			{
				TIM17->CNT=0+brillado_led_rgb;
				brillado_led_rgb=255;
			}
		}
		else
		{
			GPIOA->BSRR=led_rgb.pin[estado_led];			//Set_pin
			estado_led++;
		}

		if(brillado_led_rgb<255)
		{
			if(estado_led==0)
			{
				if(led_rgb.brillo[0]>0)
				{
					GPIOA->BRR=LED_GREEN;				//Clear_pin
					GPIOA->BRR=LED_RED;				//Clear_pin
					GPIOA->BRR=LED_BLUE;				//Clear_pin
				}
				else if(led_rgb.brillo[1]>0)
				{
					estado_led=1;
					GPIOA->BRR=led_rgb.pin[1];			//Clear_pin
					GPIOA->BRR=led_rgb.pin[2];			//Clear_pin
				}
				else if(led_rgb.brillo[2]>0)
				{
					estado_led=2;
					GPIOA->BRR=led_rgb.pin[2];			//Clear_pin
				}
				else
				{
					estado_led=2;
				}
			}

			brillado_led_rgb=brillado_led_rgb+led_rgb.brillo[estado_led];
			if(estado_led==1)
			{
				if(led_rgb.brillo[estado_led]==0)
				{
					GPIOA->BSRR=led_rgb.pin[estado_led];
					estado_led=2;
				}
			}

			if(led_rgb.brillo[estado_led]>0)
			{
				TIM17->CNT=255-led_rgb.brillo[estado_led];
			}
			else
			{
				estado_led=2;
				TIM17->CNT=0+brillado_led_rgb;
				brillado_led_rgb=255;
				GPIOA->BSRR=led_rgb.pin[estado_led];
			}
		}
	}

  /* USER CODE END TIM1_TRG_COM_TIM17_IRQn 0 */
  
  /* USER CODE BEGIN TIM1_TRG_COM_TIM17_IRQn 1 */

  /* USER CODE END TIM1_TRG_COM_TIM17_IRQn 1 */
}

Control led RGB mediante interrupciones con el TIM17

Suscripción

Recibir un email cuando se publique un nuevo artículo.