Conversion between HEX, BIN and DEC

Hello,

I am using a Trinamic TMC2130 stepper driver, which is configured via SPI.

To transmit the commads, there is already this function available:

uint8_t tmc_write(int axis, uint8_t cmd, uint32_t data)
{
  uint8_t s;

...
...
...

  s = SPI.transfer(cmd);
  SPI.transfer((data >> 24UL) & 0xFF) & 0xFF;
  SPI.transfer((data >> 16UL) & 0xFF) & 0xFF;
  SPI.transfer((data >> 8UL) & 0xFF) & 0xFF;
  SPI.transfer((data >> 0UL) & 0xFF) & 0xFF;

  return s;
}

It is called for example like this:

tmc_write(5, WRITE | REG_IHOLD_IRUN, 0x00051D1AUL);

Note: I have deleted some parts, which are for the chip select.

The code is working this way. But now I want to change the settings within the Arduino code.

Until now, I have done it manually with a Excel-tool.

To get an better understanding, I need to explain the syntax(see attachment):

Several parameters (in this case 3), are within a binary number.

Parameter 1: Bit 0 to 4
Parameter 2: Bit 8 to 12
Parameter 3: Bit 16 to 19

Parameter 1 is set to DEC 26
Parameter 2 is set to DEC 29
Parameter 3 is set to DEC 5

So at first all decimal numbers must be converted to binary numbers and be placed at the correct bit position. Then there must be a conversion from binary to HEX.

Then I would get 51D1A as HEX-number. But I need it in the syntax with 0x as prefix and the "UL" at the end. So I must get 0x00051D1AUL, which can transfered to the SPI-function.

What is the best way to do this?

I have already created a function, which will generate the right code, but as String and not as uint32_t, like it is neccessery for the SPI-function. Is there an easy solution to convert this String to a uint32_t or is this solution totally wrong?

  //--------IHOLDELAY-------------

  String strIHOLDELAY;
  byte zerosIHOLDELAY = 4 - String(IHOLDELAY, BIN).length();
  for (byte i = 0; i < zerosIHOLDELAY; i++) {
    strIHOLDELAY = strIHOLDELAY + "0";
  }
  strIHOLDELAY = strIHOLDELAY + String(IHOLDELAY, BIN);

  String hex0IHOLDELAY;
  String hex1IHOLDELAY;
  hex0IHOLDELAY = strIHOLDELAY.substring(0, 4);
  hex1IHOLDELAY = strIHOLDELAY.substring(4, 8);

  convHEX(hex0IHOLDELAY);
  IHOLDIRUN[0] = charvalue;		

//--------String-Contruction-------------
  String zeros = IHOLDIRUN;
  byte zerosAmount = 8 - String(zeros).length();
  zeros="";
  for (byte i = 0; i < zerosAmount; i++) {
    zeros = zeros + "0";
  }
  zeros = "0x" + zeros + IHOLDIRUN+ "UL";

  IHOLDIRUNstr = zeros;

Unbenannt.PNG

The simplest way is to remember that computers work in binary, and are really, really good at doing the repetitive arithmetic for you.
If you want to express a mask as "1 + 8 + 16 + 64", instead of 0b01011001 or 0x59 or 0131 or 8910 just do it, and let the compiler do the heavy lifting.

The conversion from DEC to BIN is still necessery, to set all parameter at the right position.

To get to my example:

The HEX-Value is store into a char array:

char IHOLDIRUN[5];

So in this case this would be 51D1A.

The SPI-functions need uint32_t a data format. How do I convert the char array into uint32_t?

The conversion from DEC to BIN is still necessery, to set all parameter at the right position.

No, it isn't.

The SPI-functions need uint32_t a data format. How do I convert the char array into uint32_t?

Assuming the endianness is correct, a simple cast pointer will do that.

I suspect you may be over-thinking the problem, if there really is a problem.

If you declare a variable as uint32_t, and set it equal to some decimal value, and want to see it in 8digit hex, does the Arduino automatically add 0x prefix with padded 0's?

Parameter 1: Bit 0 to 4
Parameter 2: Bit 8 to 12
Parameter 3: Bit 16 to 19

