Hardware ESC 8xSERVO CONTROL on PIC (Oshonsoft BASIC)

Status
Not open for further replies.
The error says it's not defined as an array.
Try,
Code:
dim varptr() as word

Mike.
Edit, that won't work, It'll just be an array of words not a byte pointer. Do you have a link to somewhere that discusses pointers?
Hi M,
Here's where the original #INCUDES would be handy
I couldn't find any reference to POINT, but in OSH it has POINTER.
I wrote a program that appears to work using POINTER.
NOTE: Even though I wrote it, It will take me a while to understand how it works, especially in the SERVO program
C.
 

Attachments

  • Pointer 200123.jpg
    344.7 KB · Views: 137
Last edited:
Hi,
I've updated the program and commented it the best I can.
I'll have to go through it a few times, while getting used to POINTERS, and try to check the comments are correct.
It compiles, so now I can watch it in the SIM

I can see errors e,g [ ccp1out ] plus others, but that's it for today.
Here's the program:
C
 

Attachments

  • 18F4431 32MHz XTL REMOTE_SLAVE POMMIE BASIC 200123 1300.bas
    3.9 KB · Views: 138
In the original C code there was this:
Code:
    unsigned char *point=(unsigned char *)&RB0PPS;
    point[servoCount]=0;    //release the old CCP1 output
What that does is create a pointer to the RB0PPS register, and then as servoCount is incremented
it sets the RBxPPS register to 0, remapping the pin back to the LATx.

It relies on the RB0PPS to RB7PPS registers being located in sequential order in the register map.
point[0] = addressof(RB0PPS)
point[1] = addressof(RB1PPS)
point[2] = addressof(RB2PPS)
etc

This statement:
Code:
point[servoCount]=9;    //setup new CCP1 output
is responsible for mapping the RBxPPS to the CCP1 output


The equivalent code would be something like:
Code:
dim servoPPS as word

'Pointer(servocount) = servopos(i)
servoPPS = addressof(RB0PPS)
Pointer(servoPPS+servoCount) = 0

servoCount = servoCount + 1
If servoCount >= num_servos Then
    servoCount = 0
Endif

'Pointer(servocount) = 9  'Setup new CCP1 output
Pointer(servoPPS+servoCount) = 9
I have no idea what the correct syntax for that would be in OSH
 
Last edited:
Hi T,
I understand most of it, I'll give it a go.
Thanks.
C
 
I thought the chip you're using didn't have PPS so this code can never work. Does your chip have RB0PPS?

Mike.
 
I had some time today so rewrote the code using a similar chip to the one you're using. I only had an 18F2620 available but that is from a similar time to the one you're using and, most importantly, doesn't have PPS and seems to have similar SFRs.

Here it is,
Code:
#include <xc.h>
#include "config.c"
#include <stdint.h>
#define _XTAL_FREQ 32000000
#define NUM_SERVOS 8
#define FRAME 16000L    //how long each frame is

uint16_t servoPos[NUM_SERVOS];
uint8_t servoCount=0;
uint8_t servoTemp;

void main(void) {
    OSCCON=0b01110000;      //8MHz
    PLLEN=1;                //x4=32MHz
    T1CON=0b00000001;       //timer 1 clocks at
    CCP1CON=0b10001010;     //generate interrupt only
    CCP1IE=1;               //enable CCP1 interrupts
    for(uint8_t i=0;i<NUM_SERVOS;i++){
        servoPos[i]=i*1000+8000; //1ms(8000) to 1.875(7/8ths - 15000)ms in 1/8th mS steps
    }
    TRISB=0;                //all output
    LATB=0;
    servoTemp=0;
    PEIE=1;
    GIE=1;
    while(1){
            //adjust servo positions here
    }
}

void __interrupt() inter(void){
    PORTB=servoTemp;        //output as soon as possible to minimise jitter.
    if(CCP1IE && CCP1IF){
        if(servoTemp==0){    //which phase of the output are we in? True=just cleared output pin and started output gap
            //outputting the gap
            CCPR1=CCPR1+FRAME-servoPos[servoCount]; //add gap time
            servoTemp=1<<servoCount;                //make up what is required on port B NEXT TIME
            if(servoCount==7)
                servoCount=0;
            else
                servoCount=servoCount+1;
        }else{
            //outputting the pulse
            CCPR1=CCPR1+servoPos[servoCount];       //add on time to
            servoTemp=0;                            //next time clear port B
        }
        CCP1IF=0;
    }
}
The interrupt has the main changes. It precalculates what is output and immediately outputs that to port B to minimise jitter. The maximum jitter should be 1 clock cycle (125uS). I also avoided using += etc. so hopefully the A.I. you used might produce working code.

