|
|
View previous topic :: View next topic |
Author |
Message |
spilz
Joined: 30 Jan 2012 Posts: 219
|
pointer help ^^ please !!! |
Posted: Tue Aug 27, 2013 2:30 pm |
|
|
hello,
I'm trying to do something with pointer for more than 1 hour and I don't find the solution :(
Can you help me please Smile
What I want to do:
I have an int16 and I would like to change the first 8bit and the last 8bit separately
For example:
"something with * and & " to change 12 in 56 to obtain directly x = 0x5634
and
"something with * and & " to change 34 in 78 to obtain directly x = 0x5678
It's to understand how pointer work.
Thanks for your help |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Tue Aug 27, 2013 8:04 pm |
|
|
You might look in the manual for the functions make16(), make8() as well as 'unions'.
I'm fairly sure the make16() function will do what you want and not have to figure out 'pointers'.
hth
jay |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Tue Aug 27, 2013 8:41 pm |
|
|
Using pointers is a poor way to do that. Within CCS make8() and make16() are the best way to do this. If you want to write code for reuse with other compilers you should use AND and OR or use modulo math depending on what should happen if you switch between big endian and little endian processors. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
Posted: Tue Aug 27, 2013 10:49 pm |
|
|
Using a union will probably solve the problem as it allows byte addressing then.
Regards |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Wed Aug 28, 2013 2:53 am |
|
|
As explained, pointers are a poor way to do this. The PIC, has to work quite hard to deal with pointers, compared to many other processors. Hence all the comments about the better ways of doing this.
However I note your 'last line'.
Now, pointers are just an address. So with your declaration of 'x', then:
&x
will give a number giving the address of 'x' in memory.
Now the 'pointer', has a (possibly strange) thing about it. It remembers the 'size' of the target object. So:
(&x)+1
Will give an address _two_ bytes higher in memory, since it 'knows' that 'x' is an int16. This is standard C, and covered in textbooks about C.
The next thing is that in C, you can change the 'type' of something with a 'cast' (putting a type in brackets in front of the 'thing'). So:
(int8 *)&x
is the same number for the address, but C has now been told to treat it _as if_ it was the address of an int8, not an int16.
So:
((int8 *)&x)+1
Now gives an address just one byte higher in memory.
So:
Code: |
int16 x = 0x1234;
int8 val1,val2;
val1=*((int8*)&x);
val2=*(((int8*)&x)+1);
|
Will read your two bytes using pointers, while:
Code: |
int16 x = 0x1234;
*((int8*)&x) = 56;
*(((int8*)&x)+1) = 78;
|
Would change your two bytes 'using pointers'.
However, this takes 23 instructions, versus:
x=make16(78,56);
at just 3 instructions....
A 'pointer' is also the same thing in C as an array. So you can do some slightly 'odd' things:
Code: |
int16 x = 0x1234;
int8 byte_list[]; //declares 'byte_list' to be a pointer to int8's
//int8 * byte_list; //alternative doing the same declaration
byte_list=&x; //'byte_list', now _points_ to X
byte_list[0] = 56;
byte_list[1] = 78; //writes the two values
//or
*byte_list = 56;
*(byte_list+1) = 78; //and does it using 'byte_list' as a pointer instead.
|
This is all in standard C text books, and should really be where to start.
Now the 'possibly interesting' thing here is that except for the extra operation of copying the address to 'byte_list', when you use it as an array, with fixed indexes, the compiler can solve the location in memory at compile time, and automatically does this, while once you start using a 'pointer' the efficiency again plummets...
Best Wishes |
|
|
spilz
Joined: 30 Jan 2012 Posts: 219
|
|
Posted: Wed Aug 28, 2013 3:35 am |
|
|
thanks a lot for your reply,
I understand beter how it works
I'm just surprised about the number of instructions when we use pointers, I thought it was more complicate for compilation and easier for Pic..
bad thinking
Anyway, thanks again :D |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Wed Aug 28, 2013 7:51 am |
|
|
It is down to the fundamentally '8bit' architecture of the PIC16/18.
An address is a 16bit value.
So to calculate a location based on an address, two memory cells have to be accessed, the offset added, to get the address required. A handful of instructions straight away. Then the resulting 16 bit value has to be copied to two CPU registers which are used for 'indirect addressing'. Another handful of instructions, and then the 'target' value has to be read/written through the indirect access register. Yet more work....
Now many historically '8bit' processors, have some registers that work as 16bit registers and can be accessed/have maths done on them, directly as 16bit values. The smaller PIC's don't.
Best Wishes |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Aug 28, 2013 1:50 pm |
|
|
Ttelmah, what do you think of this method ? The compiler generates
code that uses the FSR and the code is fairly compact. Do you see
anything wrong it ? I tested it with MSVC 6.0 and it worked there as well.
The generated code size for a zero offset is the same as your pointer
code. But for non-zero offsets, the macro generates the same small
amount of code as with a zero offset. That's where it's better.
Explanation of the code below:
The BytePtr macro casts the int16 to a char, and then it takes the address
of it. Pointer arithmetic will now work on a byte basis. Adding the offset
creates a pointer to the desired byte in the int16, which then can be
dereferenced in the main program.
Code: |
#include <18F4520.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)
#define BytePtr(var, offset) (&(char)var + offset)
//======================================
void main(void)
{
int16 x;
x = 0x1234;
*BytePtr(x, 0) = 0x56;
printf("%lx \r", x);
*BytePtr(x, 1) = 0x78;
printf("%lx \r", x);
while(1);
} |
|
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Wed Aug 28, 2013 4:11 pm |
|
|
PCM programmer wrote: | Ttelmah, what do you think of this method ? The compiler generates
code that uses the FSR and the code is fairly compact. Do you see
anything wrong it ? I tested it with MSVC 6.0 and it worked there as well.
The generated code size for a zero offset is the same as your pointer
code. But for non-zero offsets, the macro generates the same small
amount of code as with a zero offset. That's where it's better.
Explanation of the code below:
The BytePtr macro casts the int16 to a char, and then it takes the address
of it. Pointer arithmetic will now work on a byte basis. Adding the offset
creates a pointer to the desired byte in the int16, which then can be
dereferenced in the main program.
Code: |
#include <18F4520.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)
#define BytePtr(var, offset) (&(char)var + offset)
//======================================
void main(void)
{
int16 x;
x = 0x1234;
*BytePtr(x, 0) = 0x56;
printf("%lx \r", x);
*BytePtr(x, 1) = 0x78;
printf("%lx \r", x);
while(1);
} |
|
I frequently use this method when working with PICs. This is because I use compilers from different vendors and make8() and make16() are not portable. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Aug 29, 2013 12:44 am |
|
|
Quote: | I frequently use this method when working with PICs. This is because I use compilers from different vendors and make8() and make16() are not portable. |
Code: | #define BytePtr(var, offset) (&(char)var + offset)
*BytePtr(x, 1) = 0x78; |
I think, most portable codes are using this kind of "universal typecasting". But unfortunately, CCS C doesn't recognize it's nature, resulting in a superfluous real pointer operation. So an effective implementation should refer to make8() or unions for CCS C. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Thu Aug 29, 2013 1:07 am |
|
|
Very little is really 'portable'.
You have to remember that the byte order is not the same in many CPU's, so the pointer operation won't be portable.
At the end of the day, the most 'portable', is a union, with the bytes named as high/low, which can then be created for any chip, whatever the memory arrangement.
The 'byte pointer', works well for any chip using LSB lowest, and the CCS compiler is (fortunately) smart enough to not use pointer based operations for constant offsets here, giving good efficiencies, though it's 'efficiency' is really only a result of the compiler being reasonably smart here, and will disappear as soon as variables are used, but that is because of the inefficiencies of the chip here.
At the end of the day, the 'lesson' is that you as a user, need to be aware of the hardware limitations.
A big 'caveat' comes here on 16bit chips, requiring _word_ alignment.
'make8', and 'make16' equivalents, are very easy to generate as defines on other chips.
Best Wishes |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Aug 29, 2013 9:55 am |
|
|
Quote: | the CCS compiler is (fortunately) smart enough to not use pointer based operations for constant offsets here |
Really?
Code: | #define hi(x) (*(&x+1))
#define lo(x) (*(&x))
00758 .................... b = lo(h);
0238 6A03 00759 CLRF 03
023A 0E76 00760 MOVLW 76
023C 6EE9 00761 MOVWF FE9
023E C003 FFEA 00762 MOVFF 03,FEA
0242 CFEF F064 00763 MOVFF FEF,64
00764 .................... b = hi(h);
0246 6A03 00765 CLRF 03
0248 0E78 00766 MOVLW 78
024A 6EE9 00767 MOVWF FE9
024C C003 FFEA 00768 MOVFF 03,FEA
0250 CFEF F064 00769 MOVFF FEF,64
00770 ....................
00771 .................... b=make8(h,0);
0254 C076 F064 00772 MOVFF 76,64
00773 .................... b=make8(h,1);
0258 C077 F064 00774 MOVFF 77,64 |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Thu Aug 29, 2013 11:15 am |
|
|
Yes, using the pointers as shown in PCM programmers post, _not_ in general operation.
It's an 'oddity' that it doesn't do it for normal operations....
Best Wishes |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Aug 29, 2013 1:55 pm |
|
|
Also note that the hi() macro posted above will produce incorrect results.
The 'h' variable is an int16 so when hi() adds 1 to the address, it does
pointer arithmetic based on the word size, giving 0x78:
Code: |
#define hi(x) (*(&x+1))
#define lo(x) (*(&x))
00758 .................... b = lo(h);
0238 6A03 00759 CLRF 03
023A 0E76 00760 MOVLW 76
023C 6EE9 00761 MOVWF FE9
023E C003 FFEA 00762 MOVFF 03,FEA
0242 CFEF F064 00763 MOVFF FEF,64
00764 .................... b = hi(h);
0246 6A03 00765 CLRF 03
0248 0E78 00766 MOVLW 78 **** <== Should be 0x77
024A 6EE9 00767 MOVWF FE9
024C C003 FFEA 00768 MOVFF 03,FEA
0250 CFEF F064 00769 MOVFF FEF,64
|
But you want byte arithmetic, giving 0x77, so then hi() has to be
something like this,
Code: | #define hi(x) (*((int8 *)&x+1)) |
which then expands to a large amount of ASM code:
Code: |
.................... b = hi(h);
00FC: CLRF @@0B
00FE: MOVLW h
0100: MOVWF @@0A
0102: MOVLW 01
0104: ADDWF @@0A,W
0106: MOVWF @01
0108: MOVLW 00
010A: ADDWFC @@0B,W
010C: MOVFF 01,FSR0L
0110: MOVWF FSR0H
0112: MOVFF INDF0,b |
|
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Aug 29, 2013 2:46 pm |
|
|
Quote: | Also note that the hi() macro posted above will produce incorrect results. |
Yes, thanks for pointing this out. I don't use it in real code, it was only for demonstrating how CCS C treats
this kind of constructs. And it's even worse, as your example shows. |
|
|
|
|
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
|