Skip to content

Instantly share code, notes, and snippets.

@ghent360
Created July 31, 2022 01:34
Show Gist options
  • Save ghent360/fe34f4ec663db133117e92e70445cc6f to your computer and use it in GitHub Desktop.
Save ghent360/fe34f4ec663db133117e92e70445cc6f to your computer and use it in GitHub Desktop.
SAME51 can bus timing calculator. Update VARIANT_GCLK2_FREQ value to whatever clock you are using.
/*
This sketch aims to calculate a proper canbus timing config for the SAME51 Bosch M_CAN Variant.
Order of operations:
1 Calculate NBRP to keep time quanta within whole divisions of bit time
(some odd non integer kbaud timings will be off ~1%)
2 Calculate NTSEG1 using requested sample point, to whole integer
3 Increment NSJW if NTSEG1 is not a whole number or within limits and try again
4 Calculate NTSEG2 once NTSEG1 is done,
5 Check limits and try prescaler again
6 If failed Increment QBST down if sum of calculated NTSEG1/NTSEG2 is off, or try prescaler again if failed
7 If run out of prescaler, fail with error... (So far, only 800Kbit at 150MHz overclock)
8 Calculation finished!
*/
#include <stdio.h> /* printf */
#include <math.h> /* fmod */
#include <stdint.h>
#define VARIANT_GCLK2_FREQ 100000000
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof(*(x)))
#define ROUND_INT_DIV(n, d) ((2 * (n) + (d)) / (2 * (d)))
struct Can {
uint8_t unused;
};
static Can CAN0;
struct can_config
{
uint32_t id; /* peripheral ID (ID_xxx) */
Can regs; /* set of MCAN hardware registers */
uint32_t* msg_ram; /* base address of the Message RAM to be
* assigned to this MCAN instance */
uint8_t array_size_filt_std; /* #of 11 - bit Message ID Rx Filters */
uint8_t array_size_filt_ext; /* #of 29 - bit Message ID Rx Filters */
uint8_t fifo_size_rx0; /* #of Rx Buffers in Rx FIFO 0 */
uint8_t fifo_size_rx1; /* #of Rx Buffers in Rx FIFO 1 */
uint8_t array_size_rx; /* #of dedicated Rx Buffers */
uint8_t fifo_size_tx_evt; /* #of Tx Event Elements in the Tx Event FIFO */
uint8_t array_size_tx; /* #of dedicated Tx Buffers */
uint8_t fifo_size_tx; /* #of Tx Buffers in the Tx FIFO or Tx Queue */
uint8_t buf_size_rx_fifo0; /* size of the data field in each Rx
* Buffer of Rx FIFO 0, in bytes */
uint8_t buf_size_rx_fifo1; /* size of the data field in each Rx
* Buffer of Rx FIFO 1, in bytes */
uint8_t buf_size_rx; /* size of the data field in each
* dedicated Rx Buffer, in bytes */
uint8_t buf_size_tx; /* size of the data field in each Tx
* Buffer, in bytes.Applies to all Tx
* Buffers, dedicated and in Tx FIFO Queue. */
uint32_t bit_rate; /* requested CAN bit rate in CAN mode, in bps */
uint16_t sample_point; /* Sample point requested, in percentage
* of total bit time(*100) */
uint8_t quanta_prescale; /* Gclk divider for standard rate mode */
uint8_t quanta_before_sp; /* duration of the time segment before the
* sample point(Sync_Seg + Prop_Seg + Phase_Seg1),
* while in CAN mode, expressed in CAN time quanta */
uint8_t quanta_after_sp; /* duration of the time segment after the
* sample point(Phase_Seg2), while in CAN
* mode, expressed in CAN time quanta */
uint8_t quanta_sync_jump; /* duration of a(re) synchronization jump,
* while in CAN mode, expressed in CAN
* time quanta */
uint32_t bit_rate_fd; /* requested CAN bit rate in fast CAN FD mode, in bps */
uint16_t sample_point_fd; /* Sample point requested, in percentage
* of total bit time(*100) */
uint8_t quanta_prescale_fd; /* Gclk divider for FD Bit Rate Switching */
uint8_t quanta_before_sp_fd; /* duration of the time segment before the
* sample point(Sync_Seg + Prop_Seg + Phase_Seg1),
* while in fast CAN FD mode, expressed in CAN time quanta */
uint8_t quanta_after_sp_fd; /* duration of the time segment after the
* sample point(Phase_Seg2), while in fast CAN FD mode,
* expressed in CAN time quanta */
uint8_t quanta_sync_jump_fd; /* duration of a (re)synchronization jump,
* while in fast CAN FD mode, expressed in
* CAN time quanta */
};
#define CAN_QUANTA_OK 0
#define CAN_QUANTA_CFG 1
#define CAN_QUANTA_FAIL 0xFF
struct can_quanta
{
uint32_t quanta_count;
uint32_t prescale;
uint32_t can_bit_time;
};
uint32_t can_msg_ram[500] __attribute__((aligned(4))) // All ram used by canbus perpheral, aligned
__attribute__((section(".canram.msg"))); // At the beginning of RAM
uint32_t can_msg_ram_size = ARRAY_SIZE(can_msg_ram); // Size of ram used by canbus perpheral
uint32_t canbaud[] = {
4096, 5000, 10000, 20000, 31250, 33333, 40000, 50000, 80000,
83333, 100000, 125000, 200000, 250000, 500000, 800000, 1000000}; // can bitrates to test
uint32_t canbaud1[] = {
5000, 10000, 20000, 31250, 33333, 40000, 50000, 80000,
83333, 100000, 125000, 200000, 250000, 500000, 800000,
1000000, 1250000, 1500000, 1750000, 2000000, 2250000,
2500000, 2750000, 3000000, 3250000, 3500000, 3750000,
4000000, 4250000, 4500000, 4750000, 5000000}; // can bitrates to test
enum
{
ID_CAN0
};
uint8_t _canid = ID_CAN0;
uint8_t can_calc_timings(struct can_config *cfg, struct can_quanta *set, bool canfd);
void can_calc_quanta_count(struct can_quanta *set);
uint8_t can_calc_prescale(struct can_quanta *set, bool canfd);
uint8_t can_calc_quanta_vals(struct can_config *cfg, struct can_quanta *set, bool canfd);
void setup()
{
struct can_config can_cfg = {
id : _canid,
regs : ((_canid == ID_CAN0) ? CAN0 : CAN0),
msg_ram : can_msg_ram,
array_size_filt_std : 64,
array_size_filt_ext : 64,
fifo_size_rx0 : 64,
fifo_size_rx1 : 32,
array_size_rx : 16,
fifo_size_tx_evt : 8,
array_size_tx : 8,
fifo_size_tx : 8,
buf_size_rx_fifo0 : 8,
buf_size_rx_fifo1 : 8,
buf_size_rx : 8,
buf_size_tx : 8,
bit_rate : 500000,
sample_point : 75,
quanta_before_sp : 29,
quanta_after_sp : 16,
quanta_sync_jump : 2,
bit_rate_fd : 500000,
sample_point_fd : 75,
quanta_before_sp_fd : 29,
quanta_after_sp_fd : 15,
quanta_sync_jump_fd : 2,
};
printf(" Gclk,");
printf(" Can Baud,");
printf(" NBRP,");
printf(" NTSEG1,");
printf(" NTSEG2,");
printf(" NSJW\n");
for (int j = 0; j < sizeof(canbaud)/sizeof(canbaud[0]); j++)
{ // Test loop, canbus frequencies
can_cfg.bit_rate = canbaud[j]; // run through canbus frequencies
can_cfg.quanta_before_sp = 0;
can_cfg.quanta_after_sp = 0;
can_cfg.quanta_prescale = 0;
struct can_quanta test = {
quanta_count : 0,
prescale : 0,
can_bit_time : 0,
};
uint8_t ret = can_calc_timings(&can_cfg, &test, 0); // Calculate bit timings
printf("%10d,", VARIANT_GCLK2_FREQ);
printf("%9d,", can_cfg.bit_rate);
if (ret > CAN_QUANTA_OK)
{ // Errors...
printf(" error\n");
}
else
{
printf("%5d,", can_cfg.quanta_prescale + 1);
printf("%7d,", can_cfg.quanta_before_sp + 1);
printf("%7d,", can_cfg.quanta_after_sp + 1);
printf("%5d\n", can_cfg.quanta_sync_jump + 1);
}
}
printf("\n");
printf("FD Rates test\n");
for (int j = 0; j < sizeof(canbaud1)/sizeof(canbaud1[0]); j++)
{ // Test loop, canbus frequencies
can_cfg.bit_rate_fd = canbaud1[j]; // run through canbus frequencies
can_cfg.quanta_before_sp_fd = 0;
can_cfg.quanta_after_sp_fd = 0;
can_cfg.quanta_prescale_fd = 0;
struct can_quanta test = {
quanta_count : 0,
prescale : 0,
can_bit_time : 0,
};
uint8_t ret = can_calc_timings(&can_cfg, &test, 1); // Calculate bit timings
printf("%10d,", VARIANT_GCLK2_FREQ);
printf("%9d,", can_cfg.bit_rate_fd);
if (ret > CAN_QUANTA_OK)
{ // Errors...
printf(" error\n");
}
else
{
printf("%5d,", can_cfg.quanta_prescale_fd + 1);
printf("%7d,", can_cfg.quanta_before_sp_fd + 1);
printf("%7d,", can_cfg.quanta_after_sp_fd + 1);
printf("%5d\n", can_cfg.quanta_sync_jump_fd + 1);
}
}
}
void loop()
{
} /* @brief Calculate bit timings for given clock and baudrate * */
uint8_t can_calc_timings(struct can_config *cfg, struct can_quanta *set, bool canfd)
{
// Function to calculate canbus timings respecting a given sample point
uint8_t ret = 0; // Return value
set->can_bit_time = 0; // Canbus bit time
set->prescale = 1; // Reset prescaler
set->quanta_count = 0; // Total quanta available with configured rate
if (canfd)
{ // Calculate bit rate switching rates
set->can_bit_time = ROUND_INT_DIV(1000000000, cfg->bit_rate_fd); // Total bittime with selected canbaud
ret = can_calc_prescale(set, canfd); // Calculate an inital prescaler
}
else
{ // Calculate standard rates
set->can_bit_time = ROUND_INT_DIV(1000000000, cfg->bit_rate); // Total bittime with selected canbaud
ret = can_calc_prescale(set, canfd); // Calculate an inital prescaler
}
if (ret == CAN_QUANTA_FAIL)
{ // Error here means we are out of prescale options...
return CAN_QUANTA_FAIL; // Skip loop if ran out of prescaler
}
ret = can_calc_quanta_vals(cfg, set, canfd); // Calculate quanta values
while (ret == CAN_QUANTA_CFG)
{ // Continue until valid data is calculated, or run out of prescale...
set->prescale++; // Increment prescaler to try again
ret = can_calc_prescale(set, canfd); // Run prescale loop again
if (ret == CAN_QUANTA_FAIL)
{ // Error here means we are out of prescale options...
return CAN_QUANTA_FAIL; // Skip loop if ran out of prescaler
}
ret = can_calc_quanta_vals(cfg, set, canfd); // Run quanta calc again
if (ret == CAN_QUANTA_FAIL)
{ // Error 5 means we are out of options...
return CAN_QUANTA_FAIL; // Skip loop if ran out of prescaler
}
}
if (ret == CAN_QUANTA_OK)
{ // Calculation is OK, data is saved
return CAN_QUANTA_OK; // return OK
}
else
{ // Something went wrong
return CAN_QUANTA_FAIL; // return Failed
}
}
uint8_t can_calc_prescale(struct can_quanta *set, bool canfd)
{ // Used to calculate a prescaler for canbus quanta timing
set->quanta_count = 0; // Number of time quanta with configured prescaler
can_calc_quanta_count(set); // Calculate time quanta
if (canfd)
{ // Limits for CAN FD mode
while ((set->quanta_count < 4) || (set->quanta_count > 49))
{
// Check for valid timing and total quanta within limits
set->prescale++; // Increment prescale
can_calc_quanta_count(set); // Calculate time quanta
if (set->prescale > 31)
{ // Prescale can't be higher than 31 in FD mode
return CAN_QUANTA_FAIL; // Exit with error
}
}
}
else
{ // Limits for normal CAN mode
while ((set->quanta_count < 4) || (set->quanta_count > 385))
{
// Check for valid timing and total quanta within limits
set->prescale++; // Increment prescale
can_calc_quanta_count(set); // Calculate time quanta
if (set->prescale > 512)
{ // Prescale can't be higher than 512 in normal mode
return CAN_QUANTA_FAIL; // Exit with error
}
}
}
return CAN_QUANTA_OK; // Done!
}
uint8_t can_calc_quanta_vals(struct can_config *cfg, struct can_quanta *set, bool canfd)
{ // Used to calculate a valid set of quanta timings
uint32_t qbsp = 0; // Quanta before sample point
uint32_t qasp = 0; // Quanta after sample point
if (canfd)
{ // If calculating FD Mode
qbsp = ROUND_INT_DIV((set->quanta_count * cfg->sample_point_fd), 100);
// Calculate the timing before the sync point using percentage
qasp = ROUND_INT_DIV((set->quanta_count * (100 - cfg->sample_point_fd)), 100);
// Calculate the timing after the sync point using percentage
if ((qbsp < 1) || (qbsp > 32))
{ // Check limits of value, return an error (later)
return CAN_QUANTA_CFG; // Return from loop, Retry with new prescaler
}
}
else
{ // Standard CAN Baud Rate
qbsp = ROUND_INT_DIV((set->quanta_count * cfg->sample_point), 100);
// Calculate the timing before the sync point using percentage
qasp = ROUND_INT_DIV((set->quanta_count * (100 - cfg->sample_point)), 100);
// Calculate the timing after the sync point using percentage
if ((qbsp < 1) || (qbsp > 256))
{
// Check limits of value, return an error (later)
return CAN_QUANTA_CFG; // Return from loop, Retry with new prescaler
}
}
while (set->quanta_count != qbsp + qasp)
{
// If bit timings do not add up to total
if (set->quanta_count > qbsp + qasp)
{
// If value is greater than total
qasp++; // Decrement after sample point
}
else
{
// Otherwise value was higher (weird?)
qasp--; // Increment sync quanta
}
}
if (canfd)
{
// CAN FD Baud Rate
if ((qasp > 16) || (qasp < 1))
{
// Check limits of value, return an error (later)
return CAN_QUANTA_CFG; // Return from loop, Retry with new prescaler
}
}
else
{
// Standard CAN Baud Rate
if ((qasp > 128) || (qasp < 1))
{
// Check limits of value, return an error (later)
return CAN_QUANTA_CFG; // Return from loop, Retry with new prescaler
}
}
if (canfd)
{
cfg->quanta_before_sp_fd = qbsp - 1; // Apply values, offset data
cfg->quanta_after_sp_fd = qasp - 1; // Apply values, offset data
cfg->quanta_prescale_fd = set->prescale - 1; // Apply values, offset data
}
else
{
cfg->quanta_before_sp = qbsp - 1; // Apply values, offset data
cfg->quanta_after_sp = qasp - 1; // Apply values, offset data
cfg->quanta_prescale = set->prescale - 1; // Apply values, offset data
}
return CAN_QUANTA_OK; // Done!
}
void can_calc_quanta_count(struct can_quanta *set)
{ // Calculate and check for valid bit timing and data sync
uint32_t total_quanta1 = 0; // Total quanta available in can bit time
total_quanta1 = ROUND_INT_DIV(
(set->prescale * 1000),
(VARIANT_GCLK2_FREQ / 1000000)); // Clock in MHz
// Calculate quanta time using gclk frequency
total_quanta1 = ROUND_INT_DIV(set->can_bit_time, total_quanta1); // Calculate total quanta
uint8_t temp = fmod(set->can_bit_time, total_quanta1);
// If fmod is zero, bittime is evenly divisible by quanta time
if (temp == 0)
{ // If temp is zero, quanta and bit time are synced within 1%
set->quanta_count = (uint32_t)total_quanta1; // Cast float to int
}
else
{ // Otherwise quanta is out of sync
set->quanta_count = 0; // Return zero as a flag
}
return; // Return data/flag
}
int main()
{
setup();
loop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment