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 CCS Technical Support

Basics Working - PIC18 ADC/PWM
Goto page Previous  1, 2
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
ounvme



Joined: 21 Apr 2012
Posts: 13

View user's profile Send private message

PostPosted: Wed May 30, 2012 10:00 pm     Reply with quote

Small Update:

I have all 3 A/D and PWM working and updating. I am now able to manually adjust 4096 different colors. I am now going to start to clean up the code make it run more efficient and start slowly adding some features. For the pot adjustments. What is a good efficient way to check each channel for changes. Could I just use an if x1 != x2 update pwm or is there a better way?

I am also reading the CCS manual to create a function that will generate random PWM with a calculated seed.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Thu May 31, 2012 2:09 am     Reply with quote

What do you mean by this?
Quote:
What is a good efficient way to check each channel for changes. Could I just use an if x1 != x2 update pwm or is there a better way?
Are you wanting a neat way to:-

(1) update the PWMs?
OR
(2) have a human speed readable monitor?

Mike
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Thu May 31, 2012 3:03 am     Reply with quote

Just to go back to an earlier question:

Quote:

Its seems as the data sheet over complicates how to configure this or I am just not capable of understanding what it is trying to say. It is frustrating non the less.

From the Data Sheet: Where is the 2048 coming from? Could that be 10bit*2?


The data sheet shows example calculations. You could go through the same for your hardware, but generally its not required unless your source impedance is higher than recommended (which on many examples posted here it often is...).

The datasheet says:
Quote:

To calculate the minimum acquisition time,
Equation 23-1 can be used. This equation assumes
that 1/2 LSb error is used (1,024 steps for the A/D). The
1/2 LSb error is the maximum error allowed for the A/D
to meet its specified resolution.


That is where the 1/2048 comes from, its half the LSB for a ten bit convertor. It would be 1/8192 for 12 bit convertors, and only 1/512 for 8 bit accuracy. Basically, the hold capacitor has to charge to within half a bit of the actualy value for maximum conversion accuracy. Tacq as calculated is the minimum time needed to do this if the input voltage is at the other end of the input range from its previous value (which is usually very unlikely) and the temperature is at its worst case (probably hottest for the PIC). Normally the hold cap voltage will settle well inside this time. If you don't want full 10 bit accuracy then you can use a shorter Tacq time. Generally I find it simpler to set the acq time to anything equal or longer than the calculated Tacq time, unless its something really time critical in which I will carefully review Tacq and set the bare minimum.

Once you've decided on a Tacq then you have to implement it by some sort of wait between selecting an adc channel and tirggering a conversion. Many newer pics have hardware to do that for us. That's set by TAD_MUL_x in setup_adc(). You should select the TAD_MUL setting that's just longer than the minimum Tacq that you want.

TAD_MUL makes the hardware implimented Tacq a multiple of the ADC clock. Microchip clearly use their own "standard" ADC blocks in all their chips. The commonest one is the 10 bit version, though newer 18series PICs have a revised 12 bitter. The 10bit appears to be basically designed to work off a 1.25MHz clock. This makes Tad = 0.8us. Some are a bit faster, some a bit slower. The older 8 bit version seen in many 16 series PICs ran at a nominal 1MHz, or 1us. In all cases that I've seen, the data sheet gives recommended TAD settings for various PIC clock rates. In the case of the PIC18F26K80 that's Table 23-1. If I remember rightly the internal clock is a nominal 1MHz on all PICs, though I could well be wrong as I rarely use it. As with Tacq, select the setting that gives TAD greater than or equal to the minimum time.

All the convertors appear to be of the successive approximation type and require at east one more clock than the number of bits, so a 10 bit conversion with hardware Tacq takes 10+1+TAD_MUL+ maybe one or two addtional TAD clock periods.

RF Developer.
ounvme



Joined: 21 Apr 2012
Posts: 13

View user's profile Send private message

PostPosted: Sun Jun 03, 2012 5:20 pm     Reply with quote

@Mike, I was looking for an if statement to check if the value has changed vs constantly updating the pwm duty cycle. Since the adc values have some variation a simple if statement will not work. If the acd value is 122 it could be 121 122 or 123. I figure it does not hurt to let the pwm update the duty cycle over and over.

