in StemSocial2 months ago

After the article PWM Theoretical basis EN/ES it is appropriate to complement the information with a practical application for which we will rely on the PIC16F877A microcontroller.

In this article we will learn how to design and implement a PWM control using a microcontroller by creating the program in C language with the PIC C COMPILER software. For a better understanding of this article it is necessary to refresh the knowledge acquired in the topics PWM Theoretical basis EN/ES, TMR2: Closing the timers cycle EN/ES, Using ADC in PIC16F877a EN/ES and Driving an LCD with PIC16F877A EN/ES as codes explained in detail in these articles will be used.

Luego del artículo PWM Theoretical basis EN/ES es apropiado complementar la información con una aplicación práctica para lo cual nos apoyaremos en el microcontrolador PIC16F877A.

En este artículo aprenderemos a diseñar e implementar un control PWM usando un microcontrolador creando el programa en lenguaje C con el software PIC C COMPILER. Para una mejor comprensión de este artículo es necesario refrescar los conocimientos adquiridos en los temas PWM Theoretical basis EN/ES, TMR2: Closing the timers cycle EN/ES, Using ADC in PIC16F877a EN/ES y Driving an LCD with PIC16F877A EN/ES ya que se usarán códigos explicados a detalle en estos artículos.

Circuit designed for PWM simulation by @electronico

PWM on PIC16F877A

The PIC16F877A microcontroller has two PWM modules called CCP1 on PIN17 and CCP2 on PIN16, these pins as we know belong to the PORTC and can be used as input or output data, the CCP function is another way to use them and is not limited to PWM as they can also be used as "capture" and "compare" module, in fact these three functions is what CCP represents C(Capture) C(Compare) P(PWM).

When you decide to use the PIN as a CCP Module you must also select which of these three functions it will be performing.

El microcontrolador PIC16F877A dispone de dos módulos PWM llamados CCP1 en el PIN17 y CCP2 en el PIN16, estos pines como bien sabemos pertenecen al PORTC y pueden ser usados como entrada o salida de datos, la función CCP es otra forma de usarlos y no está limitada a PWM ya que también se pueden usar como módulo "captura" y "compara", de hecho estas tres funciones es lo que CCP representa C(Captura) C(Compara) P(PWM).

Cuando se decide usar el PIN como Módulo CCP también se debe seleccionar cual de estas tres funciones estará ejecutando.

CCP Module Configuration

When using the CCP modules we must select the mode in which it will work; capture, compare or PWM and depending on the case additional configurations may be required. The instruction we will use for the configuration will be _setup_ccpx(conf)_ where ccpx implies that x must be replaced by the module number to be configured (1 or 2) and in conf we write the configuration parameters according to how we want to use the module.

CCP_CAPTURE_FECapture by trailing edge
CCP_CAPTURE_RECapture on rising edge
CCP_CAPTURE_DIV4Capture after four pulses
CCP_CAPTURE_DIV4Capture after four pulses
CCP_CAPTURE_DIV16Capture after 16 pulses
CCP_COMPARE_SET_ON_MACHOutput to 1 in comparison
CCP_COMPARE_CLR_ON_MACHOutput to 0 in comparison
CCP_COMPARE_INTInterrupt in compare mode
CCP_PWMActivates PWM mode

As we want to work with PWM our configuration instruction would be setup_ccpx(CCP_PWM).

Al usar el los módulos CCP debemos seleccionar el modo en que va a trabajar; captura, compara o PWM y según sea el caso es posible que se requieran configuraciones adicionales. La instrucción que usaremos para la configuración será setup_ccpx(conf) donde ccpx implica que x debe ser reemplazado por el número de módulo que se desea configurar (1 o 2) y en conf escribimos los parámetros de configuración según como deseamos usar el módulo.

CCP_CAPTURE_FECaptura por flanco de bajada
CCP_CAPTURE_RECaptura por flanco de subida
CCP_CAPTURE_DIV4Captura tras cuatro pulsos
CCP_CAPTURE_DIV16Captura tras 16 pulsos
CCP_COMPARE_SET_ON_MACHSalida a 1 en comparación
CCP_COMPARE_CLR_ON_MACHSalida a 0 en comparación
CCP_COMPARE_INTInterrupción en modo compare
CCP_PWMActiva el modo PWM

Como deseamos trabajar con PWM nuestra instrucción de configuración sería setup_ccpx(CCP_PWM)

Operation mode

Recall from our theoretical article that to generate a PWM signal is necessary a square wave which must have a defined frequency that is not affected by the variation of the useful cycle, the CCP module what it does is to modulate the signal in pulse amplitude.

