I’m having a very tedious coding problem and I know it has something to do with the types/type casting in C.
I was writing/testing some PID code. It worked fine(I was just tweaking gains), but I noticed some overflow, so I changed alot of my variables from ints to longs. Now when I try to print, nearly everything prints as zero. Here is the code in question:
/*
* pid_output
*
* This function outputs a value using a PID control loop for the specified PID struct.
* Takes a measurement, maximum output, and index of the gains for this loop in EEPROM.
*/
int Pid_Output(Pid_Struct * pid, long measurement,int maxOutput,int eeprom_index)
{
int output = 0;
//Get smaller names for the gains.
int pn = EEPROM_Read(eeprom_index);
int pd = EEPROM_Read(eeprom_index+1);
int in = EEPROM_Read(eeprom_index+2);
int id = EEPROM_Read(eeprom_index+3);
int dn = EEPROM_Read(eeprom_index+4);
int dd = EEPROM_Read(eeprom_index+5);
long p, i, d;
pid->error = pid->setPoint-measurement;
if( ABS(pid->error) < (long)pid->tolerance )
pid->error = 0;
//Accumlate error into the error_i and set the last error.
pid->error_i+=pid->error;
pid->error_d = (pid->error - pid->error_last);
pid->error_last = pid->error;
//This is the PID part:
p =((pid->error * (long)pn) / (long)pd);
i= ((pid->error_i * (long)in) / (long)id);
d= (((long)pid->error_d * (long)dn) / (long)dd);
output = p + i + d;
//DEBUG UNCONVENTIONAL
**printf("\rMeasure: %d Target: %d Err: %d Err_I: %d Err_d: %d Output: %d Max: %d",measurement,pid->setPoint,
(int)p,(int)i,(int)d, (int)output, (int)maxOutput );**
//Make sure we're in bounds
if(ABS(output) > maxOutput )
output = maxOutput*SIGN(output);
output+=pid->offset;
return output;
}
(Sorry about some of the indentions, got screwed up when I copied and pasted)
so what is happening here? the value for “output” prints out fine, but everything else comes up as zero… pn, pd, etc are non zero…
I could be wrong, I havent done much with any thing other than ints on the robot but If you are overflowing an int and declared the variable as a long instead then when you print dont you need to put long in front of the variable name in the print statement rather than int.
You know, I don’t quite see why you converted everything to long. The two types are essentially the exact same: both are signed, 4 byte numerical variables. It is strange that you would have this problem by switching this yes, but I don’t see why you would think it would change anything. In my experiences, I’ve never seen any difference at all between the two, except for the name. (I may be missing something, but if I am, then I’ll learn something here too) If you were trying to make the variable larger, to prevent overflow, the only way you can go bigger is to use a long long or long long int.
Also, could you provide some sample values that those variables might have. If it is indeed a casting problem, and I have an idea of what those values should be, I can run some test code and probably figure it out.
they’re not both 4 bytes. For some reason, an int is two bytes here. I found this out by printing a variable(int type) that was being incremented. When it was incremented pasted 32,xxx it overflowed and went back to -32,xxx. A long is bigger than an int. it wouldn’t make much sense if it was the same size.
EDIT: Also, neither are unsigned, I have no idea where you got this from.
The two types are essentially the exact same: both are unsigned, 4 byte numerical variables
Both are signed values unless “unsigned” specifier user.
C18 2.1 Data Types and Limits in user guide:
char : signed 8 bits
int, short : signed 16 bits
short long: signed 24 bits
long : signed 32 bits
float : 32 bits
double : 32 bits
The v2.4 C18 library guide says it supports size qualifiers, so could try %ld instead of %d for the long variables. Drop the int cast if using %ld.
There is a lot of your declarations not shown (such as your pid structure) but I’m guessing your issue is with the printf statement. “measurement” is declared as a long at the top of your function and being passed to printf as a long but printf is expecting an int. It should be cast as “(int) measurement”. This would throw off all of your stack variables by two bytes each.
ha…it sickens me that this was actually the problem. This was the THIRD time I have been screwed by those printf’s in C(used to java, C++). Thank You. That fixed the problem, but I’m curious as to how this works. Why does it throw off the stack variables by two bytes each?(Not that I know much about the “stack” anyways:confused: ) and how did you know this? Could you point me to a paper or something that explains how some of these “inner workings” work?
EDIT: I knew it expected ints and I forgot that I changed measurement to a long, but I still wouldn’t have expected that one mistake to mess up the rest of the function.
Well, you can’t really call this a paper, but it’s certainly got what you’re looking for, and then some. It’s one of the most influential documents in my “career” as a programmer. It deals with assembly language, the lowest programming language there is, unless you count binary as a language. It’s pretty mind-boggling stuff, but if you can grasp the basic concepts, you won’t believe how much your programming skills will improve. If you can learn and understand assembly, diagnosing code and logic problems will become almost intuitive and you’ll be just as much better at coding them in the first place. Knowing assembly gives you a deep understanding of how C and other languages actually work, and how computers and processors themselves work. Of course you could just skip to the section on the Stack, and other things, but I’d recommend reading as much of it as you can, eventually.
However, you do still have a slight problem, I believe. You’re casting your 4 byte longs as 2 byte ints. So, when looking at the prints, it may seem like they’re overflowing, because you’re not seeing the entire variable through the printf function. In memory, though, the value is not overflowing (if it’s large enough to overflow 2 bytes) because its being stored as 4 bytes. If you wanted to see the whole variable, you need to use a printf escaped character (like %i and %d) that is used for longs. I can’t remember what it is at the moment, though.
printf can be passed a variable number of arguments. The passed arguments are saved onto the stack and end up just a long list of bytes - no delimiters. The format statement is parsed to determine the number and size of the passed arguments. So, if measurement is a long - 4 bytes are passed because the caller knows its data type. But if the format statement says %d - print out an int - then the only the first two bytes are pulled from the list of argument bytes. The next %d will then print out the last two bytes of measurement, etc. In C, this is a varargs routine.
I should put a caveat on this… if you did a %c - print out a character, printf wouldn’t pull just one byte from the argument list… it would pull two bytes. Thats because C18 promotes all smaller sized arguments to int before passing them to printf.
And don’t hate C just because the C18 implementation of C is somewhat, um, quirky.
I’ve been working with embedded processors for over 25 years (and vacuum tubes, et cetera for 10 years before that). I think that I first learned about C and stack variables in college (circa 1978).
dcbrown’s explanation is correct. When viewed from a pure “software” perspective, the fact that this processor passes parameters on the stack is, perhaps, confusing. C allows a variable argument list and it is up to the called function (in this case, printf) to parse the format string (the first parameter of the function is actually a pointer to a string) and determine how many variables there are and how large each one is. Your first %d in your format string indicated a 2 byte variable was the second parameter on the stack. When you put a long variable in the calling statement, 4 bytes were pushed onto the stack. That meant that all of your subsequent parameters were 2 bytes off…
I would refer you to K&R for more details. It was the bible back in the 70’s and, in my opinion, it is still the bible.
For specifics on C as implemented for this processor, I’d look here.
Last time I’ll make a printf mistake…once tried to concatenate strings in a prtinf…printf("measurement: "+measurement);…have to start remembering what language I’m in:yikes: