Understanding SPI

Ive been trying to understand and implement the provided SPI.h library from the Arduino IDE into a program that Ive been working on for too long now, and I cant seem to get it right.. it feels like I've tried so many different things.

My code has no errors, but I dont get any feedback on the LED that I have hooked up to the wiper of my MCP4151 digital potentiometer. Is there somebody who could explain to me what I am missing or doing
incorrectly?

#include <SPI.h>
const int wiper1 = 0000;  // volatile wiper0 address is 00h
const int wiper2 = 0001;  // volatile wiper1 address is 01h
const int CSpin = 53;     // chip select pin variable is set to 53
const int SCKpin = 52;    // clock pin variable set to 52
const int MOSIpin = 51;   // MasterOutSlaveIn pin set to 51
const int MISOpin = 50;   // MasterInSlaveOut pin set to 50
byte sendData;            // variable to store the values to transfer to MCP4151


void setup() {
pinMode (CSpin, OUTPUT);   // chip select is now set as an output
pinMode (SCKpin, OUTPUT);  // clock pin is now set as an output
pinMode (MOSIpin, OUTPUT); // MasterOutSlaveIn is now set as an output
pinMode (MISOpin, INPUT);  // MasterInSlaveOut is now set as an input

digitalWrite(CSpin, HIGH); // write the CS pin HIGH to make sure no transmissions happen right away

}

void loop() {

sendData = 150;                        // variable has a value of 150 
SendDataToWiper1(sendData, CSpin);     // use SendDataToWiper1() to write sendData values to MCP4151
}

void CS_high(const int CSpin) {    // function to write the CS pin HIGH for no bit transfer
      digitalWrite(CSpin, HIGH);   // writes the CS pin HIGH to disable bit transactions
}
void CS_low(const int CSpin) {     // function to write the CS pin LOW to begin bit transfer
      digitalWrite(CSpin, LOW);    // writes the CS pin LOW to enable bit transactions
}      

void SendDataToWiper1(const int sendData, const int CSpin) {
     
      // begins the SPI transaction with the settings for MCP4151
      SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));  

      CS_low(CSpin);            // write the CS pin LOW to begin bit transaction
      delay(250);               // a small delay for the clock to remain active until first edge of the clock
      SPI.transfer( wiper1);    // transfer the wiper address 00h
      SPI.transfer( sendData);  // transfer the sendData bits
      CS_high(CSpin);           // writes the CS pin HIGH to end bit transaction
      
      SPI.endTransaction();        // ends the SPI transaction 
}

void SendDataToWiper2(const byte sendData, const int CSpin) {

      // begins the SPI transaction with the settings for MCP4151
      SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));  

      CS_low(CSpin);            // write the CS pin LOW to begin bit transaction
      delay(250);               // a small delay for the clock to remain active until first edge of the clock
      SPI.transfer( wiper2);    // transfer the wiper address
      SPI.transfer( sendData);  // transfer the sendData bits
      CS_high(CSpin);           // writes the CS pin HIGH to end bit transaction
      
      SPI.endTransaction();   // ends the SPI transaction 
}

You have not told us WHICH MCP4151 you have. Digital Potentiometers tend to not do well with LEDs as the load.

The MCP4151 data sheet shows a maximum of 2.5 mA through the wiper. This may light your LED weakly if you really look for it and if you have the wiper at the extreme (3.3 volts or 5 volts). What voltage do you measure at the wiper? Loaded or unloaded?

It is better to dim LEDs using PWM which your Arduino should have available. analogWrite(...) will work and you won't need any libraries.

Good Luck.

vaj4088:
You have not told us WHICH MCP4151 you have. Digital Potentiometers tend to not do well with LEDs as the load.

The MCP4151 data sheet shows a maximum of 2.5 mA through the wiper. This may light your LED weakly if you really look for it and if you have the wiper at the extreme (3.3 volts or 5 volts). What voltage do you measure at the wiper? Loaded or unloaded?

It is better to dim LEDs using PWM which your Arduino should have available. analogWrite(...) will work and you won't need any libraries.

Good Luck.

