C Bitwise Operators and Pointers A review?
Outline Bitwise Logical Operators Other Useful Operators Pointers
Bitwise Operators C Operator Function Uses (single pipe) Bitwise OR To mask bits high & Bitwise AND To mask bits low ^ Bitwise XOR To toggle bits
Bitwise Operators What is masking? This is the process of altering bits within a byte without affecting other bits. This is a common operation with C written for MCUs since each bit within an MCU register often has a special purpose. These bitwise operators can be used with the assignment operator.
Bitwise Operators: Example 1 Suppose we would like to set Bit 5 (b5) of PORTD, an unsigned byte, to 0 without affecting other bits. PORTD b7 b6 b5 b4 b3 b2 b1 b0 & 0xDF 1 1 0 1 1 1 1 1 Result b7 b6 0 b4 b3 b2 b1 b0 Code: PORTD = PORTD & 0xDF; PORTD &= 0xDF; /* More compact version. */
Bitwise Operators: Example 2 Suppose we would like to clear the MSNibble of PORTD and set b1 and b2 without affecting other bits. PORTD b7 b6 b5 b4 b3 b2 b1 b0 & 0x0F 0 0 0 0 1 1 1 1 Result 0 0 0 0 b3 b2 b1 b0 0x06 0 0 0 0 0 1 1 0 Result 0 0 0 0 b3 1 1 b0 Code: PORTD = (PORTD & 0x0F) 0x06;
Other Useful Operators C Operator Function Uses Unary ~ 1 s Complement Toggle the bits in an entire byte << n Shift left n bits To realign bits. >> n Shift right n bits To realign bits.
Other Useful Operators Example: unsigned char i = 162; i = i << 2; /* i <<= 2 would work, too! */ i = ~i; i = 162 1 0 1 0 0 0 1 0 i = i << 2 1 0 0 0 1 0 0 0 i = ~i 0 1 1 1 0 1 1 1
Pointers C pointers are analogous to assembly Indexed Addressing modes where the address of the operand is used rather than the value itself. The use of pointers in embedded systems is very important since data memory is often limited. Special registers are often memory-mapped; using pointers is often the only method to gain access to these.
Pointers and Memory Recall that a C program makes use of two distinct types of data memory: Heap where global variables are created. Stack where local variables are created and parameters to/from functions are passed Stack space is usually limited, so large data items are usually kept on the heap. It is poor practice to hard-code the name of a global variable in a function: modularity is lost. The global variable is then passed in by reference: using a pointer!
Pointers and Memory Let s suppose that I have a large piece of data created on the heap (i.e. created as a global variable or using malloc()): image Cartman; If I want to call a function Find_Edges() that uses this data, then the function needs to accept a parameter referring to the data. (Southpark character images from http://www.freefever.com/animatedgifs/cartoons/southpark.html, Accessed 17 Nov 2009. Free Animated Gifs )
Pointers and Memory Function Find_Edges() takes a single parameter: void Find_Edges(image imdata); When this function is called Find_Edges(Cartman); what happens to memory? Heap (as before): Stack: Now there are two copies of this large piece of data!
Pointers and Memory This is too much work! The CPU must run code to duplicate the data, and precious memory is consumed. Using pointers will allow us to avoid this from happening!
Pointer Operators C Operator unary * unary & -> Function Indirection operator, or pointer operator Address operator Structure indirection operator Uses To access the value referred to by the pointer. To access the address of a variable, often used to initialize a pointer. To access the value referred to by a pointer to a structure. The use of * or -> is often referred to as dereferencing the pointer.
Pointer Operators Now we are equipped with the tools to make function Find_Edges() accept a pointer: void Find_Edges(image *imdata); (note that the * operator in this case says expecting a pointer, and is not actually dereferencing) Now when calling Find_Edges(), we need to ensure an address is passed. Find_Edges(&Cartman); The unary & operator is used since the variable Cartman is not a pointer.
Pointer Operators What has happened in memory? Heap (as before): Stack: Address Pointing to Cartman image data Much cleaner and smaller! We have also retained modularity. Heap : Find_Edges(&Stan); Stack: Address Pointing to Stan Image data
Pointers A nice side-effect of passing a parameter as a pointer is that since there is only one copy of the data, the parameter can be used for returning values to the calling scope! Heap (before): void GiveMoustache(image *imagedata); GiveMoustache(&Stan); Heap (after):
Pointers: Initialization Pointers that are not initialized are known as NULL pointers, meaning they point to nothing. Just like a variable, pointers need to be initialized before using. int variable = 32; int *ptrvar; printf( Address = %u, ptrvar); Outputs: Address = 0 (NULL). ptrvar = &variable; Pointer is now initialized. printf( Variable = %d, variable); Outputs: Variable = 32. printf( Variable = %d, *ptrvar); Outputs: Variable = 32. *ptrvar *= 2; printf( Variable = %d, variable); Outputs: Variable = 64.
Pointers: Initialization The malloc() family of functions can be used to get your program to dynamically allocate storage on the heap. These are typically used when you do not how large an array (or other data type) is at compile time. int *parray; : : : : parray = malloc(50*sizeof(int)); : : : : free(parray);
Pointers and Structures A quick demonstration of the structure indirection operator: typedef struct { double balance; int account; } bank_info; int main(void) { bank_info account1; initialize_account(&account1); printf( Account: %d,account1.account); } void initialize_account( bank_info *a) { a->balance = 78.92; a->account = 12; } Output: Account: 12
Pointers: Arrays and Strings Although there are some syntax differences (function calling, declaring, etc.), arrays and strings are passed as pointers! int main(void) { int accounts[2]; init_array(accounts); printf( Accounts[1] = %d,accounts[1]); } void init_array(int a[]){ a[0] = 22; a[1] = 12; /* What if the array had only 1 element? */ } Output: Accounts[1]= 12
Pointers: Arithmetic The compiler will perform pointer arithmetic correctly, providing an alternate method to cycling through arrays. int main(void) { int array[3]; int *ptr; array[0] = 14; array[1] = 2 * array[0]; array[2] = 1; ptr = array; printf( Contents of array[0] = %d\r\n,*ptr); printf( Contents of array[2] = %d,*(ptr+2)); } Outputs: Contents of array[0] = 14 Contents of array[2] = 1
Pointers: Aside I When dealing with C and microcontrollers, most register addresses are equated to a label corresponding to their name. For example, in the Atmega32, register PORTD is located at address 0x12. Much has taken place in the background to allow you to refer to PORTD as if it were a simple variable to get/set its values.
Pointers: Aside II With devices that have a Harvard structure where program and data space differ, it is necessary to keep pointers to ROM and RAM separate. See the AVR GCC libc manual for more information. Remember that the simulator can really help out try running programs!
Summary There are many more small details. Hopefully this will get you up-and-running! There are many good reference guides for C and C++ get one! Older books are often excellent C has been quite static.