RF Developer, Thank You very much for making that more clear to me. It is very easy to follow with the way that you explained in detail.

I will look more into the details to make sure I am using the best acquisition times.

If the sum of adcR+adcG+adcB > 255 the program goes into random_color mode. Why does that happen?

Current working version.

Code:

#include <18F26K80.h>

#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOBROWNOUT               //No brownout reset
#FUSES WDT_NOSLEEP              //Watch Dog Timer, disabled during SLEEP
#FUSES  NOPUT

#use delay(int=4000000)

#define LED PIN_C4
#define DELAY 1000
#define RAND_MAX  621
#include <stdlib.h>

int8 adcR;   
int8 adcG;
int8 adcB;
int16 pwmR;
int16 pwmG;
int16 pwmB;
int16 randR;
int16 randG;
int16 randB;
int random_flag;

void set_init()
{
   setup_adc_ports(sAN0|sAN1|sAN2);
   setup_adc(ADC_CLOCK_DIV_4|ADC_TAD_MUL_4);
   setup_timer_2(T2_DIV_BY_16,155,1);      //2.4 ms overflow, 2.4 ms interrupt
   setup_timer_3(T3_DISABLED | T3_DIV_BY_1);
   setup_timer_4(T4_DISABLED,0,1);
   setup_ccp2(CCP_PWM);
   setup_ccp3(CCP_PWM);
   setup_ccp4(CCP_PWM);
   set_pwm2_duty((int16)0);
   set_pwm3_duty((int16)0);
   set_pwm4_duty((int16)0);
   setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
   output_high(LED);// Power ON LED   
}

void check_adc()
{
   set_adc_channel(0);// AN0 reading
    delay_us(10);// 10us delay
    adcR=read_adc(); 
            
   set_adc_channel(1);// AN1 reading
    delay_us(10);// 10us delay
    adcG=read_adc();
         
   set_adc_channel(2);// AN2 reading
    delay_us(10);// 10us delay
    adcB=read_adc();
   

}

void set_pwm()
{
   
   if (random_flag==0)
   {
   pwmR=adcR*2.43;
   pwmG=adcG*2.43;
   pwmB=adcB*2.43;
   }
   
   set_pwm2_duty(pwmR);
   set_pwm3_duty(pwmG);
   set_pwm4_duty(pwmB);
}

void random_colors()
{
   delay_us(rand());
   

   while(adcR+adcG+adcB < 5)
   {
   srand((pwmR+pwmG+pwmB)/3);
   randR=rand(); //range 0-621
   randG=rand(); //range 0-621
   randB=rand(); //range 0-621

      while(pwmR!=randR && pwmG!=randG && pwmB!=randB)
      {
         if(pwmR<randR)   
            pwmR++;
         else if(pwmR>randR)
            pwmR--;

         if(pwmG<randG)   
            pwmG++;
         else if(pwmG>randG)
            pwmG--;

         if(pwmB<randB)   
            pwmB++;
         else if(pwmB>randB)
            pwmB--;
                  
         set_pwm();
         check_adc();
         if (adcR+adcG+adcB>=5)
         return;
         delay_ms(250);      
      }
   


   }
}




void main()
{
   set_init();
   while(true)
   {
      check_adc();
      
      if (adcR+adcG+adcB < 5)
        {
            output_low(LED);// Power OFF LED
           random_flag=1;
         random_colors();
      }
      else
      {
         random_flag=0;   
      output_high(LED);
      }
      
      set_pwm();
   }

}





Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Mon Jun 04, 2012 4:23 pm     Reply with quote

I don't see how this answers my original question.
Quote:
@Mike, I was looking for an if statement to check if the value has changed vs constantly updating the pwm duty cycle. Since the adc values have some variation a simple if statement will not work. If the acd value is 122 it could be 121 122 or 123. I figure it does not hurt to let the pwm update the duty cycle over and over.


Quote:
If the sum of adcR+adcG+adcB > 255 the program goes into random_color mode. Why does that happen?
Because you told it to.

You've declared each of the three variables as int8. If their sum goes just over 255, say 257, the value will be 1. Int8's are 8bit unsigned variables in CCS.

Your test for going random is for the sum to be <5! Nuf said.

Mike
ounvme



Joined: 21 Apr 2012
Posts: 13

View user's profile Send private message