Parameter 1 is set to DEC 26
Parameter 2 is set to DEC 29
Parameter 3 is set to DEC 5

Bit shifts + addition.

uint32 setting = (UINT32_C(26)<<0) + 
                 (UINT32_C(29)<<8) +
                 (UINT32_C( 5)<<16);

INTP:
If you declare a variable as uint32_t, and set it equal to some decimal value, and want to see it in 8digit hex, does the Arduino automatically add 0x prefix with padded 0's?

What are you printing to?

want to see it in 8digit hex, does the Arduino automatically add 0x prefix with padded 0's?

No.

If you want to convert a 32bit variable "num" from binary into an ASCII string in that fashion, you can use sprintf(), e.g.

   char buf[12];
   sprintf(buf,"0x%08X",num);

See http://www.cplusplus.com/reference/cstdio/sprintf/

I was going to suggest bitfields but they seem to be broken in a strange way. I wrote a sample sketch (full source below) with these bitfields:

  struct {
    unsigned long : 12; // Unused: 31-20
    unsigned long P3: 4; // Parameter3: 19-16
    unsigned long : 3; // Unused 15-13
    unsigned long P2: 5; // Parameter2: 12-8
    unsigned long : 3; // Unused: 7-5
    unsigned long P1: 5; // Parameter1: 4-0
  } fields;

That produced the output:

26 (0x1A, 0b11010)
29 (0x1D, 0b11101)
5 (0x5, 0b101)
3504885760 (0xD0E85000, 0b11010000111010000101000000000000)

It appears that the first bitfield (12 bits of padding) is IGNORED so everything shows up 12 bits to the left of where they were expected. Naming the padding field didn't help. I then tried moving the 12-bit padding to between P3 and P4:

  struct {
    unsigned long P3: 4; // Parameter3: 19-16
        unsigned long : 12; // Unused: 31-20
    unsigned long : 3; // Unused 15-13
    unsigned long P2: 5; // Parameter2: 12-8
    unsigned long : 3; // Unused: 7-5
    unsigned long P1: 5; // Parameter1: 4-0
  } fields;

The padding then appears... but AFTER P4!

26 (0x1A, 0b11010)
29 (0x1D, 0b11101)
5 (0x5, 0b101)
3504865285 (0xD0E80005, 0b11010000111010000000000000000101)

The bitfield part of the compiler appears to be badly broken is some strange way.

The full sketch:

union {
  struct {
    unsigned long : 12; // Unused: 31-20
    unsigned long P3: 4; // Parameter3: 19-16
    unsigned long : 3; // Unused 15-13
    unsigned long P2: 5; // Parameter2: 12-8
    unsigned long : 3; // Unused: 7-5
    unsigned long P1: 5; // Parameter1: 4-0
  } fields;
  unsigned long full;
} IHIR_Params;

void setup() {
  Serial.begin(9600);
  // put your setup code here, to run once:
  IHIR_Params.full = 0;

  IHIR_Params.fields.P1 = 26;
  IHIR_Params.fields.P2 = 29;
  IHIR_Params.fields.P3 = 5;

  show(IHIR_Params.fields.P1);
  show(IHIR_Params.fields.P2);
  show(IHIR_Params.fields.P3);
  show(IHIR_Params.full);
}

void show (unsigned long val) {
  Serial.print(val, DEC);
  Serial.print(" (0x");
  Serial.print(val, HEX);
  Serial.print(", 0b");
  Serial.print(val, BIN);
  Serial.println(")");
}
void loop() {
  // put your main code here, to run repeatedly:
}

I now have added a new variable:

uint32_t IHOLDIRUNvalue;

so the char array will be converted back to a int:

IHOLDIRUNvalue = strtol(IHOLDIRUN, NULL, 16);

This seems to work.

The solution from Jiggy-Ninja seems to be less work. Especially for the other settings, in which each bit covers an other parameter.

Unpacking involves the reverse process, with a bitmask to remove unnecessary information.

uint8_t P1 = (setting>> 0) & 0xF;
uint8_t P2 = (setting>> 8) & 0xF;
uint8_t P3 = (setting>>16) & 0xF;

0 isn't necessary and will probably get optimized out by the compiler, but it makes the code look all nice and square.