|
|
View previous topic :: View next topic |
Author |
Message |
Guest
|
problem with variable ADC & RC servo motor |
Posted: Tue Apr 22, 2008 1:02 am |
|
|
Hi.. my project is to control RC servo motor using ADC(0-5volts).. i use 10bit ADC and variable resistor.. in my simulation using proteus, the ADC is working but the servo motor only turn and stay at position 90 deg...
is my calculation in delay wrong? but the total freq is 50Hz=20,000us
OR it is not suitable using this methods or there are others way?
can anyone help me? thanks..
Code: |
#include <16F877A.H>
#device ADC=10
#use delay (clock=20000000)
#use rs232 (baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#FUSES HS, NOWDT, NOPROTECT, BROWNOUT, NOLVP
#define servo1 PIN_b0 //pin 38
#BYTE ADCON0 = 0x1F
int16 ch1_result;
double ADC;
int i;
void main()
{
setup_adc_ports(ALL_ANALOG); // SETTING ADC CHANNEL // A0 A1 A2 A3 A5 E0 E1 E2 Ref=Vdd
setup_adc(ADC_CLOCK_DIV_32);
while(1)
{
set_adc_channel(1); //A1
ch1_result = read_adc();
ADC=ch1_result;
printf("\f ADC: %3.2f",ADC);
delay_ms(500);
for (i=0;i<50;i++)
{
output_high(SERVO1);
delay_us(450+((1023-ADC)*1.98));//initial 0deg
output_low(SERVO1);
delay_us(19550-((1023-ADC)*1.98)); //initial 0deg
}
}
}
|
|
|
|
Matro Guest
|
|
Posted: Tue Apr 22, 2008 1:46 am |
|
|
In a first very quick review, I see something wrong.
You have to know that the 10-bit result of ADC is left-justified by default.
So the values will go from 0x0000 to 0xFFC0 with step of 0x0040.
So your calculations (based on a right-justified values) will be definitely wrong.
The 2nd thing is that it should be better to use timer interrupts for delay, but this code should work if you correct the calculations.
Matro. |
|
|
Guest
|
Re: problem with variable ADC & RC servo motor |
Posted: Tue Apr 22, 2008 1:52 am |
|
|
Code: |
#include <16F877A.H>
#device ADC=10
#use delay (clock=20000000)
#use rs232 (baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#FUSES HS, NOWDT, NOPROTECT, BROWNOUT, NOLVP
#define servo1 PIN_b0 //pin 38
#BYTE ADCON0 = 0x1F
#BIT ADFM = 0x9F.7 //used to choose ADC result justification
int16 ch1_result;
double ADC;
int i;
void main()
{
setup_adc_ports(ALL_ANALOG); // SETTING ADC CHANNEL // A0 A1 A2 A3 A5 E0 E1 E2 Ref=Vdd
setup_adc(ADC_CLOCK_DIV_32);
ADFM = 1; //1 for ADC result to be right-justified, 0 for left-justified
while(1)
{
set_adc_channel(1); //A1
ch1_result = read_adc();
ADC=ch1_result;
printf("\f ADC: %3.2f",ADC);
delay_ms(500);
for (i=0;i<50;i++)
{
output_high(SERVO1);
delay_us(450+((1023-ADC)*1.98));//initial 0deg
output_low(SERVO1);
delay_us(19550-((1023-ADC)*1.98)); //initial 0deg
}
}
}
|
In this code I added the tip that sets a right justification for ADC results.
That's one solution, the other is to change calculation appropriately and keeping the left justification as I already explained.
Matro. |
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 22, 2008 2:46 am |
|
|
The ADC value, is not 'left justified by default'. In CCS, if you select 'ADC=10', the chip is setup to give a right justified result. Selecting 'ADC=16', gives a left justified result.
Look at the table, in the 'read_adc' section of the manual, for how the value is returned.
Now, first thing. When you select on ADC channel, you _must_ wait for Tacq, _after_ this, before reading the ADC result. Move your delay down to between selecting the channel, and reading the ADC. This shouldn't mtter in your case (since once you have 'selected' the channel the first time, the selection remains the same on subsequent loops. In fact, move the selection outside the loop entirely (never do anything you don't have to in a loop!).
Now, get rid of the floating point arithmetic. Treat 'FP', as something you only use if you must. In your original example, you take a integer value of '1023' max, and convert it to a float value of '1023' max, then print it displaying decimal points. This does nothing, since the number is integer. Unfortunately, if you print using a FP number, this takes a lot of time, since the printf, has to perform successive FP divisions.
Big thing missing, is that you are not disabling the internal comparators. On the 'A' chip, the analog sources _default_ to connecting to this, rather than the ADC.
Code: |
#include <16F877A.H>
#device ADC=10
#use delay (clock=20000000)
#use rs232 (baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#FUSES HS, NOWDT, NOPROTECT, BROWNOUT, NOLVP
#define servo1 PIN_b0 //pin 38
int16 ch1_result;
int32 time;
int i;
void main() {
setup_adc_ports(ALL_ANALOG); // SETTING ADC CHANNEL // A0
//A1 A2 A3 A5 E0 E1 E2 Ref=Vdd
setup_adc(ADC_CLOCK_DIV_32);
set_adc_channel(1); //Put outside the loop
setup_comparator(NC_NC_NC_NC); //Probably the problem
while(1) {
ch1_result = read_adc();
time=((1023-ch1_result)*99)/50; //This generates the required
//time in uSec, without using FP.
printf("\f Time: %5.3Lw",time); //Display the time in mSec
//Shows if the maths is right.
//delay_ms(500); //This risks making the servo hiccup between
//the loops. If the gap between servo pulses is too long, some
//servos will shift. Why have it?.
for (i=0;i<50;i++) {
output_high(SERVO1);
delay_us(450+time);//initial 0deg
output_low(SERVO1);
delay_us(19550-time); //initial 0deg
}
}
}
|
Hopefully you will find this gets close to what you want.
Best Wishes |
|
|
arawana
Joined: 06 Apr 2008 Posts: 19 Location: SINGAPORE
|
still cannot work.. |
Posted: Tue Apr 22, 2008 6:55 am |
|
|
huhu~ i try both program, but it still same.. the ADC change but the motor still stay at 90 degree position..
for program Ttelmah write;
Code: | printf("\f Time: %5.3Lw",time); //Display the time in mSec |
when i compile, an error occur.. it said --> print (%) format invalid
so i just // that line and it can compile.. but the motor still dont move.. the PWM signal also cannot see from oscilloscope
for program Matro write;
it can be compile.. but motor still cannot work.. stay at 90 deg..
here is the picture of simulation using Proteus 7.1..
if i try the program without ADC, the RC motor can turn to the position i want... |
|
|
Matro Guest
|
|
Posted: Tue Apr 22, 2008 7:16 am |
|
|
Try to replace
Code: |
output_high(SERVO1);
delay_us(450+((1023-ADC)*1.98));//initial 0deg
output_low(SERVO1);
delay_us(19550-((1023-ADC)*1.98)); //initial 0deg
|
By
Code: |
output_high(SERVO1);
delay_us(450+((1024-ADC)*2));//initial 0deg
output_low(SERVO1);
delay_us(19550-((1024-ADC)*2)); //initial 0deg
|
To see if it works.
Matro. |
|
|
arawana
Joined: 06 Apr 2008 Posts: 19 Location: SINGAPORE
|
signal |
Posted: Tue Apr 22, 2008 7:21 am |
|
|
from the signal i see in oscilloscope.. i use program matro write..
the frequency is change when the ADC change..
from what i learn, the position of RC motor only change due to the change of duty cycle and the freq still remain same (50Hz)
here is a picture at 0V=1 (ADC value)
here is a picture at 5V=1023 (ADC value)
but for Telmah program [i double slash the printf("\f Time: %5.3Lw",time);]
..the PWM signal i get .. when the duty cycle getting bigger n bigger suddenly it will turn small again and then bigger and bigger again when i increase the voltage... same when i decrease the voltage... |
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 22, 2008 7:37 am |
|
|
It sounds as if you must have a very old compiler. The '%w' format as existed for a couple of years...
This might be part of the problem, since some older compiler have major issues with the initialisation of some parts of particular chips.
What is the compiler version?.
Just substitute:
printf("\f Time: %5Ld",time); //Display the time in uSec
And see what numbers you are getting.
Best Wishes |
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 22, 2008 7:46 am |
|
|
Mine is hiccuping, because of a missing cast. The maths line needs to be:
time=((int32)(1023-ch1_result)*99)/50; //This generates the required
With this, and using the normal numeric output, it should work.
Best Wishes |
|
|
arawana
Joined: 06 Apr 2008 Posts: 19 Location: SINGAPORE
|
|
Posted: Tue Apr 22, 2008 7:51 am |
|
|
i use ide version 3.4 PCB PCM PCH 3.18
after i change %5Ld , the value i get,
0.00V = 712
0.51V = 508
1.02V = 302
1.53V = 97
2.03V = 1201
2.54V = 995
3.05V = 790
3.56V = 584
4.07V = 378
4.58V = 172
5.08V = 0 |
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 22, 2008 7:59 am |
|
|
Yes. The error will be at 1.796 volts, 'going downwards', without the arithmetic cast given.
Best Wishes |
|
|
arawana
Joined: 06 Apr 2008 Posts: 19 Location: SINGAPORE
|
still cannot work.. |
Posted: Tue Apr 22, 2008 8:00 am |
|
|
i have change this line but still the duty cycle increase and turn small again and increase again..
Code: |
time=((int32)(1023-ch1_result)*99)/50; //This generates the required |
RC servo not move.. only stay at 90deg...
is your simulation work Ttelmah? or u have try it on hardware? |
|
|
Matro Guest
|
|
Posted: Tue Apr 22, 2008 8:03 am |
|
|
Matro wrote: | Try to replace
Code: |
output_high(SERVO1);
delay_us(450+((1023-ADC)*1.98));//initial 0deg
output_low(SERVO1);
delay_us(19550-((1023-ADC)*1.98)); //initial 0deg
|
By
Code: |
output_high(SERVO1);
delay_us(450+((1024-ADC)*2));//initial 0deg
output_low(SERVO1);
delay_us(19550-((1024-ADC)*2)); //initial 0deg
|
To see if it works.
Matro. |
Did you try this tip?
To be applied on the original code I gave. ;-)
Matro. |
|
|
arawana
Joined: 06 Apr 2008 Posts: 19 Location: SINGAPORE
|
|
Posted: Tue Apr 22, 2008 8:13 am |
|
|
yes i have change the code that Matro gave..
The ADC change but the the RC motor stay at 90deg.. |
|
|
Matro Guest
|
|
Posted: Tue Apr 22, 2008 8:21 am |
|
|
I think there is a mistake in your calculation.
Could you try this code :
Code: |
#include <16F877A.H>
#device ADC=10
#use delay (clock=20000000)
#use rs232 (baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#FUSES HS, NOWDT, NOPROTECT, BROWNOUT, NOLVP
#define servo1 PIN_b0 //pin 38
#BYTE ADCON0 = 0x1F
#BIT ADFM = 0x9F.7 //used to choose ADC result justification
int16 ch1_result;
double ADC;
int i;
void main()
{
setup_adc_ports(ALL_ANALOG); // SETTING ADC CHANNEL // A0 A1 A2 A3 A5 E0 E1 E2 Ref=Vdd
setup_adc(ADC_CLOCK_DIV_32);
ADFM = 1; //1 for ADC result to be right-justified, 0 for left-justified
while(1)
{
set_adc_channel(1); //A1
ch1_result = read_adc();
ADC=ch1_result;
printf("\f ADC: %3.2f",ADC);
delay_ms(500);
for (i=0;i<50;i++)
{
output_high(SERVO1);
delay_us(2000+ADC));//initial 0deg
output_low(SERVO1);
delay_us(3024-ADC)); //initial 0deg
}
}
}
|
Matro |
|
|
|
|
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
|