@@ -545,8 +545,16 @@ void STM32_CAN::setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescal
545
545
uint32_t _TimeSeg1 = 0 ;
546
546
uint32_t _TimeSeg2 = 0 ;
547
547
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
+
548
555
switch (sjw)
549
556
{
557
+ case 0 :
550
558
case 1 :
551
559
_SyncJumpWidth = CAN_SJW_1TQ;
552
560
break ;
@@ -557,12 +565,9 @@ void STM32_CAN::setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescal
557
565
_SyncJumpWidth = CAN_SJW_3TQ;
558
566
break ;
559
567
case 4 :
568
+ default : /* limit to 4 */
560
569
_SyncJumpWidth = CAN_SJW_4TQ;
561
570
break ;
562
- default :
563
- // should not happen
564
- _SyncJumpWidth = CAN_SJW_1TQ;
565
- break ;
566
571
}
567
572
568
573
switch (timeseg1)
@@ -660,73 +665,67 @@ void STM32_CAN::setBaudRateValues(CAN_HandleTypeDef *CanHandle, uint16_t prescal
660
665
CanHandle->Init .Prescaler = _Prescaler;
661
666
}
662
667
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 ;
704
673
}
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 ;
705
678
}
706
679
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 ;
722
724
}
723
- bs1 = 5 ;
724
- bs2 = 1 ;
725
- prescaler++;
726
725
}
727
- bs1 = 5 ;
728
- sjw++;
729
726
}
727
+
728
+ /* uhoh, failed to calculate an acceptable baud rate... */
730
729
}
731
730
732
731
uint32_t STM32_CAN::getAPB1Clock ()
0 commit comments