Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Hardware ESC 8xSERVO CONTROL on PIC (Oshonsoft BASIC)

Status
Not open for further replies.
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).
This only applies to Timers, CCP values don't increment. If by some rare coincidence writing the low byte first causes a match then it will be cleared lower down before exiting the ISR. I don't know if Oshonsoft will write a 16 bit register or if it will need splitting, depends on the compiler.

The conversion (?) has some strange bits like,
Code:
'T1CON=0b00000001;       'timer 1 clocks at
T1CON.7 = 0  '7=RD16 0 = Enables register read/write of Timer1 in two 8-bit operations
T1CON.6 = 1  'T1RUN 1 = Device clock is derived from Timer1 oscillator
T1CON.5 = 1  'T1CKPS.5-4 10 = 1:4 Prescale value
T1CON.4 = 0
T1CON.3 = 1  'T1OSCEN 1 = Timer1 oscillator is enabled
T1CON.2 = 0  'T1SYNC This bit is ignored. Timer1 uses the internal clock when TMR1CS = 0.
T1CON.1 = 0  'TMR1CS 0 = Internal clock (FOSC/4)
T1CON.0 = 1  'TMR1ON 1 = Enables Timer1
This converts T1CON=1 to T1CON=105. Also, it's running from Timer1 oscillator which I assume doesn't exist.
and,
Code:
INTCON.PEIE = 1  '[6]PEIE/GIEL: Peripheral Interrupt Enable bit: When IPEN = 1: 1 = Enables all low-priority peripheral interrupts
INTCON.GIE = 1  '[7]GIE/GIEH: Global Interrupt Enable bit: When IPEN = 1:1 = Enables all low-priority peripheral interrupts

Enable High  'This is set for SERVOS
Enable Low  'This is set for GPS later
Aren't these doing the same thing?

I also note that your code enables interrupt priority.

Mike.
Edit, the CCP1CON value is wrong too.
 
Last edited:
Luckily, your chip doesn't have the ECCP modules or this may apply,
From the 2620 datasheet,
Code:
In 28-pin devices, the two standard CCP modules
(CCP1 and CCP2) operate as described in this
chapter. In 40/44-pin devices, CCP1 is implemented
as an Enhanced CCP module with standard Capture
and Compare modes and Enhanced PWM modes.
I'm using a 28 pin device so it's pretty much identical to yours where the CCP module is concerned.

Mike.
 
This only applies to Timers, CCP values don't increment. If by some rare coincidence writing the low byte first causes a match then it will be cleared lower down before exiting the ISR. I don't know if Oshonsoft will write a 16 bit register or if it will need splitting, depends on the compiler.

The conversion (?) has some strange bits like,
Code:
'T1CON=0b00000001;       'timer 1 clocks at
T1CON.7 = 0  '7=RD16 0 = Enables register read/write of Timer1 in two 8-bit operations
T1CON.6 = 1  'T1RUN 1 = Device clock is derived from Timer1 oscillator
T1CON.5 = 1  'T1CKPS.5-4 10 = 1:4 Prescale value
T1CON.4 = 0
T1CON.3 = 1  'T1OSCEN 1 = Timer1 oscillator is enabled
T1CON.2 = 0  'T1SYNC This bit is ignored. Timer1 uses the internal clock when TMR1CS = 0.
T1CON.1 = 0  'TMR1CS 0 = Internal clock (FOSC/4)
T1CON.0 = 1  'TMR1ON 1 = Enables Timer1
This converts T1CON=1 to T1CON=105. Also, it's running from Timer1 oscillator which I assume doesn't exist.
and,
Code:
INTCON.PEIE = 1  '[6]PEIE/GIEL: Peripheral Interrupt Enable bit: When IPEN = 1: 1 = Enables all low-priority peripheral interrupts
INTCON.GIE = 1  '[7]GIE/GIEH: Global Interrupt Enable bit: When IPEN = 1:1 = Enables all low-priority peripheral interrupts

Enable High  'This is set for SERVOS
Enable Low  'This is set for GPS later
Aren't these doing the same thing?

I also note that your code enables interrupt priority.

