Map numbers in C language - c

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

Related

0 is converted to 128

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.

Need some bug identification with Bit Manipulation

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.

Bit Selection in C

I am trying to select bits [0:2] and bits [6:8] of the bit-string 1010000000001. Bits [0:2] are 001 and bits [6:8] are 000. I tried to select these bits with:
int instr = 0x1401;
int src2 = (instr & 0x0006); //get bits [2:0]
int src1 = (instr & 0x01C0) >> 6; //get bits [6:8]
printf("%04x, %04x",src2, src1);
However I am getting that src1 and src2 are both 0000. Can someone please help me understand what I am doing incorrectly so I can select bits [0:2] and [6:8]?
Look at this code:
#include <stdio.h>
int main (void) {
unsigned instr = 0x1401;
unsigned src2 = instr & 0x0007; // 7 in hex == 0000 0000 0111 in binary
unsigned src1 = (instr & 0x01C) >> 6; // 1C in hex == 0001 1100 0000 in binary
printf("%04x, %04x", src2, src1);
}
It masks out the desired bits in instr and shifts them by the correct offset. Also, when doing bit manipulation, unsigned types are preferred.
It's easier to just write a function to calculate any arbitrary bit slice (here using 1 rather than 0 as the least significant bit):
#include <stdio.h>
#include <assert.h>
int bit_select(int num, size_t start, size_t end)
{
assert(end >= start);
const int mask = (1 << (end-start+1)) - 1;
const int shift = start - 1;
return (num & (mask << shift)) >> shift;
}
int main(void)
{
printf("Bits 1...3 of 01100101: %d\n", bit_select(0x65, 1, 3));
printf("Bits 3...3 of 01100101: %d\n", bit_select(0x65, 3, 3));
printf("Bits 4...4 of 01100101: %d\n", bit_select(0x65, 4, 4));
printf("Bits 3...7 of 01100101: %d\n", bit_select(0x65, 3, 7));
return 0;
}
with output:
paul#horus:~/src/sandbox$ ./bitselect
Bits 1...3 of 01100101: 5
Bits 3...3 of 01100101: 1
Bits 4...4 of 01100101: 0
Bits 3...7 of 01100101: 25
paul#horus:~/src/sandbox$
From what I can see if you get the result from 0x1401 & 0x0006 you get 0 and you get the same from 0x1401 & 0x01c0. The bit shift you do on src1 is just 0 shift right 6 bits which is still 0.
Because you provided a wrong mask.
To make life easier if you are using gcc, just provide binary literal rather than hex version so that you can see what you are masking off without pain:
unsigned src2 = instr & 0b111;
int instr = 0x1401;
//results in instr containing (ignoring endian)
//0x00001401
//or in binary
//0b0000 0000 0000 0000 0001 0100 0000 0001
//extracting bits 2:0 is normally done by:
int src2 = instr & 0x00000007;
//extracting bits 8:6 is normally done by:
int src1 = (instr & 0x000001C0) >> 6;
//note that if bit 31 is to be extracted,
//the bit shifting will not work
//due to sign propagation of a negative number

Setting Bits in C Microcontroller

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);

moving bits from different ports and storing it in memory

I have this question, I got a dipswitch like a selector for different modes within my application. there are 3 bits to create the selector, but they are from different ports for example, the bit number 3 from portB, the bit number 1 from portC and the bit number 3 from portC, I want to move those 3 bits and storaged them in a register so that way I can create the selector, I already did it in assembler but i'm new into C programming, and till now the only similar answer was to move all the information from the entire port but anything related to a single bit. What's the command that I should use to move those bits from different ports?
In C, to get bit-level precision, you will need to use bitwise operators. For example, let's say you have an 8 bit container (which is also the smallest data type in C), but you're only interested in 1 bit; the best way to extract that bit would be to do a bitwise AND. In C, the bitwise AND is performed like this:
char c = 0x0B;
char bit = 0;
// Let's get the value of the second bit, so we must shift
// our bits to the right by one, then perform a bitwise AND
// to invalidate all other bits except the first.
bit = (c >> 1) & 0x01;
In binary, it would look like this:
00001011
Shift right once:
00000101
AND:
00000101 & 000000001
Yields: 000000001
To store different bits in the same memory location, you can use the bitwise-OR operator, which is the vertical pipe in C |. Here's an example:
char c = 0x00;
c = c | 0x01; /* c will now yield 0x01 */
c = c | 0xF0; /* c will now yield 0xF1 */
Final binary result: 11110001
With & and |, you can do some powerful things that are included in a lot of libraries to pass multiple options in one parameter. With 8 bits, you can store 8 different flags (or options), with 16 bits, 16 options. You could use this philosophy for your different ports. If you only have 4 bits worth of data in each port, you could use a 16-bit container, with 4 bits left that could simply be ignored. You'd do it like this:
short port_values = 0;
port_values = port_values | (port_a & 0x000F); // pretend port_a only has 4 bits of data and contains 0x0001
// port_values is now 0000 0000 0000 0001
// prepare to receive port_b by shifting the bits 4 to the left
port_values = port_values << 4;
// port_values is now 0000 0000 0001 0000
port_values = port_values | (port_b & 0x000F); // pretend port_b only has 4 bits of data and contains 0x000F
// port_values is now 0000 0000 0001 1111
// prepare to receive port_b by shifting the bits 4 to the left
port_values = port_values << 4;
// port_values is now 0000 0001 1111 0000
port_values = port_values | (port_c & 0x000F); // pretend port_c only has 4 bits of data and contains 0x0002
// port_values is now 0000 0001 1111 0010
I decide to do the following code, but when i'm running the the code it doesn't enter into the switch-case just retur after to set the last bit of "COMBINACION" what could be my error. i'm new is this world of C thanks for your time and consideration.
void SECUENCIA_1();
void SECUENCIA_2();
void SECUENCIA_3();
void SECUENCIA_4();
void SECUENCIA_5();
void SECUENCIA_6();
void SECUENCIA_ERROR();
void SALIDA_OK();
int i;
unsigned char COMBINACION;
void main(void) {
ADCON1 = 0x0F;
PORTA = 0x00;
TRISA = 0b00000;
PORTC = 0x00;
TRISC = 0b11100000;
PORTD = 0x00;
TRISD = 0b110000;
PORTE = 0x00;
TRISE = 0b000;
PORTB = 0x00;
TRISB = 0b11111111;
COMBINACION = 0x00;
COMBINACION = COMBINACION | (PORTCbits.RC5);
COMBINACION = COMBINACION << 1;
COMBINACION = COMBINACION | (PORTCbits.RC4);
COMBINACION = COMBINACION << 1;
COMBINACION = COMBINACION | (PORTDbits.RD3);
switch (COMBINACION)
{
case 0x0: SECUENCIA_ERROR; break;
case 0x1: SECUENCIA_1; break;
case 0x2: SECUENCIA_2; break;
case 0x3: SECUENCIA_3; break;
case 0x4: SECUENCIA_4; break;
case 0x5: SECUENCIA_5; break;
case 0x6: SECUENCIA_6; break;
default:SECUENCIA_ERROR;
}
}

Resources