Created
July 31, 2022 01:34
-
-
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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