felis / USB_Host_Shield_2.0

Revision 2.0 of USB Host Library for Arduino.
https://chome.nerpa.tech
1.79k stars 779 forks source link

Is it possible to use multiple SPI devices with MAX3421 ? #260

Closed didix21 closed 6 years ago

didix21 commented 7 years ago

Well right now I'm working with Arduino Host Shield and a SD card adaptor with an Arduino DUE. The problem is when I use both devices with the same SPI (MISO, MOSI and CLK) but different chipselect pins I have some errors.

Lauszus commented 7 years ago

What SD card adapter are you using? Please send links to any other libraries you might be using.

didix21 commented 7 years ago

Well, I am developing a Marlin for a new variant Board that I have created based on Arduino DUE. The Marlin I am working with is based on work done by Wurstnase who develops a Marlin called Marlin4Due

The idea is I want to implement a USB Stick, using the Arduino Host Shield and the following library: UsbFat and SD which is incorporated in a TFT LCD panel.

The thing is they work perfectly well separately but when I use both with the same MISO, MOSI and CLK but different chipselect pin they don't work.

BlueGene00 commented 7 years ago

I have a similar question:

I use the SilentStepStick-driver: http://www.watterott.com/de/SilentStepStick-TMC2130

They are controled by an Arduino Due or Teensy 3.6. The configuration of those driver is done by SPI. I have 6 drivers, so each driver has its own CS-pin.

Definition: SPISettings settingsA(4000000, MSBFIRST, SPI_MODE3);

In setup-loop:

  pinMode(Ucs, OUTPUT);
  digitalWrite(Ucs, HIGH);
  pinMode(Vcs, OUTPUT);
  digitalWrite(Vcs, HIGH);
  pinMode(Wcs, OUTPUT);
  digitalWrite(Wcs, HIGH);
  pinMode(Xcs, OUTPUT);
  digitalWrite(Xcs, HIGH);
  pinMode(Ycs, OUTPUT);
  digitalWrite(Ycs, HIGH);
  pinMode(Zcs, OUTPUT);
  digitalWrite(Zcs, HIGH);

  SPI.begin();

And the function, which is called, when a parameter should be changed:

uint8_t tmc_write(int axis, uint8_t cmd, uint32_t data)
{
  uint8_t s;

  //  Serial.println("SPI------------------------------");
  // Serial.println(data);

  SPI.beginTransaction(settingsA);
  switch (axis) {
    case 1:
      digitalWrite(Xcs, LOW);
      break;
    case 2:
      digitalWrite(Ycs, LOW);
      break;
    case 3:
      digitalWrite(Zcs, LOW);
      break;
    case 4:
      digitalWrite(Ucs, LOW);
      break;
    case 5:
      digitalWrite(Vcs, LOW);
      break;
    case 6:
      digitalWrite(Wcs, LOW);
      break;
  }

  s = SPI.transfer(cmd);
  SPI.transfer((data >> 24UL) & 0xFF) & 0xFF;
  SPI.transfer((data >> 16UL) & 0xFF) & 0xFF;
  SPI.transfer((data >> 8UL) & 0xFF) & 0xFF;
  SPI.transfer((data >> 0UL) & 0xFF) & 0xFF;

  switch (axis) {
    case 1:
      digitalWrite(Xcs, HIGH);
      break;
    case 2:
      digitalWrite(Ycs, HIGH);
      break;
    case 3:
      digitalWrite(Zcs, HIGH);
      break;
    case 4:
      digitalWrite(Ucs, HIGH);
      break;
    case 5:
      digitalWrite(Vcs, HIGH);
      break;
    case 6:
      digitalWrite(Wcs, HIGH);
      break;
  }
  SPI.endTransaction();
  return s;
}

So the CS-pins are always HIGH and if there is an access to a specific SPI-device, the according CS-pin will be LOW during the SPI-transfer. After the transfer, the CS-pin gets to HIGH again.

This works so far without issues. Now I want to add the mini USB-host-shield and include the host library. I just added the XBOX-controller example to my exisiting code and it seems to work without further modification.

Since I am not the only one, I would recommend to somehow make the use of additional SPI-devices easier to use. Two big points here are the setting of the CS-pin inside the sketch and not fix in the library and the second point is the handling of the CS-pin. Using the library it is not obvious how to address different devices. Manually setting the CS HIGH/LOW would be a good way to go, since then it is clear which device is now active.

xxxajk commented 7 years ago

Your absolute best bet is to use transactions, especially since UHS3 uses an ISR. This is very important, since it could be accessed at any time. Sure this may not bite you right now, but it WILL bite you in the future.

Lauszus commented 7 years ago

