CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Branching to the end of a loop
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

Branching to the end of a loop
PostPosted: Sat May 23, 2020 5:20 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Sat May 23, 2020 6:33 pm     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Sun May 24, 2020 7:37 am     Reply with quote

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: 19432

View user's profile Send private message

PostPosted: Sun May 24, 2020 7:49 am     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Sun May 24, 2020 8:11 am     Reply with quote

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: 19432

View user's profile Send private message

PostPosted: Sun May 24, 2020 9:54 am     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Sun May 24, 2020 12:32 pm     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Sun May 24, 2020 2:46 pm     Reply with quote

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: 19432

View user's profile Send private message

PostPosted: Mon May 25, 2020 12:45 am     Reply with quote

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: 9196
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon May 25, 2020 4:48 am     Reply with quote

heck, I'm impressed someone actually makes FLOWCHARTS !!!
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Mon May 25, 2020 1:46 pm     Reply with quote

Thanks for the excellent lesson Ttelmah.
I have saved that to my Program Control Notes.
I tried making a flowchart of it seen here:
https://www.dropbox.com/s/k8i5uqmk1o7lyxc/Continue%20%26%20Break%20HiRes.jpg?dl=0
If its wrong let me know. If its of any use you may use it or share.

For anyone interested I use yEd Graph Editor which is free in its simpler form.
Gabriel



Joined: 03 Aug 2009
Posts: 1067
Location: Panama

View user's profile Send private message

PostPosted: Mon May 25, 2020 8:46 pm     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Thu May 28, 2020 9:25 am     Reply with quote

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

View user's profile Send private message AIM Address

PostPosted: Thu May 28, 2020 12:23 pm     Reply with quote

Here is the revised flow chart
https://www.dropbox.com/s/fhpimeuol0yapun/Rotate%20Motors_4.jpg?dl=0
I notice 0xF1 in the code instead of 0xFE in setting the last speed.
I was not able to write my code with simple if() statements but the break: from a single for() loop made it easy.
Ttelmah



Joined: 11 Mar 2010
Posts: 19432

View user's profile Send private message

PostPosted: Thu May 28, 2020 12:53 pm     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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