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

[SOLVED] USB CDC ADC PWM control PIC18F4550
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
aftar mukhriz



Joined: 03 Nov 2020
Posts: 11
Location: Johor, Malaysia

View user's profile Send private message

[SOLVED] USB CDC ADC PWM control PIC18F4550
PostPosted: Tue Nov 17, 2020 10:42 pm     Reply with quote

Hello All,

Right now, I'm trying to control PWM at pin C2 using ADC reading from pin A0. I have connected potentiometer to pin A0 and connected LED with resistor at pin C2. When I connected USB to the PC and opened up Tera Term, ADC reading can be displayed but LED is not responsive. I also tried to control PWM without USB using code in this forum:
https://www.ccsinfo.com/forum/viewtopic.php?p=120731
and it works. Below is my code for using USB.
Code:

#include <18F4550.h> //if your version of the compiler doesn't support the 4553, you can put the 4550 in your code, which is almost exactly the same.
#device ADC=8;
#fuses XT,NOWDT,NOPROTECT,BROWNOUT,PUT,NOLVP
#fuses HSPLL,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,NOPBADEN
#use delay(clock=4M)
#include <stdlib.h>
#define USB_CON_SENSE_PIN PIN_B2
#define __USB_PIC_PERIF__ 1
// Includes all USB code and interrupts, as well as the CDC API
#include <usb_cdc.h>
int1 usb_cdc_oldconnected=FALSE;
#define SP 0x20

void main(void)
{
   
   char c;
   usb_init_cs();
   usb_cdc_init();
   unsigned int8 value,i;
   setup_ccp2(CCP_PWM);   
   // Set the PWM frequency for 244 Hz (with a 4 MHz crystal)
   setup_timer_2(T2_DIV_BY_16, 255, 1);
   
   setup_port_a(AN0);
   setup_adc(ADC_CLOCK_DIV_8); // Divisor for 4 MHz crystal
   set_adc_channel(0);
   delay_us(20);
   
   while (true)
   {
      if (usb_attached())
      {
         usb_task();   
         if (usb_enumerated())
         {
            if (usb_cdc_carrier.dte_present)
            {
               if (usb_cdc_oldconnected==FALSE)
               {
//!                  printf(usb_cdc_putc,"Hello World\n\r");
                  usb_cdc_oldconnected=TRUE;
               }
           
              if (usb_cdc_kbhit())
              {
                  c = usb_cdc_getc();
                  if ( c == SP )
                  {
                     printf(usb_cdc_putc, "Sampling:");
//!                     usb_cdc_puts("Sampling:");
                        do {
                           for(i=0; i<=30; ++i) {
                              value = read_adc();
                              set_pwm2_duty(value);
                           }
                       
                        printf(usb_cdc_putc,"\r\nRead value: %u \n\r",value);
                     } while (1);
                     
                  }
               }
          }
         }
      }
      else
      {
         usb_cdc_oldconnected=FALSE;
         usb_cdc_init(); //clear buffers if disconnected
      }
   }
}



Can anyone show me what is wrong with the code? Thank you for your time.
_________________
Learning process never ends


Last edited by aftar mukhriz on Mon Nov 23, 2020 6:09 am; edited 1 time in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Nov 18, 2020 2:01 am     Reply with quote

Your code shown below has a change from the sample code in other thread.
You changed it to use CCP2. But CCP2 outputs PWM on pin C1. You want
it on pin C2.

