CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Stability issues with ADC?
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
sonicfire



Joined: 11 Feb 2007
Posts: 19
Location: Cedar Rapids

View user's profile Send private message

Stability issues with ADC?
PostPosted: Thu Feb 15, 2007 2:10 pm     Reply with quote

I've been attempting to write a program for the 16f877 that reads a variable dc voltage and adjusts the duty cycle on the output (via the PWM). I started out using a modified example from PIC-C with limited sucess. The output on the scope had a continuously changing duty cycle for a constant input voltage. I'm fairly certain that there is a problem with the analog to digital conversion, but am uncertain of what is causing this issue. Any advice would be appreciated.

Here's the code:

Code:
#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=10000000)

void main() {
   char selection;
   byte value;
   
   selection = '2';

   setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM

          //   The cycle time will be (1/clock)*4*t2div*(period+1)
          //   In this program clock=10000000 and period=127 (below)
          //   For the three possible selections the cycle time is:
          //     (1/10000000)*4*1*128 =  51.2 us or 19.5 khz
          //     (1/10000000)*4*4*128 = 204.8 us or 4.9 khz
          //     (1/10000000)*4*16*128= 819.2 us or 1.2 khz

   switch(selection) {
     case '1' : setup_timer_2(T2_DIV_BY_1, 127, 1);
                break;
     case '2' : setup_timer_2(T2_DIV_BY_4, 127, 1);
                break;
     case '3' : setup_timer_2(T2_DIV_BY_16, 127, 1);
                break;
   }


  setup_port_a(ALL_ANALOG);
  setup_adc(adc_clock_internal);
  set_adc_channel( 0 );

  while( TRUE ) {
    value=read_adc();

    set_pwm1_duty(value);          // This sets the time the pulse is
                                   // high each cycle.  We use the A/D
                                   // input to make a easy demo.
                                   // the high time will be:
                                   //  if value is LONG INT:
                                   //    value*(1/clock)*t2div
                                   //  if value is INT:
                                   //    value*4*(1/clock)*t2div
                                   // for example a value of 30 and t2div
                                   // of 1 the high time is 12us
                                   // WARNING:  A value too high or low will
                                   //           prevent the output from
                                   //           changing.
 
   }

}
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

Re: Stability issues with ADC?
PostPosted: Thu Feb 15, 2007 2:29 pm     Reply with quote

Change the value you read to a 16 bit number. Both the ADC and PWM support 10 bits of resolution.

Code:

   INT16 value;
Ttelmah
Guest







PostPosted: Thu Feb 15, 2007 4:16 pm     Reply with quote

Look at the recommended ADC clock sources, and as a second comment, think 'time'. At 10MHz, the internal RC clock, is _not_ recommended for the ADC (not above 1MHz).
There is a minimum time specified between ADC reads, of 2Tad. You are only updating the PWM registers, which does not wait for the PWM.
Consider synchronising the loop to the PWM frequency.

Best Wishes
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Feb 15, 2007 4:35 pm     Reply with quote

This code is basically the same as the CCS example.

I took your code verbatim and dropped it into MPLAB and compiled it
with PCM vs. 3.249. I programmed it into a 16F877-20, running with
a 10 MHz crystal on a PicDem2-Plus board. I looked at pin C2 with
an oscilloscope while turning the 5K trimpot (R1).

The waveform looks stable. It smoothly changes duty cycle as
I turn the trimpot. I don't see any problems.

If you have a problem, it could be your hardware or possibly your
compiler version.
sonicfire



Joined: 11 Feb 2007
Posts: 19
Location: Cedar Rapids

View user's profile Send private message

PostPosted: Thu Feb 15, 2007 4:45 pm     Reply with quote

I may be seeing some noise on lower input voltages. I will try a low pass filter first, and then try your suggestions. They are all very helpful; thank you.
vsmguy



Joined: 13 Jan 2007
Posts: 91

View user's profile Send private message Visit poster's website

PostPosted: Fri Feb 16, 2007 9:40 am     Reply with quote

Did I hear that the ADC should not use the internal clock for > 1MHz ?

I am using 10bit ADC on the 877a with internal clock and ref and facing unstable values (what could the reson be besides noise ?)
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Feb 16, 2007 12:20 pm     Reply with quote

Quote:
Did I hear that the ADC should not use the internal clock for > 1MHz ?