Mike.
Edit, the CCP1CON value is wrong too.
Hi M,
Please note that I'm out of my depth, but hope to give correct answers.

The line: (In the starnge bits): [ T1CON.7 = 0 '7=RD16 0 = Enables register read/write of Timer1 in two 8-bit operations ] is my guess when going through the D/S to set the registers etc, and allows the choice between 1x 16BIT or 2x 8BIT register. It is set for 2x 8BIT, bit I can change it.

I can change the T1CON settings as you wish. It has TIMER1, which is set by T1CON.

The 2x INTCON settings, don't look correct! I think the G one controls the P one??

ENABLE HIGH and LOW are the importance of each INTERRUPT. At the moment I've chosen the HIGH for the SERVOS and LOW for the GPS.

What should CCP1CON be set to?
C
 
Luckily, your chip doesn't have the ECCP modules or this may apply,
From the 2620 datasheet,
Code:
In 28-pin devices, the two standard CCP modules
(CCP1 and CCP2) operate as described in this
chapter. In 40/44-pin devices, CCP1 is implemented
as an Enhanced CCP module with standard Capture
and Compare modes and Enhanced PWM modes.
I'm using a 28 pin device so it's pretty much identical to yours where the CCP module is concerned.

Mike.
Hi m,
Yes, well chosen:)
C
 
Can you run the above code through your A.I. and post the results. The Basic posted above seems to be a mix of things.

CCP1CON should be as in the C program above,
Code:
    CCP1CON=0b10001010;     //generate interrupt only
Actually, I see you have that but I read the comment instead of the code.
Bit 7 isn't implemented on the 2620 either.

Mike.
 
Can you run the above code through your A.I. and post the results. The Basic posted above seems to be a mix of things.

CCP1CON should be as in the C program above,
Code:
    CCP1CON=0b10001010;     //generate interrupt only
Actually, I see you have that but I read the comment instead of the code.
Bit 7 isn't implemented on the 2620 either.

Mike.
Hi M,
Which CODE above? Can you show the # Number please? The last CODE I put through AI, I posted in #195 is after AI, but I had to add the usual sections that I use with that PIC in OSH, so it would compile. It may look strange!
NOTE: There are a couple of settings, that I know will be used in the future, so I don't miss them, they don't affect this CODE.
(Bedtime here)
C.
 
#188

Mike.
Hi M,
Here:
C

Code:
'Include OSHONSOFT Basic Library
#include <xc.bas>
#include "config.bas"
#define _XTAL_FREQ 32000000
#define NUM_SERVOS 8
#define FRAME 16000L    'how long each frame is

Dim servoPos(NUM_SERVOS) As Word
Dim servoCount As Byte
Dim servoTemp As Byte

'Main Program
Sub Main()
    OSCCON = &B01110000    '8MHz
    PLLEN = 1              'x4=32MHz
    T1CON = &B00000001     'timer 1 clocks at
    CCP1CON = &B10001010   'generate interrupt only
    CCP1IE = 1             'enable CCP1 interrupts
    For i = 0 To NUM_SERVOS - 1
        servoPos(i) = i * 1000 + 8000  '1ms(8000) to 1.875(7/8ths - 15000)ms in 1/8th mS steps
    Next
    TRISB = 0              'all output
    LATB = 0
    servoTemp = 0
    PEIE = 1
    GIE = 1
    Do
        'adjust servo positions here
    Loop
End Sub

'Interrupt Routine
Sub inter()
    PORTB = servoTemp       'output as soon as possible to minimise jitter.
    If CCP1IE And CCP1IF Then
        If servoTemp = 0 Then  '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 Then
                servoCount = 0
            Else
                servoCount = servoCount + 1
            End If
        Else
            'outputting the pulse
            CCPR1 = CCPR1 + servoPos(servoCount)   'add on time to
            servoTemp = 0                          'next time clear port B
        End If
        CCP1IF = 0
    End If
End Sub
 
This only applies to Timers, CCP values don't increment.
OK, if the CCPs don't use the high byte latch, then order is irrelevant.

