|
|
View previous topic :: View next topic |
Author |
Message |
luanfagu
Joined: 10 Feb 2015 Posts: 6
|
adc -> pwm problems |
Posted: Tue Feb 10, 2015 1:58 am |
|
|
So, I'm making a code for job, my program read a potentiometer and returns a 10 bit value that i convert to a number (0~255) and put this number as a pwm duty cycle.
My problem is that comes to some of the potentiometer, and it just back, back to around 60% (which is the point that set minimum for him), when it is put in 100% it does not reach 255.
I don't know if the problem is in adc or when i set the duty cycle.
Code: | #include <18f4431.h>
#device adc=10
#fuses INTRC_IO, NOWDT, NOPROTECT, NOBROWNOUT, PUT, NOLVP
#use delay(clock=8MHz, crystal)
#define POWER_PWM_PERIOD 2048
/*
*
*/
//global variables
float valorpwm, velopwm, valorad;
int valorad1 = 0;
int valorpwm1 = 0, velopwm1 = 0;
float decrementa(float velo, float valor){
if(velo > valor){
velo-=0.01;
delay_ms(24);
}
if(valor == 0.0 && velo <=0.6){
velo = 0.0;
}
return velo;
}
int decrementa1(int velo, int valor){
if(velo > valor){
velo--;
delay_ms(24);
}
if(valor == 0 && velo <=153){
velo = 0;
}
return velo;
}
int main() {
setup_adc_ports(sAN0 | sAN1 | sAN2);
setup_adc(ADC_CLOCK_DIV_4);
setup_power_pwm_pins(PWM_ODD_ON, PWM_ODD_ON, PWM_ODD_ON, PWM_ODD_ON);
setup_power_pwm(PWM_CLOCK_DIV_4|PWM_FREE_RUN, 1, 0, POWER_PWM_PERIOD, 0, 1,0);
setup_ccp1(CCP_PWM);
setup_ccp2(CCP_PWM);
setup_timer_2(T2_DIV_BY_4, 255, 1);
set_pwm1_duty(0);
set_pwm2_duty(0);
while(1){
while(input(PIN_E1)){
set_adc_channel(2);
delay_us(416);
valorad1 =(int) (read_adc());
delay_us(416);
valorpwm1 =(int) (((valorad1 - 511)*102)/510)+153;
if(valorpwm1 > velopwm1 && input(PIN_E1)){
if(velopwm1 == 0) velopwm1 = 153;
else{
while(velopwm1 < valorpwm1 && input(PIN_E1)){
velopwm1++;
delay_ms(24);
set_pwm1_duty(velopwm1);
}
}
}else{
while(velopwm1 > valorpwm1){
velopwm1 = decrementa1(velopwm1, valorpwm1);
set_pwm1_duty(velopwm1);
}
}
}
valorpwm1 = 0;
while(velopwm1 > valorpwm1){
velopwm1 = decrementa1(velopwm1, valorpwm1);
set_pwm1_duty(velopwm1);
}
while(input(PIN_E2)){
set_adc_channel(2);
delay_us(416);
valorad1 =(int) (read_adc());
delay_us(416);
valorpwm1 =(int) ((((valorad1 - 510)*102)*(-1))/510)+153;
if(valorpwm1 > velopwm1 && input(PIN_E2)){
if(velopwm1 == 0) velopwm1 = 153;
else{
while(velopwm1 < valorpwm1 && input(PIN_E2)){
velopwm1++;
delay_ms(24);
set_pwm2_duty(velopwm1);
}
}
}else{
while(velopwm1 > valorpwm1){
velopwm1 = decrementa1(velopwm1, valorpwm1);
set_pwm2_duty(velopwm1);
}
}
}
valorpwm1 = 0;
while(velopwm1 > valorpwm1){
velopwm1 = decrementa1(velopwm1, valorpwm1);
set_pwm2_duty(velopwm1);
}
}
return (0);
} |
|
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
Divide and conquer |
Posted: Tue Feb 10, 2015 3:34 am |
|
|
Test the ADC and PWM separately
You could try:-
1) Sending ADC output to a PC.
2) Send PWM control from PC to PIC.
3) Make sure each is working as it should on its own.
Mike |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9282 Location: Greensville,Ontario
|
|
Posted: Tue Feb 10, 2015 6:03 am |
|
|
Why not simply the program ?
Read the ADC in 8 bit mode! That would automatically give you an 8 bit data to send to the PWM.
Also there is NO need to use floating point math. Again simplify your program! The CCP doesn't accept FP#s, so keep it simple ,use integer math.
As well FP math takes a HUGE amount of time(seconds vs microseconds) so your 'plant' will not respond as fast as it should.
Jay |
|
|
luanfagu
Joined: 10 Feb 2015 Posts: 6
|
|
Posted: Tue Feb 10, 2015 6:14 am |
|
|
the float is to another thing, im using power control pwm in this code, and need the float to this works.
@temtronic
thx for the tip, i will try 8bit conversion
EDIT: it doest work, my pic only accept 10 bit conversion.
@Mike Walne
nothing strange, its all right about the conversion, and pwm works fine with a number insted of variable. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Feb 10, 2015 6:34 am |
|
|
luanfagu wrote: |
EDIT: it doest work, my pic only accept 10 bit conversion.
|
Read the CCS manual.
As I understand it, this will make the ADC operate in 8 bit mode.
You believe the ADC is OK, so, have you examined what data is actually being sent to the PWM?
Mike |
|
|
luanfagu
Joined: 10 Feb 2015 Posts: 6
|
|
Posted: Tue Feb 10, 2015 7:14 am |
|
|
@Mike Walne
so, all working fine, i realy dont know what happening, when i used another compiler. it happens also, but i rewrote the code and works fine ( i realy dont know why).
if you can, i ask you to test the code and see if this problem occurs with you too. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19605
|
|
Posted: Tue Feb 10, 2015 9:14 am |
|
|
The reason it doesn't work, is maths....
You have the variable used to take the ADC reading declared as 'int' This in CCS, is an _8 bit integer_.
So 10 bit reading into an 8bit integer. Immediate possible overflow.
Then you subtract 510. Result number overflows again (the default integer is _unsigned_) - the maths for this will use int16, since 510 is inherently an int16, but still unsigned..
Then you multiply by 102. Overflow again.
Then you divide by 510. Overflow again.
Then add 153.....
Result potential garbage. |
|
|
luanfagu
Joined: 10 Feb 2015 Posts: 6
|
|
Posted: Tue Feb 10, 2015 9:23 am |
|
|
I solve the problem!, i change int to float and it works perfectly.
thank you all guys!
here the code fixed:
Code: | #include <18f4431.h>
#device adc=10
#fuses INTRC_IO, NOWDT, NOPROTECT, NOBROWNOUT, PUT, NOLVP
#use delay(clock=8MHz, crystal)
#define POWER_PWM_PERIOD 2048
#include <lcd.c>
#define LCD_ENABLE_PIN PIN_D0 ////
#define LCD_RS_PIN PIN_D1 ////
#define LCD_RW_PIN PIN_D2 ////
#define LCD_DATA4 PIN_D3 ////
#define LCD_DATA5 PIN_D4 ////
#define LCD_DATA6 PIN_D5 ////
#define LCD_DATA7 PIN_D6
//global variables
float valorpwm, velopwm, valorad;
int valorpwm1 = 0, velopwm1 = 0;
float decrementa(float velo, float valor){
if(velo > valor){
velo-=0.01;
delay_ms(24);
}
if(valor == 0.0 && velo <=0.6){
velo = 0.0;
}
return velo;
}
int decrementa1(int velo, int valor){
if(velo > valor){
velo--;
delay_ms(24);
}
if(valor == 0 && velo <=153){
velo = 0;
}
return velo;
}
int main() {
setup_adc_ports(sAN0 | sAN1 | sAN2);
setup_adc(ADC_CLOCK_DIV_4 | VSS_VDD);
setup_power_pwm_pins(PWM_ODD_ON, PWM_ODD_ON, PWM_ODD_ON, PWM_ODD_ON);
setup_power_pwm(PWM_CLOCK_DIV_4|PWM_FREE_RUN, 1, 0, POWER_PWM_PERIOD, 0, 1,0);
setup_ccp1(CCP_PWM);
setup_ccp2(CCP_PWM);
setup_timer_2(T2_DIV_BY_4, 255, 1);
lcd_init();
set_pwm1_duty(0);
set_pwm2_duty(0);
while(1){
while(input(PIN_E1)){
set_adc_channel(2);
delay_ms(1);
valorad = (float) (read_adc());
delay_ms(1);
valorpwm = (float) ((((valorad - 510.0)*102.0)/510.0)+153.0);
valorpwm1 = (int) (valorpwm);
if(valorpwm1 > velopwm1 && input(PIN_E1)){
if(velopwm1 == 0) velopwm1 = 153;
else{
while(velopwm1 < valorpwm1 && input(PIN_E1)){
velopwm1++;
delay_ms(24);
set_pwm1_duty(velopwm1);
}
}
}else{
while(velopwm1 > valorpwm1){
velopwm1 = decrementa1(velopwm1, valorpwm1);
set_pwm1_duty(velopwm1);
}
}
}
valorpwm1 = 0;
while(velopwm1 > valorpwm1){
velopwm1 = decrementa1(velopwm1, valorpwm1);
set_pwm1_duty(velopwm1);
}
while(input(PIN_E2)){
set_adc_channel(2);
delay_ms(1);
valorad = (float) (read_adc());
delay_ms(1);
valorpwm = (float) (((((valorad - 510.0)*102.0)*(-1.0))/510.0)+153.0);
valorpwm1 = (int) (valorpwm);
if(valorpwm1 > velopwm1 && input(PIN_E2)){
if(velopwm1 == 0) velopwm1 = 153;
else{
while(velopwm1 < valorpwm1 && input(PIN_E2)){
velopwm1++;
delay_ms(24);
set_pwm2_duty(velopwm1);
}
}
}else{
while(velopwm1 > valorpwm1){
velopwm1 = decrementa1(velopwm1, valorpwm1);
set_pwm2_duty(velopwm1);
}
}
}
valorpwm1 = 0;
while(velopwm1 > valorpwm1){
velopwm1 = decrementa1(velopwm1, valorpwm1);
set_pwm2_duty(velopwm1);
}
}
return (0);
} |
thanks again! |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9282 Location: Greensville,Ontario
|
|
Posted: Tue Feb 10, 2015 9:59 am |
|
|
You could also get rid of the floating points altogether and the program will run a LOT faster !!! Maybe 100* faster....
There is zero need for FP, neither the ADC nor CCP use FP.
just food for thought
Jay |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19605
|
|
Posted: Tue Feb 10, 2015 1:31 pm |
|
|
and seriously, the last thing you want for power control, is floats.
They are too slow for most applications. For the last power control application I did, I used scaled 24bit maths (custom written fixed point), to get the speed needed for PWM control. FP is far to slow, resulting in overshoot in the regulator algorithm. 32bit fixed got 'close'. For me, 16bit didn't quite have the range needed, so I wrote 24bit versions. The values were treated as if they were fixed point binary values with an 8bit mantissa.
You need to start understanding why fp is probably not the solution. |
|
|
luanfagu
Joined: 10 Feb 2015 Posts: 6
|
|
Posted: Tue Feb 10, 2015 4:31 pm |
|
|
unfortunately my program has to be complex, I'm controlling 14 hydraulic coils with a astounding accuracy, and all this things with a series of parameters of a volvo hydraulic motor, its not easy for a beginner, like me. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Feb 10, 2015 5:14 pm |
|
|
I actually meant, start small, and prove that the basics are working
before you add complexity. Prove that you can read the adc and get
a smooth PWM duty cycle increase as the adc voltage increases. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19605
|
|
Posted: Wed Feb 11, 2015 2:22 am |
|
|
PCM programmer has said it all. You _need_ (mandatory, not optional), to start small. Start with the individual components. ADC. How good is a reading?. Does your circuit still read correctly when a coil is moving (odds are it won't). Then try to control one coil. If you must work with lots of other signals, then simulate these. Development is a series of small steps, not one big leap.
Your 'astounding accuracy' comment is exactly _why_ FP maths should not be used. It's worth realising, that FP is _never_ used for serious financial calculations, because it is too inaccurate. FP is a 'shortcut' to allow quick working with numbers over an enormous range, but comes at the cost of accuracy. When dealing with numbers over a fixed range (which you inherently are, since they come from an ADC), integer is vastly better, but does mean you need to actually understand the numbers and the ranges involved. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9282 Location: Greensville,Ontario
|
|
Posted: Wed Feb 11, 2015 6:23 am |
|
|
hmm.. just noticed this....
#fuses INTRC_IO, NOWDT, NOPROTECT, NOBROWNOUT, PUT, NOLVP
#use delay(clock=8MHz, crystal)
....
pretty sure the fuses(INTRC_IO) is replaced by the use delay(...) so I'm assuming you have an 8MHz xtal for the PIC clock?
If not, you _must_ use a real xtal NOT the internal RC of the PIC. The internal Rc is NOT accurate enough for 'real' tight timing over any range of temperature over time..
just food for thought
also
since you're controlling 14 valves, start with one first, get it working 100% THEN proceed to #2,test,confirm, then keep adding more.
if you try cutting code all of them at one time you'll fail miserably,start small, in the end it's faster and easier to do.
jay |
|
|
|
|
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
|