PostPosted: Mon Jun 04, 2012 5:01 pm     Reply with quote

Mike Walne wrote:
What do you mean by this?
Quote:
What is a good efficient way to check each channel for changes. Could I just use an if x1 != x2 update pwm or is there a better way?
Are you wanting a neat way to:-

(1) update the PWMs?
OR
(2) have a human speed readable monitor?

Mike


I am wanting a neat way to update the PWM.

I will look more into the adc summing issue and see if I can find my mistake.
ounvme



Joined: 21 Apr 2012
Posts: 13

View user's profile Send private message

PostPosted: Mon Jun 04, 2012 5:21 pm     Reply with quote

Mike Walne wrote:

Quote:
If the sum of adcR+adcG+adcB > 255 the program goes into random_color mode. Why does that happen?
Because you told it to.

You've declared each of the three variables as int8. If their sum goes just over 255, say 257, the value will be 1. Int8's are 8bit unsigned variables in CCS.

Your test for going random is for the sum to be <5! Nuf said.

Mike


I was under the impression that the sum would always be larger than 5 if one of them was set to anything over 5. So the statements that contain variables also use the definition of the variables

Example?
Code:

int8 x;
int8 y;
int8 z;

if(x+y+z)
do something;

//The if statement can only contain up to 255 numerically summed?

Ttelmah



Joined: 11 Mar 2010
Posts: 19591

View user's profile Send private message

PostPosted: Tue Jun 05, 2012 1:29 am     Reply with quote

ounvme wrote:
Mike Walne wrote:

Quote:
If the sum of adcR+adcG+adcB > 255 the program goes into random_color mode. Why does that happen?
Because you told it to.

You've declared each of the three variables as int8. If their sum goes just over 255, say 257, the value will be 1. Int8's are 8bit unsigned variables in CCS.

Your test for going random is for the sum to be <5! Nuf said.

Mike


I was under the impression that the sum would always be larger than 5 if one of them was set to anything over 5. So the statements that contain variables also use the definition of the variables

Example?
Code:

int8 x;
int8 y;
int8 z;

if(x+y+z)
do something;

//The if statement can only contain up to 255 numerically summed?



x=128
y=129
z=1

Add them using int8 arithmetic, and the result is 128+129+1 = 258 -> int8 _2_. So the sum of these three numbers will test as <5.

Arithmetic wraps. Go past the largest value that can be held, and you start again at the bottom.

_You_ have to handle such things. So:
Code:

    int16 result;
    result=(int16)x+y+z;
    if (result>255) result=255;

Adds the three 8bit values, and gets the '258' result, then since this is >255, stores 255 as the 'result'.
Two key things. First is using an int16 value for the result (even if latter you then only use the bottom 8bits of this), and the second is telling the compiler to do the addition using int16 arithmetic, rather than int8. This is done using the 'cast' (int16), which converts the 8bit value into an int16, before the sum is performed. The compiler says 'Value is an int16 type, so arithmetic must use int16 maths'.

Best Wishes
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Tue Jun 05, 2012 3:09 am     Reply with quote

I asked
Quote:
Are you wanting a neat way to:-

(1) update the PWMs?
OR
(2) have a human speed readable monitor?

We seem to have our lines crossed.

I often use printf's to a PC, so that I can see what is going on inside a PIC.

I was wanting to know if you were looking for a way of watching the current state of your PWM's without rapidly filling the monitor screen.

You now seem happy with updating the PWMs continuously, I don't see that causing any problems. The PWM system is buffered, any new PWM data is not used until the end of the current PWM cycle. You should not get any glitches by updating the PWM_duty at any time.

Mike
ounvme



Joined: 21 Apr 2012
Posts: 13

View user's profile Send private message

PostPosted: Tue Jun 05, 2012 7:35 pm     Reply with quote

Ttelmah wrote:


x=128
y=129
z=1

Add them using int8 arithmetic, and the result is 128+129+1 = 258 -> int8 _2_. So the sum of these three numbers will test as <5.

Arithmetic wraps. Go past the largest value that can be held, and you start again at the bottom.

_You_ have to handle such things. So:
Code:

    int16 result;
    result=(int16)x+y+z;
    if (result>255) result=255;