However your code from post #188 is still messed up so the "AI" translation is not going to produce working BASIC.

C:
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;
    }
}

"servoTemp" can never be zero as you are constraining the left shift to 0-7 rather than letting it shift endlessly after initialisation.

servoCount is being incremented during initialisation rather than the pulse sequence.

servoTemp is being forced to zero every time it hits the "}else{" section.

An attempted fix:

C:
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?
           
            // servoTemp == 0 means outputs havr just been cleared.
            // Output the frame gap and reset the sequence.
           
            // outputting the frame gap. roughly 20 - (number of servos * 1.5) in mS
            CCPR1 = CCPR1 + FRAME;                     // add gap time
            servoCount = 0;                         // Reset counter
            servoTemp = 1;                          // And initialise output bit
        }
        else {
            // Servotemp was not zero; Above output was a pulse start, keep sequencing
            // Setting  the pulse duration
            CCPR1=CCPR1+servoPos[servoCount];       // Add time for this channel
           
            servoCount = servoCount + 1;            // Increment channel number and
            servoTemp = servoTemp << 1;             // Shift output bit ready for next pass
        }
        CCP1IF=0;
    }
}
 
OK, if the CCPs don't use the high byte latch, then order is irrelevant.

However your code from post #188 is still messed up so the "AI" translation is not going to produce working BASIC.

C:
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;
    }
}

"servoTemp" can never be zero as you are constraining the left shift to 0-7 rather than letting it shift endlessly after initialisation.

servoCount is being incremented during initialisation rather than the pulse sequence.

servoTemp is being forced to zero every time it hits the "}else{" section.

An attempted fix:

C:
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?
         
            // servoTemp == 0 means outputs havr just been cleared.
            // Output the frame gap and reset the sequence.
         
            // outputting the frame gap. roughly 20 - (number of servos * 1.5) in mS
            CCPR1 = CCPR1 + FRAME;                     // add gap time
            servoCount = 0;                         // Reset counter
            servoTemp = 1;                          // And initialise output bit
        }
        else {
            // Servotemp was not zero; Above output was a pulse start, keep sequencing
            // Setting  the pulse duration
            CCPR1=CCPR1+servoPos[servoCount];       // Add time for this channel
         
            servoCount = servoCount + 1;            // Increment channel number and
            servoTemp = servoTemp << 1;             // Shift output bit ready for next pass
        }
        CCP1IF=0;
    }
}
Hi R,
Here is my OSH translation:

Check IF---ENDIFs

