0

I'm making some code for a 16 bits microprocessor. My memory is quite limited at 128 KB. IAR C/C++ Compiler for MSP430 I need to implement some code to save some memory.

I tried to implemented this with this C characteristic implementation.

struct {
    unsigned int widthValidated : 1;
    unsigned int heightValidated : 1;
} status;

But with this bit of code I still only use 1 bit of a 16 bit word.

My goal is to use the same word sized bit of memory for two 8 bit variables. The first variable should be 8 bits left of the second variable.

struct {
   unsigned int widthValidated : 8; //8 bits for this
   unsigned int heightValidated : 8; // 8 left over for this
} status;

Is this possible? Are there any implementations of this or is there a library in C for this? How should I go about doing this?

MrSpartan
  • 37
  • 5
  • 1
    Use a `char` instead of a bitfield. – dbush Jul 04 '19 at 13:00
  • You can use `char`, but what's the problem with the code there? – Thomas Jager Jul 04 '19 at 13:01
  • 3
    Bitfields is the part of C language that I really dislike: they are hard to use and port across different implementations. I stick to characters or bitmasks. – Serge Ballesta Jul 04 '19 at 13:03
  • Your question is not clear. Do you want to convert the bitfield to the new type? Do you want to implement something using the 8-bit fields instead of 1-bit fields? Do you want to match the bit positions with some specific requirement? What is the problem you want to solve? – Bodo Jul 04 '19 at 13:03
  • 2
    @SergeBallesta I'd say that bitfields are **impossible** to port across different implementations. – Andrew Henle Jul 04 '19 at 13:07
  • 1
    Or you could just use unsigned shorts if you really want bitfields – Vio Ariton Jul 04 '19 at 13:07
  • For _portable_ bit fields, use `unsigned` (not `unsigned shorts`), and give up precise control of endian, size. Do not populate with `fread()`. – chux - Reinstate Monica Jul 04 '19 at 13:19
  • Restate "goal is use the same space of memory for the two variables 8 bits reserved for the first variable and 8 bits left over for the second varible." to add clarity. – chux - Reinstate Monica Jul 04 '19 at 13:22

3 Answers3

1

You shouldn't use bit-fields for any purpose, particularly not for memory mapping, since they are very poorly standardized.

What you should do is to use the stdint.h types. Declare two uint8_t variables. You save memory in microcontrollers by carefully picking the necessary type whenever declaring a variable.

For example, in professional programs for limited microcontrollers, a typical for loop is written as for(uint8_t i=0; ... and not with "sloppy typing" for(int i=0; ....

The trick is: whenever you declare any variable, then always consider what is the maximum value that variable might get. By doing so, you save memory and prevent overflow bugs.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I disagree. It makes code easier to read. It is not portable but porability is not main uC programmers target. – 0___________ Jul 04 '19 at 14:17
  • @P__J__ Even if you don't write code which is portable between different MCUs often (I do), you _will_ eventually port code between compilers for the same MCU. Also, bit-fields make code impossible to read. Given `struct { int MSB:1 }`, we have potentially created code where the LSB is named MSB - we don't know. How exactly is that readable? – Lundin Jul 04 '19 at 14:24
  • **you will eventually port code between compilers for the same MCU** I use IAR, Keil, gcc & Green Hills compilers at work and bitfields are 100% percent portable between them. I think your opinion is not backed by the real life experience, only based on the C standard. PS At the moment we use only ARM Cortex-Mx micros. But we were using PIC years ago (MCC18 compiler and this one was not supporting > 32 bits bitfields) – 0___________ Jul 04 '19 at 14:28
  • @P__J__ Go ahead and read the actual compiler docs for each compiler and you'll find that they aren't. – Lundin Jul 04 '19 at 14:29
  • start using them and then comment – 0___________ Jul 04 '19 at 14:30
0

If you want to save memory just use smaller types when you declare the struct

typedef struct
{
    uint8_t flag1: 1;
    uint8_t flag2: 1;
    uint8_t flag3: 1;
    uint8_t flag4: 1;
    uint8_t flag5: 1;
}myflags;

uint8_t foo(myflags f)
{
    return f.flag4;
}

uint8_t foo1(uint8_t flags)
{
    return !!(flags & (1 << 3));
}

void foo3()
{
    printf("%zu\n", sizeof(myflags));
}

it makes code easier to read debug and modify.

foo:
        ubfx    r0, r0, #3, #1
        bx      lr
foo1:
        ubfx    r0, r0, #3, #1
        bx      lr
foo3:
        movs    r1, #1
        ldr     r0, .L5
        b       printf
.L5:
        .word   .LC0
.LC0:
        .ascii  "%zu\012\000"

Many uCs have special instructions to extract bitfields move them to the lower bits and signed and unsigned extend the result to the register/variable size

0___________
  • 60,014
  • 4
  • 34
  • 74
0

You haven't told us what specific platform you are using so I can't give you a detailed enough answer but what you want is to remove the padding from your struct. Or in other words you want to pack your structs.

This is how I understand you to want your memory layout to be two bytes in one 16 bit word.

struct {
    uint8_t widthValidated;
    uint8_t heightValidated;
} status;

To require the compiler to create a certain boundary you can do:

for arm:

struct __attribute__((packed)){
    uint8_t widthValidated;
    uint8_t heightValidated;
} status;

for gcc:

#pragma pack (1)
struct __attribute__((packed)){
    uint8_t widthValidated;
    uint8_t heightValidated;
} status;

Your platform may differ but it should come up how to do this when you search for struct packing.

A union inside the struct would also be an option but this wouldn't necessarily be portable as bit/byte ordering may differ from platform to platform.

You see me using uint8_t above . This is a typedef done inside <stdint.h>. This is a standardized way to declare an integer variable with a defined width. Useful types inside a microcontroller are the undefined integer family of: uint8_t (8-bits), uint16_t (16-bits) and uint32_t (32-bits). And their signed counterparts: int8_t (8-bits), int16_t (16-bits) and int32_t (32-bits).

Tarick Welling
  • 3,119
  • 3
  • 19
  • 44