drivers: can: change sample point for can_set_bitrate() at high bitrates

CAN in Automation (CiA) 301 v4.2.0 recommends a sample point location of
87.5% percent for all bitrates. However, some CAN controllers have
difficulties meeting this for higher bitrates.

Change can_set_bitrate() to use a sample point of 75.0% for bitrates
over 800 kbit/s, 80.0% for bitrates over 500 kbit/s, and 87.5% for all
other bitrates. This is in line with the sample point locations used by
the Linux kernel.

Regard a sample point error of more than +/- 5.0% as an error in setting
the bitrate. Previously, any sample rate error was accepted without
providing any feedback to the caller. This is in line with the CAN
sample point calculation criteria used by the Linux kernel.

Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
This commit is contained in:
Henrik Brix Andersen 2022-04-06 17:22:46 +02:00 committed by Marti Bolivar
commit fba27b4bde
2 changed files with 49 additions and 4 deletions

View file

@ -11,6 +11,9 @@
LOG_MODULE_REGISTER(can_common, CONFIG_CAN_LOG_LEVEL); LOG_MODULE_REGISTER(can_common, CONFIG_CAN_LOG_LEVEL);
/* Maximum acceptable deviation in sample point location (permille) */
#define SAMPLE_POINT_MARGIN 50
/* CAN sync segment is always one time quantum */ /* CAN sync segment is always one time quantum */
#define CAN_SYNC_SEG 1 #define CAN_SYNC_SEG 1
@ -180,6 +183,30 @@ int can_calc_prescaler(const struct device *dev, struct can_timing *timing,
return core_clock % (ts * timing->prescaler); return core_clock % (ts * timing->prescaler);
} }
/**
* @brief Get the sample point location for a given bitrate
*
* @param bitrate The bitrate in bits/second.
* @return The sample point in permille.
*/
uint16_t sample_point_for_bitrate(uint32_t bitrate)
{
uint16_t sample_pnt;
if (bitrate > 800000) {
/* 75.0% */
sample_pnt = 750;
} else if (bitrate > 500000) {
/* 80.0% */
sample_pnt = 800;
} else {
/* 87.5% */
sample_pnt = 875;
}
return sample_pnt;
}
int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate_data) int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate_data)
{ {
struct can_timing timing; struct can_timing timing;
@ -187,6 +214,7 @@ int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate
struct can_timing timing_data; struct can_timing timing_data;
#endif /* CONFIG_CAN_FD_MODE */ #endif /* CONFIG_CAN_FD_MODE */
uint32_t max_bitrate; uint32_t max_bitrate;
uint16_t sample_pnt;
int ret; int ret;
ret = can_get_max_bitrate(dev, &max_bitrate); ret = can_get_max_bitrate(dev, &max_bitrate);
@ -201,11 +229,16 @@ int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate
return -ENOTSUP; return -ENOTSUP;
} }
ret = can_calc_timing(dev, &timing, bitrate, 875); sample_pnt = sample_point_for_bitrate(bitrate);
ret = can_calc_timing(dev, &timing, bitrate, sample_pnt);
if (ret < 0) { if (ret < 0) {
return -EINVAL; return -EINVAL;
} }
if (ret > SAMPLE_POINT_MARGIN) {
return -EINVAL;
}
timing.sjw = CAN_SJW_NO_CHANGE; timing.sjw = CAN_SJW_NO_CHANGE;
#ifdef CONFIG_CAN_FD_MODE #ifdef CONFIG_CAN_FD_MODE
@ -213,11 +246,16 @@ int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate
return -ENOTSUP; return -ENOTSUP;
} }
ret = can_calc_timing_data(dev, &timing_data, bitrate_data, 875); sample_pnt = sample_point_for_bitrate(bitrate_data);
ret = can_calc_timing_data(dev, &timing_data, bitrate_data, sample_pnt);
if (ret < 0) { if (ret < 0) {
return -EINVAL; return -EINVAL;
} }
if (ret > SAMPLE_POINT_MARGIN) {
return -EINVAL;
}
timing_data.sjw = CAN_SJW_NO_CHANGE; timing_data.sjw = CAN_SJW_NO_CHANGE;
return can_set_timing(dev, &timing, &timing_data); return can_set_timing(dev, &timing, &timing_data);

View file

@ -845,7 +845,14 @@ static inline int z_impl_can_set_mode(const struct device *dev, enum can_mode mo
/** /**
* @brief Set the bitrate of the CAN controller * @brief Set the bitrate of the CAN controller
* *
* The sample point is set to the CiA DS 301 recommended value of 87.5%. * CAN in Automation (CiA) 301 v4.2.0 recommends a sample point location of
* 87.5% percent for all bitrates. However, some CAN controllers have
* difficulties meeting this for higher bitrates.
*
* This function defaults to using a sample point of 75.0% for bitrates over 800
* kbit/s, 80.0% for bitrates over 500 kbit/s, and 87.5% for all other
* bitrates. This is in line with the sample point locations used by the Linux
* kernel.
* *
* @note The parameter ``bitrate_data`` is only relevant for CAN-FD. If the * @note The parameter ``bitrate_data`` is only relevant for CAN-FD. If the
* controller does not support CAN-FD or if @kconfig{CONFIG_CAN_FD_MODE} is not * controller does not support CAN-FD or if @kconfig{CONFIG_CAN_FD_MODE} is not
@ -857,7 +864,7 @@ static inline int z_impl_can_set_mode(const struct device *dev, enum can_mode mo
* *
* @retval 0 If successful. * @retval 0 If successful.
* @retval -ENOTSUP bitrate not supported by CAN controller/transceiver combination * @retval -ENOTSUP bitrate not supported by CAN controller/transceiver combination
* @retval -EINVAL bitrate cannot be met. * @retval -EINVAL bitrate/sample point cannot be met.
* @retval -EIO General input/output error, failed to set bitrate. * @retval -EIO General input/output error, failed to set bitrate.
*/ */
int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate_data); int can_set_bitrate(const struct device *dev, uint32_t bitrate, uint32_t bitrate_data);