|
|
View previous topic :: View next topic |
Author |
Message |
Mitch B.
Joined: 23 Jul 2012 Posts: 6
|
Decoding rotary encoder with PIC18F452 |
Posted: Mon Jul 23, 2012 7:46 am |
|
|
hi folks.
This is my first ccs-project and the reason for my post is to obtain some opinions about the code i use.
The task of the project is to control a rc-servo and get information about rotation and the needed time between two positions by decoding a optical rotary encoder.
Therefore i spent ages in this forum to understand and find a proper #int_rb-routine.
Referring to this i want to thank Ttelmah for his code snippet, which i adapted for my usage.
spec's:
servo: ASCOM AS-16, 5Volts, speed = 0.23 sec/60°
encoder: MEGATRON MOM20(optoelectronic encoder), 5Volts, 500ppr
encoder pull-up's: 2x 680Ohm
CCS version: 4.074
PIC18F452, channelA = pin_b4, channelB = pin_b5, pwm_signal = pin_c0
##############
Code: |
#include <18F452.h>
#device HIGH_INTS=TRUE
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use fast_io(b)
#use delay(clock=10000000)
#use rs232(baud=19200,xmit=pin_C6, rcv=pin_C7)
#byte portb = 0xF81
// ****************************************************************************
#define TIMER0_RELOAD 252 //0,2ms
#define CONTROL_SERVO 1
#define MAX_VALUE 20
int1 buffer_full=FALSE;
int8 inbuffer[4]; //RS232 buffer
int8 buffer_cnt=0;
int8 selected_servo=0;
int8 mode=0;
unsigned int16 setpoint[1]={3700}; //Variable for servo-position
unsigned int16 temp=0;
int16 pulse_max=0; //PWM-count
static int value;
static int16 rb_new = 0;
static int16 rb_old = 0;
signed int32 PosCnt=0; //Positioncount
signed int32 cnt;
unsigned int16 i=0;
unsigned int16 TimeCnt; //Speedcount
// ************************* INTERRUPT SUBROUTINE******************************
#INT_TIMER0 HIGH
void TIMER0_isr(void) {
set_timer0(TIMER0_RELOAD);
TimeCnt++;
pulse_max++;
if(pulse_max == MAX_VALUE) {
output_high(pin_c0);
set_timer1(60000+setpoint[0]);
pulse_max=0;
}
}
// ****************************************************************************
#INT_TIMER1 HIGH
void TIMER1_isr(void) {
output_low(pin_c0);
}
// ****************************************************************************
#int_RB
void change_on_RB(void) {
rb_new = input_b();
cnt++;
delay_us (500);
value = rb_new^rb_old;
if (value & 0x10) {
if (rb_new & 0x10) {
if (rb_new & 0x20) --PosCnt;
else ++PosCnt;
}
else {
if (rb_new & 0x20) ++PosCnt;
else --PosCnt;
}
}
else {
if (rb_new & 0x20) {
if (rb_new & 0x10) ++PosCnt;
else --PosCnt;
}
else {
if (rb_new & 0x10) --PosCnt;
else ++PosCnt;
}
}
rb_old = rb_new;
}
// ****************************************************************************
// ****************************** FUNCTIONS ***********************************
void clear_buffer(void) {
buffer_cnt=0;
inbuffer[0]=0;
inbuffer[1]=0;
inbuffer[2]=0;
inbuffer[3]=0;
buffer_full=FALSE;
}
void clear_delta(void) {
PosCnt=0;
cnt=0;
TimeCnt=0;
}
void position(void) {
if(PosCnt>=0) {
printf("Rotation CW. Position = %5ld\n\r",PosCnt);
}
else {
printf("Rotation CCW. Position = %ld\n\r",PosCnt);
}
printf("Time[s]: %f\n\r",(float)(TimeCnt*0.0002)); //time between servo-movement
printf("ISR-calls: %ld\n\r",cnt); //counts for calling isr
PosCnt = 0;
}
// ****************************************************************************
// ****************************************************************************
// MAIN
// ****************************************************************************
// ****************************************************************************
void main(void) {
set_tris_b(0x30);
// ****************************************************************************
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_128|RTCC_8_BIT);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
// ****************************************************************************
set_timer0(TIMER0_RELOAD);
// ****************************************************************************
// 0,4us * (65536 - (60000+setpoint[]))
// -> shortest Impuls (setpoint=0) = 0,7ms
// -> longest Impuls (setpoint=3700) = 2,2ms
set_timer1(60000+setpoint[0]);
// ****************************************************************************
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_RB);
enable_interrupts(GLOBAL);
// ****************************************************************************
// ****************************************************************************
printf("Press 'c' and ENTER to control Servo.\n\r");
printf("Range for servoposition: from '000' to '999'\n\r\n\r");
printf("Choose position:\n\r>");
while(TRUE) {
if(kbhit()) {
inbuffer[buffer_cnt]=getc();
if(buffer_full) {
if(inbuffer[buffer_cnt]=='\r') {
putc(inbuffer[buffer_cnt]);
putc('\n');
clear_buffer();
printf("puffer cleared\n\r>");
}
}
else {
putc(inbuffer[buffer_cnt]);
if(inbuffer[buffer_cnt]=='\r') {
putc('\n');
if((inbuffer[0]=='c')&&buffer_cnt==1) {
mode=CONTROL_SERVO;
printf("mode=CONTROL_SERVO\n\r");
}
else if((mode==CONTROL_SERVO)&&(inbuffer[0]>='0')&&(inbuffer[0]<='9')&&(inbuffer[1]>='0')&&(inbuffer[1]<='9')&&(inbuffer[2]>='0')&&(inbuffer[2]<='9')) {
temp=(inbuffer[0]-'0')*100.0+(inbuffer[1]-'0')*10.0+(inbuffer[2]-'0')*1.0; //calculation for servo-setpoint
setpoint[selected_servo]=(999-temp)*3.703704; //=3700/999;
clear_delta();
for (i=1;i<=60000;++i){} //delay, servo movement finished before printf
position();
}
else {
printf("ERROR: Wrong input!\n\r");
}
putc('>');
clear_buffer();
}
else if(inbuffer[buffer_cnt]==8) {
if(buffer_cnt>0) {
buffer_cnt--;
}
}
else if(buffer_cnt<3) {
buffer_cnt++;
}
else {
buffer_full=TRUE;
printf("\n\rERROR: Buffer is full, press ENTER to clear!\n\r>");
}
}
}
} //End while-loop
} //End main
|
the code is compiling without errors and is working.
for example:
cw-rotation -> position-count = 630, time = 0.65s
ccw-rotation -> position-count = -631, time = 0.65s
as a newbie in ccs-programming i will appreciate every hint to improve my code.
so, if you found something incorrect or soemthing you would do in another way, don't hesitate to post it.
kind regards,
Mitch B.
++++++++++++++++++++++
Formatting fixed. BBCode enabled.
- Forum Moderator
++++++++++++++++++++++ |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Jul 23, 2012 8:00 am |
|
|
on the forum - preserve indents by using the code buttons.
it is a major pain to try to parse what you submitted with indents trashed out.
i am also wondering about the wisdom of the 500us delay in the encoder port b reader. i think a state machine,without delay, is a better way to do the encoder reading than the approach you are using- as NO delay is required in an optimal program.
lastly - U do know there is a fine hardware based PWM generator in that PIC, right ??
unless you need such a LOW pwm core frequency, you would be well advised to use the excellent hardware PWM support that CCS offers.
Last edited by asmboy on Mon Jul 23, 2012 10:46 am; edited 1 time in total |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Mon Jul 23, 2012 8:04 am |
|
|
two points...
1) ALWAYS put 'errors' in the use RS232(...options...)
It will keep the hardware UART from 'freezing', 'stalling','hanging' or whatevere term you want to apply to when the rcv buffer gets more than 3 characters sent to it.
2) ONLY use 'fast_i/o' for very time critical programs.Unless you are very,very careful you stand a 99.9999% chance of causing program to fail as you've mis-set an I/O pin for input instead of output or vice versa.
The compiler will automatically do the 'right thing' as required,though it will take a coupe of microseconds.It's best to use 'standard I/O' and get your program up and running, then IF required tweak it by using fast I/O and set_tris....
3) When uploading code..there's a 'code' option when making a message.It'll format the program allowing it to be easily read.
4) create buffers in nice binary values like 16,32,etc.It'll run faster for some reason...others can explain...
hth
jay |
|
|
Mitch B.
Joined: 23 Jul 2012 Posts: 6
|
|
Posted: Tue Jul 24, 2012 1:53 am |
|
|
at first i want to thank you for your suggestions and i am sorry about the bad posting.
@asmboy:
Quote: | i am also wondering about the wisdom of the 500us delay in the encoder port b reader. i think a state machine,without delay, is a better way to do the encoder reading than the approach you are using- as NO delay is required in an optimal program. |
in similar posts i read that it's not the best idea to put a delay into an isr but without it i get the position printed on the screen while the servo is still moving.
Quote: | U do know there is a fine hardware based PWM generator in that PIC, right ?? |
jep, i know. i read hundreds of posts about how to get the servo-control done with this on-chip pwm generator till i found a answer from ttelmah. he said that it's not possible to create a 50Hz hardware pwm signal. the lowest frequency i can get is 610Hz, everything beneath would infect the resolution. the other thing was that the explaination for the ccp-module in the ccs-manual confuses me more than it should do. so i stick to the most common way with two timers.
due to the fact that i still have some questions about this hardware based PWM generator i would appreciate it if i could ask you some questions about it after this topic is cleared.
@temtronic:
Quote: | ALWAYS put 'errors' in the use RS232(...options...)
|
due to the lack of informations in the ccs-manual i tryed to put it like this
Code: | #use rs232(baud=19200,xmit=pin_C6, rcv=pin_C7, errors)
|
but this doesn't work because i get following warning ->
Warning 202 "Dart_Encoder.c" Line 34(5,8): Variable never used: rs232_errors
where's the fault?
Quote: | ONLY use 'fast_i/o' for very time critical programs
|
at the beginning of the project i have many problems with the right #int_rb-routine. sometimes the servo moves smoothly but i get position counts looking like this -> 0,1,0,1,2,3,4,5,6,7,8,9,8,9. sometimes the servoarm was jumping around during he moves from one position into another. to get rid of this i used fast I/O. anyway, i tried the code without it an it still works. so, i will dump it. but because of you i now understand this function a little bit better.
Quote: | create buffers in nice binary values like 16,32,etc.It'll run faster for some reason...others can explain... |
i would interpret your suggestion like this -> change
Code: | int16 inbuffer[4];
int16 buffer_cnt=0; |
to
Code: | int32 inbuffer[4];
int32 buffer_cnt=0; |
is this right?
i am very glad about your response. things are getting clearer now. i hope to read from both of you soon, because there are more things i have to ask.
so long,
Mitch B. |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1346
|
|
Posted: Tue Jul 24, 2012 8:01 am |
|
|
Quote: |
but this doesn't work because i get following warning ->
Warning 202 "Dart_Encoder.c" Line 34(5,8): Variable never used: rs232_errors
where's the fault?
|
This is not an error, this is a warning. Nothing is wrong with it. If you don't want to see the warning add:
near the beginning of your main. This is optional though as your code will run the same regardless of the warning.
I checked the manual. If you look at the #use rs232() section of the compiler manual it provides information on the ERRORS option and mentions how it uses the rs232_errors variable. Other options will also sometimes indicate how they affect rs232_errors if you read their descriptions. More information on the variable is discussed in the "purpose" section as well. You should check it out. |
|
|
Mitch B.
Joined: 23 Jul 2012 Posts: 6
|
blind as a bat |
Posted: Tue Jul 24, 2012 9:02 am |
|
|
@jeremiah:
i checked it out and now it's a little bit clearer but i couldn't find any hint in the manual for the advise you gave me ->
Quote: | put rs2323_errors=0; near the beginning of your main |
anyway, the warning is gone and everything seems to be fine.
Thanks a lot for your help jeremiah. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Jul 24, 2012 10:28 am |
|
|
can you post a CIRCUIT of the schematic design this code is supposed to work with?
being blind to the bigger picture makes it hard to comment with more specificity on your problems.
as for instance why a 50 hz PWM base frequency is required in the first place.
the schematic might clear up a lot, as servo code in particular,
does not operate in a vacuum . |
|
|
Mitch B.
Joined: 23 Jul 2012 Posts: 6
|
|
Posted: Wed Jul 25, 2012 2:59 am |
|
|
Negative asmboy, to my shame i am not able to upload a image of the schematic. and to be honest i don't have the time to figure it out how to do so.
May this will help:
The main informations i need are the accuracy of the servo movement and the time between two positions. i.e. how often does the servo reach the same position and how long does he need for this movement.
Therefore i need a value that represents the exact position of the servoarm and a routine that returns the estimated time for each position change.
With this value I should be able to confirm if the servo is suitable for higher applications in the future.
Main voltage - 5V (for servo, demoboard and encoder), two extern pull-up's(680Ohm) with direct connection to port_b-pin 4 and 5,
Quote: | why a 50 hz PWM base frequency is required in the first place. |
According to rc-forums the period for servos is 20ms -> 1/50Hz = 0,02s. therefore 50Hz. Besides this, I couldn't find any kind of information about which max. frequency my servo is capable of.
Please correct me if I get something wrong.
Thanks for your patience,
Mitch B. |
|
|
Mitch B.
Joined: 23 Jul 2012 Posts: 6
|
project accomplished! |
Posted: Mon Jul 30, 2012 1:27 am |
|
|
Hi again.
The following code is running and is free for everybody.
Thanks to everybody who helped me to understand a little bit more about CCS programming.
Please inform me if there were some troubles compiling the code or if you have some questions about it.
Code: |
// Compiler: CCS v4.057
#include <18F452.h>
#device HIGH_INTS=TRUE
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
#use delay(clock=10000000)
#use rs232(baud=19200,xmit=pin_C6, rcv=pin_C7)
#byte portb = 0xF81
#byte portc = 0xF82
// ****************************************************************************
#define TIMER0_RELOAD 252 //0,2ms
#define CONTROL_SERVO 1
#define MAX_VALUE 20
int1 buffer_full=FALSE;
int8 inbuffer[4]; //RS232 buffer
int8 buffer_cnt=0;
int8 selected_servo=0;
int8 mode=0;
unsigned int16 setpoint[1]={4300}; //Variable for servo-position
unsigned int16 temp=0;
int1 run = FALSE;
int1 direction = FALSE;
int16 pulse_max=0; //PWM-count
static int value;
static int16 rb_new = 0;
static int16 rb_old = 0;
unsigned int32 PosCnt = 0; //Positioncount
unsigned int32 PosRS232 = 0;
unsigned int32 TimeCnt = 0; //Speedcount
// ****************************************************************************
void move_time(void) {
if(PosRS232 == PosCnt) {
run = FALSE;
printf("Dauer[s]: %f\n\r",(float)(TimeCnt*0.004)); //20*0.2ms = 4ms
printf("TimeCnt: %ld\n\r",TimeCnt);
printf("PosCnt: %lu\n\r",PosCnt);
if (direction) printf("clockwise\n\r");
else printf("counter clockwise\n\r");
}
}
// ****************************************************************************
// ************************* INTERRUPT SUBROUTINE******************************
#INT_TIMER0 HIGH
void TIMER0_isr(void) {
set_timer0(TIMER0_RELOAD);
pulse_max++;
if(pulse_max == MAX_VALUE) {
TimeCnt++;
output_high(pin_c0);
set_timer1(60000+setpoint[0]);
pulse_max=0;
}
}
// ****************************************************************************
#INT_TIMER1 HIGH
void TIMER1_isr(void) {
output_low(pin_c0);
if (run==TRUE) { //cyclical call of move-time calculation
move_time();
}
}
// ****************************************************************************
#int_RB
void change_on_RB(void) {
rb_new = input_b();
delay_us (500);
value = rb_new^rb_old;
if (value & 0x10) {
if (rb_new & 0x10) {
if (rb_new & 0x20) { if(PosCnt >0) --PosCnt;
direction = FALSE;
}
else if(PosCnt < 65534) { ++PosCnt;
direction = TRUE;
}
}
/* else { //no need for my project. only if higher resolution is needed.
if (rb_new & 0x20) { if (PosCnt <65534)++PosCnt; }
else if (PosCnt > 0 ) --PosCnt;
}
}
else {
if (rb_new & 0x20) {
if (rb_new & 0x10) { if (PosCnt < 65534) ++PosCnt; }
else if(PosCnt > 0) --PosCnt;
}
else {
if (rb_new & 0x10) { if(PosCnt>0) --PosCnt; }
else if(PosCnt <65534) ++PosCnt;
}
}*/
rb_old = rb_new;
}
// ****************************************************************************
// ****************************** FUNCTIONS ***********************************
void clear_buffer(void) {
buffer_cnt=0;
inbuffer[0]=0;
inbuffer[1]=0;
inbuffer[2]=0;
inbuffer[3]=0;
buffer_full=FALSE;
}
void clear_counts(void) {
TimeCnt = 0;
run = TRUE;
PosRS232 = (int)(temp*182.0/999.0 + 0.5); //182 = max.PosCnt-value for Servopos.999
}
// ****************************************************************************
// ****************************************************************************
// MAIN
// ****************************************************************************
// ****************************************************************************
void main(void) {
rs232_errors =0;
set_tris_b(0x30);
// ****************************************************************************
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_128|RTCC_8_BIT);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
// ****************************************************************************
set_timer0(TIMER0_RELOAD);
// ****************************************************************************
// 0,4us * (65536 - (60000+setpoint[]))
// -> shortest Impuls (setpoint=0) = 0,7ms
// -> longest Impuls (setpoint=3700) = 2,2ms
set_timer1(60000+setpoint[0]);
// ****************************************************************************
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_RB);
enable_interrupts(GLOBAL);
// ****************************************************************************
// ****************************************************************************
printf("Press 'c' and ENTER to control Servo.\n\r");
printf("Range for servoposition: from '000' to '999'\n\r\n\r");
printf("Choose position:\n\r>");
while(TRUE) {
if(kbhit()) {
inbuffer[buffer_cnt]=getc();
if(buffer_full) {
if(inbuffer[buffer_cnt]=='\r') {
putc(inbuffer[buffer_cnt]);
putc('\n');
clear_buffer();
printf("puffer cleared\n\r>");
}
}
else {
putc(inbuffer[buffer_cnt]);
if(inbuffer[buffer_cnt]=='\r') {
putc('\n');
if((inbuffer[0]=='c')&&buffer_cnt==1) {
mode=CONTROL_SERVO;
printf("mode=CONTROL_SERVO\n\r");
}
else if((inbuffer[0]=='p')&&buffer_cnt==1) { //check PosCnt-value
printf("PosCnt=%lu\n\r",PosCnt);
}
else if((inbuffer[0]=='z')&&buffer_cnt==1) { //clear PosCnt-value manually
PosCnt = 0;
printf("PosCnt cleared\n\r");
}
else if((mode==CONTROL_SERVO)&&(inbuffer[0]>='0')&&(inbuffer[0]<='9')&&(inbuffer[1]>='0')&&(inbuffer[1]<='9')&&(inbuffer[2]>='0')&&(inbuffer[2]<='9')) {
temp=(inbuffer[0]-'0')*100.0+(inbuffer[1]-'0')*10.0+(inbuffer[2]-'0')*1.0; //calculation for servo-setpoint
setpoint[selected_servo]=(999-temp)*4.3043; //=4300/999;
clear_counts();
}
else {
printf("ERROR: Wrong input!\n\r");
}
putc('>');
clear_buffer();
}
else if(inbuffer[buffer_cnt]==8) {
if(buffer_cnt>0) {
buffer_cnt--;
}
}
else if(buffer_cnt<3) {
buffer_cnt++;
}
else {
buffer_full=TRUE;
printf("\n\rERROR: Buffer is full, press ENTER to clear!\n\r>");
}
}
}
} //End while-loop
} //End main
|
if you find some faults or something else don't hesitate to inform me. |
|
|
|
|
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
|