NOTE: [ ' ] = commented out.

This line doesn't compile, otherwise the CODE attached compiles:
[ CCPR1 = CCPR1 + servopos[servocount ]
C
Code:
On High Interrupt
Save System

'RCODE
'void __interrupt() inter(void){
PORTB = servotemp  'output as soon as possible to minimise jitter.

'If(CCP1IE && CCP1IF){
If PIE1.CCP1IE And PIR1.CCP1IF Then
   
    'If(servotemp == 0)  'which phase of the output are we in?
    If servotemp = 0 Then  'which phase of the output are we in?

        'servotemp == 0 means outputs havr just been cleared.
        'Output the frame gap And reset the sequence.

        'outputting the frame gap. roughly 20 - (number of servos * 1.5) in ms
        CCPR1 = CCPR1 + frame  'add gap time
        servocount = 0  'Reset counter
        servotemp = 1  'And initialise output bit
        'Endif
    Else
        'servotemp was Not zero; above Output was A pulse start, keep sequencing
        'setting  the pulse duration
'  CCPR1 = CCPR1 + servopos[servocount]  'Add time for this channel [[COMMENTED OUT ]]<<<<<<<<<<<<

        servocount = servocount + 1  'Increment channel number and
        servotemp = servotemp << 1  'Shift output bit ready for next pass
        'Endif
        PIR1.CCP1IF = 0
    Endif
Endif

Resume
 
Hi R,
I changed SERVOCOUNT to [ i ] and now it compiles and runs.

It now can count to [ 2 ] so that's an improvement :) then it stops counting.
C
 

Attachments

  • Servopos.jpg
    Servopos.jpg
    438.4 KB · Views: 121
The end of the routine has the interrupt clear in the wrong place, it should be like


Endif
PIR1.CCP1IF = 0
Endif

Resume


In that other line, try

CCPR1 = CCPR1 + servopos(servocount)

I think BASIC does not use the [] square brackets?

Also, I forgot, when you dimension the servopos array in the start of the program, make it two more than the number of servos you are actually using; eg. 10 for the 8 servo count in these tests.
 
The end of the routine has the interrupt clear in the wrong place, it should be like


Endif
PIR1.CCP1IF = 0
Endif

Resume


In that other line, try

CCPR1 = CCPR1 + servopos(servocount)

I think BASIC does not use the [] square brackets?

Also, I forgot, when you dimension the servopos array in the start of the program, make it two more than the number of servos you are actually using; eg. 10 for the 8 servo count in these tests.
Hi R,
With "Dim servopos(10) As Word" (no [ ] ) it now counts to "8" as the image shows. then resets to "0"

This line doesn't compile, so I changed it to CCPR1 = CCPR1 + servopos(i), which works.

It appears to be working, in the SIM, I'll now try it "live"
C
 

Attachments

  • Counting to 8.jpg
    Counting to 8.jpg
    509.7 KB · Views: 123
Hi R,
"live" each SERVO at PORTB seems to try to move to "8" fixed positions in app 3 secs.

I realise that those times can be adjusted, but each SERVO should go to the temporary set e,g, "servopos(0) = 1.5 * 8000" position.

EDIT: I changed the [ ] to ( ) in "CCPR1 = CCPR1 + servopos(servocount)" and it compiles. just going to try the SIM and "live"

C
 
Last edited:
Hi,
I've now tried M's and R's program,and they apear to be outputting 8 positions onto each SERVO pin.
C
 
That sounds like the bit shift is not working right??

Try changing
servotemp = servotemp << 1 'Shift output bit ready for next pass

to
servotemp = servotemp + servotemp 'Shift output bit ready for next pass
 
Hi M and R,

Here are your 2x programs. I'll keep them separate and comment on each of your updates, with results etc
Make sure all of the settings are as you wish.
Each of your initials is in the title. Please check this, so I don't get them mixed up.

EDIT: "R" line "servotemp = servotemp + servotemp 'Shift output bit ready for next pass" didn't work, but check all settings etc in case of errors.
C
 

Attachments

  • 18F4431 32MHz XTL REMOTE_SLAVE M 220123 1400.bas
    7.8 KB · Views: 126
  • 18F4431 32MHz XTL REMOTE_SLAVE R 220123 1400.bas
    8.1 KB · Views: 123
Last edited:
However your code from post #188 is still messed up so the "AI" translation is not going to produce working BASIC.
I can't see how it's messed up. My code is outputting a pulse of 1 to 2 mS every 2.5mS to give a total time of 20mS. The servoTemp=0; is required to output the gap between pulses. I did it this way as some (mainly analogue) servos depend on the overall frame-rate being 20mS. Or rather, don't like it to vary.

Look at the code again with the fact that servoTemp is written in the next interrupt. It can definitely be zero.
This bit
Code:
        }else{
            //outputting the pulse
            CCPR1=CCPR1+servoPos[servoCount];       //add on time to
            servoTemp=0;                            //next time clear port B <<<<<<<<<<<<<<<<<<<<<<<<<<<
        }
is executed if servoTemp wasn't zero so it delays for the required time (servoPos[servoCount]) and ensures that a zero is outputted the next interrupt to create the gap. You talk of initialization in the ISR but there isn't any, that's done before interrupts are enabled.
OK, if the CCPs don't use the high byte latch, then order is irrelevant.
The order isn't actually irrelevant. If the low byte is written first then a match could happen before the high byte is written which could cause a very nasty bug. However, it will result in the (already set) CCP1IF being set which will be cleared later. If CCP1IF was cleared at the beginning of the code then it could be a big problem.

Mike.
Edit, not sure how the PIR1.CCP1IF = 0 instruction got moved but it would have resulted in all the gaps being very short.
 
I can't see how it's messed up. My code is outputting a pulse of 1 to 2 mS every 2.5mS to give a total time of 20mS. The servoTemp=0; is required to output the gap between pulses. I did it this way as some (mainly analogue) servos depend on the overall frame-rate being 20mS. Or rather, don't like it to vary.

Look at the code again with the fact that servoTemp is written in the next interrupt. It can definitely be zero.
This bit
Code:
        }else{
            //outputting the pulse
            CCPR1=CCPR1+servoPos[servoCount];       //add on time to
            servoTemp=0;                            //next time clear port B <<<<<<<<<<<<<<<<<<<<<<<<<<<
        }
