The correct code was in the previous (#178) post.I don't know how to correct the [ CCPR1 = frame - servopos(servocount) ] but i'll give it a try.
Mike.
Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
The correct code was in the previous (#178) post.I don't know how to correct the [ CCPR1 = frame - servopos(servocount) ] but i'll give it a try.
Hi M,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,The correct code was in the previous (#178) post.
Mike.
unsigned char *point=(unsigned char *)&RB0PPS;
point[servoCount]=0; //release the old CCP1 output
point[servoCount]=9; //setup new CCP1 output
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
Hi T,In the original C code there was this:
What that does is create a pointer to the RB0PPS register, and then as servoCount is incrementedCode:unsigned char *point=(unsigned char *)&RB0PPS; point[servoCount]=0; //release the old CCP1 output
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:
is responsible for mapping the RBxPPS to the CCP1 outputCode:point[servoCount]=9; //setup new CCP1 output
The equivalent code would be something like:
I have no idea what the correct syntax for that would be in OSHCode: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
#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;
}
}
Hi P,I thought the chip you're using didn't have PPS so this code can never work. Does your chip have RB0PPS?
Mike.
Hi M,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,
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.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; } }
Mike.
Edit, and NO pointers too.
#include <xc.h>
#include "config.c"
#include <stdint.h>
// 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)
#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT enabled)
Hi M,Note that the comments in the config don't always match the code. I.E.
Code=Watch Dog Timer off but comment says enabled.Code:#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT enabled)
Just me being lazy.
Mike.
Edit, I'll be interested to see what your A.I. come up with.
Where I'm using a XTL with RA6 and 7 input.#pragma config OSC = INTIO67 // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)
Hi M,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,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.
#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.
}
}
}
Hi R,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?
OK, that's a .BAS file, it downloads rather than being viewable on here.As for editable text, Iposted that along side the SIM view in #196.
//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,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