@BlueGene00 the configuration of the CS/SS pin is already documented in the README: https://github.com/felis/USB_Host_Shield_2.0#interface-modifications.

BlueGene00 commented 7 years ago

Thanks for the info, I already found the part where to change the pin. But in the past I saw libraries as a fixed file, which should not be changed. This gets especially important if you deal with different projects. I use 3-4 different projects/boards, so I must change each time the library to the correct pin. Same for the users, which use my projects. At some point you get confused or just forget this and wonder why it is not working.

Instead it would be easier if the CS/SS pin could be changed/declared from inside the sketch, so I declare this once inside each project and don't have to change each time I switch to another project. I don't know how much work that would be for you, but it would be a great help when dealing with different projects.

As you can see in my code above, I use transactions. It is the same as described here: https://www.dorkbotpdx.org/blog/paul/spi_transactions_in_arduino

But I don't still understand when the CS of the USB-host library will be HIGH/LOW? I have tested the XBOX-RECV example and will test the PS4-controller example soon. The code is very clean, because everything is done by the library. But I want to enable/disable manually the host shield to make use of my other SPI-devices. I guess the easiest way is just to cut of the power with a transistor which also has the advantage of power saving when it is not used (my projects are mostly battery-powered).

xxxajk commented 7 years ago

No need to manually enable or disable it, that is what the transaction stuff does. As far as power saving, you need to add a USB power switch. There is a place for it on the most recent shield (2.0.1). A transistor isn't going to work very well, and will waste a ton of energy.

BlueGene00 commented 7 years ago

If I don't enable/disable the CS manually, how do I choose between my six SPI-devices?

Somehow I need to cut off the power of the USB-host-shield. I only need it during setup-mode(about 2-3 minutes), then it will run about 1-2 hours without the need of the USB-device. I guess using the transistor for this short time amount is still more power-efficient then powering a Bluetooth-Dongle for the whole time.

xxxajk commented 7 years ago

What do you mean by cutting the entire power exactly? If you mean powering down the max chip and the level shifters, that will cause problems. Which host shield do you have?

Lauszus commented 7 years ago

@BlueGene00 the library only set the CS low when it is doing a transfer and then sets it back up again when it's done. Please see: https://github.com/felis/USB_Host_Shield_2.0/blob/master/usbhost.h. I can't see how you would benefit from doing this manually?

xxxajk commented 7 years ago

I think (hope) he means cutting the power to the device, not the shield, which the new board supports when outfitted properly.

xxxajk commented 7 years ago

@BlueGene00 please read this article. https://www.circuitsathome.com/mcu/vbus-power-control-on-usb-host-shield/

BlueGene00 commented 7 years ago

I am using the Mini-shield, since I want to make my project as compact as possible. Thanks for the article of the Vbus. I can build the same circuit into my custom-board, so it will behave the same.

Powering off Vbus should be enough for my needs. Why is it a bad idea to power off the complete shield with the chip?

@Lauszus As mentioned, I am using in addition to the shield 6 further SPI-devices on the same SPI-bus. For them enabling/disabling the CS manually is the only way to select on specific device.

Now that a want to include the host shield as 7th SPI-device, I need to ensure, that only one SPI device is addressed at once. For this reason I need to know when the CS of the host shield is enabled. Or in my specific application: https://github.com/felis/USB_Host_Shield_2.0/blob/master/examples/Bluetooth/PS4BT/PS4BT.ino

I want to use the controller only "on call" at some specific times. The rest of the time, the controller is not needed in my application. I guess it would be sufficient if the Usb.Task(); is simply not called during the "down-time". If the Task is not called, my other 6 SPI-devices would be the only one on the SPI-bus. Is that correct?

xxxajk commented 7 years ago

@BlueGene00 I'll give you just Three reasons not to, these are the biggest ones, and they are all the same root reason for common failures.

First, and most important reason it is bad because that it will cause massive problems. Briefly, you end up having what is called a parasitic effect on any data lines, which cause a larger power draw than if left alone. This will cause data failures on all I/O lines that it shares IF YOU ARE LUCKY. It also isn't what you want at all-- drawing more power.

Secondly, this parasitic effect can also charge up any capacitors on the board, and when they reach a minimum threshold will briefly power on the shield, causing random spikes on I/O connected to it. This in turn would cause all sorts of random havoc, such as corrupted data on your shared I/O lines.

Lastly, the parasitic effect can fry the MCU I/O lines because of the excessive power draw . Worse case, the whole MCU dies. Best case, you get non-working pins. Neither are desirable. This of course depends entirely on the MCU.

Lauszus commented 6 years ago

Closing this, as I believe the issue is solved.