|
|
View previous topic :: View next topic |
Author |
Message |
Chris
Joined: 11 Nov 2004 Posts: 8
|
Migrating servo controller code from 16F877 to 16F684 |
Posted: Thu Nov 11, 2004 6:16 am |
|
|
Hi Everyone,
I am developing a Radio Control style DC servo unit and have nearly completed the code functionality.
I am not an expert using pics but have done 1 or 2 small projects in the past this is the first time I have used the CCP1 feature
I started the development using a 16F877 chip because it has convenient serial for debugging. A few days back I was ready to migrate the code to the final PCB which has 16F684 and H bridge chips.
I use CCP1 to capture the incoming PWM signal to tell the servo what position to be in. I have to continuously switch the CCP1 from capture "Hi 2 Lo" then capture "Lo 2 Hi" then subtract 1 timer value from the other to get the pulse width in time.
The CCP1 worked fine on the 16F877 but I have spent 2 days trying to fire up the CCP1 of the 16F684. I am aware the 16F684 has ECCP which seems subtly different but I cannot see how this works.
I am not an expert using pics but I have done 1 or 2 small projects in the past this is the first time I have used the CCP1 feature
Any help greatly appreciated. Code used on the 16F877 follows
Best Regards
Chris
#include <16F877.h> // Header file for the code, <16f877.h>
#ZERO_RAM // Always do this, zeros all RAM at startup
#fuses HS,NOWDT,NOPROTECT,PUT,NOBROWNOUT,NOLVP//, NOWRT, NOCPD, NOLVP
#use delay(clock=20000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7)//PIC16F877 Tx = C6 = pin 25, Rx = C7 = pin 26.
#include <input.c>
void Initialise(void);
void GetAtoD(void);
void SpeedControl(void);
void PID(void);
char keypress;
unsigned int Load=0, capture_mode, AtoD=0, Speed=127;
unsigned int Duty_Hi, Duty_Lo, RangeLeft, RangeRight, clock_full;
signed int BoundLeft=64, BoundRight=-64, DeadbandLeft=1, DeadbandRight=-1;
unsigned long x, rise, fall, pulse_width;
signed long Error, Error_Store=0, Error_Temp;
#int_ccp1
void isr()
{
if (capture_mode == 0)
{
rise = CCP_1; // save start time in "rise"
setup_ccp1(CCP_CAPTURE_FE); // Configure CCP1 to capture fall
capture_mode = 1;
}
else if (capture_mode == 1)
{
fall = CCP_1;
setup_ccp1(CCP_CAPTURE_RE); // Configure CCP1 to capture rise
pulse_width = fall - rise;
capture_mode = 0;
pulse_width = (pulse_width/5); // divide by 5 (I dunno why, works!) is done in orig EX_CCPMP.c prog
speed = (pulse_width + 1000.0175)/3.9215;//y=mx + c
}
}
void Initialise(void)
{
setup_adc( ALL_ANALOG ); // Set up PORTA for analogue capture
setup_adc( ADC_CLOCK_INTERNAL );
OUTPUT_HIGH ( PIN_c3 ); //MOSFET chip, disable2 pin 13, enable pin 20, set to 5 v
OUTPUT_LOW ( PIN_D4 ); //MOSFET chip, disable pin 27 set to 0 v
RangeLeft = (BoundLeft-DeadbandLeft+1); //(BoundLeft-DeadbandLeft+2);
RangeRight = (DeadbandRight-BoundRight+1);//BoundLeft=64, BoundRight=-64, DeadbandLeft=1, DeadbandRight=-1;
setup_ccp1(CCP_CAPTURE_RE); // Con figure CCP1 to capture rise
setup_timer_1(T1_INTERNAL); // Start timer
enable_interrupts(INT_CCP1); // Setup interrupt on falling edge
enable_interrupts(GLOBAL);
capture_mode = 0; // 0 = rising edge, 1 = falling edge
}
void GetAtoD(void)
{
set_adc_channel(0); // Select the channel to log
delay_us(20); // Wait 20us for ADC conversion to finish
AtoD = Read_ADC(); // Read the value and convert to 10 bit number
set_adc_channel(1);
delay_us(20);
Load = Read_ADC();
}
void PID(void)
{
Load = (Load*Load*Load) + 100; //increase load values to stiffen up gearbox output
Error = (((signed long)(Error * Load) / 100));
}
void SpeedControl(void)
//calculation. Overall pulse width = 100us, duty cycle is varied to achieve speed control
//direction & speed resolution = 8bit = 256 steps (0 - 255)
//0 - 127 = clockwise, 0 = full speed, 127 = full stop
//128 - 255 = anticlockwise, 128 = full stop, 255 = full speed
//example calc, duty cycle = [127 (from pc)/255 (full range)] * 100
//check if pot value is > or < than current speed and set direction of motor
//
//zoning speed and direction
//Error = Speed - AtoD
//127 = 127 - 0
//64 = 127 - 63
//2 = 127 - 125
//254 = 127 - 129
//192 = 127 - 191
//128 = 127 - 255
{
Error = ((signed long)Speed - AtoD);
//BoundLeft = 63, BoundRight = 191, DeadbandLeft=2, DeadbandRight=254, RampLeft, RampRight;
if(Error <= 127 && Error > BoundLeft) //full speed ahead
{
OUTPUT_low ( PIN_c0 ); //motor is full speed here
OUTPUT_high ( PIN_c1 );
delay_us(1000);
//printf("full spe clock\r\n");
}
else if(Error <= BoundLeft && Error > DeadbandLeft) //slowing down for setpoint
{
OUTPUT_LOW ( PIN_c0 ); //make other motor pin low
Duty_Hi = ((float)Error/Rangeleft)*100;
if (Duty_Hi < 25) //speed must prevent overshoot & oppose rotation of out shaft
{
Duty_Hi = 25;
}
Duty_Lo = 100-Duty_Hi; // how long pulse is high
// printf("slowing clock\r\n");
for (x=0;x<1000;x++)
{
OUTPUT_high ( PIN_c1 ); //MOSFET chip, disable2 pin 13, enable pin 20, set to 5 v
delay_us(Duty_Hi);
OUTPUT_low ( PIN_c1 ); //MOSFET chip, disable1 pin 18 set to 0 v
delay_us(Duty_Lo);
}
}
else if(Error >= DeadbandLeft && Error >= DeadbandRight)//dead stop
{
OUTPUT_LOW ( PIN_c0 ); //set motor to off
OUTPUT_LOW ( PIN_c1 );
// printf("stop\r\n");
}
else if(Error < DeadbandRight && Error >= BoundRight) //slowing down for setpoint
{
OUTPUT_LOW ( PIN_c1 ); //make other motor pin low
Error = Error*-1;
Duty_Lo = ((float)Error/RangeRight)*100;
if (Duty_Lo < 25) //speed must prevent overshoot & oppose rotation of out shaft
{
Duty_Lo = 25;
}
Duty_Hi = 100-Duty_Lo; // how long pulse is high
// printf("slowing anti\r\n");
for (x=0;x<1000;x++)
{
OUTPUT_high ( PIN_c0 ); //MOSFET chip, disable2 pin 13, enable pin 20, set to 5 v
delay_us(Duty_Lo);
OUTPUT_low ( PIN_c0 ); //MOSFET chip, disable1 pin 18 set to 0 v
delay_us(Duty_Hi);
}
}
else if(Error < BoundRight && Error >= -128) //full speed ahead
{
OUTPUT_low ( PIN_c1 ); //motor is full speed here
OUTPUT_high ( PIN_c0 );
delay_us(1000);
// printf("full spe anti\r\n");
}
}
main() {
setup_adc( ALL_ANALOG ); // Set up PORTA for analogue capture
setup_adc( ADC_CLOCK_INTERNAL );
Initialise();
while(1)
{
GetAtoD();
PID();
//KeyHit();
// printf("Speed %u, AtoD %u, Error %ld, Error_Store %ld\r\n", Speed, AtoD, Error, Error_Store);
// printf("Duty_Hi %u, Duty_Lo %u\r\n", Duty_Hi, Duty_Lo);
// printf("Error_Temp %u\r\n", Error_Temp);
printf("Error %ld, AtoD %u, Load %u\r\n",Error, AtoD, Load);
// printf("Error %ld, Error_Store %ld, Error_Temp %ld\r\n", Error, Error_Store, Error_Temp);
SpeedControl();
}
}
:confused |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Nov 11, 2004 12:19 pm |
|
|
What's the version of your compiler ?
It's possible that your version of the compiler doesn't
setup the ECCP correctly on the 16F684. |
|
|
Chris
Joined: 11 Nov 2004 Posts: 8
|
|
Posted: Thu Nov 11, 2004 1:36 pm |
|
|
Hi PCM Programmer,
I spotted your thorough reply to a similar question at:
http://www.ccsinfo.com/forum/viewtopic.php?t=18181&highlight=eccp
an hour ago.
Could I test my code and target pic in the same manner you describe for the 18 series pics? If so I will try but it'll take me a while to understand all steps involved and try it.
My versions are:
PCB ver 3.184
PCM ver 3.184
PCH ver DLL Error
I am a bit surprised by the DDL Error, could this be part of the problem?
How should I proceed to fix this issue. Is there a patch or do I have to purchase something? A last resort is changing my PCB for a conventional CCP oriented pic but that seems drastic.
Regards
Chris |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Nov 11, 2004 2:00 pm |
|
|
PCM 3.184 doesn't even compile this line:
setup_ccp1(CCP_CAPTURE_RE);
It says:
Error[12] C:\PICC\TEST\test.c 12 : Undefined identifier setup_ccp1
The 16F684.H file doesn't have any constants in it for the CCP capture
mode. So I wonder how you're compiling the program.
Anyway, I compiled two lines with the setup_ccp1() function for
the latest version of the compiler.
Here is the code produced by PCM vs. 3.212 for the 16F684:
Code: | 0000 00305 .................... setup_ccp1(CCP_CAPTURE_RE);
000F 1683 00306 BSF 03.5
0010 1505 00307 BSF 05.2
0011 1283 00308 BCF 03.5
0012 0195 00309 CLRF 15
0013 3005 00310 MOVLW 05
0014 0095 00311 MOVWF 15
0015 0197 00312 CLRF 17
0016 0196 00313 CLRF 16
0000 00314 ....................
0000 00315 .................... setup_ccp1(CCP_CAPTURE_FE);
0017 1683 00316 BSF 03.5
0018 1505 00317 BSF 05.2
0019 1283 00318 BCF 03.5
001A 0195 00319 CLRF 15
001B 3004 00320 MOVLW 04
001C 0095 00321 MOVWF 15
001D 0197 00322 CLRF 17
001E 0196 00323 CLRF 16 |
|
|
|
Chris
Joined: 11 Nov 2004 Posts: 8
|
|
Posted: Thu Nov 11, 2004 2:35 pm |
|
|
Hi PCM Programmer,
I reached the point where I was trying to hack the code by looking through what is available in .h file. The last setup I tried follows:
SETUP_TIMER_1(T1_INTERNAL);
EXT_INT_EDGE(L_TO_H);//(CCP_CAPTURE_RE);
ENABLE_INTERRUPTS(INT_CCP1);
ENABLE_INTERRUPTS(GLOBAL);// This line says "Go ! (start running) "
I hoped that if it compiled I wasnt far off. The above has probably changed 50 times in the last 48 hours. I tried everything.
I am very grateful for your help.
Just so I understand fully, the version I have will not correctly compile 16F684 code so that the ECCP pin functionality works but if I upgrade to PCM ver 3.212 for the 16F684 the problem is solved?
Many Thanks for your help. I will contact CCS tomorrow for details of an upgrade.
Best Regards
Chris |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Nov 11, 2004 2:54 pm |
|
|
When I posted that code, I didn't check it. I just posted it to show
that a newer version of the compiler did compile code for it.
Upon examining the code, I can a see bug.
They're setting the bit 2 of the TRISA register = 1. This will make pin
A2 be an input. But there's a problem with that. The CCP1 module is
on pin C5. They should have set TRISC, bit 5, as an input.
The truth is, the PIC powers-up with the TRIS registers set to all 1's,
and this configures the i/o ports as all inputs. So as long as you
haven't changed Pin C5 to an output with a previous line of code,
it should be in the proper state. The bug would only cause a problem
if you were using pin A2 as an output.
If it was me, I would just write directly to the registers to setup the
ECCP module manually. Why upgrade for something that isn't even
correct ? Do it yourself in this case. |
|
|
Chris
Joined: 11 Nov 2004 Posts: 8
|
|
Posted: Thu Nov 11, 2004 4:17 pm |
|
|
Hi PCM Programmer,
I can see what your getting at but I cant read assembler and therefore am not sure which part of the assembled text shows when the TRISA bit 2 gets changed. I also cannot see which commands I need to edit out or keep when setting up the ECCP pin.
I have tried setting the TRISC, bit 5 (input) and bit 2 (output on C2). But I still cant see any pulse width out changes on pin C2. I am using C2 to generate a pulse of changing width depending on the pulse width measured.
I have stripped the code down so it only measures incoming pulse widths on C5 and creates an output pulse (C2) from the measurements.
The code below does compile and run but the outgoing pulse stream doesnt change width when the input pulse to the ECCP pin changes.
Could you describe in a bit more detail how to configure the ECCP pin. I think its mostly the initialise section thats wrong but am I also using the correct command to measure a rising edge? Or have I missed somthing obvious out?
I think the reason I am struggling here is that my knowledge is a bit too low level to keep up, but with a nudge I should get it.
Regards
Chris
Code follows:
#include <16F684.h> // Header file for the code, <16f684.h>
#ZERO_RAM // Always do this, zeros all RAM at startup
#fuses HS,NOWDT,PUT,NOMCLR,NOPROTECT,NOBROWNOUT,IESO,FCMEN,NOCPD
#use delay(clock=20000000)
void Initialise(void);
void SpeedControl(void);
unsigned int capture_mode, Speed=127;
unsigned long rise, fall, pulse_width;
#int_ccp1
void isr()
{
if (capture_mode == 0)
{
rise = INT_CCP1; // save start time in "rise"
EXT_INT_EDGE(H_TO_L);//setup_ccp1(CCP_CAPTURE_FE);
capture_mode = 1;
}
else if (capture_mode == 1)
{
fall = INT_CCP1;
EXT_INT_EDGE(L_TO_H) ;//setup_ccp1(CCP_CAPTURE_RE);
pulse_width = fall - rise;
capture_mode = 0;
pulse_width = (pulse_width/5); // divide by 5
speed = (pulse_width + 1000.0175)/3.9215;//y=mx + c
}
}
void Initialise(void)
{
SET_TRIS_C( 0x0F );
SETUP_TIMER_1(T1_INTERNAL);
EXT_INT_EDGE(L_TO_H);//(CCP_CAPTURE_RE);
ENABLE_INTERRUPTS(INT_CCP1);
ENABLE_INTERRUPTS(GLOBAL);
}
void main()
{
Initialise();
while(1)
{
OUTPUT_low ( PIN_c2 ); //motor is full speed here
delay_us(Speed);
OUTPUT_high ( PIN_c2 );
delay_us(10);
}
} |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Nov 11, 2004 4:38 pm |
|
|
I just scanned your current program without trying to understand it
and saw two errors, at least.
You have these two lines in your isr.
Code: | rise = INT_CCP1; // save start time in "rise"
fall = INT_CCP1; |
But these two lines are assigning a CCS constant value to
your variables. You should be using 'CCP1', as you did
in the first program you posted.
Here's what your code above is assigning to those variables.
#define INT_CCP1 0x8C20 |
|
|
|
|
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
|