That is why the CCP module works in conjunction with the TMR2 timer, if we remember in our article on TMR2 we learned how to generate frequencies and the factors that define its value in TMR2. This is important because it is not enough to modulate in PWM because the frequency to use will depend on the application so you must take into account both the modulation and the frequency of the wave and for this TMR2 will always be used as a complement to CCP.

The CCP module is an 8-bit register which means that it can handle values between 0 and 255 that we can interpret between 0 and 100% thus defining the percentage of the useful cycle in PWM, ie, when the value is 255 will be delivering a useful cycle of 100% and when it is 0 will be 0%, obviously this is equivalent for intermediate values. If we want to vary the pulse width we can do it by varying the PWM value and this can be done by the instruction set_pwmx_duty(value from 0 to 255) we must replace the x by the number of the module used.

Recordemos de los visto en nuestro artículo teórico que para generar una señal PWM es necesario una onda cuadrada la cual debe tener una frecuencia definida que no se ve afectada por la variación del ciclo útil, el módulo CCP lo que hace es modular la señal en amplitud de pulso.

Es por ello que el módulo CCP trabaja en conjunto con el temporizador TMR2, si recordamos en nuestro artículo sobre TMR2 aprendimos a generar frecuencias y los factores que definen su valor en TMR2. Esto es importante ya que no basta con modular en PWM pues la frecuencia a usar dependerá de la aplicación así que se debe tener en cuenta tanto la modulación como la frecuencia de la onda y para esto siempre se usará TMR2 como complemento a CCP.

El modulo CCP es un registro de 8bits lo que implica que puede manejar valores entre 0 y 255 que podemos interpretar entre 0 y 100% definiendo así el porcentaje del ciclo útil en PWM, es decir, cuando el valor sea 255 se estará entregando un ciclo útil del 100% y cuando sea 0 será de 0%, obviamente esto se hace equivalente para valores intermedios. Si deseamos variar el ancho de pulso lo podemos hacer variando el valor de PWM y esto se puede hacer mediante la instrucción set_pwmx_duty(valor de 0 a 255) debemos reemplazar la x por el número del módulo utilizado.

Application example

For our application example we will generate a PWM signal using the CCP1 module, we will connect it to a led to see the effects caused by the pulse variation (since it is a simulator the effect is not appreciated in real time which gives us an advantage of appreciation), we will also connect the signal to a frequency meter to verify that the frequency is not affected by the pulse variation (except when we place the useful cycle at the value 0) and an oscilloscope to observe how the wave changes shape.

To produce the variation in pulse width we will use an ADC, we will make the potentiometer value produce an equivalent variation in percentage in PWM and we will use a new library (<map_function.c>) to make the equivalent conversion by means of a rule of 3.

We will be showing at all times the results by a LCD in which we will be observing the PWM value in bits and the percentage of the useful cycle in each value.

Para nuestro ejemplo de aplicación vamos a generar una señal PWM usando el módulo CCP1, la conectaremos a un led para ver los efectos que causa la variación del pulso (dado que es un simulador el efecto no se aprecia en tiempo real lo que nos da una ventaja de apreciación), también conectaremos la señal a un medidor de frecuencias para comprobar que la frecuencia no se ve afectada por la variación del pulso (salvo cuando situamos el ciclo útil en el valor 0) y un osciloscopio para observar como la onda cambia de forma.

Para producir la variación en el ancho de pulso usaremos un ADC, haremos que el valor del potenciometro produzca una variación equivalente en porcentaje en PWM y nos apoyaremos con una nueva librería (<map_function.c>) para hacer la conversión equivalente mediante una regla de 3.

Estaremos mostrando en todo tiempo los resultados por una LCD en la cual estaremos observando el valor de PWM en bits y el porcentaje del ciclo útil en cada valor.


We start our code by configuring the usual parameters taking into account that we will use ADC and LCD20x4 so we also define their configuration and add the library that will allow us to use the rule of 3 for the equivalence between the PWM register value and the percentage of the duty cycle. We also create variables to store adc and pwm values.

Iniciamos nuestro código configurando los parámetros habituales tomando en cuenta que usaremos ADC y LCD20x4 por lo que definimos también la configuración de los mismos y añadimos la librería que nos facilitará usar regla de 3 para la equivalencia entre el valor del registro PWM y el porcentaje del ciclo útil. Además creamos las variables para almacenar valores del adc y pwm.

#include <16f877a.h>

#device ADC = 10


#use delay(clock=20M)

#use standard_io(D)

#define LCD_DB4  PIN_D4

#define LCD_DB5  PIN_D5

#define LCD_DB6  PIN_D6

#define LCD_DB7  PIN_D7

#define LCD_RS   PIN_D2

#define LCD_E    PIN_D3

#include <LCD_20X4.c>

#include <map_function.c

