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...