|
|
View previous topic :: View next topic |
Author |
Message |
aftar mukhriz
Joined: 03 Nov 2020 Posts: 11 Location: Johor, Malaysia
|
[SOLVED] USB CDC ADC PWM control PIC18F4550 |
Posted: Tue Nov 17, 2020 10:42 pm |
|
|
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
|
|
Posted: Wed Nov 18, 2020 2:01 am |
|
|
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: 19506
|
|
Posted: Wed Nov 18, 2020 2:08 am |
|
|
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
|
|
Posted: Wed Nov 18, 2020 9:11 pm |
|
|
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: 19506
|
|
Posted: Thu Nov 19, 2020 2:14 am |
|
|
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: 9225 Location: Greensville,Ontario
|
|
Posted: Thu Nov 19, 2020 7:58 am |
|
|
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
|
|
Posted: Thu Nov 19, 2020 7:38 pm |
|
|
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: 19506
|
|
Posted: Fri Nov 20, 2020 3:34 am |
|
|
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: 9225 Location: Greensville,Ontario
|
|
Posted: Fri Nov 20, 2020 7:19 am |
|
|
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
|
|
Posted: Sun Nov 22, 2020 6:40 pm |
|
|
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: 19506
|
|
Posted: Sun Nov 22, 2020 11:55 pm |
|
|
You are right in the analysis. |
|
|
aftar mukhriz
Joined: 03 Nov 2020 Posts: 11 Location: Johor, Malaysia
|
|
Posted: Mon Nov 23, 2020 3:58 am |
|
|
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: 19506
|
|
Posted: Mon Nov 23, 2020 4:17 am |
|
|
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
|
|
Posted: Mon Nov 23, 2020 6:08 am |
|
|
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: 19506
|
|
Posted: Mon Nov 23, 2020 7:34 am |
|
|
Have fun.
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!... |
|
|
|
|
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
|