Edit the line below and put it back to CCP1. Then it might work.
Quote:
void main(void)
{

char c;
usb_init_cs();
usb_cdc_init();
unsigned int8 value,i;
setup_ccp2(CCP_PWM);
// Set the PWM frequency for 244 Hz (with a 4 MHz crystal)
setup_timer_2(T2_DIV_BY_16, 255, 1);
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Wed Nov 18, 2020 2:08 am     Reply with quote

There are significant problems with your clock settings.

#fuses XT,NOWDT,NOPROTECT,BROWNOUT,PUT,NOLVP
#fuses HSPLL,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,NOPBADEN
#use delay(clock=4M)

Now these show you as using an 'XT' crystal, so up to 4MHz. You speak
as if you are using a 4MHz crystal, but you have 'PLL5'. Now 'PLL5', tells the
chip to divide the oscillator by 5 to feed the USB PLL. So you are running
this at 800KHz, when it requires 4MHz....
However you also have the HSPLL fuse selected, which says to take the
CPU clock from the USB PLL, not from the crystal. So with the USB
PLL at 24MHz (which is what it would give from 800KHz), the CPU would
be running at 9.6MHz, not the 4Mhz you specify.... :(
There is another issue, that in general, running the CPU at 4MHz, can
have problems handling USB. USB needs significant performance from the
CPU, and normally around 8MHz is the slowest that will be reliable.
I'm surprised the USB does actually work. Almost certainly the compiler
or chip are 'defaulting' to a working configuration, but it is not what you
think it is....

So, assuming you do have a 4MHz crystal, change the fuses and clock
to:

#fuses XTPLL,NOWDT,NOPROTECT,BROWNOUT,PUT,NOLVP
#fuses NODEBUG,USBDIV,PLL1,CPUDIV4,VREGEN,NOPBADEN
#use delay(clock=16M)

This says to clock the USB off the 4Mhz crystal (PLL1), and to clock
the CPU off the USB PLL/6 = 16MHz. Change your ADC divisor to
/16. (just to confuse you CPUDIV4 gives /6 when used with the USB PLL).

This then should give you a running CPU and USB.

Then, there has to be at least Tacq between one ADC reading and the
next. As posted you read the ADC and set the PWM thirty times. Why?.
Read the adc a number of times, average the readings, and set the PWM
once. With a delay in the loop between the ADC readings.
When you currently do this loop, it tries to read the ADC 'flat out' without
any pause. At this point every thirty readings (so only a very few uSec),
it'll send the 'value' to the PC. This loop never exits, and will be sending
data far faster than the interface will support.

So, something more like:
Code:

    int16 sum;

                  if ( c == SP )
                  {
                     printf(usb_cdc_putc, "Sampling:");
                     do
                     {
                          sum=0;
                          for(i=0; i<16; ++i)
                          {
                               sum+ = read_adc();
                               delay_us(10); //allow ADC to reacquire                             
                          }
                          value=sum/16; //averaged ADC reading
                          set_pwm2_duty(value);
                          printf(usb_cdc_putc,"\r\nRead value: %u \n\r",value);
                          delay_ms(250); //bring loop speed down
                     } while (usb_cdc_kbhit()==FALSE);
                     //this way if you touch another key the loop will exit
                 }


Then you talk about an 'LED', but there is nothing in the code doing anything
to an LED, unless it is on the PWM?. Remember if so, it needs a current
limiting resistor. Otherwise the peak current will make the chip
misbehave....
aftar mukhriz



Joined: 03 Nov 2020
Posts: 11
Location: Johor, Malaysia

View user's profile Send private message

PostPosted: Wed Nov 18, 2020 9:11 pm     Reply with quote

Thank you, PCM programmer for the pointer. I made silly mistake there. Thank you, too for your reply, Ttelmah. Actually, I'm using 20MHz crystal for this project. But, your reply helps me understand a bit on the fuses setting. So, I have changed the fuses and clock to below:
Code:

#fuses HSPLL,NODEBUG,USBDIV,PLL5,CPUDIV2,VREGEN,NOPBADEN,NOWDT,
NOPROTECT,BROWNOUT,PUT,NOLVP
#use delay(clock=48M)

However, I have question to Ttelmah regarding on why you are using int16 for sum? And why the divisor for ADC is /16? Also, if I use internal clock for ADC, is the clock equals to 48MHz?
Code:

int16 sum;
unsigned int8 value,i;
setup_timer_2(T2_DIV_BY_1, 127, 1);
setup_adc_ports(AN0, VREF_VDD);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel(0);
delay_us(20);


Thanks.
_________________
Learning process never ends
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Thu Nov 19, 2020 2:14 am     Reply with quote

Generally on the PIC, the internal ADC clock should not be used if the CPU
frequency is above 1MHz. In the data sheet for your PIC, the table
'Tad vs. device operating frequencies', has a note against the RC entry:

For device frequencies above 1 MHz, the device must be in Sleep for the
entire conversion or the A/D accuracy may be out of specification.

If you want to use the RC oscillator (internal), then you have to start the
conversion and put the PIC to sleep.

This same table also shows the maximum frequencies supported for a
given division.

Now /16, was for a 16MHz master clock. For 48Mhz, you would
need to use /64. However they don't warrant the ADC for device
frequencies above 40Mhz.

However, you are not running at 48MHz. You are running at 32Mhz, so
/32 for the ADC.

It is critical to get your head round the CPUDIV values. They are not
obvious....

What happens is that when the CPU is running off the crystal oscillator,
there are four CPUDIV values. DIV1, DIV2, DIV3 & DIV4. These give
/1, /2, /3 & /4. When you run off the USB PLL, these same values have
different meanings:
Code:


             CRYSTAL      USB PLL
CPUDIV1       /1                /2
CPUDIV2       /2                /3
CPUDIV3       /3                /4
CPUDIV4       /4                /6


So your CPUDIV2 selection, will actually give 96MHz/3 = 32MHz CPU clock.

Getting this understood is one of the complex parts of the xx50 chips....
temtronic



Joined: 01 Jul 2010
Posts: 9096
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Nov 19, 2020 7:58 am     Reply with quote

To follow up what Mr. T is saying....
I printed out the 'clock' diagram to SEE just how to get that PIC with USB to work. There's a LOT of paths and combinations !!!
Once you've chosen a 'path' then you can figure out what fuses, clock options should work.
it was a LOT simpler 25 years ago....grab a real xtal, 2 caps and PICs ran.
aftar mukhriz



Joined: 03 Nov 2020
Posts: 11
Location: Johor, Malaysia

View user's profile Send private message

PostPosted: Thu Nov 19, 2020 7:38 pm     Reply with quote

Thank you, Ttelmah & temtronic for the feedback given. My mistake is that I thought CPUDIV2 will produce 48MHz clock frequency. So, huge thanks for the table.
Next, based on the info in this forum, https://www.ccsinfo.com/forum/viewtopic.php?t=56171, I just want to confirm that the way to sleep internal clock is to set ADC clock with correct divisor, am I right? So, I changed the ADC clock from this:
Code:
setup_adc(ADC_CLOCK_INTERNAL);
to this one:
Code:
setup_adc(ADC_CLOCK_DIV_32);

Apart from that, when I set the ADC clock divisor to 32, do I need to change int sum and value to int32?
Code:

int32 sum, value;
unsigned int8 i;

printf(usb_cdc_putc, "\r\nSampling:");
                     do
                     {
                          sum=0;
                          for(i=0; i<32; ++i)
                          {
                               sum += read_adc();
                               delay_us(10); //allow ADC to reacquire                             
                          }
                          value=sum/32; //averaged ADC reading
                          set_pwm1_duty(value);
                          printf(usb_cdc_putc,"\r\nRead value: %lu \n\r",value);
                          delay_ms(250); //bring loop speed down
                     } while (usb_cdc_kbhit()==FALSE);


Regards.
_________________
Learning process never ends
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Fri Nov 20, 2020 3:34 am     Reply with quote

No,
The size needed is simply based on the size of the ADC value, and how
many you add up. So 1023 * 32 = 32736, which fits nicely into the int16.
temtronic



Joined: 01 Jul 2010
Posts: 9096
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Nov 20, 2020 7:19 am     Reply with quote

Depending on the type of 'sensor' and accuracy you require, you may not need 32 readings. Try 16 or 8 or 4 or 4. They make QUICK math..... using say 10 or 12 samples makes the 'math' more complicated... computers love binary math....
aftar mukhriz



Joined: 03 Nov 2020
Posts: 11
Location: Johor, Malaysia

View user's profile Send private message

PostPosted: Sun Nov 22, 2020 6:40 pm     Reply with quote

Thank you, Ttelmah and temtronic.
So, the size of ADC data depends on the sum and value that I set, right? If I set the sum to i<16 and the value to sum/16, the ADC would be 1023*16 = 16368, right? Still, I need to assign unsigned int16 for sum and value due to both of them are larger than 255 for unsigned int8.
If my understanding is wrong, can someone point me to the right direction? Thanks for your time.
As for temtronic's comment, what I understand is that if I reduce the sample readings to let say 16 readings, though lesser samples, it will increase the PIC speed since lesser samples are needed for data processing, am I right? As for 10 to 12 samples, I believe the keyword is binary, is it?
Code:
do
                     {
                          sum=0;
                          for(i=0; i<16; ++i)
                          {
                               sum += read_adc();
                               delay_us(10); //allow ADC to reacquire                             
                          }
                          value=sum/16; //averaged ADC reading
                          set_pwm1_duty(value);
                          printf(usb_cdc_putc,"\r\nRead value: %lu \n\r",value);
                          delay_ms(250); //bring loop speed down
                     } while (usb_cdc_kbhit()==FALSE);

_________________
Learning process never ends
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Sun Nov 22, 2020 11:55 pm     Reply with quote

You are right in the analysis. Very Happy
aftar mukhriz



Joined: 03 Nov 2020
Posts: 11
Location: Johor, Malaysia

View user's profile Send private message

PostPosted: Mon Nov 23, 2020 3:58 am     Reply with quote

Thanks, Ttelmah. However, is my analysis from temtronic's comment correct?
Quote:
using say 10 or 12 samples makes the 'math' more complicated... computers love binary math....
Is the first comment related with the second one? I'm sorry, but I don't quite understand comment from temtronic.
_________________
Learning process never ends
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Mon Nov 23, 2020 4:17 am     Reply with quote

Imagine you do (say) 10 samples, and have the code take:

sum/10

If you look at the size of the code, you will find this is perhaps 10* larger
than if you do:

sum/8

It also takes an enormous amount more time (perhaps about 30* as long)...

For averaging on a micro-controller, it is 'best' to use a 'binary' number
or samples, so 2, 4, 8, 16, 32 etc..
This reduces both the size and time needed for the division.

So as a general rule, for things like this, always try to ensure the final
division is by a binary number.
aftar mukhriz



Joined: 03 Nov 2020
Posts: 11
Location: Johor, Malaysia

View user's profile Send private message

PostPosted: Mon Nov 23, 2020 6:08 am     Reply with quote

Wow, huge thanks to you, Ttelmah and temtronic. Maybe later I'll try to use /10 or /12 like temtronic suggested to see the difference. Thanks again to both of you.
_________________
Learning process never ends
Ttelmah



Joined: 11 Mar 2010
Posts: 19215

View user's profile Send private message

PostPosted: Mon Nov 23, 2020 7:34 am     Reply with quote

Have fun. Smile

For an example of the speed involved, /10, on an int16 takes 70.8 uSec
on a 20MHz processor. /8 takes less than 2 uSec!...
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