|
|
View previous topic :: View next topic |
Author |
Message |
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Jun 03, 2015 7:12 am |
|
|
I find it difficult to give you advice on which route to go. It depends on all other tasks you have left to do for the project. 40 days isn't a lot, especially when you have to order new components.
dsPIC are great processors and with all the hardware peripherals they can offload a lot of work from software to the hardware. Your program will be smaller and run smoother but the large number of configuration options will make it more difficult to figure out the right settings.
The dsPIC is less used on this forum as it requires another compiler than the PIC16, making it expensive. Are you free to choose any dsPIC you want? For example the dsPIC30F2020? |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Wed Jun 03, 2015 7:35 am |
|
|
the 18f46k22 will be a pin for pin replacement for your older 16f pic.
use a 16mhz crystal with the PLL x4 and you are in 64mhz clock land.
this rate will divide nicely to give a clean 2 or 4 usec output resolution in a 1:16 prescaled timer based state machine core. should be fast enough for what you are doing.
with the above hardware setup you are running at 16 I-cycles/micro-second.
more important - if you use DIGITAL hobby servos - they typically do NOT need the oft quoted 50hz "refresh" - thus enabling set and forget ONLY when a sensor input changes.
the 18f46k80 is also pin for pin but has a 12 bit A/D which could benefit the ultimate resolution of your device - since with 1 part in 4096 position sensing and 2 usec servo resolution - "smooth" might just become possible :;-)):
you need to rework your circuitry to have a pic output pin per servo you wish to control - ideally all on port B or D . Port D would might be best-- while leaving enough of your A/D capable inputs free - OR use an external analog multiplexer with ONE analog in pin on the pic.
P O S T your present schematic so you can be helped along ok ?? |
|
|
gaugeguy
Joined: 05 Apr 2011 Posts: 306
|
|
Posted: Wed Jun 03, 2015 8:29 am |
|
|
The 18F46K80 is not quite pin-pin compatible. RA4 is lost for a VDDcore/Vcap pin that requires a capacitor as described in the data sheet.
Care must also be taken with the A/D in the K80 due to the significant errata. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Mon Jun 08, 2015 8:57 am |
|
|
You will have to make a decision on the processor you are going to use.
Thing is, we still have no clue as to what your resources are. Some people on this forum come from large companies and can order any PIC processor they want to. Other people come from poor countries where no processor can be ordered and they have to do with the ancient 16F877a the school is providing.
The 16F877a is an old workhorse. Simple but limited. New processors are better in all respects and cheaper as well. But if you have to, it is possible to use some tricks and squeeze a few software PWM's at low resolution into it.
Doing PWM in hardware is the easiest software solution and would be my recommendation for any professional project, then the mentioned dsPIC is a good option. The earlier mentioned PIC16 with PWM is also an option but PIC16 have some hardware limitations that I'm not happy with.
For software PWM I recommend choosing a high performance PIC18 if possible. Choose the mentioned PIC18 if pin compatibility is an issue but otherwise you are free to choose any other model. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9310 Location: Greensville,Ontario
|
|
Posted: Mon Jun 08, 2015 2:48 pm |
|
|
I like the 18F46K22, kind of a Swiss army knife in PIC form !! It will handle the task of 8 RC servos without any problems.
Jay |
|
|
BLDD
Joined: 24 May 2015 Posts: 8
|
|
Posted: Tue Jun 09, 2015 3:40 am |
|
|
ckielstra wrote: | You will have to make a decision on the processor you are going to use |
Hello, yes i can use the pic18 but my problem is the delivery time/price.
To the dsPIC, i can get the board and the pickit3 to program. so if i use the dsPIC i can control the servos only using adc/pwm hardware, and i think its less complicated to do. what your opinion? i know most of people here don't use dsPIC, but in terms of programming and pwm configuration, can you help me? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19625
|
|
Posted: Tue Jun 09, 2015 4:39 am |
|
|
Here's a rather large 'hint'. You'll have fun working out what I'm doing though....
Code: |
#include <18F4620.h>
#device ADC=10
#device HIGH_INTS=TRUE
#fuses HS,NOPBADEN,NOMCLR,NOXINST,NOLVP,NODEBUG,NOWDT
#use delay(crystal=20000000)
#use RS232 (UART1, ERRORS, baud=9600, RECEIVE_BUFFER=16)
//Using crude serial program to update the required position
//using interrupt driven RX buffer (generated by compiler)
//demo program to control 8 RC servos on B0 to B7
//with positions defined as values stored in an array
//1 to 2mSec for 0 to 1249
#INCLUDE <ctype.h> //needed for type tests
int16 positions[8] = {625,625,625,625,625,625,625,625};
//Middle positions for all servos
#use fast_io(B) //fastest update for the servo bits
#define 50Hz 25000
//count for 50Hz
#INT_CCP1 HIGH //ensure this is not affected by other interrupts
void change(void)
{
//This is the main actual control code.
static int1 start=TRUE; //operation to perform
//Each time the CCP 'ticks', I have to either turn a pulse on or off
//and advance to the next pulse
//These values determine which servo to update on the pass
static int8 mask=1; //access mask for the servo
static int8 channel=0; //which RC servo to update
static int16 sum;
if (channel<7)
{
if (!start)
{
//Now advance to next channel
mask*=2;
channel++;
}
else
{
start=FALSE;
sum=0;
}
//here we need the start of the servo pulse
output_b(mask); //turn the servo pulse on
//Now time comes from the array
CCP_1=positions[channel]+1250; //1 to 2mSec
sum+=CCP_1;
}
else
{
//Finished all eight pulses
output_b(0); //turn off the servo pulses
mask=1;
channel=0;
start=TRUE;
//now longer delay to give approx 50Hz
CCP_1=50Hz-sum;
//remaining time for 50Hz
}
}
void main()
{
int8 channel;
char temp;
enum {chan_letter,number} state=chan_letter;
int16 value; //temporary variables used for serial interpreter
setup_timer_1(T1_INTERNAL | T1_DIV_BY_4); //master timer for timings
setup_ccp1(CCP_COMPARE_RESET_TIMER); //CCP used to control servos
//Master CCP clock is 20000000/(4*4) = 1.25MHz
//servo pulses are 1-2mSec wide, so 1250 to 2499 counts
clear_interrupt(INT_CCP1);
set_timer1(0);
CCP_1=10; //force an almost immediate interrupt with clock reset
output_b(0);
set_TRIS_B(0); //drive all the servo pins low
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL); //and start servo pulses
//Now interpret serial to position servos
//commands are Annnn<LF> to set the position of the first servo, to
//Hnnnn<LF> for the last (upper or lower case)
while(TRUE)
{
if (kbhit())
{
//have data
temp=getc();
switch (state)
{
case chan_letter:
temp=toupper(temp); //ignore case
//looking for alpha character to determine channel to set
if (temp>='A' && temp<='H')
{
channel=temp-'A'; //channel number to update
state++; //start looking for number
value=0;
}
break;
case number:
//only accept numeric characters or LF
if (temp==13)
{
//line feed - now number needs to update position
//momentarily disable interrupts
if (value>1249)
value=1249; //ensure cannot go beyond 2mSec
disable_interrupts(GLOBAL);
positions[channel]=value;
enable_interrupts(GLOBAL);
//ensures you don't get an erratic position as the value updates
state=chan_letter;
}
if (isdigit(temp))
{
//digit
value*=10;
value+=temp-'0'; //get numeric value of digit
}
else
{
//abort if invalid character
state=chan_letter;
}
break;
}
}
}
}
|
Setup to run on a PIC18F4620, at 20MHz. It can be tweaked for almost any similar PIC.
This takes serial data and puts it to the 8 servos. Nice thing is though that the chosen resolution (0-1249), if fed with a 10bit ADC, will give about 80% of the servo full scale. |
|
|
BLDD
Joined: 24 May 2015 Posts: 8
|
|
Posted: Tue Jun 09, 2015 1:34 pm |
|
|
Hello,
Tomorrow i'll get my dspic, it seems the best solution at the moment! i'll get dsPIC33FJ128GP706 and the pickit3, so my project will work with the adc/pwm like i sayd before. to this i need to use the math to adapt my adc scale to the servo delays scale!
but after i present my project i'll stay here to learn more and more :D
my goal will be make it work with only software to improve my knowledges
Regards |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Jun 09, 2015 2:40 pm |
|
|
I'm just wondering if we are not overcomplicating things.
You've got a program working for one servo that you are happy with.
The more complicated setups are required when you have to do asynchronous tasks next to the PWM creation. For example, many application require a serial port communication which has to be handled quick or otherwise received characters will get lost.
If you don't have these asynchronous requirements then there is another easy setup possible by just multiplying your original program for as many motors as you need (maximum about 10).
Code: | #include <16F877a.h>
#FUSES HS, NOWDT, PUT, NOLVP
#use delay(clock=4MHz)
void readAdcAndSetMotor(int8 adcNr, motorPin)
{
int16 value;
int16 servodelay;
set_adc_channel(adcNr);
delay_us(20);
value = read_adc();
servodelay = servodelay*.8+(value*2.8+250)*.2;
output_high(motorPin);
delay_us(servodelay);
output_low(motorPin);
}
void main()
{
setup_adc_ports(AN0_AN1_AN4_AN5_AN6_AN7_VREF_VREF);
setup_adc(ADC_CLOCK_DIV_8);
while(TRUE)
{
readAdcAndSetMotor(0, PIN_B1);
readAdcAndSetMotor(1, PIN_B2);
readAdcAndSetMotor(4, PIN_B3);
readAdcAndSetMotor(5, PIN_B4);
readAdcAndSetMotor(6, PIN_B5);
// The cycle should repeat about every 20ms, but will accept a large spread.
// With 5 motors and approx. 2ms per motor we have left: 20ms - (5 x 2ms) = 10ms
delay_ms(10);
}
} | This crude program should work but can be tweaked a lot further, for example by replacing the float calculation through 'scaled integers' for higher performance. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19625
|
|
Posted: Wed Jun 10, 2015 2:20 am |
|
|
I think it might be worth explaining a little.
Historically the 'RC servo' standard came about quite a while ago, with the earliest (post escapement) 'proportional' radio systems.
The way these worked, was that there was just a tone, sent over an AM radio. It was done a bit like Morse. There was a pause in the tone, then a sequence of bursts of tone. The receiver looked for the 'pause', and reset a counter when this was seen. Then on the first burst of tone after the pause, it sent a '1' to the first servo output, whose duration matched the tone. When the tone again stopped, it advanced a counter, and a pulse for the second burst, was sent to the second servo. This was repeated for all the servos involved. Then there was the long pause to start the whole sequence again.
Each servo, just receives a single logic pulse, whose duration controls the position it moves to. This comes at an interval depending on how many channels the radio has, and the total gaps between the pulses.
Now at the start there were several vaguely similar standards, with different tone durations, and different pause lengths, but manufacturers eventually standardised on 1.5mSec +/- 0.5mSec for the pulse period. The '50Hz' (often quoted), was never actually part of the standard. This corresponded to how often a servo would receive a new pulse, and typically the sequence was 2.5mSec for each servo (allowing a maximum 2mSec pulse with a 0.5mSec pause for the radio to detect the end of this), times the number of channels (typically 4 to 6), and then the longer pause to allow the counting to restart. This gave a total around 20mSec (hence 50Hz). However early radios that implemented more channels, then had longer frames. Some systems offered up to 10 channels, and with a 5mSec pause between the frames, the actual repetition rate to the servos, was down at around 30Hz. On early servos, a 'minimum' of around 25Hz, was typically suggested, with some units tending to creep/jitter if the rate went very much lower (also a few implement 'fail safe' operations if a gap over a second or two is met - while not common, things like sail winches, sometimes do this). Now with the creation of digital radios, with faster links, exactly the same pulse, was repeated ASAP to the servos, and the repetition rate climbed to 400Hz. Still exactly the same actual pulse.
Very low repetition rates slow down how quickly the 'position' can be updated. This is why multi-copters in particular typically run the update at the 400Hz rate, since the actual update is being done based on local calculations by the navigation system, and needs to be able to correct quickly.
Anyway, the key point is that the actual repetition rate, doesn't matter at all. Modern servos will typically accept rates reliably, anywhere between 1Hz, and 400Hz, without a murmur. It is the pulse width that matters.
So the point being made by Ckielstra, is to not worry about the repetition rate at all. Simply use your existing approach, and repeat it *8.
So read first ADC.
Calculate time.
Generate pulse for servo 1.
Read next ADC.
Calculate time for this one.
Generate pulse for servo 2.
Repeat for as many servos as needed.
Since you have the code already working for one channel, this should work fine for you, and is simple to code. The timings will vary as ADC readings change, but this doesn't matter at all.
The approach I posted, shows instead how to approach the problem, if the pulses must be synchronous, but other operations need to be carrying on with their own timings. As posted, it does not care whether positions are changed every 10 mSec, or every 10 minutes. It'll merrily carry on generating a stable pulse train whatever the main code is doing. Ideal for something like BLDC control. |
|
|
BLDD
Joined: 24 May 2015 Posts: 8
|
|
Posted: Thu Jun 11, 2015 1:12 am |
|
|
Thank you, it really helps me!! But I'm a little ambitious and i always want more and more, so its possible, using maths or something to create the scales? My math formula to define the servo delay gives us a little random position. Can i define my min adc value = min servo delay value and the same with the max values? i want to make the adc read more proportional with the delay assigned!
Regards |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19625
|
|
Posted: Thu Jun 11, 2015 8:40 am |
|
|
Yes.
However this is where skill is really needed. You can calculate the output position (generally), as:
With values
ADC_minimum, ADC_maximum, Output_minimum, Output_maximum
and ADC_reading.
ADC_span= ADC_maximum-ADC_minimum
ADC_position = (ADC_reading-ADC_Minimum)/(ADC_span)
Output_span=Output_maximum-Output_minimum
output_value = (ADC_position*Output_span)+Output_minimum
Now the two 'span' values can be calculated in advance when you have calibrated the system.
However the 'ingenious' users, then realise that the values all depend on how you have the system setup. For instance on my servo code, the number of 'steps' over the range, depends on the clock rate being fed to the timer. You can select a different crystal, a different pre-scaler value, or even use a PWM output, to feed the timer input, to give you programmability of the number range involved. Similarly the values from the ADC, depend on what you choose for Vref- and Vref+. Now adjusting the values could (for instance) allow you to have a 'span' in the first calculation, that is a binary value (256, 512 etc.). Division by these values is faster than division by a normal number.
There are also some more ingenious ways to save time (binary scaled integer arithmetic in particular). However one solution on a PIC that has a reasonable amount of RAM, is simply to pre-calculate. Once you have your calibration points for ADC_minimum, ADC_maximum, and the required Output_minimum, and Output_maximum values, simply calculate the output that needs to be generated for every ADC input value between the minimum and maximum. The calculation can be done using float (slow), but then once you have the numbers, stored in an int16 array. The calculation for something like 1000 points, will still only take perhaps a second. Then in future for every ADC value a single lookup in the array gives the output required. Faster and simpler than doing the calculations as needed..... |
|
|
|
|
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
|