You need to understand that SPI is an exchange protocol - white you are sending the bits of a value from the master to the slave device, the slave will be sending you bits. Therefore you DON'T send values and then read values - you do it all at once.
Typically you need just one function to perform an SPI exchange, at least from the master device's point of view. The function takes a parameter that is the value you want to send and returns the value that you have received. (By the way, this is a blocking function; there are ways to use interrupts but that is another story.)
Therefore the function has the steps
- check that the SPI peripheral is free and ready to start a new transfer
- write the value you want to send to the peripheral buffer - this normally starts the exchange
- wait until the exchange is complete
- handle any errors that may occur (this can be skipped for initial testing but is really needed for the final version)
- return the received value
Then, for your purposes, to read the ID value you need a set of calls such as
- if necessary, select the slave by lowering the \CS\ pin
- call the function with 0x4b as the parameter and ignore the return value
- call the function with some dummy value (I generally use 0x00 but if the slave will ignore it then anything will do) 4 times and ignore the return value
- call the function with some dummy value 8 times but this time record each of the returned values
- if necessary, deselect the slave device by raising the \CS\ pin
You the need to reconstruct the unique ID value. As the data sheet shows, the value is a 64-bit number and you will get the top 8 bits first, then the next 8 bits and so on until the 8th value will be the bottom 8 bits.
Can I suggest that you start with something a little simpler such as reading the JEDEC ID or the manufacturers ID as these sequences are a bit shorter and you have the advantage of knowing what values you should be getting back, whereas the unique ID will (be definition) change from device to device. Yes you should always get the same value back but unless you know what they should be beforehand, it is hard to know if you are getting back the correct values.
Finally, I would suggest that you get used to reading the status register. Before you start many operations, it is often a good idea to check the status register to make sure the EEPROM is ready for the operation. For example, some operations (reset, write, erase) can take a while to perform and the chip will ignore other commands (except status register reads) while it is busy. Therefore you need to check that it is free for your command before you start sending it.
Susan