Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SERCOM::initSPI() Prevents Hardware Control of SPI SS Signal by Clearing SERCOM_SPI_CTRLB_MSSEN #283

Open
Rymar-Eng opened this issue Feb 27, 2021 · 3 comments

Comments

@Rymar-Eng
Copy link

Rymar-Eng commented Feb 27, 2021

In cores/SERCOM.cpp, the SPI init function SERCOM::initSPI completely overwrites the value of the CTRLB register:

...
  //Setting the CTRLB register
  sercom->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(charSize) |
                          SERCOM_SPI_CTRLB_RXEN; //Active the SPI receiver.
...

In order to support hardware control of the SPI SS output, the MSSEN bit must be set in the CTRLB register. For example, on the Grand Central M4 board (SAMD51), pin 53 ('SS' signal) will be driven as a hardware controlled SS pin if the MSSEN bit is set in the corresponding SERCOM controller, in this case SERCOM7.

SERCOM7->SPI.CTRLB.reg|= SERCOM_SPI_CTRLB_MSSEN`

However, each call to SERCOM::InitSPI() as a result of the call to SPI.beginTransaction() clears the MSSEN bit, and leaves pin 53 disconnected.

One method (perhaps not the best one), is to preserve the existing state in the register. This must be done before the call to resetSPI(), which clears all the internal register, including CTRLB.

The completed function looks like this

void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize charSize, SercomDataOrder dataOrder)
{
  uint32_t ctrlb_image = sercom->SPI.CTRLB.reg;
  resetSPI();
  initClockNVIC();

#if defined(__SAMD51__)
  sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE(0x3)  |  // master mode
                          SERCOM_SPI_CTRLA_DOPO(mosi) |
                          SERCOM_SPI_CTRLA_DIPO(miso) |
                          dataOrder << SERCOM_SPI_CTRLA_DORD_Pos;
#else
  //Setting the CTRLA register
  sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE_SPI_MASTER |
                          SERCOM_SPI_CTRLA_DOPO(mosi) |
                          SERCOM_SPI_CTRLA_DIPO(miso) |
                          dataOrder << SERCOM_SPI_CTRLA_DORD_Pos;
#endif

  //Setting the CTRLB register
  sercom->SPI.CTRLB.reg = (ctrlb_image & SERCOM_SPI_CTRLB_MSSEN) |
                          SERCOM_SPI_CTRLB_CHSIZE(charSize) |
                          SERCOM_SPI_CTRLB_RXEN; //Active the SPI receiver.

  while( sercom->SPI.SYNCBUSY.bit.CTRLB == 1 );
}

An explicit parameter could also be used to pass in the value in, but would affect higher layers of the SPI interface.

@ladyada
Copy link
Member

ladyada commented Feb 28, 2021

well, the arduino API in general does not use the hardware SS pin control, it does SS with software!

@Rymar-Eng
Copy link
Author

Yes, after looking at this in even more detail, there are a number of reasons why the hardware SS control in the SAMD architecture is, ummm..., sadly lacking:

  1. SS cannot be controlled to stay low between bytes.
  2. In the SAMD51P20, there is only one hardware SS control available for each SERCOM-based SPI

What's surprising is that in the SAM architecture (Arduino Due), multiple hardware SS controls were available, and the Arduino SPI API was "enhanced" to provide SPI_CONTINUE and SPI_LAST controls, so that SS could remain low between bytes. Then the SAMD architecture came out, and seemed to take a step backwards.

So, how does SPI DMA work? I can't find info on SS control when DMA is used. If using software SS control, then it must just stay low (active) for the entire DMA transfer?

@ladyada
Copy link
Member

ladyada commented Feb 28, 2021

correct, you can use a callback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants