12 Bit data inside 16 bit words: how to truncate blanking bits

J

johnny

Dear comp.lang.c members,

I have the following problem:
I am writing an application which reads data from a device. This data
is 12 bit wide and comes as a stream of words to be stored to a file.
At the moment I write it to 16 bit words, which creates an unessecary
overhead.

I currently use uint16_t.

How may I do this?

Any comments welcome ;-)

Thank you and
best regards,
Johann
 
S

s0suk3

Dear comp.lang.c members,

I have the following problem:
I am writing an application which reads data from a device. This data
is 12 bit wide and comes as a stream of words to be stored to a file.
At the moment I write it to 16 bit words, which creates an unessecary
overhead.

I currently use uint16_t.

How may I do this?

It depends on what you need to do with the data. If all you need is to
read it and then write it to somewhere else, you can just use unsigned
char[] (i.e., "bytes") and be done with it.

But if you need to perform arithmetic operations on the 12-bit
integers, for example, you can still use unsigned char[] to hold the
data and then copy individual 12-bit units into uint16_t variables to
perform the desired operations:

unsigned char* data;
size_t len;
ReadDeviceData(&data, &len);

// Fetch the first 12-bit unit and store it in a uint16_t
uint16_t num = *(uint16_t*) data >> 4;

(Your program may need to take byte ordering into account.) You may
write a macro to fetch a 12-bit value that is at a specific "bit
offset":

//
// Returns a 12-bit unit as a uint16_t
//
// @param offset the bit offset of the 12-bit value; must be
// a multiple of 12
// @param ptr a pointer to the block of memory
//
#define GET_UNIT(offset, ptr) \
(*(uint16_t*) ((ptr) + (offset) / CHAR_BIT) >> \
4 - (offset) % CHAR_BIT & \
~0U >> (offset) % CHAR_BIT)

Using such a macro you could step through every 12-bit value in an
arbitrary block of memory (unsigned char[]). For example, this loop
would print all 12-bit values as decimal numbers:

for (size_t offset = 0; offset + 12 < len * CHAR_BIT; offset +=
12)
printf("%" PRIu16 "\n", GET_UNIT(offset, data));

The point is that now you're using an array of unsigned char to hold
the data (thus saving space), and then copying individual 12-bit
values into uint16_t variables whenever you need to perform an
operation on them.

Sebastian
 
T

Tim Rentsch

Eric Sosman said:
johnny said:
Dear comp.lang.c members,

I have the following problem:
I am writing an application which reads data from a device. This data
is 12 bit wide and comes as a stream of words to be stored to a file.
At the moment I write it to 16 bit words, which creates an unessecary
overhead.

I currently use uint16_t.

How may I do this?

With a bit of shifting and OR-ing you can pack two 12-bit
samples into three 8-bit bytes (uint8_t) and write those to
the file. [snip]

It seems to me that doing such packing takes at least four bits
of shifting.

(Sorry, I couldn't resist...)
 
T

Tomás Ó hÉilidhe

     With a bit of shifting and OR-ing you can pack two 12-bit
samples into three 8-bit bytes (uint8_t) and write those to
the file.

The amount of memory need is calculable as follows:

(amount_twelves * 3 / 2) + (amount_twelves * 3 % 2)

Throw together in five minutes so likely to contain a bug:

#include <assert.h>
#include <stdint.h>

uint_fast16_t GetChunk(uint8_t *const mem, uint_fast32_t chunk_index)
{
if ( !(chunk_index & 1) ) /* If index is not odd */
{
/* The first 8 bits are in the leading octet,
The last 4 bits are the lowest 4 in the trailing octet */

return (uint_fast16_t)(mem[chunk_index * 3 / 2])
| (mem[chunk_index * 3 / 2 + 1] & 0xF);
}
else
{
/* The first 4 bits are the high 4 in the leading octet,
The last 4 bits are the low 4 in the trailing octet */

--chunk_index;

return (uint_fast16_t)(mem[chunk_index * 3 / 2 + 1] >> 4)
| (mem[chunk_index * 3 / 2 + 2] & 0xF);
}
}


void SetChunk(uint8_t *const mem, uint_fast32_t chunk_index,
uint_fast16_t const val)
{
assert(val <= 0xFFFu);

if ( !(chunk_index & 1) ) /* If index is not odd */
{
/* The first 8 bits are in the leading octet,
The last 4 bits are the lowest 4 in the trailing octet */

mem[chunk_index * 3 / 2] = val;

mem[chunk_index * 3 / 2 + 1] &= 0xF0;

mem[chunk_index * 3 / 2 + 1] |= val >> 8;
}
else
{
/* The first 4 bits are the high 4 in the leading octet,
The last 8 bits are in the trailing octet */

--chunk_index;

mem[chunk_index * 3 / 2 + 1] &= 0x0F;

mem[chunk_index * 3 / 2 + 1] = val & 0xFu << 4;

mem[chunk_index * 3 / 2 + 2] = val >> 4;
}
}

Again, likely to contain a bug.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top