arduino / arduino/ArduinoCore-samd
SPI / SERCOM library sets SCK rate higher than maxSpeed specified in SPISetting
- 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