Related
Take macro:
GPIOxMODE(gpio,mode,port) ( GPIO##gpio->MODER = ((GPIO##gpio->MODER & ~((uint32_t)GPIO2BITMASK << (port*2))) | (mode << (port * 2))) )
Assuming that the reset value of the register is 0xFFFF.FFFF, I want to set a 2 bit width to an arbitrary value. This was written for an STM32
MCU that has 15 pins per port. GPIO2BITMASK is defined as 0x3. Is there a better way for clearing and setting a random 2 bits in anywhere in the
32-bit wide register.
Valid range for port 0 - 15
Valid range for mode 0 - 3
The method I came up with is to bit shift the mask, invert it, logically AND it with the existing register value, logically OR the result with a bit shifted new value.
I am looking to combine the mask and new value to reduce the number of logical operations bit shift operations. The goal is also keep the process generic enough so that I can use for bit operations of 1,2,3 or 4 bit widths.
Is there a better way?
In the long and sort of it, is there a better way is really an opened question. I am looking specifically for a method that will reduce the number of logical operations and bit shift operations, while being a simple one lined statement.
The answer is NO.
You MUST do reset/set to ensure that the bit field you are writing to has the desired value.
The answers received can be better (in a matter of opinion/preference/philosophy/practice) in that they aren't necessary a macros and have have parameter checking. Also pit falls of this style have been pointed out in both the comments and responses.
This kind of macros should be avoided as a plaque for many reasons:
They are not debuggable
They are hard to find error prone
and many other reasons
The same result you can archive using inline functions. The resulting code will be the same effective
static inline __attribute__((always_inline)) void GPIOMODE(GPIO_TypeDef *gpio, unsigned mode, unsigned pin)
{
gpio -> MODER &= ~(GPIO_MODER_MODE0_Msk << (pin * 2));
gpio -> MODER |= mode << (pin * 2);
}
but if you love macros
#define GPIOxMODE(gpio,mode,port) {volatile uint32_t *mdr = &GPIO##gpio->MODER; *mdr &= ~(GPIO_MODER_MODE0_Msk << (port*2)); *mdr |= mode << (port * 2);}
I am looking to combine the mask and new value to reduce the number of
logical operations bit shift operations.
you cant. You need to reset and then set the bits.
The method I came up with is to bit shift the mask, invert it,
logically AND it with the existing register value, logically OR the
result with a bit shifted new value.
That or an equivalent is the way to do it.
I am looking to combine the mask and new value to reduce the number of
logical operations bit shift operations. The goal is also keep the
process generic enough so that I can use for bit operations of 1,2,3
or 4 bit widths.
Is there a better way?
You must accomplish two basic objectives:
ensure that the bits that should be off in the affected range are in fact off, and
ensure that the bits that should be on in the affected range are in fact on.
In the general case, those require two separate operations: a bitwise AND to force bits off, and a bitwise OR (or XOR, if the bits are first cleared) to turn the wanted bits on. There may be ways to shortcut for specific cases of original and target values, but if you want something general-purpose, as you say, then your options are limited.
Personally, though, I think I would be inclined to build it from multiple pieces, separating the GPIO selection from the actual computation. At minimum, you can separate out a generic macro for setting a range of bits:
#define SETBITS32(x,bits,offset,mask) ((((uint32_t)(x)) & ~(((uint32_t)(mask)) << (offset))) | (((uint32_t)(bits)) << (offset)))
#define GPIOxMODE(gpio,mode,port) (GPIO##gpio->MODER = SETBITS32(GPIO##gpio->MODER, mode, port * 2, GPIO2BITMASK)
But do note that there appears to be no good way to avoid such a macro evaluating some of its arguments more than once. It might therefore be safer to write SETBITS32 as a function instead. The compiler will probably inline such a function in any case, but you can maximize the likelihood of that by declaring it static and inline:
static inline uint32_t SETBITS32(uint32_t x, uint32_t bits, unsigned offset, uint32_t mask) {
return x & ~(mask << offset) | (bits << offset);
}
That's easier to read, too, though it, like the macro, does assume that bits has no set bits outside the mask region.
Of course there are other, similar formulations. For instance, if you do not need to support discontinuous bit ranges, you might specify a bit count instead of a bit mask. This alternative does that, protects against the user providing bits outside the specified range, and also has some parameter validation:
static inline uint32_t set_bitrange_32(uint32_t x, uint32_t bits, unsigned width,
unsigned offset) {
if (width + offset > 32) {
// error: invalid parameters
return x;
} else if (width == 0) {
return x;
}
uint32_t mask = ~(uint32_t)0 >> (32 - width);
return x & ~(mask << offset) | ((bits & mask) << offset);
}
how is:
GIMSK |= (1 << PCIE);
PCMSK |= (1 << PCINT4);
equal to (I can use the above or the below in my setup of my program, both work and activate pin 4), the GIMSK and the PCMSK are for some reason equal to each other, I am trying to learn why.
GIMSK = 0b00100000;
PCMSK = 0b00010000;
first:
https://thewanderingengineer.com/2014/08/11/pin-change-interrupts-on-attiny85/
second:
https://embeddedthoughts.com/2016/06/06/attiny85-introduction-to-pin-change-and-timer-interrupts/
data:
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf
The sheet says PCIE is 0b00100000 in the bit mask, so somehow |= (1 << PCIE) equals that? I don't get it if PCIE is supposed to be that, doing a shift would change that value..
how and why would you use that instead of binary? I would guess it changes it but obviously, somehow it doesn't. I've asked this on several different places nobody has an answer so I came here. Hopefully someone can explain.
I'm new to C, I just learned bitwise operations today to try to figure out what is going on here, my code does work with either or but I want to know why! Thanks.
It is equal because all other bits of those registers were 0 before the OR operation
1u << x shifts one by x positions left. As a result you have the number with all bits except x cleared
Short Version
Break apart the compound expression from x |= y to x = x | y
Load integer '1' which is just binary `[0000 0001] and shift it to the spot we want.
Variable is defined in a header, or elsewhere which says where that bit is for compatibility. New card? Just get a new definition file- this is done automatically- usually). PCIE and PCINT4 are defined somewhere, look for it if needed, but this is supposed to handle those details for you.
Yet- we know is PCINT4 is 4 and PCIE is 5 respectively because of the second set- where we see a shifted '1'- 5 spaces for PCIE and 4 spaces for PCINT4. That's why they seem equivalent. Because they are literally equivalent- once you evaluate the expression you know that 0b means that what follows is binary (which might have been obvious). But they are NOT equivalent. Not exactly- see below--
4-So if you "OR" a register, it overrides whatever is there if TRUE and forces it to be what True and leaves everything else alone. We shift that 1 to show the bit we want, then we choose the operant that will have the effect we want. Look at the table for others.**
But we get GIMSK = GIMSK OR 0b00100000 and PCMSK = PCMSK OR 0b00010000;
Which is similar, but not exactly the same thing.
The devil is in the details, see below for
Detailed Explanation.
AKA someone better read this, took me forever.
GIMSK |= (1 << PCIE);
PCMSK |= (1 << PCINT4);
equal to (I can use the above or the below in my setup of my program, both work and activate pin 4), the
GIMSK and the PCMSK are for some reason equal to each other, I am
trying to learn why.
GIMSK = 0b00100000;
PCMSK = 0b00010000;
Let's take it lexicographically by the token to start, so we are all talking about the same thing.
GIMSK |= (1 << PCIE);
Starting at the first bit. Pick it apart-
GIMSK- variable or 'id'
|= - operator and assignment combo bitwise OR and =
1 - the integer 1
<< - shift operation
PCIE - Variable and ID
Of course, this makes it harder to explain. What is |=? I'm certain that led to confusion on this question for some. The better-known one is +=. So if I have a variable x, and I always add to itself, really any time you are counting, etc. the variable is on both sides of the equation. Like this:
x = x + 1 ;this is so common though, that in C, it was shortened to +=
x += 1 ; now its written like this. It takes some time
so programmers are lazy, and if something 'cool' pops up in one language, it usually spreads to the others, so most languages allow this now. It does make a difference if you write += or =+, at least in Java. Won't hit that though.
y = y * 2; it works for other types of operands
y *= 2; Now we take y, multiply by 2 and assign back to y.
Now, let's look at 'C' style bitwise operators- most languages have adopted similar notation though there are exceptions.
Usually in C you use two symbols to compare '&&" or '||' or '=='
Well, that's because the single operator compares bits. This got one symbol because it's much more natural on a computer and much more common. Not to us anymore, we are abstracted away from it by layers of software.
So we have: ** Good Source for more info
Bitwise AND (&)
Bitwise OR (|)
Bitwise XOR (^)
Bitwise NOT (~)
And we can also make this compond (Click for more info)
Basically, they compare some variable on the right with the left and assign it back to the right. Like this x = x* y => x *= y.
Likewise we have x &= y, x |= y and, x ^= y
So for the above- let's unwrap it first- write it out longhand to make it easier to understand-
GIMSK |= (1 << PCIE)
GIMSK = GIMSK | (1 << PCIE) #OK! much easier to understand if your new.
#NOW we can lexigraphically analyze this
VarA {assignment} VarA OR ( 1 {Operator} VarB )
#Ignore the assignment side, for now, practice order of Operations
#Start with Parenthetical Exp.
1 {Operator} VarB
#It turns out this is defined.
#OP Didnt know but computer does. = 5 in this case.
#so 1, shift left 5. To bitwise shift, need bits
1 => 0b00000001 << 5 = 0b00100000
# shift left is really multiplied by 2 in base 10, divide by 2 in shift right. Beware Right Shift, esp in float.
So now we have: GIMSK = GIMSK OR 0b00100000
GIMSK |= (1 << PCIE); PCMSK |= (1 << PCINT4);
GIMSK = 0b00100000; PCMSK = 0b00010000;
Which is just what you already said. More or less. The 2nd operations are not equivalent though as I mentioned above in the short answer. Thats covered at the end.
This is Assembly format, GIMSK is an 8-bit register. We created a bitmask, by moving a 1 to the register we want to effect, and putting a 0 in the bits we want to leave alone. The |= means we will compare the two the save it back to the same register. That's it. OR 1 will always turn it on. Which is what we want.
Think about what we want to do to start. We want to set a boolean value to HIGH or TRUE, or 1, however, you put it. We say "Lets set the register bit that we specify if your value (0/1) OR my value (1) is 1." Well, we know our value is 1 because that's what we put. So when you bit-wise OR (is that a verb?), you are writing a value on the basis of one of 2 values being a 1. It either writes a one or leaves a one, unless you send 0 and it sends 0 it stays off.
It says "I think this should be on. If any other process thinks this should be on, leave it even if I don't need it so (it sends a 0)" It's worth thinking through on paper, and thinking through each of the operands. Make a colored table, that's how I got to understand them. Not of the operands will flip the values whatever it is. AND checks the value for you, it leaves the register the same- Operating on each reg will have this effect. I used to have a cheat sheet, I would have loved to include, but I lost it- but it summarized for my dumb brain the behavior of each operand.
REMEMBER- we can not operate on a single bit. This is a critical bit you need to understand. You cant change just one bit. If I have 0010 0010 and I want to say, hey computer, change byte 6. You cant! You have to load the whole word or byte into a register or at least half (16bits in MIPS, 8 in ATMEL 16bit controllers**), and operate on the whole thing. You can't operate directly from memory (Ram, SSD, L2 Cache- way too far away). There's no such thing as popping a single bit into a register to change it, though there are tricks to make new bytes (8bit) in the shape you want. Want just the 6th bit, well {0100 000} -with AND, will get it for you. Then you can shift right, or divide by 2^6, etc. We will get back to this. First- the actions of the Comparators if you care to learn more:
*this chip is 8 bit. Doubt they have half read. Bit
Logical Operators v. Registers
Register(b) Me OR NOR XOR AND NAND N XNOR
1 0 1 0 1 0 1 0 0
0 0 0 1 0 0 1 1 1
1 1 1 0 0 1 0 0 1
0 1 1 0 1 0 1 1 0
So the above looks at the single bit we want to effect. The left 2 columns are all possible scenarios (just 4), where we show the bit of the register in that byte. I keep saying byte. A register is, as I mentioned 16 or 32 bits usually, so I really mean Word. I have just organized this example around a hypothetical 8 bit machine. edit- this is an 8 bit chip, 32 registers. one of which is this one
Now! What do we want to do? We Want to change 1 byte, which represents a boolean value, but we don't want to mess with the rest! If you OR across all 8 bytes- do this on paper- it leaves the values in there already alone. Perfect! That's what we want.
Whatever they are set to, they stay, if it's 1, OR leaves it a 1, if it's 0, it stays.
Ah, I should mention why.
Its because you start with 0000 0001 (1) so everything is 0, except the 1st bit. Why did we start with 0000 0001? because you told it to.
See here syntax Arduino Doc Bitwise Ops
So, without reviewing Binary, 1 in binary is 0000 0001 It should be noted, that in a computer, it can't tell that 11001100 isn't 11,001,100 (eleven million), or if its 204 (binary) or even 285,217,024 (if it was HEX), or 2,359,872 (in Octal).
The compiler and computer 'know' we always think base 10, but the computer never does, just base 2 or compressed for easy human reading, into octal (2^2,) or hex (2^4) eg, each 'character' is 2 bits or 4 bits. 0x0A is 0b1010. And right there is where I am getting at. We indicate the values are not base 10, with a prefix. 0b***** is binary. 0x**** is hex. And I can never remember Octal- no one uses it anyway.
So!
see here if needed: Another practical book I wrote on Bitwise Ops, that covers basic Binary a little.
Then you shift that bit by the PIN NUMBER I don't know the right term, and this is certainly not it, but you say the register you want to effect is the 5th register. Ok.
#take a 1,
0000 0001 = $temp
#shift it 5 spots "<<" , where 5 is the PCIE 'bit' value spot number.
1<<5 = 32
#binary equals 32.
You could replace either GIMSK value with 32 and it would be fine, again equivalent, or 0x020
# 0010 0000
# Then OR this with whats in the register now:
1010 1010 (made up number, a mix of ones and 0s)
0010 0000 (Our Value)
OR=>
1010 1010 Result.
Note how we left the other bits alone, and only changed what we wanted! Effective bit mask!
Now, why does it say PCIE, and whatever the other one is. Its because somewhere, when you compile, there is a file that assigns values to those variables. This allows the code to be compatible across several different chip designs. The ATMEGA and the ATTINY do not have the same interrupt pin. Though it likely goes to the same internal register.
#Take it bit by bit, no Pun intended
GIMSK |= (1 << PCIE);
PCMSK |= (1 << PCINT4);
GIMSK = 0b00100000;
PCMSK = 0b00010000;
Again, starting at the first bit from above.
GIMSK- some variable
|= - bitwise OR
1 - the integer 1
`<`< - shift operation
PCIE - another var
So all you are doing is taking a base 10 integer- 1, which we know equals 0b0000 0001, then we are pushing that 1 (now in binary to the spot indicated by PCIE or PCINT4. So the latter 2 are just simply variables that hold the bit number, so if it changes, the code doesn't break.
From the latter 2 lines, we infer that PCIE is 5 and PCINT4 is 4. GIMSK is now equal to 32 and the other 16. Shifting << and >> has the effect of multiplying or diving by 2. Although, shifting down is risky for reasons I won't get into, but if you need to multiply a number by 2, for a computer, it's much faster to shift left by 1 bit than it is to go through the multiplayer.
We talked about the OR already. It sets a 1, if there's not one, otherwise it leaves the other bits alone because they are 0.
Equivalent or Not??
GIMSK |= (1 << PCIE);
GIMSK = 0b00100000;
GIMSK = GIMSK OR 0b00100000
PCMSK |= (1 << PCINT4);
PCMSK = 0b00010000;
PCMSK = PCMSK OR 0b00010000
So evaluating the 1st expression in each set gets the 3rd equation. But notice they look a little different. They are not equivalent statements, though, as you say they may work. It depends on what those other bits are.
PCMSK = 0b00010000; #This sets the PCMSK register to be exactly
=> PCMSK = `0|0|0|1|0|0|0|0
#While
PCMSK = PCMSK OR 0b00010000; # yields PCMSK = `?|?|?|1|?|?|?|?`
#Obviously,
GIMSK = 0b00001000; # This sets the GIMSK register to be exactly
=> GIMSK = `0|0|0|0|1|0|0|0`
While`GIMSK = GIMSK OR 0b00001000; # yields
GIMSK = ` ?|?|?|?|1|?|?|? `
while the OR statement leaves the other bits alone, if they were set by something else, and just changes the 5 (or 4th bit) as the case my be. The OR statement is probably the better statement. If you found the latter statement suggested in a reputable place though, it's probably fine.
Conclusion
So that's it. It's much easier than you thought probably now that the different bits make sense. I wrote this though with the hope it'll give some lasting insight rather than just a quick answer. Although in truth- it was complicated. There are a LOT of computer science concepts buried in those 2 statements, that if you're not in the know, might as well be hieroglyphics.
All this makes much more sense if you dive into how a computer works.
Check out Chapter 2 and 3 of Computer Organization and Design (5th ed) Patterson and Hennesy. It's the standard. If this is for fun, you can skim it. But the computer has Registered, of a defined width- 8, 16, 32. and 64 or even 128 (rarely e.g. x86 AVX- Intel x86). but usually 32 bit. These are the bits of data in hand, what the processor actually touches. The processor can only operate on registers. So everything, under the hood, will end up back there.
Now using interrupts correctly is a whole other topic. I again recommend the same book- Ch 5 and Appendix A7
Note- My assembly class was in MIPS. I've never specifically studied this microcontroller. If I get some of the architecture wrong, forgive me.
I'm trying to change the 4 middle bits of a byte to correspond to the High nibble of another byte:
Suppose we start with:
In = 0bABCDEFGH
Out = 0bXXXXXXXX // Some random byte
I want:
Out = 0bXXABCDXX
Leaving whatever other bits were in Out's extremes unchanged.
How can I do this?
Note: The 'X' represents any bit, 0 or 1, just to distinguish what came from the input.
I got to:
(0b00111100 & (IN>>2)) = 0b00ABCD00
, which filters the high nibble and centers it but then what? How can I move it to Out?
simple:
out &= 0b11000011;
out |= (in >> 2 & 0b00111100);
out &= 0b11000011 sets out to 0bxx0000xx preserving 2 most significant bits and 2 least significant bits. in >> 2 shifts input by 2 giving us 0xYYABCDEF, YY could be 00 or 11 depending on what A is. To get rid of YY and EF we do & 0b00111100.
As pointed by #JB 0B is not standard notation, thus you should use something else, most preferably hex 0x notation. See this for more info.
Thus using hex this would be:
out &= 0xC3;
out |= (in >> 2 & 0x3C)
here is conversion table
`0xf` is `0b1111`
`0x3` is `0b0011`
`0xc` is `0b1100`
Assuming in and out are unsigned char, and that CHAR_BIT == 8:
out = (out & 0xC3) | ((in >> 2) & 0x3C);
i.e. 4 operations in total.
There are multiple alternatives. From a high-level perspective, you could
force the four middle bits of Out off, prepare a mask from In as show in your question, and combine Out and mask via bitwise OR (|)
force the four middle bits of Out off, prepare a mask from In as show in your question, and combine Out and mask via bitwise EXCLUSIVE OR (^)
force the four middle bits of Out on, prepare a mask from In similarly to how you do now, but with the outer bits on, and combine Out and mask via bitwise AND (&)
use a series of shifts, masks, and addition or bitwise OR operations to build up the wanted result section by section
Forcing bits off is achieved by bitwise AND with a mask that has 0s at (only) the positions you want to turn off.
Forcing bits on is achieved by bitwise OR with a mask that has 1s at (only) the positions you want to turn on.
You already seem to have a handle on shifting, though you do need to be careful there if you happen to be shifting objects of signed types. Prefer to use unsigned types for bit manipulation wherever possible.
This question already has answers here:
Real world use cases of bitwise operators [closed]
(41 answers)
Closed 6 years ago.
I am new to bitwise operators.
I understand how the logic functions work to get the final result. For example, when you bitwise AND two numbers, the final result is going to be the AND of those two numbers (1 & 0 = 0; 1 & 1 = 1; 0 & 0 = 0). Same with OR, XOR, and NOT.
What I don't understand is their application. I tried looking everywhere and most of them just explain how bitwise operations work. Of all the bitwise operators I only understand the application of shift operators (multiplication and division). I also came across masking. I understand that masking is done using bitwise AND but what exactly is its purpose and where and how can I use it?
Can you elaborate on how I can use masking? Are there similar uses for OR and XOR?
The low-level use case for the bitwise operators is to perform base 2 math. There is the well known trick to test if a number is a power of 2:
if ((x & (x - 1)) == 0) {
printf("%d is a power of 2\n", x);
}
But, it can also serve a higher level function: set manipulation. You can think of a collection of bits as a set. To explain, let each bit in a byte to represent 8 distinct items, say the planets in our solar system (Pluto is no longer considered a planet, so 8 bits are enough!):
#define Mercury (1 << 0)
#define Venus (1 << 1)
#define Earth (1 << 2)
#define Mars (1 << 3)
#define Jupiter (1 << 4)
#define Saturn (1 << 5)
#define Uranus (1 << 6)
#define Neptune (1 << 7)
Then, we can form a collection of planets (a subset) like using |:
unsigned char Giants = (Jupiter|Saturn|Uranus|Neptune);
unsigned char Visited = (Venus|Earth|Mars);
unsigned char BeyondTheBelt = (Jupiter|Saturn|Uranus|Neptune);
unsigned char All = (Mercury|Venus|Earth|Mars|Jupiter|Saturn|Uranus|Neptune);
Now, you can use a & to test if two sets have an intersection:
if (Visited & Giants) {
puts("we might be giants");
}
The ^ operation is often used to see what is different between two sets (the union of the sets minus their intersection):
if (Giants ^ BeyondTheBelt) {
puts("there are non-giants out there");
}
So, think of | as union, & as intersection, and ^ as union minus the intersection.
Once you buy into the idea of bits representing a set, then the bitwise operations are naturally there to help manipulate those sets.
One application of bitwise ANDs is checking if a single bit is set in a byte. This is useful in networked communication, where protocol headers attempt to pack as much information into the smallest area as is possible in an effort to reduce overhead.
For example, the IPv4 header utilizes the first 3 bits of the 6th byte to tell whether the given IP packet can be fragmented, and if so whether to expect more fragments of the given packet to follow. If these fields were the size of ints (1 byte) instead, each IP packet would be 21 bits larger than necessary. This translates to a huge amount of unnecessary data through the internet every day.
To retrieve these 3 bits, a bitwise AND could be used along side a bit mask to determine if they are set.
char mymask = 0x80;
if(mymask & (ipheader + 48) == mymask)
//the second bit of the 6th byte of the ip header is set
Small sets, as has been mentioned. You can do a surprisingly large number of operations quickly, intersection and union and (symmetric) difference are obviously trivial, but for example you can also efficiently:
get the lowest item in the set with x & -x
remove the lowest item from the set with x & (x - 1)
add all items smaller than the smallest present item
add all items higher than the smallest present item
calculate their cardinality (though the algorithm is nontrivial)
permute the set in some ways, that is, change the indexes of the items (not all permutations are equally efficient)
calculate the lexicographically next set that contains as many items (Gosper's Hack)
1 and 2 and their variations can be used to build efficient graph algorithms on small graphs, for example see algorithm R in The Art of Computer Programming 4A.
Other applications of bitwise operations include, but are not limited to,
Bitboards, important in many board games. Chess without bitboards is like Christmas without Santa. Not only is it a space-efficient representation, you can do non-trivial computations directly with the bitboard (see Hyperbola Quintessence)
sideways heaps, and their application in finding the Nearest Common Ancestor and computing Range Minimum Queries.
efficient cycle-detection (Gosper's Loop Detection, found in HAKMEM)
adding offsets to Z-curve addresses without deconstructing and reconstructing them (see Tesseral Arithmetic)
These uses are more powerful, but also advanced, rare, and very specific. They show, however, that bitwise operations are not just a cute toy left over from the old low-level days.
Example 1
If you have 10 booleans that "work together" you can do simplify your code a lot.
int B1 = 0x01;
int B2 = 0x02;
int B10 = 0x0A;
int someValue = get_a_value_from_somewhere();
if (someValue & (B1 + B10)) {
// B1 and B10 are set
}
Example 2
Interfacing with hardware. An address on the hardware may need bit level access to control the interface. e.g. an overflow bit on a buffer or a status byte that can tell you the status of 8 different things. Using bit masking you can get down the the actual bit of info you need.
if (register & 0x80) {
// top bit in the byte is set which may have special meaning.
}
This is really just a specialized case of example 1.
Bitwise operators are particularly useful in systems with limited resources as each bit can encode a boolean. Using many chars for flags is wasteful as each takes one byte of space (when they could be storing 8 flags each).
Commonly microcontrollers have C interfaces for their IO ports in which each bit controls 1 of 8 ports. Without bitwise operators these would be quite difficult to control.
Regarding masking, it is common to use both & and |:
x & 0x0F //ensures the 4 high bits are 0
x | 0x0F //ensures the 4 low bits are 1
In microcontroller applications, you can utilize bitwise to switch between ports. In the below picture, if we would like to turn on a single port while turning off the rest, then the following code can be used.
void main()
{
unsigned char ON = 1;
TRISB=0;
PORTB=0;
while(1){
PORTB = ON;
delay_ms(200);
ON = ON << 1;
if(ON == 0) ON=1;
}
}
I need to count from 0 to 10 and store those values in binary format in ADCON0(5:2). How do I point at bit 5 of this register? Bit 5 is named ADCON0bits.CHS3. If I store a 4 bit variable to ADCON0bits.CHS3, will bits 1 - 3 be written to bits 4 - 2 of the register?
Also, are there any 4 bit data types that I could use?
This is all on a PIC microcontroller.
Edit: I need to store 4 bits in the register like so:
unsigned char count = 10 //max value
[X][X][1][0][1][0][X][X]
This is in line with what was assumed below, but I figured I would clear up my question a bit.
When you say you are writing bits 1-3 of your count into positions 4-2 of your register, do you explicitly mean you are reversing the order of the bits? In this answer I will presume that that was not what you meant.
You can express a bit field explicitly as a struct.
Presuming that you are dealing with a 16 bit register, your struct could look something like this:
struct adcon {
unsigned char someflag : 2;
unsigned char count : 4;
unsigned char other_bits : 2;
};
With each struct member, you specify the number of bits. Then you can operate on the appropriate bits in the register by casting the register to the struct type, and operating on the members of the struct.
(adcon) ADCON0.count = count;
Edit: fixed up the code based on feedback, thanks.
Writing to a bit variable stores the truth value of that variable to the bit. For example, writing:
ADCON0bits.CHS3 = 3;
will set that bit to 1.
If bit5 refers to the bit masked by 0x20 (00100000) and you need to store the 4 bit number in bits masked 0x3c (00111100) then you can use bit shifts and bitwise operations:
// First clear bits 1-5:
ADCON0 &= ~0x3c;
// Now set the bits to correct value:
ADCON0 |= (count << 2); // <-- remember to shift 2 bits to the left
update: As mentioned by Ian in the comments. This sets ADCON0 to an intermediate value before updating. In this case it is OK since it is only selecting the A/D channel and not actually executing the conversion. But in general it's better to do:
unsigned char temp_adcon;
temp_adcon = ADCON0 & ~0x3c;
ADCON0 = temp_adcon | (count << 2);
See the answers for this SO question.
Note that you are doing a read-modify-write operation. You have to be careful of race conditions when doing this. Race conditions may be caused by:
The hardware itself changing bits in the register (e.g. A/D converter operation completes and sets flags). The design of the hardware should provide a means for you to avoid this problem—there are several possible solutions—read the manual for the micro/peripheral to find out.
Your own interrupt routine(s) also writing to the register. If so, when your main (non-interrupt) code writes to the register, it should be done within an "interrupts disabled" context.
I'm not sure about the exact register ADCON0, but often you can read the register, mask the 4 bits and insert your count and then use that value to write back to the register.
Just in case, masking is performed with an AND operation and inserting is an OR operation with the count shift over 2 bits in your case.