I think every PIC data sheet has that warning in the A/D section.

For example, the 16F877 data sheet says this:
Quote:
Note:
When the device frequencies are greater than 1 MHz, the RC A/D conversion clock source is only recommended for SLEEP operation.


You have to read the data sheet when you use a peripheral module.
There are always "gotchas" that must be kept in mind. You can't
just choose setup parameters based on what "looks good".

When I made the comment that his code works, I wasn't trying to
undercut Ttelmah's statement. I myself have made the same
statement and referenced the data sheet as shown above.

His problem with unstable values could be due to using the Internal
clock for the A/D. It could also be that his circuit that drives the
A/D pin on the PIC has too high of an output impedance.
The PIC data sheet specifies the upper limit on the impedance.
For some PICs, it can be as high as 10K. For the more modern
PICs, it is often only 2.5K maximum. You have to read the data sheet.
sonicfire



Joined: 11 Feb 2007
Posts: 19
Location: Cedar Rapids

View user's profile Send private message

PostPosted: Fri Feb 16, 2007 3:46 pm     Reply with quote

I'm using a 10 MHz oscillator to drive the pic. Would this be a more advisable clock to use for the ADC? If so, could you give me a code snippet of how to implement such a feature?
Ttelmah
Guest







PostPosted: Fri Feb 16, 2007 3:56 pm     Reply with quote

The 'second' point I was making in my original post, related to sample intervals. The 'set_pwm_duty' code, when dealing with an 'int' value, is very fast. It only has to write one byte to the PWM register. Now on the PIC, the actual interval, does not update till the end of the PWM period. So the interval, may well be changed about 40 times, before the physical update occurs. The ADC, requires the input capacitor to recharge between readings, and as posted, this is not allowed to happen, which will make the behaviour noisier. I'd suggest trying this:
Code:

#bit CCPIF=0XC.2

void main() {
   char selection;
   byte value;
   
   selection = '2';

   setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM

          //   The cycle time will be (1/clock)*4*t2div*(period+1)
          //   In this program clock=10000000 and period=127 (below)
          //   For the three possible selections the cycle time is:
          //     (1/10000000)*4*1*128 =  51.2 us or 19.5 khz
          //     (1/10000000)*4*4*128 = 204.8 us or 4.9 khz
          //     (1/10000000)*4*16*128= 819.2 us or 1.2 khz

   switch(selection) {
     case '1' : setup_timer_2(T2_DIV_BY_1, 127, 1);
                break;
     case '2' : setup_timer_2(T2_DIV_BY_4, 127, 1);
                break;
     case '3' : setup_timer_2(T2_DIV_BY_16, 127, 1);
                break;
   }


  setup_port_a(ALL_ANALOG);
  setup_adc(adc_clock_div_32);
  set_adc_channel( 0 );

  CCPIF=0;
  while( TRUE ) {
    //Wait for the output pulse to update
    while (CCPIF==0) ;
    value=read_adc();

    set_pwm1_duty(value);          // This sets the time the pulse is
                                   // high each cycle.  We use the A/D
                                   // input to make a easy demo.
                                   // the high time will be:
                                   //  if value is LONG INT:
                                   //    value*(1/clock)*t2div
                                   //  if value is INT:
                                   //    value*4*(1/clock)*t2div
                                   // for example a value of 30 and t2div
                                   // of 1 the high time is 12us
                                   // WARNING:  A value too high or low will
                                   //           prevent the output from
                                   //           changing.
 
   CCPIF=0;
   }

}

This makes two tiny changes to the original code. The first is to change the ADC clock source, and the second, is to synchronise the read to the PWM.
I'll be interested to hear if this is better.

Best Wishes
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Feb 16, 2007 4:03 pm     Reply with quote

I can tell you how to find the information. That's a better way.

Download the 16F877 data sheet:
http://ww1.microchip.com/downloads/en/DeviceDoc/30292c.pdf

Go to section 11.0 on the Analog to Digital Converter.

Then go to this sub-section:
11.2 Selecting the A/D Conversion Clock
All PICs that have an A/D will have this section in the data sheet.

You'll see a chart that show the Maximum Device Frequency on the
right side, and on the left side it shows the divisor. Find the lowest
frequency in the table that is greater than or equal to your PIC's
frequency. Then go over to the left column and find the divisor value.
Then look in the 16F877.H file, in the A/D section, and find the CCS
constant for that clock frequency. Example:

Let's say your crystal frequency is 4 MHz. Look in the table on the
right side. The lowest frequency that is greater than or equal to 4 MHz
is 5 MHz. Then go over to the left and you see the divisor is 8Tosc.
Then look in 16F877.H, and you see the CCS constant for this divisor is:
ADC_CLOCK_DIV_8

Now you try it for a 10 MHz crystal.

--------------------
Regarding 18F PICs with a PLL:
Note that the table corresponds to the PIC's operating frequency.
If your PIC has a 10 MHz crystal running in 4x PLL, the PIC will be
running at 40 MHz. PICs that have a PLL will have additional entries
in the table to show you the divisor for use at 40 MHz.
vsmguy



Joined: 13 Jan 2007
Posts: 91

View user's profile Send private message Visit poster's website

PostPosted: Fri Feb 16, 2007 8:16 pm     Reply with quote

Why does not CCS categorize the forum into section.. like ADC/PWM/I2C etc ???

All this information bundled in one big forum head is so messy...

I am currently having to manage all this in groove...
Ttelmah
Guest







PostPosted: Sat Feb 17, 2007 3:28 am     Reply with quote

The problem with 'categorising', is that few problems would then be in the right place. If you look at posted questions, you will find that it is rare for the final solution, to match what the poster 'thought' the problem was, and most quations relate to multiple things (this thread for example, relates to the ADC, the PWM, and also clocking, and timing issues). You can categorise for yourself, to a large extent with the search function. Also, remember that CCS, really have almost nothing to 'do' with this forum. It is a _user_ forum, with CCS providing the space, and one CCS person (Daren Rook), acting to administer it, but with only a very small amount of time involved. At the end of the day, it'd be 'nice' to have posters make sure that the thread title, has some good relationship to the question involved, since this would make searching a lot easier, but beyond this, it would require a significant input of time from somewhere...

Best Wishes
sonicfire



Joined: 11 Feb 2007
Posts: 19
Location: Cedar Rapids

View user's profile Send private message

PostPosted: Tue Feb 20, 2007 2:39 pm     Reply with quote

I'm still not sure what was causing the instability, but now I have it working. Rather than using a variable dc power supply for the input voltage, I am now using a 5KOhm potentiometer. There is no longer any instability with my PWM. Thanks.
Ttelmah
Guest







PostPosted: Tue Feb 20, 2007 3:18 pm     Reply with quote

My 'suggestion' would be that it'd be worth looking with a scope at what the output voltage of the 'variable DC supply', was actually 'doing', into the low load represented by the ADC. You may well find there is a minimum load required for it to become stable.

Best Wishes
sonicfire



Joined: 11 Feb 2007
Posts: 19
Location: Cedar Rapids

View user's profile Send private message

PostPosted: Thu Feb 22, 2007 11:26 am     Reply with quote

Okay, here's a new problem. I've been informed that I need to output a 500KHz PWM signal, instead of a 20KHz signal. I manipulated the code a little bit and was able to get an output of 500KHz, but now the output is unstable again and very sensitive to the analog input. In other words, if I input as little as .5V, the duty cycle is now 100% (where as for the 20KHz, 5V would max out at 50%). I also tried introducing a 500uS delay into the loop, with little added benefit. Here's the code:

Code:
#if defined(__PCM__)
#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=10000000)
//#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, BRGH1OK)

#elif defined(__PCH__)
#include <18F452.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, BRGH1OK)
#endif


void main() {
   INT16 value;

   setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM

  setup_timer_2(T2_DIV_BY_1, 4, 1);  //output at 500KHz

  setup_port_a(ALL_ANALOG);
  setup_adc(adc_clock_internal);
  set_adc_channel( 0 );

  while( TRUE ) {
    value=read_adc();

    set_pwm1_duty(value);          // This sets the time the pulse is
                                   // high each cycle.  We use the A/D
                                   // input to make a easy demo.
                                   // the high time will be:
                                   //  if value is LONG INT:
                                   //    value*(1/clock)*t2div
                                   //  if value is INT:
                                   //    value*4*(1/clock)*t2div
                                   // for example a value of 30 and t2div
                                   // of 1 the high time is 12us
                                   // WARNING:  A value too high or low will
                                   //           prevent the output from
                                   //           changing.
  }

}
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group