Adds the three 8bit values, and gets the '258' result, then since this is >255, stores 255 as the 'result'.
Two key things. First is using an int16 value for the result (even if latter you then only use the bottom 8bits of this), and the second is telling the compiler to do the addition using int16 arithmetic, rather than int8. This is done using the 'cast' (int16), which converts the 8bit value into an int16, before the sum is performed. The compiler says 'Value is an int16 type, so arithmetic must use int16 maths'.

Best Wishes


I changed the adcR,G,B definitions to int16. The summing problem went away without having to add a variable or the (int16) to the math. It seems that if int16 variables are used in the math the result is an int16.

The results thus far are very pleasing. Especially in random_color mode. The color transitions are very smooth and nice to watch.

Code:

#include <18F26K80.h>

#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOBROWNOUT               //No brownout reset
#FUSES WDT_NOSLEEP              //Watch Dog Timer, disabled during SLEEP
//#FUSES  NOPUT

#use delay(int=4000000)

#define LED PIN_C4
#define DELAY 1000
#define RAND_MAX  621
#include <stdlib.h>

int16 adcR;   
int16 adcG;
int16 adcB;
int16 pwmR;
int16 pwmG;
int16 pwmB;
int16 randR;
int16 randG;
int16 randB;
int random_flag;

void set_init()
{
   setup_adc_ports(sAN0|sAN1|sAN2);
   setup_adc(ADC_CLOCK_DIV_4|ADC_TAD_MUL_4);
   setup_timer_2(T2_DIV_BY_16,155,1);      //2.4 ms overflow, 2.4 ms interrupt
   setup_timer_3(T3_DISABLED | T3_DIV_BY_1);
   setup_timer_4(T4_DISABLED,0,1);
   setup_ccp2(CCP_PWM);
   setup_ccp3(CCP_PWM);
   setup_ccp4(CCP_PWM);
   set_pwm2_duty((int16)0);
   set_pwm3_duty((int16)0);
   set_pwm4_duty((int16)0);
   setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
   output_high(LED);// Power ON LED   
}

void check_adc()
{
   set_adc_channel(0);// AN0 reading
    delay_us(10);// 10us delay
    adcR=read_adc(); 
            
   set_adc_channel(1);// AN1 reading
    delay_us(10);// 10us delay
    adcG=read_adc();
         
   set_adc_channel(2);// AN2 reading
    delay_us(10);// 10us delay
    adcB=read_adc();
   

}

void set_pwm()
{
   
   if (random_flag==0)
   {
   pwmR=adcR*2.43;
   pwmG=adcG*2.43;
   pwmB=adcB*2.43;
   }
   
   set_pwm2_duty(pwmR);
   set_pwm3_duty(pwmG);
   set_pwm4_duty(pwmB);
}

void random_colors()
{
   delay_us(rand());
   

   while(adcR+adcG+adcB < 8)
   {
   srand(get_timer2());
   randR=rand(); //range 0-621
   randG=rand(); //range 0-621
   randB=rand(); //range 0-621

      while(pwmR!=randR && pwmG!=randG && pwmB!=randB)
      {
         if(pwmR<randR)   
            pwmR++;
         else if(pwmR>randR)
            pwmR--;

         if(pwmG<randG)   
            pwmG++;
         else if(pwmG>randG)
            pwmG--;

         if(pwmB<randB)   
            pwmB++;
         else if(pwmB>randB)
            pwmB--;
                  
         set_pwm();
         check_adc();
         if (adcR+adcG+adcB>=8)
         return;
         delay_ms(100);      
      }
   


   }
}




void main()
{
   set_init();

   while(true)
   {
      check_adc();
      
      if (adcR+adcG+adcB < 8)
        {
            output_low(LED);// Power OFF LED
           random_flag=1;
         random_colors();
      }
      else
      {
         random_flag=0;   
      output_high(LED);
      }
      
      set_pwm();
   }

}




Ttelmah



Joined: 11 Mar 2010
Posts: 19591

View user's profile Send private message

PostPosted: Wed Jun 06, 2012 1:32 am     Reply with quote

Yes, but you are then using 2bytes to store each number - RAM space is often _critical_ in a PIC, hence being able to store the values in single bytes, and yet still use int16 arithmetic, _may_ be essential, if space is tight.

Best Wishes
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 Previous  1, 2
Page 2 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