martes, 28 de marzo de 2017

PWM -ARM Cortex-M4-

Programa

/* Febrero de 2016
Tarjeta: STM32 Discovery
Microcontrolador: STM32F411VET6U
IDE: IAR Embedded Workbench 

Programa que genera una señal PWM variable en PD12, PD13, PD14 y PD15 (pines
conectados a los cuatro LED de la tarjeta Discovery). Esta señal hace que los
LED se iluminen progresivamente hasta llegar a su valor máximo, entonces se
apagan y se vuelve a repetir el proceso de manera indefinida.
*/

#include "stm32f4xx.h"



//---  Función para generar un retardo ---
void retardo (void){
  int j = 900; 
  while (j > 0){
       for(int i=0; i<1000; i++);
       j = j -1;
     }
}

//---  Función principal del programa ---

void main (void)  {  
   int valor_CCRx = 0;
   int valor_ARR  = 0x0100;
   int prescaler = 0x0C80;
          
   RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // Habilita el reloj para el puerto D
   RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;     // Habilita el reloj para TIM4
   
   GPIOD->MODER   |= 0xAA000000;   // Configura PD12, PD13, PD14 y PD15 como AF  
   GPIOD->OSPEEDR |= 0xFF000000;   // y trabajarán en modo de alta velocidad
   GPIOD->AFR[1]  |= 0x22220000;        // Selecciona AF2 (TIM3...5) para esos pines
  
   TIM4->PSC    = prescaler;         // Ajuste del 'prescaler'
   TIM4->ARR    = valor_ARR;    // Carga el valor de 'auto-reload'
     
   TIM4->CCMR1 |= 0x6060;     // PWM modo 1,  canales CC1 y CC2 son salidas
   TIM4->CCMR2 |= 0x6060;     // PWM modo 1,  canales CC3 y CC4 son salidas
   TIM4->CCER  |= 0x1111;       // Activa la salida para OC1, OC2, OC3 y OC4
     
   TIM4->CR1   |= 0x0081;    // Habilita el contador de TIM4, habilita
                                                // 'auto-reload preload'. También: cuenta asc.
                                                // 'edge-aligned mode'                           
   // Bucle infinito
   while (1) {   
     // Carga en CCRx el valor a comparar con ARR. Se hace para cada canal.
     TIM4->CCR1 = valor_CCRx;     // PD12: canal CC1, LED verde
     TIM4->CCR2 = valor_CCRx;     // PD13: canal CC2, LED naranja
     TIM4->CCR3 = valor_CCRx;     // PD14: canal CC3, LED rojo
     TIM4->CCR4 = valor_CCRx;     // PD15: canal CC4, LED azul
     retardo();
     valor_CCRx++;
     if (valor_CCRx > valor_ARR) valor_CCRx = 0;     
  }
}



➤ Figuras




➤ Observaciones

- No es necesario ningún componente electrónico externo a la tarjeta Discovery.

- Para no complicar el programa, el retardo utilizado se realiza con un bucle for. Las cuestiones relativas a la señal de reloj del microcontrolador se explican en esta entrada del blog y la forma de generar un retardo, de una manera más adecuada, se detalla en esta otra.

- En el programa se accede a los siguientes registros: RCC_AHB1ENR, RCC_APB1ENR, GPIOD_MODER,  GPIOD_OSPEEDR, GPIOD_AFR, TIM4_PSC, TIM4_ARR, TIM4_CCMRx, TIM4_CCER, TIM4_CR1 y TIM4_CCRx.

- Este programa modifica la luminosidad de los cuatro LED de la tarjeta Discovery. Esto se consigue generando una señal PWM para cada LED, aunque, en este ejemplo, todas ellas poseen las mismas características. Para generar estas señales PWM se utilizan los canales TIM4_CH1 (CC1), TIM4_CH2 (CC2), TIM4_CH3 (CC3) y TIM4_CH4 (CC4), los cuales están asociados a PD12 (LED verde), PD13 (LED naranja), PD14 (LED rojo) y PD15 (LED azul) respectivamente.

- Para crear una señal PWM se debe utilizar uno de los temporizadores del microcontrolador, en este ejemplo se usa TIM4. Un contador cuenta ascendentemente hasta llegar al valor contenido en el registro TIMx_ARR, entonces empieza otra vez su cuenta desde 0; esto lo hace de manera cíclica. Mientras el valor de la cuenta sea menor que el valor almacenado en TIMx_CCRx, la salida OCxREF (ver figura de arriba) permanece en estado alto. Cuando es mayor pasa a estado bajo. Es decir, la generación de una señal PWM se consigue con la comparación entre los valores numéricos guardados en TIMx_ARR y TIMx_CCRx, ambos elegibles por el usuario en su programa. El valor de TIMx_ARR determina el período del PWM y el valor de TIMx_CCRx determina su ciclo de trabajo.

- El contador necesita una señal de reloj para poder funcionar, que puede ser interna (CK_INT) o externa. En este ejemplo se usa el reloj interno (ver figura de arriba). La frecuencia de la señal de reloj del contador puede ser modificada mediante un prescaler, el cual puede dividir esta frecuencia por un factor comprendido entre 1 y 65536, utilizando el registro TIMx_PSC. Por defecto, el valor de este registro es 0; en ese caso, dividiría la frecuencia de CK_INT por 1. 

- RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN: habilita el reloj para el puerto D. A diferencia de los ejemplos anteriores de este blog, aquí se usa la constante RCC_AHB1ENR_GPIODEN, la cual está definida en stm32f4xx.h.

- RCC->APB1ENR |= RCC_APB1ENR_TIM4EN: habilita el reloj para el temporizador TIM4 empleando otra constante definida en stm32f4xx.h. Hay que poner a ‘1’ el bit 2 del registro RCC_APB1ENR.


- GPIOD->MODER |= 0xAA000000: configura los pines PD12, PD13, PD14 y PD15 en modo de función alterna (alternate function mode), escribiendo ‘10’ en las posiciones MODER12
MODER13MODER14MODER15 del registro GPIOD_MODER. Es necesario este modo de trabajo para poder generar las señales PWM.

- GPIOD->OSPEEDR |= 0xFF000000: configura PD12, PD13, PD14 y PD15 para que trabajen en modo de alta velocidad. Ver esta entrada del blog.

- GPIOD->AFR[1] |= 0x22220000: escribe ‘0010’ en las posiciones AFRH12
AFRH13AFRH14AFRH15 del registro GPIOD_AFRH para asociar los pines PD12, PD13, PD14 y PD15 con la función alterna 2 (AF2). Escoger AF2 permite usar los temporizadores TIM3, TIM4 y TIM5 (el programa utiliza TIM4).


- TIM4->PSC  = prescaler: carga un valor numérico en el registro PSC con el fin de reducir la frecuencia de la señal CK_INT .

- TIM4->ARR = valor_ARR: escribe un valor numérico en el registro de auto-recarga (auto-reload register).


- TIM4->CCMR1 |= 0x6060: elige el modo 1 de PWM para el canal CC1, escribiendo ‘110’ en los bits 6, 5 y 4 del registro CCMR1. También, si los bits 1 y 0 valen ‘00’, el canal CC1 queda configurado como salida (modo de comparación). Con el bit 3 se consigue que el registro de precarga (preload register) en TIM4_CCR1 esté deshabilitado, con esto TIM4_CCR1 puede ser escrito en cualquier momento y el nuevo valor es tomado en cuenta inmediatamente; si estuviera habilitado, el valor de precarga de TIM4_CCR1 sería cargado en el registro activo en cada evento de actualización (update event). También esta instrucción realiza, para el canal CC2, todo lo descrito en este párrafo para el canal CC1, pero utilizando, en lugar de los 8 primeros bits del registro, los 8 siguientes (8-15).

- TIM4->CCMR2 |= 0x6060: el registro TIM4_CCMR2 es análogo a TIM4_CCMR1, con lo cual las explicaciones del párrafo anterior para el canal CC1 son válidas para CC3 y las de CC2 lo son para CC4.

- TIM4->CCER  |= 0x1111: si el canal CC1 está habilitado como salida, como es el caso, poniendo a '1' el bit 0 del registro, la señal OC1 (ver figura de arriba) queda habilitada en el pin correspondiente, que es PD12. Con un '1' en los bits 4, 8 y 12 se consigue lo mismo para CC2 (OC2, PD13), CC2 (OC2, PD13) y CC3 (OC2, PD13) respectivamente.



- TIM4->CR1 |= 0x0081: con esta orden se habilita el contador de TIM4 poniendo el bit 0 a ‘1’. También, con un '1' en el bit 7 (ARPE), se habilita la auto-recarga del contenido del registro TIM4_ARR (auto-reload preload enable). Con el bit 4 (DIR) se escoge el tipo de cuenta del contador de TIM4, ascendente o descendente; por defecto, con un '0', la cuenta es ascendente. Escribiendo ‘00’ en los bits 6 y 5 (CMS), el contador de TIM4 funcionará en edge-aligned mode; con este modo de trabajo, su cuenta será ascendente o descendente según el valor del bit de dirección (DIR). Por defecto, ésta es la configuración de CMS.
  
- TIM4->CCR1 = valor_CCRx: carga un valor numérico en el registro de captura/comparación. Comparando este valor con el guardado en TIM4_ARR, se obtiene el ciclo de trabajo de la señal PWM del canal CC1. Se hace lo mismo para los canales CC2, CC3 y CC4 con TIM4_CCR2, TIM4_CCR3 y TIM4_CCR4 respectivamente.

  

➤ Registros empleados

Las siguientes imágenes representan los registros utilizados en el programa y en ellas están señalados los bits que éste modifica o consulta. Las marcas rojas se refieren a las salidas digitales del programa y las moradas, a las señales PWM

Para más información, ver el documento RM0383 Reference Manual.























La siguiente figura es una representación del registro TIM4_CCR2, pero, excepto la linea de Adress offset, todo lo que contiene la imagen sirve también para TIM4_CCR2TIM4_CCR3 y TIM4_CCR4.



No hay comentarios: