I'm relatively new in the embedded world, so I may be a little newbie asking this.
I'm having trouble to understand the syntax of an example program in C that checks if a button is pressed and then turning a LED on if so. I'm using a STM32F207VCT6 (Cortex-M3) microcontroller.
The part of the program that I'm having trouble to understand is the following:
if(!(GPIOA->IDR & (1<<4)))
Key=1;
Port 4 from GPIOA is set to be an input with pull-up register. The signal from the button goes to GND once it's pressed. IDR is the input data register.
So my question is:
I don't understand in this code why the NOT symbol, !, inside the if statement is needed. And I do not know what comparison is being made inside the if, since there is no sign of equal. Can someone help me with that?
PS: I already know that (1<<4) is the bit-wise operation for left shifting the bit 1 four times.
There are 16 pins per port in the STM32F207VCT6. This operation is trying to determine the state of a single pin, number 4, of port A. Let's expand each part to better understand what is going on.
We start with the expression:
if(!(GPIOA->IDR & (1<<4)))
We're working with a 16-bit number to account for the 16 pins. Expanding this we get:
if(!(GPIOA->IDR & ((0000 0000 0000 0001) << 4)))
You already know that << is a bitwise left shift. Applying this we get:
if(!(GPIOA->IDR & (0000 0000 0001 0000)))
Expanding the IDR, I will use X's for bits that we don't care about and a ? for the unknown bit we are testing for:
if(!((XXXX XXXX XXX? XXXX) & (0000 0000 0001 0000)))
Next we have a & which is the bitwise AND operator. If you remember your AND truth table:
& 0 1
- -
0 | 0 0
1 | 0 1
So we have the operation:
XXXX XXXX XXX? XXXX
& 0000 0000 0001 0000
---------------------
0000 0000 000? 0000
If Bit 4 in Port A is a 0, the the result will be 0000 0000 0000 0000. If Bit 4 in Port A is a 1, the the result will be 0000 0000 0001 0000.
If the switch is pressed, the input will be pulled to GND and Bit 4 will be set to 0. If the switch is unpressed, then the internal pull-up will pull the input high and Bit 4 will be set to 1.
So we have two possibilities:
//Button is pressed
if(!(0000 0000 0000 0000))
Or:
//Button is not pressed
if(!(0000 0000 0001 0000))
Now it's important to understand that ! is the Logical NOT operator. Which inverts true and false in conditional statements. It is also important to understand that in the C language:
Logical operators (&&, ||, !, etc.) and condition-testing
statements (if, while) assume that zero is false and all other
values are true.
Emphasis mine
So what we really have is:
//Button is pressed
if(!(false))
Or:
//Button is not pressed
if(!(true))
Applying the NOT:
//Button is pressed
if(true)
Key=1;
Or:
//Button is not pressed
if(false)
Key=1;
if(!(GPIOA->IDR & (1<<4))) Key=1;
is equivalent to
if((GPIOA->IDR & (1<<4)) == 0) Key=1;
In C, !E is equivalent to E == 0.
So here basically, Key = 1; statement is executed if the value of bit 4 of GPIOA->IDR is 0.
Assume GPIOA->IDR is a byte.
[1111 1111] would infer that no GIPOA->IDR bits are cleared (grounded)
[1111 1111] & [0000 1000] = 0000 1000 => != 0
If the button was pressed...
[1111 0111] & [0000 1000] = 0000 0000 => == 0
Bob's Your Uncle.
Related
I need to understand why i have to use &=(and) instead of |=(or).
I tried drawing and using it but had no success.
char readPin(char port,char pinNum){
switch(port){
case'A' :
return ((portA &=(1<<pinNum))>>pinNum);
case'B' :
return ((portB &=(1<<pinNum))>>pinNum);
case'C' :
return ((portC &=(1<<pinNum))>>pinNum);
case'D' :
return ((portD &=(1<<pinNum))>>pinNum);
case'E' :
return ((portE &=(1<<pinNum))>>pinNum);
case'F' :
return ((portF &=(1<<pinNum))>>pinNum);
}
}
I need to know why and is used instead of or.
portA &=(1<<pinNum)
returns a '1' at the position shifted to.
For Example: PinNum = 4, Port A.4 is Set / High, Port A.1 is Set / High
PortA - 0001 0010
(1<<pinNum) - 0001 0000 & // use bitwise and
portA &=(1<<pinNum) - 0001 0000 // Result (bit 1 is discarded due to and operation)
((portA &=(1<<pinNum))>>pinNum) - 0000 0001 // Result is shifted back to position 0
The returned value is a 1.
If you were to use use a bitwise or like:
portA |= (1<<pinNum)
You would return a non 0 value in all cases.
For Example: PinNum = 4, all Port A bits are low.
PortA - 0000 0000
(1<<pinNum) - 0001 0000 | // use bitwise or
portA |=(1<<pinNum) - 0001 0000 // Result (bit 4 is set)
((portA |=(1<<pinNum))>>pinNum) - 0000 0001 // Result is shifted back to position 0
The returned value is a 1.
As you can see, you tried to check for Pin 4 (which is not set) but you got a non zero result back (caused the masking bit).
I am trying to do SNMP Set from host Linux to target system. But, instead of correct values, wrong values are getting set. After a bit of research, I made this table:
Hex representation of decimal value in Linux snmp
0 - 0x80 - 1000 0000 - 0 is converted to 128
1 - 0x40 - 0100 0000 - 1 is converted to 64
2 0x20 - 0010 0000 - 2 is converted to 32
3 0x10 - 0001 0000 - 3 is converted to 16
4 0x08 - 0000 1000 - 4 is converted to 8
5 0x04 - 0000 0100 - 5 is converted to 4
6 0x02 - 0000 0010 - 6 is converted to 2
7 0x01 - 0000 0001 - is converted to 1
Hex representation of decimal value in target system
0 - 0x00 - 0000 0000
1 - 0x01 - 0000 0001
2 0x02 - 0000 0010
3 0x03 - 0000 0011
4 0x04 - 0000 0100
5 0x05 - 0000 0101
6 0x06 - 0000 0110
7 0x07 - 0000 0111
I have two questions:
What could be the reason behind this issue?
Does anyone know how I can convert those Linux values to correct target values in a C program?
If I understand your question correctly, you receive a byte that encode 8 values (0 to 7) using a one-hot encoding. See https://en.wikipedia.org/wiki/One-hot (notice: your bit order seems reversed though).
If you simply put a one-hot encoded bit pattern into a byte variable on your target system, you'll not get the original value as your target system uses another encoding (probably 2's complement). In other words - a given bit pattern has different meanings in one-hot encoding and 2's complement encoding.
So the task is to convert the one-hot encoded values to equivalent values on your target system.
You could go for a simple switch-statement - like:
int main(void)
{
unsigned char linux_snmp_value = 0x20;
unsigned char target_value = 255;
switch(linux_snmp_value)
{
case 0x80:
target_value = 0;
break;
case 0x40:
target_value = 1;
break;
case 0x20:
target_value = 2;
break;
// Add the remaining cases here
default:
// Illegal value
// Add some error handling
break;
}
printf("Target value %d\n", target_value);
return 0;
}
If you prefer a loop, it could be something like:
int main(void)
{
unsigned char linux_snmp_value = 0x01;
unsigned char target_value = 0;
unsigned char mask = 0x80;
while (mask)
{
if (mask == linux_snmp_value) break;
++target_value;
mask = mask >> 1;
}
if (mask == 0)
{
// Illegal value
// Add some error handling
printf("ERROR\n");
return -1;
}
printf("Target value %d\n", target_value);
return 0;
}
If the portability is not the issue (for example you do not play to compile on the VAX or AVR8 machine) you can use machine instructions for it
asm (“bsrl %1, %0” : “=r” (position) : “r” (number));
you can also wrap it into nice inline function.
Similar instructions are available on ARM, MIPS, PIC and many other.
The problem is SegmentOFF. For example if DATA[18] = 0x10 after calling SegmentON and I want to clear 6th bit of DATA[18]. Calling SegmentOFF clears all the bit and ends up DATA[18] = 0x00
What is wrong with the code.
unsigned char DATA[24];
unsigned int Segment2BitMap[48] =
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0204, 0x0300, 0x0302, 0x0307, 0x0600, 0x0601, 0x0602, 0x0603,
0x0604, 0x0605, 0x0606, 0x0607, 0x0804, 0x0900, 0x0902, 0x0907,
0x0C00, 0x0C01, 0x0C02, 0x0C03, 0x0C04, 0x0C05, 0x0C06, 0x0C07,
0x0E04, 0x0F00, 0x0F02, 0x0F07, 0x1200, 0x1201, 0x1202, 0x1203,
0x1204, 0x1205, 0x1206, 0x1207, 0x1404, 0x1500, 0x1502, 0x1507
};
void SegmentON(unsigned char Number)
{
unsigned int Data = Segment2BitMap[Number];
unsigned char UpperByte = (Data/256); //upper byte
unsigned char LowerByte = (Data%256 & 0x07); //lower byte
DATA[UpperByte] |= (0x01<<LowerByte);
}
void SegmentOFF(unsigned char Number)
{
unsigned int Data = Segment2BitMap[Number];
unsigned char UpperByte = (Data/256); //upper byte
unsigned char LowerByte = (Data%256 & 0x07); //lower byte
DATA[UpperByte] &= (0x01<<LowerByte);
}
int main()
{
SegmentON(40);
SegmentOFF(42);
}
1010 & 0101 == 0000
If you want to clear a specific bit and the rest should not be changed you have to invert it.
For 8 bit:
var &= (1<<2)^0xFF
This would set the 3rd least significant bit (1<<2) == 0x04 and then invert it with xor everything with ones
And then you apply that mask to var to set the 3rd bit to zero and the rest stays unchanged
This would be
var = var & 0b11111011
Ok, I mostly do ruby but the bit-wise operators look the same. (thus I'm assuming they are the same)
Lets work with a random number say 201. The binary representation is as follows:
1100 1001
To turn a bit on you simply do an OR operation.
201 |= 1<<2
The machine then bit shifts 0000 0001 2 steps to the left creating 0000 0100 and then makes an OR operation, if either of the numbers has a 1 at the nth position the resulting number will have a 1 at the same position.
1100 1001 # first input
0000 0100 # second input
1100 1101 # output => 209
So far so good, but when you try to set a bit to 0 using the AND operator something goes wrong.
201 &= 1<<2
Same as before but now it performs an AND operation instead. If the number share a 1 at the nth position the resulting number will have a 1 at that same position.
1100 1001 # first input
0000 0100 # second input
0000 0000 # output => 0
This happens because they have no common 1s. This can be solved in 2 ways. (both using the XOR operation)
The 'conditional' way.
if 209&(1<<2) != 0
209 ^= 1<<2
end
This code will do the following. if 201 and 1<<2 (8) is not equal to 0 then perform an exclusive or operation. In this case it is not equal to 0 so it will do as follows. Wherever the bits are the same it will put a 0 and where they differ it will put a 1.
1100 1101 # first input (209)
0000 0100 # second input (8)
1100 1001 # output => 201
The 'pure bit-wise' solution.
209&(255^(1<<2))
This will generate the following operation.
1100 1101 # first input (209)
1111 1011 # second input number (247)
1100 1001 # output => 201
We end up doing one extra bit operation, but we also loose the conditional. So it should be a bit faster. Get it bit, hehe. The code here can't directly be implemented in c, but the principal should be the same.
PS Your definition of the lower byte looks a bit odd, you are aware that it will only return the first 3 bits, right?
Hope it helps.
How can i map numbers like this:
1 => 0x01;
2 => 0x03;
3 => 0x07;
4 => 0x0F;
....
8 => 0xFF;
I have only 8 numbers to map and i need this for my RT embedded system so the solution must be efficient.
Is there a way to implement this using ENUM or DEFINE?
I don't want to use switch statement. Should i use an array:
BYTE bMap[8] =
{
0x01,
0x03,
0x07,
0x0F,
....
0xFF,
}
or is there another way?
Thank you! Max.
The two most obvious solutions would be:
Use an array. const uint8_t masks[] = { 1, 3, ... }.
Your mask seems to be "the i + 1 rightmost bits should be set", so you can trivally compute that at runtime using (1 << (i + 1)) - 1 which is easier to implement and less error-prone.
There's nothing wrong with using the lookup table to get your numbers but you could consider another approach.
If you're simply looping through those values in order, each one can be obtained by left-shifting the previous and setting the low order bit to 1:
0000 0001 (x01)
<< 1: 0000 0010
| 1: 0000 0011 (x03)
<< 1: 0000 0110
| 1: 0000 0111 (x07)
<< 1: 0000 1110
| 1: 0000 1111 (x0f)
<< 1: 0001 1110
| 1: 0001 1111 (x1f)
So, something like:
for (unsigned int i = 1; i < 0x100; i = (i << 1) | 1) ...
should do the trick.
The only possible advantage I can see that may have would be not having to go out to memory for a lookup table. Depending on your hardware architecture, that may or may not be a problem.
The following complete program:
#include <stdio.h>
int main (void) {
unsigned int i;
for (i = 1; i < 0x100; i = (i << 1) | 1)
printf ("%02x ", i);
putchar('\n');
return 0;
}
shows it in action:
01 03 07 0f 1f 3f 7f ff
If you only have 8 values and that's not going to change, use the array. But note that the mapping would be 0=>0x01, 1=>0x03, ... etc., because C indexing is zero-based.
Also, look for a pattern in your numbers: you could find a logic or arithmetic operation that will set the least significant N bits in a byte. I.e. N => (2 * N) -1
I'm attempting to learn how to program micro-controllers in C and have a question regarding bit assignments. Say for example I were to declare an 8 bit number.
binary_number = 0b00000000;
Now, lets say I wanted to set bit 3 only. Example texts I have seen use an operation like the following:
binary_number |= (1<<4)
Am I understanding this correctly? We are taking binary_number and 'or-ing' it with essentially 0b00001000 and then assigning that outcome to binary_number?
Likewise, when resetting this bit:
binary_number &= ~(1<<4)
We are essentially taking binary_number (0b00001000) and 'and-ing' it with 0b11110111 and then assigning binary_number to the outcome of that expression?
Do I understand this correctly?
Yes, your understanding is correct! :)
but a little change...
For resetting or setting the bit 3, you need to left shift the 1 by 3 places only.
1<<4 : 0b00010000
1<<3 : 0b00001000
Use the bitwise OR operator (|) to set xth bit.
n |= 1 << x;
That will set bit x.
Use the bitwise AND operator (&) to reset xth bit.
n &= ~(1 << x);
That will reset bit x.
Yes, you are understanding it correctly. That is exactly what you have to do to set and unset bits. Looks pretty complicated I know but you could always write a helper function that you pack around with you.
As pointed out others, you are setting/resetting the 4th bit not the 3rd
Set mask: 1<<4 = 10000 = 0001 0000
Reset Mask: ~(1<<4) = ~(10000)= ~(0001 0000) = 1110 1111
So, binary_number |= (1<<4):
Original Number - 0000 0000
Mask to set 4th bit - (OR) 0001 0000
--------
Result 0001 0000 //4th bit set
And, binary_number &= ~(1<<4):
Original Number - 000X 0000 //4th bit could be a 1 or a 0; X =1/0
Mask to set 3rd bit - (AND) 1110 1111
--------
Result 0000 0000 //4th bit reset
If you seek efficiency the best way to do it is to define the bits individually:
#define BIT_0 0b00000001
#define BIT_1 0b00000010
#define BIT_2 0b00000100
#define BIT_3 0b00001000
#define BIT_4 0b00010000
#define BIT_5 0b00100000
#define BIT_6 0b01000000
#define BIT_7 0b10000000
and then to set the bit in a byte:
unsigned char byteWhoseBitsAreSetReset = 0;
//To Set bit 3
byteWhoseBitsAreSetReset |= BIT_3;
//To Set Multiple bits
byteWhoseBitsAreSetReset |= (BIT_3 + BIT_4 + BIT_5);
//OR
byteWhoseBitsAreSetReset |= (BIT_3 | BIT_4 | BIT_5);
//To reset bit 3
byteWhoseBitsAreSetReset &= ~(BIT_3);
//To reset Multiple bits
byteWhoseBitsAreSetReset &= ~(BIT_3 + BIT_4 + BIT_5);
//OR
byteWhoseBitsAreSetReset &= ~(BIT_3 | BIT_4 | BIT_5);