I was looking up at the SPI pull requests and browsed trough the issues, and saw this one again:
https://github.com/PaulStoffregen/SPI/issues/22
Which has to do with with which SPI buss speeds we automatically setup and which one we currently do not... And wondering if we should improve this or not... That is it would be nice to have an exact 4mhz... Makes things like logic analyzers happier
As mentioned the SPI clock is derived from the F_BUSS and depends on several fields in the CTAR:
SPI_CTAR_DBR - Double the speed
SPI_CTAR_PBR(x) - 0-3 which maps to (2, 3, 5, 7)
SPI_CTAR_BR(X) - 0-15 maps to (2, 4, 6, 8, 16, 32, 64, 128, ..., 32768)
Note: as part of this we also setup the Chip Select delay which relies on
SPI_CTR_PBR(x) - above
SPI_CTAR_CSSCK(x) 0-15 maps to (2, 4, 8, 16, 32, 64, ...., 65536) --- Like BR except skips 6
My quick look through the code looks like we do not use the 6 value at all... Example the code checks for /24(3*8) and then checks for /32(2*16) but does not do /30(5*6)...
Should the 30 be added? Would give exact 2MHz in the above cases... But since there is no CSSCK value of 6, we could not choose the chip select delay of exactly one clock. Could either make it larger or smaller...
Now back to 4mhz - as mentioned if F_BUS = 60mhz and we use:
t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(2) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(2);
We have something like:
(60*2)/(5*6) = 4MHz. As you mentioned
But this does have another side effect. That is when you do not use DBR, the SPI clock always has a 50/50 SCK duty cycle. However if DBR is set, the duty cycle depends on PBR and in this case with PBR(2) the duty cycle is: either 40/60 or 60/40 depending on the clock phase (SPI_CTAR_CPHA) would this be a problem?
Another question which came up recently currently the code is setup to only go down to SPI speed /* F_BUS / 768 */, whereas the we can go a whole lot slower...
Side question (need to get back to reading and playing more with c++11...) Wondering with code like we are using here:
Code:
void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
__attribute__((__always_inline__)) {
uint32_t t, c = SPI_CTAR_FMSZ(7);
if (bitOrder == LSBFIRST) c |= SPI_CTAR_LSBFE;
if (__builtin_constant_p(clock)) {
if (clock >= F_BUS / 2) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR
| SPI_CTAR_CSSCK(0);
} else if (clock >= F_BUS / 3) {
t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_DBR
| SPI_CTAR_CSSCK(0);
} else if (clock >= F_BUS / 4) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
} else if (clock >= F_BUS / 5) {
t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_DBR
| SPI_CTAR_CSSCK(0);
} else if (clock >= F_BUS / 6) {
t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
} else if (clock >= F_BUS / 8) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
} else if (clock >= F_BUS / 10) {
t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
} else if (clock >= F_BUS / 12) {
t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
} else if (clock >= F_BUS / 16) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
} else if (clock >= F_BUS / 20) {
t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(0);
} else if (clock >= F_BUS / 24) {
t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
} else if (clock >= F_BUS / 32) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(3);
} else if (clock >= F_BUS / 40) {
t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
} else if (clock >= F_BUS / 56) {
t = SPI_CTAR_PBR(3) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
} else if (clock >= F_BUS / 64) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4);
} else if (clock >= F_BUS / 96) {
t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4);
} else if (clock >= F_BUS / 128) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5);
} else if (clock >= F_BUS / 192) {
t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5);
} else if (clock >= F_BUS / 256) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
} else if (clock >= F_BUS / 384) {
t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
} else if (clock >= F_BUS / 512) {
t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7);
} else if (clock >= F_BUS / 640) {
t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
} else { /* F_BUS / 768 */
t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7);
}
} else {
for (uint32_t i=0; i<23; i++) {
t = ctar_clock_table[i];
if (clock >= F_BUS / ctar_div_table[i]) break;
}
}
Where we have this big set of ifs that we use if the clock value passed in is a constant, else we look up in table... Wondering if this can somehow be all condensed by using something like constexpr. Again still learning about this new fangled stuff
Thoughts?