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

SPI / SERCOM library sets SCK rate higher than maxSpeed specified in SPISetting #675

Open
greiman opened this issue Jul 2, 2022 · 0 comments

Comments

@greiman
Copy link

greiman commented Jul 2, 2022

Edit: This issue was for 1.8.11. The problem of SCK rate higher than SPISettings is still in 1.8.13 but max clock is limited to 12 MHz. A requested max clock of 9 MHz still results in 12 MHz.

I found this problem while using a scope to investigate SPI signals on an SD card. I was surprised to see 24 MHz SCK whenever clock was greater than 12 MHz in SPISetting.

SPISettings(uint32_t clock, BitOrder bitOrder, SPIMode dataMode)

The problem appears to be this function at about line 362 of samd/1.8.11/cores/arduino/SERCOM.cpp.

uint8_t SERCOM::calculateBaudrateSynchronous(uint32_t baudrate)
{
  return SERCOM_FREQ_REF / (2 * baudrate) - 1;
}

I replaced it with this function which seems give the best rate less or equal to the clock value in SPISettings.

uint8_t SERCOM::calculateBaudrateSynchronous(uint32_t baudrate)
{
  return (SERCOM_FREQ_REF + 2*baudrate - 1)/(2*baudrate) - 1;
}

This function is based on ceil((double)a/b) for integer a, b is (a + b - 1)/b.

Here are values of speedMax requested in SPISetting, speedOld for the current function and speedNew for the new function.

speedMax,speedOld,speedNew
1000000,1000000,1000000
2000000,2000000,2000000
3000000,3000000,3000000
4000000,4000000,4000000
5000000,6000000,4800000
6000000,6000000,6000000
7000000,8000000,6000000
8000000,8000000,8000000
9000000,12000000,8000000
10000000,12000000,8000000
11000000,12000000,8000000
12000000,12000000,12000000

Here is a sketch that calculated the above table:

#ifndef SERCOM_FREQ_REF
#define SERCOM_FREQ_REF 48000000UL
#endif  // SERCOM_FREQ_REF

// Current uint8_t SERCOM::calculateBaudrateSynchronous(uint32_t baudrate)
// from about line 362 of samd/1.8.11/cores/arduino/SERCOM.cpp
uint8_t calculateBaudrateSynchronous(uint32_t baudrate)
{
  return SERCOM_FREQ_REF / (2 * baudrate) - 1;
}
// New based on ceil((double)a/b) for integer a, b is (a + b - 1)/b
uint8_t newCalculateBaudrateSynchronous(uint32_t baudrate)
{
  return (SERCOM_FREQ_REF + 2*baudrate - 1)/(2*baudrate) - 1;
}
// Convert baud register value to frequency.
uint32_t baudToFreq(uint8_t baud) {
  return SERCOM_FREQ_REF/(2*(baud + 1));
}
 
void setup() {
  Serial.begin(9600);
  while (!Serial) {}
  Serial.println("\nspeedMax,speedOld,speedNew");
  for (uint8_t mhz = 1; mhz < 25; mhz++) {
    // frequency used in SPISettings
    uint32_t speedMaximum = 1000000UL*mhz;
    Serial.print(speedMaximum);
    Serial.write(',');   
    uint8_t baud = calculateBaudrateSynchronous(speedMaximum);
    Serial.print(baudToFreq(baud));
    Serial.write(',');
    baud = newCalculateBaudrateSynchronous(speedMaximum);
    Serial.println(baudToFreq(baud));
  }
}
void loop() {}
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

1 participant