Oh, I thought there was only one MCP4151.. Where might I find the other ones and what is the difference? I don't see them on the data sheet. I haven't measured the wiper voltage because I figured I'd make sure the code isn't whats wrong with my setup. When I can get to it, I'm sure somebody at my work has a meter I can use.
I know about the PWM and how to use it but I'm not sure if I should use it for my project because I don't know how it'll effect the 0-5V analog input that the mass flow controller requires to operate(or it probably wouldn't be too much of a difference but I haven't enough experience to say). I understand I can achieve basically the same thing with PWM, I just would rather have more of a challenge and to learn more about the correct way to use SPI for my application.

Ive been trying to understand and implement the provided SPI

In the datasheet should be a SPI timing diagram that, pretty much describes the principle of how SPI communications do the thing. If the data sheet to your device does not have a SPI talk about how it works section, you can do a search on "mpu9250 datasheet" you will get a few references to some pdf files the one from InvenSnse, table 7 and the little write up is some basic PI stuff. You, also, can dig a little deeper and read up on 7.5 SPI Interface.

AsaBrown:
Oh, I thought there was only one MCP4151.

There are dual potentiometer versions. There are also various versions with different end-to-end resistances. You need to provide the manufacturer and the complete part number, including the dash and anything that follows the dash.

AsaBrown:
I just would rather have more of a challenge

Using a digital potentiometer to control the brightness of a LED will be quite a challenge.

The MEGA, LED1, and MCP4151 setup.

Good drawing, but let me get this straight. The data sheet says that the wiper resistance is 75 ohms. That is going to get added to the 220 ohms resistor, BUT the data sheet also says that the wiper will have a maximum of 2.5 mA. That seems like a pretty weak light from most LEDs.

Tine to buy a meter. The cheapest ones from the cheap tool stores (like $5USD) are good enough for 95% of all Arduino projects.

Get rid of these:
const int SCKpin = 52; // clock pin variable set to 52
const int MOSIpin = 51; // MasterOutSlaveIn pin set to 51
const int MISOpin = 50; // MasterInSlaveOut pin set to 50

pinMode (SCKpin, OUTPUT); // clock pin is now set as an output
pinMode (MOSIpin, OUTPUT); // MasterOutSlaveIn is now set as an output
pinMode (MISOpin, INPUT); // MasterInSlaveOut is now set as an input

Add this to setup():
SPI.begin(); // default speed is 4 MHz and MSBFIRST, takes care of SCK, MISO, MOSI set up as well.

Then try again.

This code

void loop() {

sendData = 150; // variable has a value of 150
SendDataToWiper1(sendData, CSpin); // use SendDataToWiper1() to write sendData values to MCP4151
}
will blast the data out continuously. Overkill.

This is overdone as well:

void SendDataToWiper1(const int sendData, const int CSpin) {

// begins the SPI transaction with the settings for MCP4151
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); <<< don't need this, 4 MHz, MSBFIRST & Mode 0 are default settings. 10 MHz will not work on most Arduino's like an Uno, Nano, Mega.

CS_low(CSpin); // write the CS pin LOW to begin bit transaction
delay(250); // a small delay for the clock to remain active until first edge of the clock <<< don't need
SPI.transfer( wiper1); // transfer the wiper address 00h
SPI.transfer( sendData); // transfer the sendData bits
CS_high(CSpin); // writes the CS pin HIGH to end bit transaction

SPI.endTransaction(); // ends the SPI transaction <<< don't need
}

CrossRoads:
This code

void loop() {

sendData = 150; // variable has a value of 150
SendDataToWiper1(sendData, CSpin); // use SendDataToWiper1() to write sendData values to MCP4151
}
will blast the data out continuously. Overkill.

This is overdone as well:

void SendDataToWiper1(const int sendData, const int CSpin) {

// begins the SPI transaction with the settings for MCP4151
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); <<< don't need this, 4 MHz, MSBFIRST & Mode 0 are default settings. 10 MHz will not work on most Arduino's like an Uno, Nano, Mega.

delay(250); // a small delay for the clock to remain active until first edge of the clock <<< don't need

SPI.endTransaction(); // ends the SPI transaction <<< don't need
}

AWESOME! thanks for the clarity! I will make those changes and try it out again today if I can get to it.
I see your name all over the forum so its very reassuring to hear feedback from you haha. I know that we all really appreciate you taking your own time to help others learn!!!

MorganS:
Tine to buy a meter. The cheapest ones from the cheap tool stores (like $5USD) are good enough for 95% of all Arduino projects.

I think you're right! Thanks for the suggestion!

