It must be late or I must be old.. Bit shifting in C

Something I’ve never really been strong in…bit shifting.

Taking this statement from a data sheet on the Atmell AT45DB04 spi flash it says to read from a buffer :slight_smile:

Data can be read from either one of the two buffers, using different opcodes to specify which
buffer to read from. An opcode of 54H or D4H is used to read data from buffer 1, and an opcode
of 56H or D6H is used to read data from buffer 2. To perform a Buffer Read, the eight bits of the
opcode must be followed by 15 don’t care bits, nine address bits, and eight don’t care bits.

How the heck do you translate that to C lol

A rough stab at it:

//32 total bits
/* 15 “Don’t care” bits /
spi_transfer(0x00); // 8 bits
/
Rest of the “don’t care” bits + 9 address bits /
spi_transfer((uint8_t)(offset >> 7)); // uuuhhhh im stuck here
spi_transfer((uint8_t)(offset ??? ));
/
+ 8 dont care bits */
spi_transfer(0x00);

If the endianness works out, bit fields / struct then feed the entire struct to spi_transfer.

Yeah but I would still like to understand bit shifting.

and according to the datasheet, it matters about MSB going out first.

These may help

http://playground.arduino.cc/Code/BitMath

Does that correspond to offset in your rough stab?

I found this code which helps

Too bad I’m doing this in mbed with the stm32 nucleo (the 3v3 flash doesn’t agree with arduino’s 5v pins)

1 Like

:grin:

Fortunately, that adds up to a whole multiple of bytes.

I’d start by putting your data into a buffer, for example:

unsigned char stuff[5];

stuff[0] = 0x5d; /* 8 bit command /
stuff[1] = 0; /
8 of the don’t care bits /
stuff[2] = (address & 0x100) >> 8; /
7 of the don’t care bits, plus one of address */

Finish that progression and then send the buffer out. Some of this is intentionally left as an exercise for the reader. :smiley:

You do not say, but I presume:

  1. offset is a 16-bit variable
  2. spi_transfer takes an 8-bit quantity

In place of

do

If you have trouble visualizing where each bit goes, I find it easiest to resort to sketching it out on paper. Your example is no so difficult as you are not combining parts of two different values into one byte.

Yeah, that complexity is in a different section. Where you have to combine opcode, dont care bits, page address and offset address.

Try to work it out yourself first, on paper if that helps. Then come back here. Some of us have been bit-fiddling so long that it is second nature.

Edit: Remember that | and || are different operators.

Ok, been a while since I wrote anything in C (so no laughing), but here’s a quick stab at it…

unsigned int formatcommand(unsigned int address, int selector) // selector is buffer number
{
unsigned int cmd, addtemp;

 if (selector == 1) {
       cmd = 54;
  } ELSE {
      cmd = 56;
  }

cmd = (cmd << 30) | (addtemp << 9);

return cmd;
} //end function

spi_transfer(formatcommand(0x01,0x99)); // example using address = 99 to buffer1

Should validate input and out put both within function to prevent overflows and whatnot.

Thanks. I’m familiar with the operators. Its the visualization I need to tinker with. I haven’t had much need for bit shifting in my development career (web, database, etc). I know how bit shifting works and what it’s doing but in terms of transforming datasheet “protocols” into the shifting techniques that I see in someone else’s code, and the examples here is the form of the art, that’s where I’m sketchy on.

Ok just to close the topic, I have figured out a way to explain this logic to myself so that I can grasp it, thanks to the input of everyone here. Specifically drawing it out helped a lot.

So here is what I came up with.

My original problem came from attempting to adapt some code that was written for the 16 megabit atmel dataflash to the 4 megabit version of the same chip. Their are subtle differences in the SPI communications for each size dataflash.

The function I was working with is shown below:


/** 
 * Main Memory Page Read. 
 * A main memory page read allows the user to read data directly from
 * any one of the 4096 pages in the main memory, bypassing both of the
 * data buffers and leaving the contents of the buffers unchanged.
 *
 * @param page Page of the main memory to read
 * @param offset Starting byte address within the page
 **/
void ATD45DB161D::ReadMainMemoryPage(uint16_t page, uint16_t offset)
{
    DF_CS_inactive;    /* Make sure to toggle CS signal in order */
    DF_CS_active;      /* to reset Dataflash command decoder     */

    /* Send opcode */
    spi_transfer(AT45DB161D_PAGE_READ);
    
    /* Address (page | offset)  */
    spi_transfer((uint8_t)(page >> 6));
    spi_transfer((uint8_t)((page << 2) | (offset >> 8)));
    spi_transfer((uint8_t)(offset & 0xff));
    
    /* 4 "don't care" bytes */
    spi_transfer(0x00);
    spi_transfer(0x00);
    spi_transfer(0x00);
    spi_transfer(0x00);
}

It turns out it was quite a simple task of changing:

    spi_transfer((uint8_t)(page >> 6));
    spi_transfer((uint8_t)((page << 2) | (offset >> 8)));

to:

    spi_transfer((uint8_t)(page >> 7));
    spi_transfer((uint8_t)((page << 1) | (offset >> 8)));

But I wanted to understand the syntax therefore I posed the query here.

The gist of it is, that the shifting of the bits aligns them in such a way that in the space of three bytes we have all the expected data the chip needs to understand the command we’re giving it.

A long form way of approaching this is with a struct as proposed earlier. But this is rarely seen in the wild. Most C developers use the shorthand bit shift above. It’s elegant in its simplicity as long as you grasp whats happening.

I’m rusty, what can I say.

2 Likes

Congrats! The best way to get better is to try, fail, get some hints, and then succeed. Teach a man to fish, and all that jazz.