| 
	
	|  |  |  
	
		| View previous topic :: View next topic |  
		| Author | Message |  
		| rovtech 
 
 
 Joined: 24 Sep 2006
 Posts: 288
 
 
 
			      
 
 | 
			
				| 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: 288
 
 
 
			      
 
 | 
			
				|  |  
				|  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: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  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: 288
 
 
 
			      
 
 | 
			
				|  |  
				|  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: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  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: 288
 
 
 
			      
 
 | 
			
				|  |  
				|  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: 288
 
 
 
			      
 
 | 
			
				|  |  
				|  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: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  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: 9587
 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: 288
 
 
 
			      
 
 |  |  
		|  |  
		| Gabriel 
 
 
 Joined: 03 Aug 2009
 Posts: 1074
 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: 288
 
 
 
			      
 
 | 
			
				|  |  
				|  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: 288
 
 
 
			      
 
 |  |  
		|  |  
		| Ttelmah 
 
 
 Joined: 11 Mar 2010
 Posts: 19966
 
 
 
			    
 
 | 
			
				|  |  
				|  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
 
 |