Mike.
Edit, and NO pointers too.
 
Hi M,
So fast :0

Your PIC looks to have the same pinouts and footprint, good.

Will you post the INCLUDES please, it would help a lot. I'm sure they will be different but a good guide.

Ok, let's A.I. it, thanks.
Note: Start again and gnore previous CODE!!
C
 
The includes are,
Code:
#include <xc.h>
#include "config.c"
#include <stdint.h>
XC.h is a standard include to define all the SFR etc.
stdint.h contains definitions for STAndard INTegers such as uint8_t.
config.c contains the config details,
Code:
// CONFIG1H
#pragma config OSC = INTIO67    // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = SBORDIS  // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
#pragma config BORV = 3         // Brown Out Reset Voltage bits (Minimum setting)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT enabled)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = PORTC   // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = ON      // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = ON       // MCLR Pin Enable bit (MCLR pin enabled; RE3 input pin disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = OFF        // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000800-003FFFh) not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (004000-007FFFh) not code-protected)
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (008000-00BFFFh) not code-protected)
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (00C000-00FFFFh) not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000800-003FFFh) not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (004000-007FFFh) not write-protected)
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (008000-00BFFFh) not write-protected)
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (00C000-00FFFFh) not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000800-003FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (004000-007FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks)

Hope it helps.

Mike.
 
Note that the comments in the config don't always match the code. I.E.
Code:
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT enabled)
Code=Watch Dog Timer off but comment says enabled.

Just me being lazy.

Mike.
Edit, I'll be interested to see what your A.I. come up with.
 
Hi M,
It's all a bit scrambled at the moment.
I've done the AI bit, and going through the CONFIGS.
There are a couple that don't match e,g,
#pragma config OSC = INTIO67 // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)
Where I'm using a XTL with RA6 and 7 input.
I'll post it once it's in some sort of order.
C
 
As long as your chip is running at 32MHz then the code should work. Obviously, the config needs to change. I just used the internal oscillator for convenience.

Post the A.I. code.

Mike.
Edit, I'm now thinking I got the times wrong for the servos, thinking the frame time should be 2.5mS (20,000 clock cycles). Easy to fix, just change FRAME to 20000.
 
