Tuesday, February 22, 2011

Generating a PWM Signal Based on an Analog Input - miKroC

If you like to experiment with microcontrollers you've probably tried out analog-to-digital conversion and generating PWM signals. Using a popular PIC microcontroller like the 16F877A, which has both ADC and CCP (Capture/Compare/PWM) modules, you can test out both of these easily because mikroC provides easy to use library functions for both of them. (Most of you will be familiar with these libraries, so I won't be going in to much detail about them here. Leave a comment if you like to learn more.. :) )

Now, think of a scenario like this: You want to change the duty cycle of your PWM signal based on a analog signal. It can be from a variable resistor that you want to use to increase or decrease the duty cycle, or it can be a feedback from a sensor which is analog.

So, how can we do it?... we simply use the ADC to convert the analog signal and use that value as the duty cycle value. All we need is a microcontroller that has both a CCP and a PWM module. As I said before, the popular 16F877A fits the criteria.

So, now that we have our microcontroller, let's get down to the code.

Here's the sample code
 unsigned int temp_res;  
 unsigned char temp_duty;  
 void main() {  
  ADCON1 = 0x80; // Configure analog inputs and Vref  
  TRISA = 0xFF; // Set PORTA as an input  
  TRISC = 0; // Set PORTC as an Output  
  PORTC = 0;  
  // Initialize and start the PWM unit  
   temp_res = Adc_Read(2); // Get results of AD conversion  
                 from channel 2 (RA2 pin)  
   temp_duty = (temp_res/4); // Convert the 10 bit value  
                 to 8 bits  
   PWM_Change_Duty(temp_duty); // Set the duty value  
   Delay_ms(50); // Slow everything down a little  

Let's see what's happening in the code,

  • First, we declare 2 variables - temp_res to store the result of AD conversion and temp_duty to store the 8 bit value for the duty cycle.
  • We use the ADC_Read(2) function to read the analogue input from AN2 pin, convert it to digital and store the 10 bit value in temp_res. 
  • Since the value for the duty cycle should be 8 bits (0 to 255) we simply divide the temp_res by 4 and store it in temp_duty.
  • Finally, we use the PWM_Change_Duty() function and set the duty cycle to the value of temp_duty.
There's nothing new in this code that you don't already know :)

Here's the basic circuit for testing this code
Use the R2 variable resistor to get the varying analog signal. The brightness of the LED will change based on the PWM.

You can use this same method to generate a PWM based on any analog signal. Just think of the possibilities...



  1. What is different about the pic16f887 that this code doesnt work with it?

  2. i tried your code, its working but as the analog voltage is changed the pulse width changes but the frequency shown in the DSO remain same, whats the problem?????

  3. please post the link of the header file.