Am I going right with the assumption that in the code, W is 0, F is 1 and BANKED is 1, used as constants to increase readability?
Yes, these are standard defines of the assembler (or sometimes defined in the PIC specific header file, somewhere in a gputils directory)
Essentially, I get the difference of the two values, and multiply them with a 4-bit step counter which is shifted 4 bits to the left. The result then is in PRODH. But the closer I come to the high value, the more inaccurate it gets: If the max value is 150, the min value is 100 and the step number is 15 (from 0-15 steps), then I get 147 and not 150. What did I do wrong?
Have a look into the project.map file - is the pca9635_StepCounter variable located at an address >= 0x80? In such a case, you have to set the bank (“SET_BSR pca9635_StepCounter”), and (to be sure, sometimes done automatically) the “, BANKED” attribute for correct addressing.
Another potential issue: “rlncf” shifts the carry flag into the LSB, but you haven’t cleared it before starting the 4 shift operations. Accordingly, either “1” or “0” is shifted into the variable.
I think I mixed things up here: If you divide a difference by 16, you actually get 17 steps, including the step zero. So everything is correct, only the last step happens when the StepCounter reaches 16, not 15 (which is binary 1111 and is the highest value in 4 bits). If I want step 0 to be the minimum value and step 15 to be the maximum value, I will have to divide the difference by 15, not by 16 (which would be much more difficult, because then it is not a shift operation).
So the solution seems like using one additional bit for the step counter, just for the value of 16. Or does anybody have a better idea?
Thanks for the tips about the banked acces and the carry flag, I think I am starting to understand how the PIC works now.
Best, ALEXander.
PS: Illustration: Minvalue is 100, Maxvalue is 150, 16 is the number of steps
Divide 50 by 16
Add n times (50/16) to the min value, starting with n = 0
n=0 min value
n=1 min value + step
n=2 min value + 2*step
…
n=15 min value + 15*step (which is not yet the max value!)
I know all that, I’m not *that* new… I just thought C would be smart enough to do it without manipulation. Not “I don’t want to use a bitshift” but “Isn’t there a way to do it without one?”
…hmm. don’t know. didn’t want to “underestimate” you.. but what’s wrong with a shift? it’s a native operation of the processor, therefore fast and there’s a native operator in C?
maybe you could use a byte-address to do what you want?:
unsigned int i=0xffbb;
unsigned char msb = *(&i);//msb==0xff now
unsigned char lsb = *((&i)+1);//lsb==0xbb now
this (could) also work (an array var is nothing else than a pointer), but they say it’s “bad style”:
[s]unsigned int i=0xffbb;
unsigned char msb = (&i)[0];//msb==0xff now
unsigned char lsb = (&i)[1];//lsb==0xbb now[/s]
[glow=red,2,300]correction: I think you need to typecast also:[/glow]
unsigned int i=0xffbb;
unsigned char msb = *((unsigned char *)&i);//msb==0xff now
unsigned char lsb = *((unsigned char *)((&i)+1));//lsb==0xbb now
or
unsigned int i=0xffbb;
unsigned char msb = ((unsigned char *)&i)[0];//msb==0xff now
unsigned char lsb = ((unsigned char *)&i)[1];//lsb==0xbb now
this look a bit weired, I didn’t test if the syntax is correct, maybe it works.
Anyway, it should be possible to address a byte inside any var and cast it
as unsigned char.. dereferencing can be done with either * or array index.
I didn’t say there was anything wrong with a shift.
it’s a native operation of the processor, therefore fast and there’s a native operator in C?
That’s correct.
maybe you could use a byte-address to do what you want?:
<snip>
All that work demonstrates that you’ve kinda missed my point - I expected the language to take care of this, without my interaction, without writing extra code, without messing around, etc. It doesn’t. No problem! I just kinda assumed that it would be ‘smarter’ than it is.
All that work demonstrates that you've kinda missed my point - I expected the language to take care of this, without my interaction, without writing extra code, without messing around, etc. It doesn't. No problem! I just kinda assumed that it would be 'smarter' than it is.
I think the target of C is to have a language that is more “human” than asm, but enough close to the machine to generate fast code. therefore too much overhead would not do it any good.
I expected the language to take care of this
how should the language “now” what you want to do if you don’t tell it ?
but C++ is very powerfull, you could for example write your own operator, that gives you the msb of
an int as result. you can write operators like objects. I never did this, but I think this is a very powerfull
feature. you can even “overload” operators, e.g. if you want to simply add two strings with “+”, you can
overload the “+” operator and write a routine to process the string-addition. all other types will be handled
I think the target of C is to have a language that is more “human” than asm, but enough close to
the machine to generate fast code. therefore too much overhead would not do it any good.
That’s a good point. It’s one of the things I have grown to like about C, is that it stays quite low-level, without needing me to do *all* of the thinking
how should the language “now” what you want to do if you don’t tell it ?
Magic, of course ;D Hehehe… well of course there are ways to handle it, but in the end, the way it is done makes sense.
I don’t have so much experience with C, but during a period with no regular job, I read a book about the language, and made my notes like a proper student ;D
so I can refer them now, thats very usefull. also I’am familar with the syntax from writing in PHP and JavaScript. There is still some stuff that confuses me, but what I like about C: it seems to be weird, but in the end it is very consequent.
I did the interpolation algorithm in C, have a look at the code, should work. It interpolates in 33 steps, step can be 0 to 32.
/////////////////////////////////////////////////////////////////////////////
// Interpolate
/////////////////////////////////////////////////////////////////////////////
unsigned char KNOEPFLI_Interpolate32(unsigned char step, unsigned char value1, unsigned char value2)
{
unsigned char swap, diff;
//send out value2 if step is the max value
if( step == 32 ) {
return value2;
};
//send out value1 if step i 0
if( step == 0 ) {
return value1;
};
//invert the interpolation if value1 > value2
if( value1 > value2 ) {
//swap the values
swap = value2;
value2 = value1;
value1 = swap;
//--step because we are counting from 0 to 32, not to 31
--step;
//invert the step counter
step = ~step;
//clear the upper three bits
step &= 0x1f;
};
//get the difference
diff = value2 - value1;
//divide it through the total number of steps (>>5) and multiply it with step (gives an integer) then get the high byte,
//shift the step counter three to the left to make it 8bit, store it in PRODL
PRODL = step << 3;
//store diff in PRODH
PRODH = diff;
//do the multiplication in assembler
__asm
movf _PRODL, W
mulwf _PRODH, 0
__endasm;
//add the lower value
return value1 + PRODH;
}