|
|
View previous topic :: View next topic |
Author |
Message |
rovtech
Joined: 24 Sep 2006 Posts: 262
|
Branching to the end of a loop |
Posted: Sat May 23, 2020 5:20 pm |
|
|
In the flowchart seen here
https://www.dropbox.com/s/sq4u7oco0o18em0/Rotate%20Motors1.jpg?dl=0
when the program branches to the right to the box 'Target Reached' I need to then go to the beginning of the while(1) loop.
I could use
Code: | location1: ;
while(1) {
--------
goto locastion1;
------------
} |
or somehow a break statement but I think that will only exit the current loop.
I could try a switch statement.
What would be the best approach (for someone who grew up with Fortran).
I usually manage with "if, else if, else" but it won't work here.
Edit:
The object of the program is to slow the motor as it reaches a match of Required Position and Actual position. The Required position comes down 300' of cable from a joystick and gets into the program on an I2C interrupt. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat May 23, 2020 6:33 pm |
|
|
To go to the top of the inside of the loop, you can use the 'continue'
command. It's in any C language book. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun May 24, 2020 7:37 am |
|
|
Thanks PCM programmer. I found it in minutes once I knew what I was looking for in 700 pages of "Teach yourself C in 21 Days". I skipped over it yesterday between 'break' and 'goto' but maybe it is more useful than I realized.
It looks like it would work but what if I want to exit from a nested loop to the start of the outer loop?
The CCS manual seems to discourage the 'goto' and implies I have to know the address while my C book says I can use it like Fortran with a label: ;
I need C on my laptop so I can experiment more easily. I only have CCS C and MPLAB and would have to set up a PIC with switches and lights but I'm not looking for a new project or distraction. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Sun May 24, 2020 7:49 am |
|
|
Goto is dangerous in C.
The way to break out of one loop is to use a flag for the loop:
Code: |
int1 loop;
while (TRUE)
{
loop=TRUE;
while (loop)
{
//Then when you want to exit the inner loop:
loop=FALSE;
continue;
}
}
|
Generally the core 'problem' with goto, comes if you use it inside a
subroutine. It can then result in you jumping out of the loop and leaving
the stack unbalanced... |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun May 24, 2020 8:11 am |
|
|
Thanks Ttelmah, very nice. I will try that.
I want to go to the start from some place in the middle, but not all the time, and without completing the loop. See my flowchart.
Will 'continue' do that? I was going to play with that today.
Another consideration is that the ISR can fire at any time with a new position and I don't want to mess that up. I am going to use a separate variable name so it only gets updated at the start of the loop, not in some random place in the middle. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Sun May 24, 2020 9:54 am |
|
|
Yes.
Continue 'executes the next iteration of the loop'. Takes you back to the
start and re-evaluates the loop condition. If 'loop' is turned off at this point,
you drop through. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun May 24, 2020 12:32 pm |
|
|
Can continue in my code really exit the nested 'if' statements without creating havoc?
Code: | While(1)
{
---------
// set port position
if (!bit_test(rotate_status,4)) // set position if no port jam flag
{
set_adc_channel (4); // points a/d at channel 4, actual port position
delay_us(10); // wait
act_p_posn = read_adc(); // starts conversion, reads ADC, stores in p_posn
if (p_stop_flag) // if motor is stopped
{
act_p_posn = act_p_posn & 0xFC; // create deadband
req_p_posn = req_p_posn & 0xFC;
}
if (act_p_posn > req_p_posn) // if actual port position less than reqd posn
p_rot_down(); // rotate port up
else if (act_p_posn < req_p_posn) // if actual port position more than set posn
p_rot_up (); // rotate port down
else
{
p_stp_rot (); // else port posn OK, stop
p_stop_flag = TRUE;
continue;
}
}
--------- // the rest is stuff I don't want done if continue
} // end of while(1) loop |
|
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun May 24, 2020 2:46 pm |
|
|
Two problems just occurred to me.
1. I cannot do what I intended. At the bottom of the flowchart I need to do the same thing for a second motor and if the first motor reaches its destination the second motor code will never be executed. Worse, the other motor could be left running and never see the software stops or overcurrent lockout when it reaches the mechanical limit.
2. I cannot control the PWM speed for two motors independantly running on the same PIC (I don't think). This is not a huge problem as the motors track together at the present time so will both need to be slowed at the same time. The speed will be controlled by the first to get close to position.
Edit: yes the PWM can be run at different speeds (set_pwm1_duty(speed), set_pwm2_duty(speed)).
I re-drew the flowchart and this may work. It avoids aborting the while(1) loop. I will modify my present (working) software and see if the speed correcting works and actually has any benefit.
https://www.dropbox.com/s/6n8g14hkuy14d09/Rotate%20Motors_2.jpg?dl=0
I don't know if the 'stop flag set?' is necessary and can be replaced with just the 'clear stop flag'. I guess checking a bit takes the same time as clearing a bit. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Mon May 25, 2020 12:45 am |
|
|
Yes it can,
Let's go through things carefully:
First:
Code: |
while (TRUE)
{
continue; //This line would take you back to the 'while' line
break; //If instead we had this, it would take you to the 'something'.
}
something;
//Now...
int1 loop;
while(TRUE) //7
{
loop=(TRUE);
while(loop) //1
{
while(TRUE); //4
{
//
continue; //This would take us to '4'
//
break; //This would take us to '2' and then to '1', then '4'
//
loop=FALSE;
break; //but if instead we had these, it would take us to
//'2', '1', '3'
}
//2
}
//3
//something else;
loop=TRUE;
while (loop) //6
{
while(TRUE) //5
{
//
continue; //This would take us to '5'
//
break; //This would take us to '8', '6 and then to '5'
//
loop=FALSE;
break;
//but if instead we had these, it would take us to
//'8, '6, '7'
}
//8
}
}
|
So by using the correct combinations of flags, breaks, and continue, you
can elect to exit loops, stay in loops, move on to another loop, restart etc.. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Mon May 25, 2020 4:48 am |
|
|
heck, I'm impressed someone actually makes FLOWCHARTS !!! |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Mon May 25, 2020 8:46 pm |
|
|
This thread was SO good.
Thanks Ttelmah for that explanation! _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Thu May 28, 2020 9:25 am |
|
|
I got my program working, but it was not easy (for me).
This amusing flowchart (given as an example in the yEd Graph Editor) helps explain my experience. Please take a look.
https://www.dropbox.com/s/ivz9pd8vdn8pypf/ProblemSolving.jpg?dl=0
Someone asked once why I was using the obsolete 16F722 on another project and suggested a 16F882 so I started using them.
I have a policy of “if it ain’t broke don’t fix it” but it seemed reasonable to use this chip on my ROV re-build with PCBs replacing the wire wrap. The new boards did not work and someone on this forum pointed out that the 16F882 said in the errata they did not work as an I2C slave. Since the PCBs were made I needed a pin compatible replacement and someone (Microchip?) suggested the 16F1938. These worked fine until I tried to ‘improve’ the software that started this thread.
I don’t know why the 16F1938 worked before because PWM1 and PWM2 are not on the CCP1 and CCP2 pins (now ECCP1 and ECCP2), they are on ECCP4 and ECCP5. I found this by double checking the data sheet while troubleshooting. I went to get a 16F882 and saw my warning about slave use written on the package so went back to the 16F722. So much for the flowchart above!
My program needed several goto as I mentioned before. A continue or break would not work on my while(1) loop. I solved the problem by using two “for()” loops that only run once. A “break;” in these loops exits them without skipping required parts of the program. Now my motors run full speed and slow as they approach the target position without overshooting. The masking creates dead-bands of various widths.
I’m sure there is a more elegant way to do this but this works without hardware changes. Look for the break; statements, there are 8 of them.
Code: | ///////////////////////////////////////////////////////////////////////////////////////////////
/// SIDE THRUSTER ROTATE FIRMWARE Rotate PIC_v2.0.c Last modified: 28 May 2020 ///
/// This PIC is a slave at address 0x16 **WORKING** ///
/// Receive and set rotate positions for side thrusters on I2C from master. Control Lamps. ///
/// Set Status bits if any leaks or excess motor currents. Return Status to Main PIC ///
/// ///
/// Status bits: 0 Main ROV Leak, 1 Main Thr Jam, 2 Port Pod Leak, 3 Stbd Pod Leak, ///
/// 4 Port Rot Jam, 5 Stbd Rot Jam, 6 Port Thr Jam, 7 Stbd Thr Jam ///
/// ADC ports: Stbd Posn AN0, Port Posn AN4, Main leak AN2, Stbd Leak AN1, Port Leak AN3, ///
/// Stbd Jam AN9, Port Jam AN12 ///
///////////////////////////////////////////////////////////////////////////////////////////////
/* Pre-processor directives */
#include <16F722.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=8MHZ) // osc defaults to 8 MHz
// #use fast_io (B)
#use i2c (SLAVE, FORCE_HW, SCL=PIN_C3, SDA=PIN_C4, address=0x16)
#bit CKP=getenv("BIT:CKP")
// Function Prototypes
void s_rot_up (void);
void s_rot_down (void);
void s_stp_rot (void);
void p_rot_up (void);
void p_rot_down (void);
void p_stp_rot (void);
// global variables
int rpp, rsp = 0x128; // required port & stbd positions from console
int rotate_status = 0x00; // initialize to no trips
int status_reset = 0x00; // initialize to all resets
int consw = 0x00; // initialize Console Switches OFF
// Interrupt on I2C
#INT_SSP
void ssp_interrupt () // have an interrupt
{
int incoming, state; // variables
state = i2c_isr_state (); // get state
if (state <= 0x80) // master is sending data
{
if (state == 0x80) // throw away device address if state = 0x80
incoming = i2c_read (2); // but do not release i2c bus
else
incoming = i2c_read (); // throw away device address if state = 0
if (state == 1) // first data received is port position
{
rpp = incoming;
}
if (state == 2) // second data received is stbd position
{
rsp = incoming;
}
if (state == 3) // third data received Console Switches
consw = incoming;
if (state == 4) // third data received is status reset
status_reset = incoming;
}
if (state >= 0x80) // master is requesting data from slave
{
i2c_write (rotate_status); // send rotate status
CKP = TRUE; // release the bus
}
}
//* The main function */
void main(void)
{
// declare variables
// req_p_posn and req_s_posn are Global variables (required p & s positions)
int8 act_p_posn, act_s_posn; // actual port & stbd positions
int8 req_p_posn, req_s_posn; // required port and stbd positions
int8 Mleak, Pleak, Sleak; // leak detectors
const int max_cur = 71; // motor trip point 24=700mA, 31=1.2 amps
int8 mot_cur = 0; // motor current
int8 LED_count = 0x00; // used to slow down LED flashing
const int slow = 50; // slow speed
const int medium = 128; // medium speed
const int fast = 250; // fast speed
int i=0; // loop counter
// initialize port directions
set_tris_a (0b00111111); // set Port A
set_tris_b (0b11001001); // set Port B
set_tris_c (0b10011000); // set Port C
// setup for PWM
setup_CCP1 (CCP_PWM); // STBD POSITIONING
setup_CCP2 (CCP_PWM); // PORT POSITIONING
setup_timer_2 (T2_DIV_BY_16, 255, 2); // freq of 488 Hz
// initialize rotate motors
req_p_posn = req_s_posn = 128; // initialize horizontal positions
s_stp_rot(); // initialize both rotate to stop
p_stp_rot();
// setup ADC
setup_adc (ADC_CLOCK_DIV_32); // configures ADC, 2-6 us reqd in .h
setup_adc_ports (sAN0|sAN4|sAN9|sAN12); // stbd posn, port posn, stbd current, port current
setup_adc_ports (sAN1|sAN2|sAN3); // stbd pod leak, main leak, port pod leak
// Lamps and LED OFF
output_low(PIN_A7); // Stbd Lamp OFF
output_low(PIN_C5); // Port Lamp OFF
output_low(PIN_C6); // LED OFF
// setup interrupts and initialize PWM
enable_interrupts (INT_SSP); // enable I2C interrupt
enable_interrupts (GLOBAL);
set_pwm1_duty (medium); // set PWM1 to half speed
set_pwm2_duty (medium); // set PWM2 to half speed
// main loop
while (1) // endless loop
{
// control the lamps
if (bit_test(consw,1)) // Port Lamp ON
output_high(PIN_C5);
else
output_low(PIN_C5); // or OFF
if (bit_test(consw,2)) // Stbd Lamp ON
output_high(PIN_A7);
else
output_low(PIN_A7); // or OFF
// Check for Leaks
// select ADC channel to read Main Leak
set_adc_channel (2); // points a/d at channel 2
delay_us(10); // wait
Mleak = read_adc(); // starts conversion, reads ADC, stores Mleak
if (Mleak <= 200) // if leak
bit_set(rotate_status,0); // set Main Leak Bit in Rotate_Status
else bit_clear(rotate_status,0);
// select ADC channel to read Port Pod Leak
set_adc_channel (3); // points a/d at channel 3
delay_us(10); // wait
Pleak = read_adc(); // starts conversion, reads ADC, stores Mleak
if (Pleak <= 200) // if leak
bit_set(rotate_status,2); // set Port Pod Leak Bit in Rotate_Status
else bit_clear(rotate_status,2);
// select ADC channel to read Stbd Pod Leak
set_adc_channel (1); // points a/d at channel 1
delay_us(10); // wait
Sleak = read_adc(); // starts conversion, reads ADC, stores Mleak
if (Sleak <= 200) // if leak
bit_set(rotate_status,3); // set Stbd Pod Leak Bit in Rotate_Status
else bit_clear(rotate_status,3);
// update from ISR
req_p_posn = rpp; // required port position rpp
req_s_posn = rsp; // required stbd position rsp
// check port rotate motor current and set overload
set_adc_channel (12); // points a/d at current sense
delay_us(10); // wait
mot_cur = read_adc(); // reads motor current
if (mot_cur >= max_cur) // if over current port rotate
{
bit_set(rotate_status,4); // set Port jam Bit in Rotate_Status
p_stp_rot(); // and stop motor
}
if(bit_test(status_reset,4)) // if RESET
bit_clear(rotate_status,4); // clear jam
// set port position
for(i=0;i<1;i++)
{
if (!bit_test(rotate_status,4)) // set position if no port jam flag
{
if((act_p_posn & 0xFC)==(req_p_posn & 0xFC)) // if at target
{
p_stp_rot(); // stop port motor
break; // exit port position routine
}
// if not at destination set motor directions and speed
set_adc_channel (4); // points a/d at channel 4, actual port position
delay_us(10); // wait
act_p_posn = read_adc(); // starts conversion, reads ADC, stores in p_posn
if (act_p_posn > req_p_posn) // if actual port position > reqd posn
p_rot_down(); // rotate port down
else // if actual port position < set posn
p_rot_up (); // rotate port up
// set port motor speed
if((act_p_posn & 0xF0)!=(req_p_posn & 0xF0)) // if not at coarse position set speed to fast
{
set_pwm2_duty(fast);
break;
}
if((act_p_posn & 0xF8)!=(req_p_posn & 0xF8)) // if not at close position set speed to medium
{
set_pwm2_duty(medium);
break;
}
if((act_p_posn & 0xF1)!=(req_p_posn & 0xF1)) // if not at position set speed to slow
{
set_pwm2_duty(slow);
break;
}
} // end of set port motor position
} // end of Port Motor for() loop
// check stbd rotate motor current and set overload
set_adc_channel(9); // points a/d at curret sense
delay_us(10); // wait
mot_cur = read_adc(); // reads motor current
if (mot_cur >= max_cur) // if over current port rotate
{
bit_set(rotate_status,5); // set stbd jam Bit in Rotate_Status
s_stp_rot(); // and stop motor
}
if(bit_test(status_reset,5)) // if RESET
bit_clear(rotate_status,5); // clear jam
// set stbd position
for(i=0;i<1;i++)
{
if (!bit_test(rotate_status,5)) // set position if no stbd jam flag
{
if((act_s_posn & 0xFC)==(req_s_posn & 0xFC)) // if at target
{
s_stp_rot(); // stop and exit
break;
}
// if not at destination set motor directions and speed
set_adc_channel (0); // points a/d at channel 0, actual stbd position
delay_us(10); // wait
act_s_posn = read_adc(); // starts conversion, reads ADC, stores in s_posn
if (act_s_posn > req_s_posn) // if actual stbd posn > reqd posn
s_rot_down(); // rotate port down
else // if actual stbd posn < reqd posn
s_rot_up (); // rotate stbd up
// set stbd speed
if((act_s_posn & 0xF0)!=(req_s_posn & 0xF0)) // if not at coarse position set speed to fast
{
set_pwm1_duty(fast);
break;
}
if((act_s_posn & 0xF8)!=(req_s_posn & 0xF8)) // if not at approx position set speed to medium
{
set_pwm1_duty(medium);
break;
}
if((act_s_posn & 0xF1)!=(req_s_posn & 0xF1)) // if not at fine position set speed to slow
{
set_pwm1_duty(slow);
break;
}
} // end of set stbd motor position
} // end of Stbd Motor for() loop
// flash LED
if (++LED_count >= 230) // every 60 passes
{
output_toggle(PIN_C6); // flash LED
LED_count = 0; // and reset counter
}
} // end of endless while loop
} // end of main function
// functions
void p_rot_up (void)
{
output_low (PIN_B1);
output_high (PIN_B2);
}
void p_rot_down (void)
{
output_high (PIN_B1);
output_low (PIN_B2);
}
void p_stp_rot (void)
{
output_low (PIN_B2);
output_low (PIN_B1);
}
void s_rot_up (void)
{
output_low (PIN_B4);
output_high (PIN_B5);
}
void s_rot_down (void)
{
output_high (PIN_B4);
output_low (PIN_B5);
}
void s_stp_rot (void)
{
output_low (PIN_B4);
output_low (PIN_B5);
}
// end |
|
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Thu May 28, 2020 12:53 pm |
|
|
Your comments about the chips you looked at make it worth
reiterating an old 'adage' made here by many of the 'old hands'.
Always check the errata before committing to a particular PIC.....
PIC's unfortunately often have some really nasty problems. You need
to double check that the PIC that apparently fits your needs, doesn't
have a 'hidden' issue. |
|
|
|
|
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
|