CrossRoads:
SPI.transfer( wiper1); // transfer the wiper address 00h
SPI.transfer( sendData); // transfer the sendData bits
}

Very surprising that you are neither taking out data from the SPDR buffer of Master nor inserting any delay between successive SPI transactions. For my case, these types of bare codes have not worked; but, the following types have worked.

byte x1= SPI.transfer( wiper1);    // transfer the wiper address 00h
byte x2 = SPI.transfer( sendData);  // transfer the sendData bits

I have read in some literatures on SPI that one should always read the arrived data out of SPDR buffer whether they are needed or not.

GolamMostafa:
Very surprising that you are neither taking out data from the SPDR buffer of Master nor inserting any delay between successive SPI transactions. For my case, these types of bare codes have not worked; but, the following types have worked.

byte x1= SPI.transfer( wiper1);    // transfer the wiper address 00h

byte x2 = SPI.transfer( sendData);  // transfer the sendData bits




I have read in some literatures on SPI that one should always read the arrived data out of SPDR buffer whether they are needed or not.

'SPDR buffer of Master' is a new term for me, Ill check that out, thanks!

I thought I needed delays between transactions as well but I've read and seen so many different examples(none that have worked for me yet.. still have much to learn though) where there are no delays, or where the master is shown to not be reading anything back from the slave.. Its quite confusing seeing all the different setups and many ways Ive seen others go about it.. Ive been busy at work but I think Ill be able to test out the suggestions made by CrossRoads a few comments back and I'll post the outcome!

SPDR buffer?

1. Buffer is a temporary storage area which could be a MCU register or a RAM location or a variable. In the case of SPI, it is actually SPDR (SPI Data Register) Register (Fig-1). Sorry, for the inconvenience.

2. As to the need of inserting time delay between SPI transaction --
(1) The following conceptual connection diagram between UNO (Master) and Slave (NANO) using SPI Port may be helpful in this discussion.
spi328x.png
Figure-1: Master and Slave connection using SPI Port

(2) Assume that SPDR of Master contains 0x32 and SPDR of Slave contains 0x97. We wish to make a simultaneous exchange of these data between UNO and NANO. After exchange, the SPDR of Master will hold 0x97 and SPDR of Slave will hold 0x32.

(3) To shift-out/shift-in 8-bit data between UNO and NANO, we need to generate 8 SCK pulses at the Master side. Assuming 500 kbits/sec speed, the data exchange process will take 16 us (forgetting the transmission delay of the connecting wires of the SPI Port). That means that we have to wait for 16 us time to see that data are ready in the SPDR registers of Master and Slave; after 16 us time delay, Master will contain 0x97 and Slave will contain 0x32. Now, the question is: should we wait for this fixed time delay when the connecting wires could be of varying dimensions or should we let the process taking time as much as is needed? If we choose the later option, then the arrival of data (data ready) is notified to the user by the HIGH state of the SPIF flag of the SPSR Register (Fig-1).

(4) The 8 SCK pulses are automatically generated when the data byte 0x32 is written/stored into the SPDR register. The codes are:

SPDR = 0x32; //8-bit data are in transition
while(bitRead9SPSR, SPIF) != HIGH)
{
    ;    //wait and check that SPIF is HIGH to ensure that data byte (0x97) has arrived from Slave
}
byte x = SPDR;     //0x97 has arrived into Master's SPDR; save it into variable x

(5) The register level codes of Step-4 could be written as follows by the Arduino code:

byte x = SPI.transfer(0x32);

(6) Sample sketches: UNO sends command 11 to NANO to receive data 250 and 270 from NANO. In the Master sketch, we observe the use of tiny time delay between successive SPI transactions without which the sketch does not work properly. This time delay offers the Slave a scope to carry out the overhead tasks relating to updating its SPDR with new data.
UNO-Master Codes:

#include <SPI.h>
byte myData[] = {11, 0x00, 0x00, 0x00};
void setup (void)
{
  Serial.begin(115200);
  SPI.begin ();
  digitalWrite(SS, LOW);    //Slave is selected
  SPI.setClockDivider(SPI_CLOCK_DIV32);  //500 Kbits/sec
  delay(100);
  //-----------------------------------------
  for (int i = 0; i < 4; i++)
  {
    myData[i] = SPI.transfer(myData[i]);
    delay(1);
  }
  Serial.print("Received data in response of 11: ");
  Serial.print(myData[1], DEC);
  Serial.print(" and ");
  Serial.println((myData[2] << 8 | myData[3]), DEC);
}

