View previous topic :: View next topic 
Author 
Message 
rovtech
Joined: 24 Sep 2006 Posts: 256

Reversing Direction in For() statement 
Posted: Wed Jul 15, 2020 2:18 pm 


I need to drive a stepper motor and graphic sweep in sync.
Integer i is used to calculate angles and step sets.
Running CCW I can use:
Code:  for(i=0; i<20; i++) // 0 to 19, total 20
{
// code here same as running forward
} 
Running CW this works:
Code:  for(i=19; i>=0; i) // 19 to 0, total 20
// for(i=20; i>0; i) // 20 to 1, total 20
{
// code here same as running backward
} 
Note that the line commented out will mess up i for the following code in the CW direction.
How can I write this using a simple
Code:  if(direction)
{run CCW}
else
{run CW} 
to select the correct loop where direction is just a one bit flag.
I also need to use other start and stop points between 0 and 180 degrees
This (partial) code is an example that works but I don't want to repeat a whole lot of identical code. I can use a function if I use global variables or it becomes complicated.
Code:  for (i=7;i<15;i++) // lines to be drawn CCW
{
angle=i*.175; // 10i deg converted to radians
xx=63*cos(angle); // use floating
x=63+xx; // but convert to integer
xx=63*sin(angle); // 63 is the radius
y=63xx;
draw_line(63,63,x,y); // start bottom middle,
// the LCD has 0,0 top left
} 
This section of code will become more complicated as only sections, or none, of the line will be drawn representing echos from sonar.
Don't worry about the code, it works. I need a suggestion on how to deal with the general challenge of changing direction. I cannot easily use a start and stop integer in the for() statement unless math is allowed as in
Code:  for(i=stop1; i>=start; i) 



Mike Walne
Joined: 19 Feb 2004 Posts: 1782 Location: Boston Spa UK


Posted: Wed Jul 15, 2020 2:46 pm 


Will something along the lines of (not in CCS code):
if i> stop then i = start;
if i< start then i =stop;
Someone here may have a clever way of reducing it one line of code.
Mike 


rovtech
Joined: 24 Sep 2006 Posts: 256


Posted: Wed Jul 15, 2020 2:49 pm 


Maybe something similar to how I reverse the motor
Code:  // Step the motor 10 degrees, 25 steps
for(j=0;j<25;j++)
{
if (direction) // CW
{
if (n == 4)
n=0;
else
n++;
output_c (step[n]);
}
else // CCW
{
if (n == 0)
n=4;
else
n;
output_c (step[n]);
}
delay_ms(10);
} 
This sequences thru a pattern of 4 codes a number of times to turn 10 degrees forward of backward.
Edit: No this still duplicates the code but in this case it is only one line so does not matter.
I tried to mirror x as in
Code:  if(direction)
x=63+xx;
else
x=63xx; 
but it does not redraw the same sector in reverse, it just flips it left to right. 


rovtech
Joined: 24 Sep 2006 Posts: 256


Posted: Wed Jul 15, 2020 6:16 pm 


I found a way to do it. I just had to think differently. Sorry for wasting anyone's time.
Code: 
// set start and stop
i=start;
while(1)
{
for (k=start;k<=stop;k++) // lines to be drawn
{
// LCD Stuff
angle=i*.175; // 10 deg/scan, convert to radians
xx=64*cos(angle); // 63 is radius of scan lines
x=63+xx;
xx=64*sin(angle); // xx is floating point
y=63xx; // converted to integer
draw_line(63,63,x,y); // LCD function
// Step the motor 10 degrees, 24 steps
for(j=0;j<25;j++) // geared stepper motor
{
if (direction) // CW
{
if (n == 4)
n=0;
else
n++;
output_c (step[n]); // step codes
}
else // CCW
{
if (n == 0)
n=4;
else
n;
output_c (step[n]);
}
delay_ms(20); // time for stepper motor
}
// set i according to direction, keep within start/stop
if(direction)
{
i++;
if(i>stop)
i=stop;
}
else
{
i;
if(i<start)
i=start;
}
} // end of for loop
clear_LCD(); // clear the LCD
direction = direction ^ 1; // alternate direction CW & CCW
} // end of while loop 
There is a slight problem that start cannot be 0 unless i is a signed integer.
start and stop reset was wrong, now corrected
Last edited by rovtech on Thu Jul 16, 2020 1:49 pm; edited 1 time in total 