Hi M,
Here's where I am at the moment:
I'm trying not to think too much and just copy like a parrot, but I noticed this:
[ 'config pbaden = On // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset) ]
Combined with [ TRISB=0; //all output ] but I'll look closer later.
You will notice duplicate, this is because I ran the AI a couple of times, for comarison.

Things will be clearer once it compiles.
C
 

Attachments

  • 18F4431 32MHz XTL REMOTE_SLAVE 210123 0901.bas
    6.9 KB · Views: 135
Hi M,
Here's the compiled program today:
It switches the first SERVO on then doesn't INTERRUPT again.
There maybe mistakes!
C
 

Attachments

  • 18F4431 32MHz XTL REMOTE_SLAVE 210123 1530.bas
    7.7 KB · Views: 138
  • SERVO2.jpg
    495.8 KB · Views: 137
That looks very much like the servo routine I posted a few months back - but it seems to have got badly mangled through the various translations; the initialisation, frame gap and pulse seequence parts are all mixed up..

In fact it was this thread, post #54, last may!

This is the servo control interrupt routine from that:

C:
#INT_CCP1
void ccp1_interrupt_routine(void) {
    //
    // This is executed at each new compare
    //
 
    shift_reg <<= 1; // Shift the bit left one place.
    // shift_reg = shift_reg + shift reg would do the same.
    output_c(shift_reg); // Write the shift register bits to the port

    // Increment index for next servo time value
    index = index + 1;
  
    if(index < 8) { // All servos already done?
        // No, do next servo
      
        // Set the duration for this output, as a new offset
        // from the last compare time.
        time_set = time_set + servo_times[index];
 
        CCP_1 = time_set; // Write the compare time to CCP 1 register.
      
    }
    else {
        // No output needed as the port is already updated.
        // output_c(0); // Turn off all servo bits.
 
        // Add a fixed time increment for delays before
        // restarting the sequence.
      
        time_set = time_set + 1500; // add 1.5mS for the next interrupt
        // Update CCP
        CCP_1 = time_set;
      
      
        if(index > 15) {
            // 15 * average 1.5mS = 22.5mS, a reasonable frame repeat time
          
            // Set up for a new sequence
            index = 0;         
            shift_reg = 1; // Set a bit that will be at port bit 0.
          
            time_now = get_timer1(); // Get the timer present count         
            time_set = time_now + servo_times[index]; // And set the first duration
            output_c(shift_reg); // Write the shift register bits to the port

            CCP_1 = time_set; // Write the compare time to CCP 1 register.
          
        }
    }
}

Also, C, note the "Frame" time is how long before the entire output sequence to all the servos starts again; it's conventionally around 20mS or so (~50Hz) from normal RC servos.

Sorry, I'm not going to try to change the BASIC version, as I've never used that compiler.

Also, the code you posted is an image rather than actual text in a code tag, so difficult for anyone else to do anything with, even if they are familiar with Oshonsoft.

Please post editable code?
 
Hi R,
This is the first run through of P's CODE after A.I., and I've only copied it, and not looked at it, so I can compare it with yours yet. I think it is supposed to be a faster version, so as to not jitter. The one I tried previously, which I think was your, jittered once I added it inot the FULL program, so I'm interested to see if this one is different.

I'm looking at 'C' and it's more difficult for me, as I'm not a CODER, although some rubs off each time.

As for editable text, Iposted that along side the SIM view in #196.
 
As for editable text, Iposted that along side the SIM view in #196.
OK, that's a .BAS file, it downloads rather than being viewable on here.

Ian translated my original to Oshonsoft, in post #68.

The only things I can see with his version are:

When reading 16 bit registers one byte at a time, you must read LOW byte first.
When writing them, you must read HIGH byte first.
(The high byte is transferred via a latch to synchronise the two halves).

The three places it writes to CCPR1 appear to be swapped around, which could cause all sorts of odd problems and jitter.

Modified interrupt part of that post below.

Also modified for Port B rather than C

It may also need is the "Save system" command in the start of the interrupt routine?

Code:
//INT_CCP1
On High Interrupt


    shift_reg = ShiftLeft(shift_reg, 1)  //Shift the bit left one place.
    //shift_reg = shift_reg + shift reg would do the same.
    LATB = shift_reg  //Write the shift register bits to the port

    //Increment index for next servo time value
    index = index + 1

    If index < 8 Then  //All servos already done?
        //No, do next servo

        //Set the duration for this output, as a new offset
        //from the last compare time.
        time_set = time_set + servo_times(index)

        CCPR1H = time_set.HB  //Write the compare time to CCP 1 register.
        CCPR1L = time_set.LB

    Else
        //No output needed as the port is already updated.
        //output_c(0); // Turn off all servo bits.

        //restarting the sequence.

        time_set = time_set + 1500  //add 1.5mS for the next interrupt
        //Update CCP
        CCPR1H = time_set.HB  //Write the compare time to CCP 1 register. HIGH byte first
        CCPR1L = time_set.LB

            If index > 15 Then
                //15 * average 1.5mS = 22.5mS, a reasonable frame repeat time

                //Set up for a new sequence
                index = 0
                shift_reg = 1  //Set a bit that will be at port bit 0.

                time_now.LB = TMR1L  //Get the timer present count. LOW byte first
                time_now.HB = TMR1H

                time_set = time_now + servo_times(index)  //And set the first duration
                LATB = shift_reg  //Write the shift register bits to the port
                CCPR1H = time_set.HB  //Write the compare time to CCP 1 register.
                CCPR1L = time_set.LB
            Endif
    Endif

Resume
 
Hi R,
If this is the same as #68, then as mentioned earlier, it jitters once it is added into the FULL program, so I am now trying M's just to see if works without jitter, once added into the FULL program, which will take me a while.

Yes, it needs SAVE SYSTEM, as mentioned I only copied it as best as I could till it compiled, then posted it to see if M tells me it's as he intended. Then I'll add it into the FULL program, as I did yours, and see what happens.

If It looks like your program, then it's quite possible that I copied a section of yours by mistake, so this is why I have to wait for M's feedback.

Once it's been tried, and if it jitters, then perhaps you could re-look at it, and see if we can get it to stop jittering.

You posted your CODE, thanks, but I can only deal with one program at a time, and for me that's difficult.
C.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…