Author Topic: SPI transfer byte function  (Read 2979 times)

0 Members and 1 Guest are viewing this topic.

Offline Dadu@Topic starter

  • Contributor
  • Posts: 34
  • Country: in
SPI transfer byte function
« on: March 06, 2022, 01:19:05 pm »

I don't understand function written in the code  https://en.wikipedia.org/wiki/Serial_Peripheral_Interface

Code: [Select]
uint8_t SPI_transfer_byte(uint8_t byte_out)
{
    uint8_t byte_in = 0;
    uint8_t bit;

    for (bit = 0x80; bit; bit >>= 1) {
        /* Shift-out a bit to the MOSI line */
        write_MOSI((byte_out & bit) ? HIGH : LOW);

        /* Delay for at least the peer's setup time */
        delay(SPI_SCLK_LOW_TIME);

        /* Pull the clock line high */
        write_SCLK(HIGH);

        /* Shift-in a bit from the MISO line */
        if (read_MISO() == HIGH)
            byte_in |= bit;

        /* Delay for at least the peer's hold time */
        delay(SPI_SCLK_HIGH_TIME);

        /* Pull the clock line low */
        write_SCLK(LOW);
    }

    return byte_in;
}

How does function work to transfer one byte ?

 

Online DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6936
  • Country: es
Re: SPI transfer byte function
« Reply #1 on: March 06, 2022, 02:20:42 pm »
"byte" variable starts being 0x80 (binary 10000000), then it's shifted on every pass:
Code: [Select]
for (bit = 0x80; bit; bit >>= 1) {
"bit” will do 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01

Then the data bits are sent in this line:
Code: [Select]
write_MOSI((byte_out & bit) ? HIGH : LOW);Extracting the ternary operator:
Code: [Select]
(byte_out & bit) ? HIGH : LOWWhich means: Is true the result of ANDing together byte_out and bit ?
If true, send HIGH, else send LOW.

The rest is just clock toggling and delay to avoid running too fast.

« Last Edit: March 06, 2022, 02:23:54 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: Dadu@

Offline Dadu@Topic starter

  • Contributor
  • Posts: 34
  • Country: in
Re: SPI transfer byte function
« Reply #2 on: March 06, 2022, 05:08:12 pm »

The rest is just clock toggling and delay to avoid running too fast.

It seems to me that function is written in a very complicated way.
 
suppose we have to transfer binary 10000000

uint8_t byte_in =  10000000

Here is my idea to transfer byte which is incomplete. I am sending eight clock signals to transfer one byte but I don't understand how each byte will transfer one bye one in function ?

Code: [Select]
uint8_t SPI_transfer_byte(uint8_t byte_in)
{
    uint8_t bit;
uint8_t byte_out;

    for (bit = 0;  bit < 8; bit++)
   {     
        SCLK = HIGH ;             //Pull the clock line high
        delay();                  // Delay for at least the peer's setup time
        SCLK = LOW ;              // Pull the clock line low
    }

    return byte_out;
}
 

How do you write proposed function to transfer one byte ?
 

Offline gnif

  • Administrator
  • *****
  • Posts: 1939
  • Country: au
  • Views and opinions are my own
Re: SPI transfer byte function
« Reply #3 on: March 06, 2022, 05:55:28 pm »
Using DavidAlfa's example:

Code: [Select]
uint8_t SPI_transfer_byte(uint8_t byte_in)
{
    uint8_t bit;

    for (bit = 0x80; bit; bit >>= 1)
    {     
        MOSI = (byte_out & bit) ? HIGH : LOW;
        SCLK = HIGH ;             //Pull the clock line high
        delay();                  // Delay for at least the peer's setup time
        SCLK = LOW ;              // Pull the clock line low
    }

    return byte_out;
}

Let me try to explain...

your byte has 8 bits in it, and we need to send each bit, one at a time from left to right... so lets say your byte is 0b10000100, this means you need to send the bits:

1, 0, 0, 0, 0, 1, 0, 0.

So what we need to do is test each bit to see if it's on or off... to do this we use the AND operator with mask that selects the right bit.

0x80 = bit 7
0x40 = bit 6
0x20 = bit 5
0x10 = bit 4
0x08 = bit 3
0x04 = bit 2
0x02 = bit 1
0x01 = bit 0

So to test if bit 7 is on, we mask it off with the AND boolean operator.

if ((byte_in & 0x80) != 0) printf("Bit 7 is on");

The loop that DavidAlfa provided simply runs through each bit one at a time testing each one.

The following SO article may help you also:
https://stackoverflow.com/questions/11815894/how-to-read-write-arbitrary-bits-in-c-c
« Last Edit: March 06, 2022, 06:06:21 pm by gnif »
 
The following users thanked this post: Dadu@

Offline Dadu@Topic starter

  • Contributor
  • Posts: 34
  • Country: in
Re: SPI transfer byte function
« Reply #4 on: March 06, 2022, 07:32:32 pm »
Using DavidAlfa's example:

So to test if bit 7 is on, we mask it off with the AND boolean operator.


My guess that every bit is tested is it high or low.

I don't understand why  every bit is tested what happens if we don't test bit ?
 

Offline PlainName

  • Super Contributor
  • ***
  • Posts: 8935
  • Country: 00
Re: SPI transfer byte function
« Reply #5 on: March 06, 2022, 11:16:19 pm »
Quote
I don't understand why  every bit is tested what happens if we don't test bit ?

bit has a variable value which is the position of the bit being tested. The result of byte_out & bit would be a value from 0..128, but MOSI is a single bit either 1 or 0. The test simply converts the byte-wide variable to a single bit: any non-zero value results in HIGH and a zero value is LOW (which presumably map to 1 and 0 respectively).

 
The following users thanked this post: Dadu@

Online DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6936
  • Country: es
Re: SPI transfer byte function
« Reply #6 on: March 06, 2022, 11:27:02 pm »
You're suposed to send 8 bits serially.
What's the data? Your byte.
How do you send ex. 0xF1? That's binary 11110001.
So the clock had to toggle 8 times, but before every clock pulse the coresponding data bit needs to be checked and the data line adjusted accordingly.
So to send 0xF1:
data = 0xF1
(data & 0x80) = 1, (Clock pulse)
(data & 0x40) = 1, (Clock pulse)
(data & 0x20) = 1, (Clock pulse)
(data & 0x10) = 1, (Clock pulse)
(data & 0x08) = 0, (Clock pulse)
(data & 0x04) = 0, (Clock pulse)
(data & 0x02) = 0, (Clock pulse)
(data & 0x01) = 1, (Clock pulse)

Asking such basic questions here is pretty much losing the time, there're lots of great videos on YouTube explaining in great detail how SPI works  :-+
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: Dadu@

Offline Bassman59

  • Super Contributor
  • ***
  • Posts: 2499
  • Country: us
  • Yes, I do this for a living
Re: SPI transfer byte function
« Reply #7 on: March 07, 2022, 12:24:01 am »

I don't understand function written in the code  https://en.wikipedia.org/wiki/Serial_Peripheral_Interface

Code: [Select]
uint8_t SPI_transfer_byte(uint8_t byte_out)
{
    uint8_t byte_in = 0;
    uint8_t bit;

    for (bit = 0x80; bit; bit >>= 1) {
        /* Shift-out a bit to the MOSI line */
        write_MOSI((byte_out & bit) ? HIGH : LOW);

        /* Delay for at least the peer's setup time */
        delay(SPI_SCLK_LOW_TIME);

        /* Pull the clock line high */
        write_SCLK(HIGH);

        /* Shift-in a bit from the MISO line */
        if (read_MISO() == HIGH)
            byte_in |= bit;

        /* Delay for at least the peer's hold time */
        delay(SPI_SCLK_HIGH_TIME);

        /* Pull the clock line low */
        write_SCLK(LOW);
    }

    return byte_in;
}

How does function work to transfer one byte ?

The "clever" part of the function is how the for loop is used. You're probably familiar with the standard idiom:

for (bit = 0; bit < 8; bit++) { }

but remember what the three statements in the parentheses mean.

The first just initializes the index variable at the start of the loop.

The second is the test to see if the statements in the loop body are executed or if the loop ends. If the condition is false, the loop exits. The body is not executed, and execution continues at the first line after the loop. If true, then the loop body execute, and when it's finished, the third statement is executed. In the standard idiom, then, we first clear bit to zero. Then we test: is bit < 8? Yes, of course, so we execute the loop. Then we increment bit, and test again. At some point, bit is incremented to 8, so the condition fails and the loop exits.

In the SPI example, what happens? First, we initialize bit to 0x80. Then we test. What do we test? In C, a boolean test of an integer returns true if the integer is non-zero. It returns false if it's zero. Therefore, at the first test, bit is 0x80 which evaluates as true. The loop executes. At the end of the loop, bit is shifted right one position, so its new value is 0x40. Test 0x40. Is that non-zero? Yes, so do the loop again. After the 8th iteration, we right-shift 0x01, which gives us 0x00. Is 0x00 true? No, it's not, so the loop exits.

----

Inside the loop, the first thing we do is shift out a bit. SPI shifts out most-significant bit first (usually). That's why bit is initialized to 0x80. In the call to write_MOSI(), the byte we want to send (byte_out) is bit-wise ANDed with bit -- that's a standard bit-masking operation. The mask is bit. Since bit is 0x80, all but the MSb of byte_out are cleared. What about the MSb? If the MSb of byte_out is set, the bit-wise AND result will be 0x80. If not, then the result is 0x00. That result is applied to the ternary operation ?: in the same way that bit is used in the for statement. If the result of the bit-mask is true -- non-zero -- then we "write" a HIGH to the SPI MOSI port. If it's false -- zero -- then we write a LOW to that port. HIGH and LOW must be defined somewhere, and they're used like this to allow for possible inversion of the output bit, or whatever.

The rest just follows.
 
The following users thanked this post: Dadu@

Offline Dadu@Topic starter

  • Contributor
  • Posts: 34
  • Country: in
Re: SPI transfer byte function
« Reply #8 on: March 07, 2022, 08:16:46 am »
I belive this function only show the idea of how master transfer byte to slave over SPI.

Code: [Select]
uint8_t SPI_transfer_byte(uint8_t byte_out)
{
    uint8_t byte_in = 0;
    uint8_t bit;

    for (bit = 0;  bit < 8; bit++)
    {     
       MOSI((byte_out & bit) ? HIGH : LOW);
       SCLK = HIGH ;                            //Pull the clock line high
       delay();                                 // Delay for at least the peer's setup time
       SCLK = LOW ;                             // Pull the clock line low
   byte_out = byte_out << 1;
}
    return byte_out;
}
   

Master send byte and receive byte at same time so what's the modification need in function to receive byte from slave ?
« Last Edit: March 07, 2022, 08:18:28 am by Dadu@ »
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 10091
  • Country: us
Re: SPI transfer byte function
« Reply #9 on: March 07, 2022, 08:38:10 am »
I belive this function only show the idea of how master transfer byte to slave over SPI.

Code: [Select]
uint8_t SPI_transfer_byte(uint8_t byte_out)
{
    uint8_t byte_in = 0;
    uint8_t bit;

    for (bit = 0;  bit < 8; bit++)
    {     
       MOSI((byte_out & bit) ? HIGH : LOW);
       SCLK = HIGH ;                            //Pull the clock line high
       delay();                                 // Delay for at least the peer's setup time
       SCLK = LOW ;                             // Pull the clock line low
   byte_out = byte_out << 1;
}
    return byte_out;
}
   

Master send byte and receive byte at same time so what's the modification need in function to receive byte from slave ?

That function won't work at all...

The variable 'bit' must have only one bit set at a time and it must start from the high order position (80h) and work down to the low order position (01h) in the pattern 80h, 40h, 20h, 10h, 08h, 04h, 02h, 01h.  This allows you to test just a single bit of byte_out and decide whether to send a HIGH or a LOW.  The function in the original post does this correctly, use it.

The function, indeed all of the functions in this thread assume SPI is set up such that the clock idles low and data is transferred on the rising edge.  So, after you raise the clock and before you drop the clock, read MISO and shift it into a variable perhaps called byte_in.

SCLK = HIGH;
byte_in <= byte_in << 1 | MISO;  // Just OR MISO into the low order position after shifting byte_in to the left by one position.
SCLK = LOW;

There are 4 common SPI configurations and the code needs to be written to deal with one of them.  The clock can idle high or low and the data can shift on the rising or falling edge.  You can see the 4 variations here:

https://dlnware.com/theory/SPI-Transfer-Modes  Notice where the samples are taken.

You need to know what the slave requires.  There is no universal standard although I believe clock idle low, sample on rising edge is most common.

« Last Edit: March 07, 2022, 08:42:19 am by rstofer »
 
The following users thanked this post: Dadu@

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 10091
  • Country: us
Re: SPI transfer byte function
« Reply #10 on: March 07, 2022, 09:23:54 am »
Also, your function will want to return byte_in, not byte_out.
MISO, as shown in my example must be a single bit or a byte with only the low order bit set.  You could also do something like
...
SCLK = 1; // existing code
if (MISO & 0x01) {  // we only care about the low order bit if MISO isn't already a single bit
  byte_in = byte_in << 1 | 0x01; // set low order bit to 1
}
else {
  byte_in = byte_in << 1 & 0xFE; // make sure low order bit is set to 0
}
delay(<some value>); // existing code
SCLK = 0;
...

This may be easier to understand...

If you connect MISO and MOSI, you should receive what you send.  You can write a little loop-back test program.
« Last Edit: March 07, 2022, 09:28:18 am by rstofer »
 
The following users thanked this post: Dadu@


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->