void loop()
{

}

NANO-Slave codes:

#include <SPI.h>
volatile bool flag1 = false, flag2 = false;
byte myData[] = {0xFA, 0x01, 0x0E}; //250, 270 
int i = 0;
byte x;

void setup ()
{
  Serial.begin(115200);
  pinMode(SS, INPUT_PULLUP);  // ensure SS stays high for now
  pinMode(MISO, OUTPUT);
  SPCR |= _BV(SPE);
  SPCR |= !(_BV(MSTR)); //Arduino is Slave
  SPCR |= _BV(SPIE); //SPI.attachInterrupt();
  sei();
  SPDR = 0x67;  //test value
  // delay(100);
}

void loop()
{

}

ISR(SPI_STC_vect)
{
  if (flag1 == false)
  {
    x = SPDR;
    if (x == 11)
    {
      flag1 = true;
    }
  }
  SPDR = myData[i];
  i++;
  if (i == 3)
  {
    flag1 = false;
    i = 0;
  }
}

spi328x.png

So this is how Ive hooked it up to test the code/function..

The updated code suggestions from CrossRoads definitely helped! It ended up lighting up my LED a little bit, but it never changed no matter how many values i tried for my sendData variable. So I took the LED/220oHm resister out and routed the wiper output to my first analog pin to see what the reading was at.

and the reason i put the transfers into a function was because I plan on writing around 10 different values to the MCP to get 10 different voltages for a mass flow controller that I have. It is overkill but does help me and hopefully it'll simplify the rest of the code once I get past this hump..

#include <SPI.h>            // includes the SPI library provided by Arduino IDE
const int wiper1 = 0000;    // the variable 'wiper1' has a value equal to the MCP's wiper1 address
const int wiper2 = 0001;    // the variable 'wiper2' has a value equal to the MCP's wiper2 address
const int CSpin = 53;       // the variable 'CSpin' has a value of 53 to associate with the Slave Select pin on Arduino
byte sendData;              // the variable to store the values to transfer to MCP4151
int FDBK_fromWiper1;    // the variable to the analog to digital feedback from the wiper1 on MCP <----ADDED THIS

void setup() {
pinMode(CSpin, OUTPUT);     // the chip select is now set as an OUTPUT
digitalWrite(CSpin, HIGH);    // the chip select is written HIGH to make sure no transmissions happen right away

}

void loop() {
Serial.begin(9600);    

sendData = 175;                       // the data to transfer to transfer to the MCP is equal to 255
SendDataToWiper1(sendData, CSpin);    // the SendDataToWiper1() to transfer the sendData values to the MCP
FDBK_fromWiper1= analogRead(A0);     
Serial.println(FDBK_fromWiper1);       
}

void CS_high(const int CSpin) {       // the function to write the CSpin HIGH for no bit transfer
  digitalWrite(CSpin, HIGH);          // writes the CS pin HIGH to disable bit transfers
}

void CS_low(const int CSpin) {        // the function to write the CSpin LOW for bit transfer
  digitalWrite(CSpin, LOW);           // write the CS pin LOW to enable bit transfer
}

  // the function to transfer data from arduino to MCP wiper1

void SendDataToWiper1(const int sendData, const int CSpin) {  
  CS_low(CSpin);                      // write the chip select pin LOW to enable bit transfer
  SPI.transfer(wiper1);               // transfer the wiper address to write to
  SPI.transfer(sendData);           // transfer the sendData value
  CS_high(CSpin);                     // write the chip select in HIGH to disable bit transfer
}

..So Im almost positive that my connections are right.. Which leads me to believe that its still the code that needs to be written correctly. Shouldn't my sendData variable change the LED brightness if I change it and re-upload the code to the arduino?

Also My analogRead() should be displaying the feedback from the 'A0' pin to the serial monitor, but it wasnt working for the when I tried it.. I think that issue was just me not thinking it through all the way so I will double check that that nothing for the feedback display is written incorrectly...

Wow.. sorry for wasting a bit of your guys' time.. NEWWWB mistake! I forgot to add to add the SPI.begin() when I remade a new code for the forum, or it wasnt there to begin with! :o

I appreciate you all for the help! I will try not to overlook those simpler things from now on, and I'll post again if I have an issue still!