C converters

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi there,

I am using the IAR C Compiler

I am trying to read 2 bytes (unsign chars) stored in EEPROM, then to convert them to an integer (because they are the high an low byte of an integer), then make some processes, to convert the integer result in two unsigned chars (bytes) and finally to write the two bytes back in EEPROM.

I am very new in C and my problems are:
1. how to convert an integer in 2 unsigned chars?
2. how to convert 2 unsigned chars to an integer?

Thank you very much.

Michael.

User of:
IAR Embedded Workbench C/C++ Compiler
Altium Designer

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

since you're using IAR, why not jsut write teh integer to teh EEPROM, and let the compiler worry about it for you. But if you really want to do it yourself...

unsigned int myInt;
unsigned char myCharHi, MyCharLo;

/* to convert from int to chars */
myCharLo = myInt;
myCharHi = myInt >> 8;

/* to convert form chars to int */
MyInt = myCharHi << 8;
MyInt |= myCharLo;

And before someone else chimes in saying "why didn't you do it this way?..." yes there are many different ways of doing this, including using unions. Some methods may generate more compact code. The above answer is the "portable" solution, as it does not rely on byte order in memory.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can try this.
It's not the shortest way, but it's easy to understand what happens.

Gr, P.

uint8_t A,B;    // 2 8-bit vars, could be char or unsigned char or whatever.
uint16_t C;     // unsigned 16 bit var. "int" is probably ok.

// if A is the High byte we assign it first to a 16 bit number.
 C=A;
 C<<=8;    // Shift C 8 bit. A is now in the high byte of C.
 C+=B;     // Add the low byte to the big number.

// Now the other way around:

 A=C>>8;   // Put the high byte of C in A.
 B=C;      // The High byte of C does not fit in B and will be truncated.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh my god,

I catch it,
So easy, maybe I have to learn more C.

thanks at all.

Michael.

User of:
IAR Embedded Workbench C/C++ Compiler
Altium Designer

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah, something else?
Is this a safe way to do this?

Let's say that myInt(Hi:Lo) is stored at 0x60:0x61 and myCharLo at 0x80.
Doing this:
myCharLo = myInt;
Is there any posibility to build a program storing the myInt(Hi:Lo) at 0x80 (low byte) an at 0x7F the high byte? Maybe in 0x7F is stored usefull data.

thanks.

Michael.

User of:
IAR Embedded Workbench C/C++ Compiler
Altium Designer

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

icarus1 wrote:
Ah, something else?
Is this a safe way to do this?

Let's say that myInt(Hi:Lo) is stored at 0x60:0x61 and myCharLo at 0x80.
Doing this:
myCharLo = myInt;
Is there any posibility to build a program storing the myInt(Hi:Lo) at 0x80 (low byte) an at 0x7F the high byte? Maybe in 0x7F is stored usefull data.

thanks.


Nitpick:
While the AVR has an 8-bit data path and therefore there is no inherent endianness, there are several specialized instructions that assume that 16-bit numbers in the GPRs will be stored as little endian (low:high) rather than big endian (high:low).

It is natural to extend that convention to all memory spaces, so I'd expect that most AVR compilers would actually store myInt(Hi:Lo) at 0x61:0x60.

As for your question:
No, there is no danger of unintentionally overwriting unrelated data in that specific example.

The compiler knows that the target myCharLo only occupies 1 byte, and therefore only 1 byte can ever be modified as a result of that operation. The least significant byte of the source will be copied to the destination, and any other, more significant byte(s), will simply be ignored.

This behaviour is mandated by the C language's conventions on integer type promotion and demotion, so any compiler will generate code that behaves that way. (regardless of the architecture's endianness.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No adding or shifting necessary.

union intbytes {
    int16_t i; 
    struct {
        uint8_t lo;
        uint8_t hi;
    } b;
};

union intbytes A;

// A.b.lo - low byte
// A.b.hi - high byte
// A.i    - integer value

A.b and A.i will occupy same memory.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That solution comes up all the time. glitch already predicted it would come up in this thread too.

That works fine in this case because we know the endiannes of the architecture.

If we didn't know, or if we wanted portable code that works no matter what endianness we're talking about, then the bit shifting is more appropriate.

And a smart optimizing compiler should recognize a C clause like this:

(unsigned char)(my16BitUnsignedInt >> 8)

and automatically generate code that simply ignores the low byte of the integer and looks at the high byte in isolation. Theoretically, one might hope that the resulting assembly might even approach the same efficiency as the union trick.

That being said, there will always be examples where the compiler fails to find the most optimal implementation given the wrong circumstances.