Teensy 2.0, can some of the hardware SPI pins be used arbitrarily if I'm only sending data out?

bjosephs

Well-known member
Basically the title, I can't find an easy answer with google it seems. But I allocated the hardware SS and MISO pins to the wrong signals in a board layout and the SS pin (pin 0 on the Teensy 2.0) is tied a reset function and pin MISO (pin 3) is set up to act as chip enable. Doesn't work and my conclusion is that once I call SPI.begin I no longer get to pick that any of those pins do.

The peripherals are on another board with a cable and when I swap wires and adjust pin assignment in code things act normally. Permanently swapping these signals in the cable isn't a terrible consequence but I wanted to ask if there anything I can do to to maintain use of hardware SPI on MOSI and CLK while reassigning SS and MISO to the intended functions. I'm changing their states manually in both cases anyway.
 
Probably not usable for GPIO. On the old AVR chips you don't get a pin mux register like on Teensy 4.x and 3.x. So turning on a peripheral like SPI causes it to take over its pins. But those AVR chips do have some weird quirks like in some cases the GPIO register for pin direction can still have an effect. To be honest, I haven't done much with the old Teensy 2.0 hardware for so long, not really sure anymore about those finer details.
 
Actually, I'm tempted to unpack this a little more. I've found that if I use SPI.end and SPI.begin to bracket my uses of the hardware SPI pin(s) as GPIO I can get it to work with the original pinout but it doesn't seem to need the end and begin around every use and it's really confusing me. This is all to drive HCMS style LED displays.

My original pin setup:

Code:
//My LED Display
byte dataPin = 2;              // connects to the display's data in, use hadware SPI
byte clockPin = 1;             // the display's clock pin, use hardware SPI
byte chipEnable = 3;          // the display's chip enable pin
byte registerSelect = 4;       // the display's register select pin
byte resetPin = 0;             // the display's reset pin

On the Teensy 2.0 SS is actually 0 and pin 3 is MISO. I didn't need MISO so I assigned it as a GPIO for chip enable. All are set up as outputs.

If I swap chipEnable to 0 and resetPin to 3 and make a corresponding wiring change the displays work. That was the pinout change in the cable I described above. But it occurs to me today that in this situation I'm still using the same Hardware SPI pins as GPIO, just using them differently so why would it work? Leaving them with the original assignment the screens never update. I couldn't get my head around why that would be.

So I started messing around. First thing to say is that never toggling the reset pin doesn't seem to matter. I commented out every use and see no behavior changes. So that's half of why one pinout works and one doesn't. Combining this fact with my pin swap observation above, I conclude pin zero can be used arbitrarily but 3 can't. Having reset on 3 never mattered, having CE on 3 is not workable, having CE on 0 works fine.

For chip enable on pin 3 to work I need to end SPI before changing the pin... sometimes. You can see here that I toggle CE low before the transfer and toggle high after. On the preceding pull low, without the SPI.end, the screens don't receive. And obviously without the SPI.begin the transfer won't happen at all. But after the transfer when I toggle the CE pin high it doesn't require the end/begin to work here. I figured maybe hardware SPI just defaults this pin to high after transfer and I'm getting the high state I need for free but removing that toggle line altogether causes malfunction (glitchy incomplete characters on screen). I put some delays in to give my meter a shot at seeing the changes and CE does in fact change states. It does need to be told to go high here but I don't understand why I can tell it.

Code:
void writeDotRegisterToDisplay()
{
  //load dot register with character data
  int maxData = displayLength * 5;

  // select the dot register:
  digitalWrite(registerSelect, LOW);
  // enable writing to the display:

  SPI.end(); // Needed to toggle the CE pin
  digitalWrite(chipEnable, LOW);
  SPI.begin(); //Needed or SPI won't run and the program halts here

  // shift the data out:
  for (int i = 0; i < maxData; i++) {
    SPI.transfer(dotRegister[i]);
  }
  // disable writing:
  digitalWrite(chipEnable, HIGH); // I don't seem to need to end SPI before this pin use but I do need the line
}


So, in adding this all up I'm immensely curious as to why the hardware behaves this way. Both for general academic curiosity but also so I know how to budget these pins in the future. It seems I can use SS (pin 0) as a toggled pin for peripherals but I can't use MISO for anything without suspending SPI to get access sometimes. If anyone has theory on why it will let me command it high after a transfer without suspending SPI I'd love to hear it.
 
Back
Top