Jump to content

JF1980

Trusted Member
  • Content Count

    31
  • Joined

  • Last visited

Community Reputation

0 Neutral

About JF1980

  • Rank
    RC-Cam'er
  1. JF1980

    Trying to control servos with PIC

    Not quite sure where I asked for a password?!? Apparently MikroC Pro supports the 16F726; the free version is still good for up to 2K of program code. I'll just go with the 16F882, it has all the same features and is cheaper.
  2. JF1980

    Trying to control servos with PIC

    It seems neither MikroC nor Proteus support the 16F726 so I'm going to look at porting my current code to the 16F822 instead.
  3. JF1980

    Trying to control servos with PIC

    Still having problems with this, it's fine if I use a servo time of 0.6 - 2ms but if I go above 2ms then the next servo (say servo 1 if I am moving servo 0) off-sets by a small amount. Think I'm going to be best off ditching the 16F690 and looking for something similar with two CCP modules. The 28 pin 16F726 looks like a good match.
  4. JF1980

    Trying to control servos with PIC

    It's just not possible to use a whole port for servo output on a chip this size whilst still using the com ports. I know this is a dirty fix but it does the job: if (servostatus == 1) { // CCP1 Special Compare Event Interrupt // servo output on code here activeservo++; activeservo = activeservo & 7; //move to next servo switch (activeservo) { case 0 : { S1 = 1; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; servo[activeservo] = ComShadowServo[activeservo] + 10; // copy servo position from Comms array //CCPR = servo[activeservo]; // load CCPR compare value //offtime = 2500 - servo[activeservo]; // calculate off-time } break; case 1 : { S1 = 0; S2 = 1; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; servo[activeservo] = ComShadowServo[activeservo] +13; // copy servo position from Comms array //CCPR = servo[activeservo]; // load CCPR compare value //offtime = 2500 - servo[activeservo]; // calculate off-time } break; case 2 : { S1 = 0; S2 = 0; S3 = 1; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; servo[activeservo] = ComShadowServo[activeservo] + 17; // copy servo position from Comms array //CCPR = servo[activeservo]; // load CCPR compare value //offtime = 2500 - servo[activeservo]; // calculate off-time } break; case 3 : { S1 = 0; S2 = 0; S3 = 0; S4 = 1; S5 = 0; S6 = 0; S7 = 0; S8 = 0; servo[activeservo] = ComShadowServo[activeservo] + 21; // copy servo position from Comms array //CCPR = servo[activeservo]; // load CCPR compare value //offtime = 2500 - servo[activeservo]; // calculate off-time } break; case 4 : { S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 1; S6 = 0; S7 = 0; S8 = 0; servo[activeservo] = ComShadowServo[activeservo] + 25; // copy servo position from Comms array //CCPR = servo[activeservo]; // load CCPR compare value //offtime = 2500 - servo[activeservo]; // calculate off-time } break; case 5 : { S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 1; S7 = 0; S8 = 0; servo[activeservo] = ComShadowServo[activeservo] + 29; // copy servo position from Comms array //CCPR = servo[activeservo]; // load CCPR compare value //offtime = 2500 - servo[activeservo]; // calculate off-time } break; case 6 : { S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 1; S8 = 0; servo[activeservo] = ComShadowServo[activeservo] + 33; // copy servo position from Comms array //CCPR = servo[activeservo]; // load CCPR compare value //offtime = 2500 - servo[activeservo]; // calculate off-time } break; case 7 : { S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 1; servo[activeservo] = ComShadowServo[activeservo] + 37; // copy servo position from Comms array //CCPR = servo[activeservo]; // load CCPR compare value //offtime = 2500 - servo[activeservo]; // calculate off-time } break; } //servo[activeservo] = ComShadowServo[activeservo]; // copy servo position from Comms array CCPR = servo[activeservo]; // load CCPR compare value offtime = 2500 - servo[activeservo]; // calculate off-time } I'm going to take a look at SPI and I2C after which I'll have a think about controlling servo speed. With regards to the control protocol; it does strike me that in MCU/MCU communications it would be better to use binary rather than ASCII, however any operation with programs like HyperTerminal etc is out of the window as it they only send ASCII codes. Using ASCII as I currently am requires 5 bytes be sent rather than 3 bytes in binary (ServoNum, ServoPos1, ServoPos2, ServoPos3, ServoPos4 vs ServoNum, PositionH, PositionL). Don't think that will be too much of a problem. With regards to I2C, so far as I understand each I2C device should have a unique address, this is how multiple devices can share the same BUS. How is this dealt with in a production run where multiple units are being built? Does each PIC chip already have a unique address pre-programmed? How would it be known? Does a unique HEX need to be compiled for each device before programming?
  5. I use 2.4Ghz radio so have been considering 1.3Ghz or 900Mhz video transmitters. Any reason you want to stick with 2.4Ghz for video?
  6. JF1980

    Trying to control servos with PIC

    Agreed, the fact that the servo offset increases with each servo also leads me to believe that something is delaying with increasing effect on each servo update cycle. I can only imagine this is the switch statement. Here is the full ISR code block. The PIC is running on the internal ocs running at 4Mhz. It's capable of 8Mhz but I figured I don't really need 8 Mhz and the PIC would use possibly less power if running at half the frequency. void interrupt(void) { servostatus = !(servostatus); if (servostatus == 1) { // CCP1 Special Compare Event Interrupt // servo output on code here activeservo++; activeservo = activeservo & 7; //move to next servo switch (activeservo) { case 0 : { S1 = 1; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; } break; case 1 : { S1 = 0; S2 = 1; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; } break; case 2 : { S1 = 0; S2 = 0; S3 = 1; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; } break; case 3 : { S1 = 0; S2 = 0; S3 = 0; S4 = 1; S5 = 0; S6 = 0; S7 = 0; S8 = 0; } break; case 4 : { S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 1; S6 = 0; S7 = 0; S8 = 0; } break; case 5 : { S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 1; S7 = 0; S8 = 0; } break; case 6 : { S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 1; S8 = 0; } break; case 7 : { S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 1; } break; } servo[activeservo] = ComShadowServo[activeservo]; // copy servo position from Comms array CCPR = servo[activeservo]; // load CCPR compare value offtime = 2500 - servo[activeservo]; // calculate off-time } else { // Turn Off All Servo Outputs S1 = 0; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; CCPR = offtime; } PIR1.CCP1IF = 0; } I'm not sure I want to write the ISR in assembly but something is obviously effecting the timing. Actually -1' offset on the servo can be equated to 10uS, so I can centre the servos by setting them to 1510,1513,1517,1521,1525,1529,1533,1537. I could allow for this offset in code, but, that would be rather messy. I'm going to logically run through the code and see if there's a different way of manipulating the CCPR so that the switch statement doesn't effect it. Maybe there is a better way of dealing with the servo outputs being spread across multiple ports? With all servos on the same port it is easy to use a left bitshift to rotate the active output. Here you can see the servos centred by manually setting the offset timing: I'm wondering whether servo speed control will be worth the hassle, there's a lot of calculation for the PIC to do. For flight control I think maximum speed would always be required.
  7. JF1980

    Trying to control servos with PIC

    I've gotten quite some way towards building a 16F690 based servo control board. I'm using 1 CCP module to drive the servos and am currently only working with USART. I have kept the SPI/I2C pins free and will implement them once everything else works as it should. I've based my theory on the mikroBasic example here (second example from top uses 1CCP, the first uses 2CCP's). My data is sent over the serial link as ASCI characters as my terminal emulator will only allow me to send ASCI. I did post my questions over there but the forum is pretty dead and it can take weeks for people to come up with a suggestion. There are two issues at the moment: 1. Servos off-centre with 1CCP driver I've written example programs for 1 and 2 CCP operation. I've physically built and tested the 2 CCP example using the 16F877A on my development board and it seems to work fine. I would like to build a small servo driver board so have gone on to build a 1 CCP driver for the 16F690. I have written the code and set up a circuit design in Proteus VSM for testing as it saves a lot of messing around and allows for rapid debugging / development. It would appear to work ok, however, I've noticed that when loaded with a duty cycle of 1500us the servos are all off-centre, by increasing amounts ( -1.00', -1.30', -1.70', -2.10', -2.50', -2.90', -3.30', -3.70'). I can send a serial command to servos 0 and 7, setting them both to 2000us duty and they are still off-set (+49.0', +46.30'). Setting the duty cycle of each back to 1500us returns the servos to their off-centre positions. The virtual servos are configured for a range of 600 - 2400us = -+90'. I have set up a virtual copy of my 2 CCP servo driver and the virtual servos show dead centre (1500us = 0.00') so it doesn't seem to be an issue with Proteus. It looks like the timing is out, maybe because of calculations being made in the ISR. Due to the servo outputs being spread over multiple ports I had to write a switch statement setting the appropriate servo outputs: // servo output on code here activeservo++; activeservo = activeservo & 7; //move to next servo switch (activeservo) { case 0 : { S1 = 1; S2 = 0; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; } break; case 1 : { S1 = 0; S2 = 1; S3 = 0; S4 = 0; S5 = 0; S6 = 0; S7 = 0; S8 = 0; } break; etc etc etc I fear this may be causing too much of a delay in the ISR but can't really see another way of doing it. The strange thing is that the Proteus oscilloscope shows all of the servo control lines with the correct 1500us on cycle. 2: Controlling servo speed I've set up serial control on my 1CCP F690 project. It works fine. I first send the servo number, then a four digit position value (to move servo 1 across its range: 10600, 11500, 12400). I now want to also have control of the speed at which the servo moves. I understand how servo speed was controlled in the 2CCP example, with a delay being added in the loop to slow down the rate at which the duty cycle is incremented. Getting the servo to move at a constant speed seems a bit more complicated for real life applications as the latest position requested could be either very close to the current position or very far away, so the delays need to be adjusted depending on the difference between the current position and requested position. Also implementing this delay via a loop may impede the movement of other servos. I'm not quite sure how it will be achieved. Ideally I would set a servo speed of 0-255 independently from requesting it's position, serial input being something like: S(servoID)(servo speed 000-255) e.g. S1255 P(servoID)(servo position 0600-2400) e.g. P11500 I would really appreciate suggestions on a sane and practical way to control the servo speed
  8. JF1980

    Trying to control servos with PIC

    Very much so! Now I've got the basics covered I'm going to become a little more ambitious. I would like to build a servo control board so that the timers/CCP in the main MCU will be free for other things. I have some 16F690 samples which would seem to be ideal with one CCP (will modify the code to use one CCP module) and USART/SPI/I2C capabilities. I was wondering what the preferred protocol would be? There seem to be a lot of sensors using SPI so that would seem like the logical choice as I could share all but the CS line, however I'm not sure that it's fast enough? I haven't seen any commercial servo control boards using SPI or I2C, only USART. I suppose it wouldn't be too much bother to use a jumper and two extra pins for protocol selection; just don't want to waste my time if SPI and I2C are unsuitable. It would be nice to leave USART free for GPS etc if I can. I did search the forum for SPI and I2C with no results, maybe due to a minimum letter count restriction?
  9. JF1980

    Trying to control servos with PIC

    You're right Mr RC, after one rotation Servo_Output is 0b00000000 because I forgot to reset it. Threw a temporary 'if' statement in there and now the code works. The original code I used as a reference was written in Basic and targeted at the 18F chips. It uses an ASM bitshift instruction which rotates the bits in the register. I couldn't find a similar instruction for the 16F877A and forgot to reset the register after a complete cycle. Thanks for looking over it, this has been driving me crazy for the last 24 hours!
  10. JF1980

    Trying to control servos with PIC

    Thanks for the fast reply. I did think I can do this with one CCP, once this is working I'll modify it to use one CCP and run on a 628A or something similar. You're right in saying that once an interrupt is triggered, a conditional statement selects the desired course of action depending on the trigger. I've checked it with the debugger and it seems to work fine in theory, I can check on the various registers and see the expected numbers. Good call on using the LCD, it won't be much more bother than using the LED's for diagnostics and will give me more specific information. I did place commands to turn an LED on at various stages so can confirm that it's running through both sets of interrupt routine. No oscilloscope I'm afraid. My multi-meter has a Hz setting with 2000 or 20000 range options. Don't think that'll be much use.
  11. I'm trying to learn how to drive servos using a PIC 16F877A. My project is trying to drive up to 8 servos on PORTB using two CCP modules; I have the servo signal wire connected to the PIC output pins with the servos getting 5v from a standard 4.8v RX pack. I have connected the -ve battery terminal to my proto board so that the servos share a common ground. My code is written in MikroC: /* * Project name: MoveServo1 * Description: Move Servo with TMR0/1 Interrupts. * Test configuration: MCU: PIC16F877A Dev.Board: EasyPic4 Oscillator: HS, 08.000 MHz Ext. Modules: - SW: mikroC v7.0 * NOTES: Drive up to 8 servos on PortB using CCP1, CCP2 and TMR1 Configure TMR1 for 1us ticks CCP1 is set up for special event on match with TMR1 CCP1 is loaded with servo cycle length = 20000us / number of servos; 2500us for 8 servos. It triggers an interrupt and resets TMR1. CCP1 triggers every servo cycle resetting TMR1, moves program to next servo via single left bitshift, turns turns that servo on. CCP2 is set up for a normal event which does not reset the timer. CCP2 value is loaded from the servo position array when the servo is switched on via CCP1 interrupt service. CCP2 triggers when the servo-on cycle has been completed. CCP2 interrupt service switches off servo output (all of PORTB) and clears the CCPIR2.0 interrupt flag. */ // Define variables #define ServoOnIntEnable PIE1.F2 #define ServoOnFlag PIR1.F2 #define ServoOffIntEnable PIE2.F0 #define ServoOffFlag PIR2.F0 //#define ServoPeriod CCPR1L //#define ServoDuty CCPR2L unsigned int volatile ServoPeriod absolute 0x0015; // CCPR1L unsigned int volatile ServoDuty absolute 0x001B; // CCPR2L unsigned int Servo[8]; // servo position array unsigned int xx; // var for test loop unsigned short Servo_Output; // Shadow register for Output unsigned short sn; // Servo number, to ref Servo array // Define functions void init(void); // Main void main(void) { init(); while (1) { for (xx = 350; xx <= 2400; xx++){ // cycle servos Servo[0] = 500; Servo[1] = xx; Servo[2] = xx; Servo[3] = xx; Servo[4] = xx; Servo[5] = xx; Servo[6] = xx; Servo[7] = 2400; Delay_us(5500); } } } // Setup CPU void init(void) { PORTB = 0; // servo port is portB TRISB = 0; // set portB as output T1CON = 0b00010000; // prescaler = 2 = 1us ticks; timer1 off TMR1L = 0; // Timer1L starts at 0 TMR1H = 0; // Timer1H starts at 0 CCP1CON = 0b00001011; // CCP1 triggers special compare event on Timer1 match CCP2CON = 0b00001010; // CCP2 normal compare event on Timer1 match ServoPeriod = 2500; // special event interrupt every 2500us; jumps to ServoON ISR Servo_Output = 0b00000001; // servo outputs start at PORTB.F0, then shift and rotate sn = 0; // start with servo(0) INTCON = 0b11000000; // enable GIE and PEIE ServoOnFlag = 0 ; ServoOffFlag = 0; ServoOnIntEnable = 1; ServoOffIntEnable = 1; T1CON.F0 = 1; // start Timer1 } // Interrupt void interrupt(void) { if (ServoOnFlag == 1) { // CCP1 Special Compare Event Interrupt PortB = Servo_Output; // Turn On Next Servo ServoDuty = Servo[sn]; // Active Servo Value loaded in CCPR2 Servo_Output = Servo_Output << 1; // Prepare Servo_Output for next cycle with bitshift sn++; sn = sn & 7; ServoOnFlag = 0; } else { // CCP2 Normal Compare Event Interrupt PORTB = 0; // Turn Off Active Servo ServoOffFlag = 0; } } When I power the circuit on the servos jump which I would put down to static (the same as they do when connecting RX power with the transmitter off), then nothing. They can be moved by hand so are not holding a static position, it's as if they aren't seeing the PWM signal. If I run through the code with a software debugger then everything looks like it works fine. I have to simulate interrupts by changing PIR1 to 4, following it through the interrupt routine, then setting PIR2 to 1 and repeating the process. I can watch all of the variables change and the program seems to be working as it should. PORTC has a bank of LED's so I've mirrored various registers to see if the PIC is actually doing anything. If I mirror CCPR2 or xx then I can see it is. Mirroring PORTB doesn't do anything but I'm guessing that the output is on for too short a time for the LEDs to emit any light. Any ideas of what could be wrong? It's very frustrating!
  12. JF1980

    Which MCU?

    Hi all, I've been lucky enough to have a bit of time to start playing around with my electronics gear. I would like to make my own tilt/pan head tracker and later on work towards autonomous flight. Currently I have an EasyPIC4 development board which will allow me to play with any PIC up to the 18F range. I've had it for years and have had an active interest in PIC's but very little time to get hands-on. So far I've experimented with decoding a servo signal from a R/C receiver (nav lights project) and taking readings from a two-axis accelerometer then outputting them on an LCD display. I'm now attempting to cleanly drive 1 - 2 servos; something which sounds a lot easier than it actually is! After browsing various forums and looking at the kits on offer from SparkFun, I wonder whether a PIC is the correct tool for a UAV type project. I have noticed that quite a few projects are using the Phillips LPC2148 ARM7 chip. From browsing the examples it looks like driving servos etc is much easier with this chip using the built in PWM functions. They also look to have a lot more processing power, and a free GNU-C compiler. I'm wondering whether I should switch to ARM MCU's now rather than waste time becoming closely accustomed to PIC's, only to find down the line that they aren't powerful enough and I need to switch to something else. I had considered that using PIC chips I would probably want to break everything in to modules, each with it's own MCU. So the IMU, Data Comms, Servo Control, Sensor Arrays (airspeed, pressure altitude, temp etc) would all have their own PIC chip which would feed data back to a central flight control MCU. The problems I can see there are power consumption, weight, space and limited bandwidth for comms between processors. I guess after all of that, my main question is, which MCU do you use in your UAV and hobby projects? Do other MCU's offer significant advantages over the PIC range?
  13. JF1980

    Capture and decode RX signal

    Just understood what you meant. I think you missed the point of this project. It was for me to learn how to interface a PIC and R/C system using MikroC. Sure I could have just copied someone else's design and code but that would have defeated the point. I could have saved the effort and brought a set off eBay for £10 if all I wanted were NAV lights
  14. JF1980

    Audio track as modem data NMEA

    Why not encode the GPS data along with other data like airspeed, temp, current draw etc. It would seem to be a waste, streaming the GPS data by itself.
×