rovtech
Joined: 24 Sep 2006 Posts: 256


Posted: Thu Jul 16, 2020 1:48 pm 


When i=0 in the code below, why does y end up being 62 instead of 63?
angle is float and so is xx. So when i is 0, angle should be 0, sin(0) is 0, so xx should be 0, and 630 should be 63, not 62.
Am I wrong in using this to convert floating to integer?
Code:  // LCD Stuff
angle=i*.175; // 10 deg/scan, convert to radians
xx=64*sin(angle); // xx is floating point
y=63xx; // converted to integer, add offset
draw_line(63,63,x,y); // LCD function 
I have to be careful of my units. For the coordinates the LCD is 0 to 63 but for the radius length to use in the equations it is 64.
The LCD is mapped 0,0 at top left so I have to add 63 to x and subtract y from 63 to get to the locations in normal coordinates. 


Mike Walne
Joined: 19 Feb 2004 Posts: 1782 Location: Boston Spa UK


Posted: Thu Jul 16, 2020 2:27 pm 


Try printing out the values of all variables which change as you go line by line through the code.
Should tell you where the error is!
Mike 


rovtech
Joined: 24 Sep 2006 Posts: 256


Posted: Thu Jul 16, 2020 3:03 pm 


Thats how I found the problem, xx displayed in float is 0.00 and y is displayed in integer as 0.
If I plug in 0 for xx I get 630=63.
With the program the way it is above I get 63y=62.
C promotes the right side float to the left side integer I thought, and it seems to do that.
Edit: actually I get 0.004 for xx going one way, as i is incremented, and 0.00 going the other way.
As i changes from 0,1,2 then 2,1,0 these values for xx are displayed consistently.
0.00
11.14
21.94
21.94
11.14
0.004
0.00 etc
This is how I display on the LCD:
Code:  angle=i*.175; // 10 deg = 0.175 radians
xx=64*cos(angle); // x in floating point
x=63+xx; // convert x to integer, add offset
xx=64*sin(angle); // y in floating point
text_position(0,0); // line, column
sprintf(buff, "xx = %f", xx);
send_str(buff);
delay_ms(1500); // for testing
y=63xx; // convert y to integer, add offset
draw_line(63,63,x,y); // draw line from bottom center of LCD 
and I move below "y=63xx;" and adjust to display y in integer format. 


rovtech
Joined: 24 Sep 2006 Posts: 256


Posted: Thu Jul 16, 2020 6:32 pm 


Someone please help.
If 64*sin(0) = 0.004 then 630.004 = 62.996
When 62.996 is promoted to integer is the fractional part just dropped giving 62?
That does not make much sense. How do I round a small number to 0?
Code:  i=0
angle=i*.175; // 10 deg = 0.175 radians
xx=64*sin(angle); // y in floating point
n=xx; // to drop the fractional part
y=63n; // convert y to integer, add offset
draw_line(63,63,x,y); // draw line from bottom center of LCD 
now I get for y compared to the original calculation in the second column and fraction dropped in the third column
63 62.996 62
52 51.86 51
42 41.06 41
that seems to be what is happening. sin(0) is resulting in a very small number but a big error when used then promoted to integer.
this is really dumb! There has to be a better way. 


PCM programmer
Joined: 06 Sep 2003 Posts: 21450


Posted: Thu Jul 16, 2020 7:07 pm 


The program below will display the following in MPLAB vs. 8.92 simulator:
Quote:  value = 63
value = 63

Test program:
Code:  #include <18F46K22.h>
#fuses NOWDT
#use delay(internal=4M)
#use rs232(UART1, baud=9600, ERRORS)
#define round(x) (signed int16)(x<0.0 ? x0.5 : x+0.5)
//=================================
void main()
{
float temp = 62.996;
printf("value = %ld \r", round(temp));
temp = 62.996;
printf("value = %ld \r", round(temp));
while(TRUE);
} 



rovtech
Joined: 24 Sep 2006 Posts: 256


