SPI transfer byte function - Page 1
Forum section: electronics / general

author: Dadu@
author_rank: Contributor
timestamp: March 06, 2022, 01:19:05 pm
content: I don't understand function written in the codehttps://en.wikipedia.org/wiki/Serial_Peripheral_InterfaceCode: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 ?

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 06, 2022, 02:20:42 pm
content: "byte" variable starts being 0x80 (binary 10000000), then it's shifted on every pass:Code:for (bit = 0x80; bit; bit >>= 1) {"bit” will do 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01Then the data bits are sent in this line:Code:write_MOSI((byte_out & bit) ? HIGH : LOW);Extracting the ternary operator:Code:(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.

author: Dadu@
author_rank: Contributor
timestamp: March 06, 2022, 05:08:12 pm
content: Quote from: DavidAlfa on March 06, 2022, 02:20:42 pmIt seems to me that function is written in a very complicated way.suppose we have to transfer binary 10000000uint8_t byte_in = 10000000Here 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: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 highdelay(); // Delay for at least the peer's setup timeSCLK = LOW ; // Pull the clock line low}return byte_out;}How do you write proposed function to transfer one byte ?

author: gnif
author_rank: Unknown
timestamp: March 06, 2022, 05:55:28 pm
content: Using DavidAlfa's example:Code: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 highdelay(); // Delay for at least the peer's setup timeSCLK = 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 70x40 = bit 60x20 = bit 50x10 = bit 40x08 = bit 30x04 = bit 20x02 = bit 10x01 = bit 0So 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

author: Dadu@
author_rank: Contributor
timestamp: March 06, 2022, 07:32:32 pm
content: Quote from: gnif on March 06, 2022, 05:55:28 pmMy 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 ?

author: PlainName
author_rank: Super Contributor
timestamp: March 06, 2022, 11:16:19 pm
content: Quotebit has a variable value which is the position of the bit being tested. The result ofbyte_out & bitwould 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).

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 06, 2022, 11:27:02 pm
content: 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

author: Bassman59
author_rank: Super Contributor
timestamp: March 07, 2022, 12:24:01 am
content: Quote from: Dadu@ on March 06, 2022, 01:19:05 pmThe "clever" part of the function is how theforloop 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.

author: Dadu@
author_rank: Contributor
timestamp: March 07, 2022, 08:16:46 am
content: I belive this function only show the idea of how master transfer byte to slave over SPI.Code: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 highdelay(); // Delay for at least the peer's setup timeSCLK = LOW ; // Pull the clock line lowbyte_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 ?

author: rstofer
author_rank: Super Contributor
timestamp: March 07, 2022, 08:38:10 am
content: Quote from: Dadu@ on March 07, 2022, 08:16:46 amThat 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-ModesNotice 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.

author: rstofer
author_rank: Super Contributor
timestamp: March 07, 2022, 09:23:54 am
content: 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 codeif (MISO & 0x01) { // we only care about the low order bit if MISO isn't already a single bitbyte_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 codeSCLK = 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.