Skip to content

Commit a662adf

Browse files
authored
Merge pull request #41 from attie-argentum/baudrate-calcs
Revise baudrate calculations
2 parents 52ed65a + a17efa5 commit a662adf

File tree

2 files changed

+67
-65
lines changed

2 files changed

+67
-65
lines changed

STM32_CAN.cpp

+64-65
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,16 @@ void STM32_CAN::setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescal
545545
uint32_t _TimeSeg1 = 0;
546546
uint32_t _TimeSeg2 = 0;
547547
uint32_t _Prescaler = 0;
548+
549+
/* the CAN specification (v2.0) states that SJW shall be programmable between
550+
* 1 and min(4, timeseg1)... the bxCAN documentation doesn't mention this
551+
*/
552+
if (sjw > 4) sjw = 4;
553+
if (sjw > timeseg1) sjw = timeseg1;
554+
548555
switch (sjw)
549556
{
557+
case 0:
550558
case 1:
551559
_SyncJumpWidth = CAN_SJW_1TQ;
552560
break;
@@ -557,12 +565,9 @@ void STM32_CAN::setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescal
557565
_SyncJumpWidth = CAN_SJW_3TQ;
558566
break;
559567
case 4:
568+
default: /* limit to 4 */
560569
_SyncJumpWidth = CAN_SJW_4TQ;
561570
break;
562-
default:
563-
// should not happen
564-
_SyncJumpWidth = CAN_SJW_1TQ;
565-
break;
566571
}
567572

568573
switch (timeseg1)
@@ -660,73 +665,67 @@ void STM32_CAN::setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescal
660665
CanHandle->Init.Prescaler = _Prescaler;
661666
}
662667