is executed if servoTemp wasn't zero so it delays for the required time (servoPos[servoCount]) and ensures that a zero is outputted the next interrupt to create the gap. You talk of initialization in the ISR but there isn't any, that's done before interrupts are enabled.

The order isn't actually irrelevant. If the low byte is written first then a match could happen before the high byte is written which could cause a very nasty bug. However, it will result in the (already set) CCP1IF being set which will be cleared later. If CCP1IF was cleared at the beginning of the code then it could be a big problem.

Mike.
Edit, not sure how the PIR1.CCP1IF = 0 instruction got moved but it would have resulted in all the gaps being very short.
Hi M,
I always initial who I'm addressing my replies to for clarity, and I think this one may be addressed to R.

Anyway at the moment you are both working on the same program, but now they are slightly different, in each title is your initial. If you would be good enough to refer to 'your own program' posted in #217, as I was fielding both of your replies, and getting very mixed up. I can carry out tests for either version, as long as I keep them separate.

Would you check 'your' program #217, to make sure it is how you expect it, so I can correct it if necessary?

Initially I only copied M's program #217, without knowing the actual process, but now I'm going through it with the AI's written explanation posted earlier. Hopefully I will understand it better and how it works in a few days.
C
 
Last edited:
Hi C,
Just looked at the conversion in post #208 and it looks pretty good and I think it should work with two changes.
1. make FRAME 20000L,
2. Move CCP1IF as indicated above by R.

I've changed both and this is the result,
Code:
'Include OSHONSOFT Basic Library
#include <xc.bas>
#include "config.bas"
#define _XTAL_FREQ 32000000
#define NUM_SERVOS 8
#define FRAME 20000L    'how long each frame is

Dim servoPos(NUM_SERVOS) As Word
Dim servoCount As Byte
Dim servoTemp As Byte

'Main Program
Sub Main()
    OSCCON = &B01110000    '8MHz
    PLLEN = 1              'x4=32MHz
    T1CON = &B00000001     'timer 1 clocks at
    CCP1CON = &B10001010   'generate interrupt only
    CCP1IE = 1             'enable CCP1 interrupts
    For i = 0 To NUM_SERVOS - 1
        servoPos(i) = i * 1000 + 8000  '1ms(8000) to 1.875(7/8ths - 15000)ms in 1/8th mS steps
    Next
    TRISB = 0              'all output
    LATB = 0
    servoTemp = 0
    PEIE = 1
    GIE = 1
    Do
        'adjust servo positions here
    Loop
End Sub

'Interrupt Routine
Sub inter()
    PORTB = servoTemp       'output as soon as possible to minimise jitter.
    If CCP1IE And CCP1IF Then
        If servoTemp = 0 Then  '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 Then
                servoCount = 0
            Else
                servoCount = servoCount + 1
            End If
        Else
            'outputting the pulse
            CCPR1 = CCPR1 + servoPos(servoCount)   'add on time to
            servoTemp = 0                          'next time clear port B
        End If
    End If
    CCP1IF = 0
End Sub

Does this compile and if not, what are the errors?

Mike.
Edit, Actually, CCP1IF doesn't need to be moved but as there's no other interrupts it doesn't matter. For completeness, it should be above the last End If.
Edit2, FRAME is the time for 1 pulse plus the gap after it and is 2.5mS so eight frames is 20mS.
Edit3, I assume you need to change the includes to get it to compile.
 
Last edited:
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top