arduino / arduino/ArduinoCore-samd

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

Open
#675 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C
Stars
502
Forks
740
PR merge metrics
No merged PRs in 30d

Description

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() {}
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with samd/1.8.11/cores/arduino/SERCOM.cpp, focusing on SERCOM::calculateBaudrateSynchronous(), and use the sketch included in the issue to reproduce the baud-rate table. Compare the resulting SCK rates with the requested SPISettings values, including 9 MHz; done means the selected rate does not exceed the requested maximum.

Written by the indexing model from the issue text.

Assessment

Tech stack
arduino, c
Domain
embedded-iot
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.