long adc_value;

int pwm_value;

int percent;

In the first lines of the main program we initialize the LCD, we define the TMR2 parameters to generate the frequency we want (the values are obtained by doing the calculations described in the article dedicated to TMR2 but in this case we do not activate the TMR2 interrupt.

We also configure the CCP1 module to work in PWM mode, set its value to 0 (because we are going to vary it with the ADC) and configure the ADC.

En las primeras líneas del programa principal inicializamos la LCD, definimos los parámetros de TMR2 para generar la frecuencia que deseamos (los valores los obtenemos haciendo los cálculos descritos en el artículo dedicado a TMR2) pero en este caso no activamos la interrupción por TMR2.

Configuramos además el modulo CCP1 para trabajar en modo PWM, fijamos su valor en 0 (porque lo vamos a variar con el ADC) y configuramos el ADC.

void main()



   setup_timer_2(t2_div_by_4, 255, 1);





The next thing is to read the ADC value and calculate the equivalent value to be loaded in the PWM, we must remember that the ADC varies from 0 to 1023, the PWM varies from 0 to 255 and the duty cycle varies from 0 to 100%.

By means of the map() function we can apply the rule of 3 that returns the equivalent value, for it inside the parenthesis we introduce the values separated by a comma:

The first value is the one that has the first variable in real time (only the name of the variable is written), the second data is the minimum value of the first variable, the third data is the maximum value of the first variable, the fourth data is the minimum value of the second variable, the fifth data is the maximum value of the second variable and the result is the value of the second variable in real time.

Once the values are obtained, they are displayed on the LCD.

Lo siguiente es leer el valor del ADC y calcular el valor equivalente que se cargará en el PWM, debemos recordar que el ADC varía de 0 a 1023, el PWM varía de 0 a 255 y el ciclo de trabajo varía de 0 a 100%.

Mediante la función map() podemos aplicar la regla de 3 que devuelve el valor equivalente, para ello dentro del parentesis introducimos los valores separados por una coma:

El primer valor es el que posee la primera variable en tiempo real (solo se escribe el nombre de la variable), el segundo dato es el valor mínimo de la primera variable, el tercer dato es el valor máximo de la primera variable, el cuarto dato es el valor mínimo de la segunda variable, el quinto dato e el valor máximo de la segunda variable y el resultado es el valor de la segunda variable en tiempo real.

Una vez obtenidos los valores los mostramos en la LCD.




      adc_value = read_adc();

      pwm_value = map(adc_value, 0, 1023, 0, 255);

      percent = map(pwm_value, 0, 255, 0, 100);


      printf(lcd_putc,"PWM: %u ",pwm_value);


      printf(lcd_putc,"Percent: %u%% ", percent);


      printf(lcd_putc,"@electronico - HIVE");


      printf(lcd_putc,"Original Content");





In our simulation we will turn on the circuit and we will vary the potentiometer from its minimum to its maximum value, we will check with an oscilloscope how the square wave is modified and we will use a frequency meter to see how the frequency is stable even though we modify the pulse width. In addition an LCD will show the PWM value and the % duty cycle.

En nuestra simulación encenderemos el circuito y vamos a variar el potenciometro desde su valor mínimo a su máximo, comprobaremos mediante un osciloscopio la forma en la que es modificada la onda cuadrada y usaremos un medidor de frecuencias para ver como la frecuencia es estable a pesar de que modificamos el ancho del pulso. Además una LCD mostrará el valor del PWM y el % del ciclo de trabajo.

Safety note: It is necessary to remember that the currents and voltages supplied by the microcontroller correspond to control signals, the microcontroller should never be used as a power source because it does not have enough power to drive a motor, instead use a device that responds to the control signal supplied by the microcontroller and delivers adequate power to the load.

Nota de seguridad: Es necesario recordar que las corrientes y tensiones suministradas por el microcontrolador corresponden a señales de control, nunca debe ser usado el micorcontrolador como fuente de potencia ya que no posee la potencia suficiente para manejar un motor, en su lugar se usa un dispositivo que responda a la señal de control suministrada por el microcontrolador y entregue la potencia adecuada a la carga.


Yay! 🤗
Your content has been boosted with Ecency Points, by @electronico.
Use Ecency daily to boost your growth on platform!

Support Ecency
Vote for new Proposal
Delegate HP and earn more

Freezing Warm Up GIF by MOODMAN

Thanks for your contribution to the STEMsocial community. Feel free to join us on discord to get to know the rest of us!

Please consider delegating to the @stemsocial account (85% of the curation rewards are returned).

You may also include @stemsocial as a beneficiary of the rewards of this post to get a stronger support. 

Happy Monday GIF by GIPHY Studios 2022