663-
void STM32_CAN::calculateBaudrate(CAN_HandleTypeDef *CanHandle, int baud)
664-
{
665-
/* this function calculates the needed Sync Jump Width, Time segments 1 and 2 and prescaler values based on the set baud rate and APB1 clock.
666-
This could be done faster if needed by calculating these values beforehand and just using fixed values from table.
667-
The function has been optimized to give values that have sample-point between 75-94%. If some other sample-point percentage is needed, this needs to be adjusted.
668-
More info about this topic here: http://www.bittiming.can-wiki.info/
669-
*/
670-
int sjw = 1;
671-
int bs1 = 5; // optimization. bs1 smaller than 5 does give too small sample-point percentages.
672-
int bs2 = 1;
673-
int prescaler = 1;
674-
uint16_t i = 0;
675-
676-
uint32_t frequency = getAPB1Clock();
677-
678-
if(frequency == 48000000) {
679-
for(i=0; i<sizeof(BAUD_RATE_TABLE_48M)/sizeof(Baudrate_entry_t); i++) {
680-
if(baud == (int)BAUD_RATE_TABLE_48M[i].baudrate) {
681-
break;
682-
}
683-
}
684-
if(i < sizeof(BAUD_RATE_TABLE_48M)/sizeof(Baudrate_entry_t)) {
685-
setBaudRateValues(CanHandle, BAUD_RATE_TABLE_48M[i].prescaler,
686-
BAUD_RATE_TABLE_48M[i].timeseg1,
687-
BAUD_RATE_TABLE_48M[i].timeseg2,
688-
1);
689-
return;
690-
}
691-
}
692-
else if(frequency == 45000000) {
693-
for(i=0; i<sizeof(BAUD_RATE_TABLE_45M)/sizeof(Baudrate_entry_t); i++) {
694-
if(baud == (int)BAUD_RATE_TABLE_45M[i].baudrate) {
695-
break;
696-
}
697-
}
698-
if(i < sizeof(BAUD_RATE_TABLE_45M)/sizeof(Baudrate_entry_t)) {
699-
setBaudRateValues(CanHandle, BAUD_RATE_TABLE_45M[i].prescaler,
700-
BAUD_RATE_TABLE_45M[i].timeseg1,
701-
BAUD_RATE_TABLE_45M[i].timeseg2,
702-
1);
703-
return;
668+
template <typename T, size_t N>
669+
bool STM32_CAN::lookupBaudrate(CAN_HandleTypeDef *CanHandle, int baud, const T(&table)[N]) {
670+
for (size_t i = 0; i < N; i++) {
671+
if (baud != (int)table[i].baudrate) {
672+
continue;
704673
}
674+
675+
/* for the best chance at interoperability, use the widest SJW possible */
676+
setBaudRateValues(CanHandle, table[i].prescaler, table[i].timeseg1, table[i].timeseg2, 4);
677+
return true;
705678
}
706679

707-
while (sjw <= 4) {
708-
while (prescaler <= 1024) {
709-
while (bs2 <= 3) { // Time segment 2 can get up to 8, but that causes too small sample-point percentages, so this is limited to 3.
710-
while (bs1 <= 15) { // Time segment 1 can get up to 16, but that causes too big sample-point percenages, so this is limited to 15.
711-
int calcBaudrate = (int)(frequency / (prescaler * (sjw + bs1 + bs2)));
712-
713-
if (calcBaudrate == baud)
714-
{
715-
setBaudRateValues(CanHandle, prescaler, bs1, bs2, sjw);
716-
return;
717-
}
718-
bs1++;
719-
}
720-
bs1 = 5;
721-
bs2++;
680+
return false;
681+
}
682+
683+
void STM32_CAN::calculateBaudrate(CAN_HandleTypeDef *CanHandle, int baud)
684+
{
685+
uint8_t bs1;
686+
uint8_t bs2;
687+
uint16_t prescaler;
688+
689+
const uint32_t frequency = getAPB1Clock();
690+
691+
if (frequency == 48000000) {
692+
if (lookupBaudrate(CanHandle, baud, BAUD_RATE_TABLE_48M)) return;
693+
} else if (frequency == 45000000) {
694+
if (lookupBaudrate(CanHandle, baud, BAUD_RATE_TABLE_45M)) return;
695+
}
696+
697+
/* this loop seeks a precise baudrate match, with the sample point positioned
698+
* at between ~75-95%. the nominal bit time is produced from N time quanta,
699+
* running at the prescaled clock rate (where N = 1 + bs1 + bs2). this algorithm
700+
* prefers the lowest prescaler (most time quanter per bit).
701+
*
702+
* many configuration sets can be discarded due to an out-of-bounds sample point,
703+
* or being unable to reach the desired baudrate.
704+
*
705+
* for the best chance at interoperability, we use the widest SJW possible.
706+
*
707+
* for more details + justification, see: https://github.com/pazi88/STM32_CAN/pull/41
708+
*/
709+
for (prescaler = 1; prescaler <= 1024; prescaler += 1) {
710+
const uint32_t can_freq = frequency / prescaler;
711+
const uint32_t baud_min = can_freq / (1 + 5 + 16);
712+
713+
/* skip all prescaler values that can't possibly achieve the desired baudrate */
714+
if (baud_min > baud) continue;
715+
716+
for (bs2 = 1; bs2 <= 5; bs2 += 1) {
717+
for (bs1 = (bs2 * 3) - 1; bs1 <= 16; bs1 += 1) {
718+
const uint32_t baud_cur = can_freq / (1 + bs1 + bs2);
719+
720+
if (baud_cur != baud) continue;
721+
722+
setBaudRateValues(CanHandle, prescaler, bs1, bs2, 4);
723+
return;
722724
}
723-
bs1 = 5;
724-
bs2 = 1;
725-
prescaler++;
726725
}
727-
bs1 = 5;
728-
sjw++;
729726
}
727+
728+
/* uhoh, failed to calculate an acceptable baud rate... */
730729
}
731730

732731
uint32_t STM32_CAN::getAPB1Clock()

STM32_CAN.h

+3
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ class STM32_CAN {
189189
void initializeBuffers(void);
190190
bool isRingBufferEmpty(RingbufferTypeDef &ring);
191191
uint32_t ringBufferCount(RingbufferTypeDef &ring);
192+
193+
template <typename T, size_t N>
194+
bool lookupBaudrate(CAN_HandleTypeDef *CanHandle, int Baudrate, const T(&table)[N]);
192195
void calculateBaudrate(CAN_HandleTypeDef *CanHandle, int Baudrate);
193196
void setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescaler, uint8_t timeseg1,
194197
uint8_t timeseg2, uint8_t sjw);

0 commit comments

Comments
 (0)