Posted: Fri Jul 17, 2020 7:47 am 


Thanks PCM programmer.
I looked for a rounding function in several of my C books including K&R and it is not in any index. Casting and Promoting are in the index but no Round.
I just found it by googling but that should not be where to find it. Maybe we should all throw our books out.
https://fresh2refresh.com/cprogramming/carithmeticfunctions/croundfunction/
No wonder the Boeing 737 Max are crashing and please, everyone, don't trust those auto features on your car. My auto braking in cruise control works most of the time but not always. Same with lane keeping and other alerts. 


rovtech
Joined: 24 Sep 2006 Posts: 256


Posted: Fri Jul 17, 2020 9:01 am 


OK, I'm stupid. I cannot get round() to work. I tried all the variations below and get
"Error 12 "Scanning Sonar.c" Line 91(13,14): Undefined identifier  round"
Code:  xx=64*sin(angle); // y in floating point
xx=round(64*sin(angle));
round(xx)=64*sin(angle);
xx=round(xx);
y=63xx; // convert y to integer, add offset 
How am I supposed to use it?
My program has #include <math.h>
Maybe this function does not exist in PCM. I don't see it in the manual.
I just saw your
Code:  #define round(x) (signed int16)(x<0.0 ? x0.5 : x+0.5) 
What is all that about? I will have to study on it.
I was looking at the google reference on how to use it. I cannot find it in my books. 


PCM programmer
Joined: 06 Sep 2003 Posts: 21450


Posted: Fri Jul 17, 2020 10:12 am 


Do you understand #define statements ? Just copy and paste that line
above main() in your program. You've been on this forum since 2006.
How can you don't know this ? 


rovtech
Joined: 24 Sep 2006 Posts: 256


Posted: Fri Jul 17, 2020 11:21 am 


Give me a break I'm 80 years old and not a professional programmer.
Sure I could do that but I don't understand it and it may not work, although it seems to.
What I am understanding is that
1. The function round() does not exist for the CCS C compiler, and maybe not in the K&R definition, so you wrote one
2. Google shows round() as a regular function so it must exist for some compilers.
3. In the function you created round(x) is replaced with (signed int16)(x<0.0 ? x0.5 : x+0.5)
which means
Code:  if(x<0)
x=x0.5
else
x=x+0.5 
then cast it to a 16 bit signed integer?? I don't understand the () () pair of brackets together. I have not run into that.
So it will do this for a floating point number? Yes it appears to
I am using 8 bit integers so I changed to int8 and it still works.
I appreciate the help but I do like to understand what I'm doing
So my code is now working
Code:  #define round(x) (signed int8)(x<0.0 ? x0.5 : x+0.5)
angle=i*.175; // 10 deg = 0.175 radians
x=63+round(64*cos(angle)); // calculate x, convert to integer, add offset
y=63round(64*sin(angle)); // calculate y, convert to integer, add offset
draw_line(63,63,x,y); // draw line from bottom center of LCD 



gaugeguy
Joined: 05 Apr 2011 Posts: 183


Posted: Fri Jul 17, 2020 11:49 am 


Programming and C aside:
When converting a float number to an integer there are different methods. One is truncating. Anything after the decimal point is dropped and only the whole number remains. Another method is to do rounding. There are different forms of rounding but the most common is if the decimal portion is 0.5 or larger then the number goes to the next higher integer, if the decimal portion is less than 0.5 then the decimal portion is discarded, or truncated.
One way to implement this type of rounding is to add 0.5 to the number before truncating the decimal portion. 


Ttelmah
Joined: 11 Mar 2010 Posts: 17202


Posted: Fri Jul 17, 2020 12:16 pm 


It casts the result into a signed int8.
This truncates.
The 'result' is the result of if the value is <0, it subtracts 0.5 from this.
otherwise it adds 0.5.
So if you start with a float value of (say) 1.4, 0.5 is added to give 1.9,
then you get 1 as the integer.. If howver you have 1.5, 0.5 added gives
2.0, which then truncates to '2'.
Similarly, 0.5 has 0.5 subtracted, so gives 1 after the truncation, while
0.4, still gives 0. 


