0

The main problem I'm having is to read out values in binary in C. Python and C# had some really quick/easy functions to do this, I found topic about how to do it in C++, I found topic about how to convert int to binary in C, but not how to convert uint32_t to binary in C.

What I am trying to do is to read bit by bit the 32 bits of the DR_REG_RNG_BASE address of an ESP32 (this is the address where the random values of the Random Hardware Generator of the ESP are stored).

So for the moment I was doing that:

#define DR_REG_RNG_BASE                        0x3ff75144

void printBitByBit( ){

    // READ_PERI_REG is the ESP32 function to read DR_REG_RNG_BASE
    uint32_t rndval = READ_PERI_REG(DR_REG_RNG_BASE);
    int i;
    for (i = 1; i <= 32; i++){ 
      int mask =  1 << i;
      int masked_n = rndval & mask;
      int thebit = masked_n >> i;   
      Serial.printf("%i", thebit);
    }
    Serial.println("\n");
}

At first I thought it was working well. But in fact it takes me out of binary representations that are totally false. Any ideas?

hcheung
  • 3,377
  • 3
  • 11
  • 23
loulou
  • 21
  • 4
  • The bits are at positions 0 thru 31, not 1 thru 32. – Adrian Mole Sep 03 '22 at 17:26
  • An expression for the bit from position `i` is simply `rndval >> i & 1`. Print them in order from position 31 to position 0, inclusive. – Eric Postpischil Sep 03 '22 at 17:30
  • So, I've finally found the nice function bitRead() https://www.arduino.cc/reference/en/language/functions/bits-and-bytes/bitread/ It gives me the exact same result as my mask. – loulou Sep 03 '22 at 17:35
  • About 0 to 31, it's been a nightmare. I did many tests and the loop must start from 1 not from 0. Since bitRead() give me the very same results ( 00100000000000000000000000000000 to represents uint32_t = 8), I guess it's just correct and the ESP32 just do it like that. – loulou Sep 03 '22 at 17:39
  • 1
    The output in your last comment is **4** backwards (see my answer for why its backwards); if it were **8** backwards, it would start with `0001`. So, you're dropping the lowest bit, there. – Adrian Mole Sep 03 '22 at 17:52
  • @loulou: `bitRead` gave you the same results as your code because you didn't fix your indices. The indices are the problem, not the masking. The fact that calling `bitRead` with wrong inputs gave the same result as using your masking code with wrong inputs doesn't mean the inputs were actually right. – user2357112 Sep 03 '22 at 20:32

2 Answers2

1

Your shown code has a number of errors/issues.

First, bit positions for a uint32_t (32-bit unsigned integer) are zero-based – so, they run from 0 thru 31, not from 1 thru 32, as your code assumes. Thus, in your code, you are (effectively) ignoring the lowest bit (bit #0); further, when you do the 1 << i on the last loop (when i == 32), your mask will (most likely) have a value of zero (although that shift is, technically, undefined behaviour for a signed integer, as your code uses), so you'll also drop the highest bit.

Second, your code prints (from left-to-right) the lowest bit first, but you want (presumably) to print the highest bit first, as is normal. So, you should run the loop with the i index starting at 31 and decrement it to zero.

Also, your code mixes and mingles unsigned and signed integer types. This sort of thing is best avoided – so it's better to use uint32_t for the intermediate values used in the loop.

Lastly (as mentioned by Eric in the comments), there is a far simpler way to extract "bit n" from an unsigned integer: just use value >> n & 1.

I don't have access to an Arduino platform but, to demonstrate the points made in the above discussion, here is a standard, console-mode C++ program that compares the output of your code to versions with the aforementioned corrections applied:

#include <iostream>
#include <cstdint>
#include <inttypes.h>

int main()
{
    uint32_t test = 0x84FF0048uL;
    int i;
    // Your code ...
    for (i = 1; i <= 32; i++) {
        int mask = 1 << i;
        int masked_n = test & mask;
        int thebit = masked_n >> i;
        printf("%i", thebit);
    }
    printf("\n");
    // Corrected limits/order/types ...
    for (i = 31; i >= 0; --i) {
        uint32_t mask = (uint32_t)(1) << i;
        uint32_t masked_n = test & mask;
        uint32_t thebit = masked_n >> i;
        printf("%"PRIu32, thebit);
    }
    printf("\n");
    // Better ...
    for (i = 31; i >= 0; --i) {
        printf("%"PRIu32, test >> i & 1);
    }
    printf("\n");
    return 0;
}

The three lines of output (first one wrong, as you know; last two correct) are:

001001000000000111111110010000-10
10000100111111110000000001001000
10000100111111110000000001001000

Notes:

(1) On the use of the funny-looking "%"PRu32 format specifier for printing the uint32_t types, see: printf format specifiers for uint32_t and size_t.

(2) The cast on the (uint32_t)(1) constant will ensure that the bit-shift is safe, even when int and unsigned are 16-bit types; without that, you would get undefined behaviour in such a case.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
0

When you printing out a binary string representation of a number, you print the Most Signification Bit (MSB) first, whether the number is a uint32_t or uint16_t, so you will need to have a mask for detecting whether the MSB is a 1 or 0, so you need a mask of 0x80000000, and shift-down on each iteration.

#define DR_REG_RNG_BASE                        0x3ff75144

void printBitByBit( ){

    // READ_PERI_REG is the ESP32 function to read DR_REG_RNG_BASE
    uint32_t rndval = READ_PERI_REG(DR_REG_RNG_BASE);

    Serial.println(rndval, HEX);   //print out the value in hex for verification purpose

    uint32_t mask = 0x80000000;
    for (int i=1; i<32; i++) {
      Serial.println((rndval & mask) ? "1" : "0");
      mask = (uint32_t) mask >> 1;
    }
    Serial.println("\n");

}

For Arduino, there are actually a couple of built-in functions that can print out the binary string representation of a number. Serial.print(x, BIN) allows you to specify the number base on the 2nd function argument.

Another function that can achieve the same result is itoa(x, str, base) which is not part of standard ANSI C or C++, but available in Arduino to allow you to convert the number x to a str with number base specified.

char str[33];
itoa(rndval, str, 2);
Serial.println(str);

However, both functions does not pad with leading zero, see the result here:

36E68B6D                          // rndval in HEX
00110110111001101000101101101101  // print by our function
110110111001101000101101101101    // print by Serial.print(rndval, BIN)
110110111001101000101101101101    // print by itoa(rndval, str, 2)

BTW, Arduino is c++, so don't use c tag for your post. I changed it for you.

hcheung
  • 3,377
  • 3
  • 11
  • 23