Last active
March 29, 2022 13:27
-
-
Save Lauszus/30ce8c9f8bc2bdaf8c42c5e1712aac39 to your computer and use it in GitHub Desktop.
Python port of the "FLEXCAN_CalculateImprovedTimingValues" function from the NXP SDK
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
#!/usr/bin/env python3 | |
# | |
# Copyright (c) 2015, Freescale Semiconductor, Inc. | |
# Copyright 2016-2021 NXP | |
# Copyright 2022 Kristian Sloth Lauszus | |
# All rights reserved. | |
# | |
# SPDX-License-Identifier: BSD-3-Clause | |
# | |
# Python port of the "FLEXCAN_CalculateImprovedTimingValues" function from the NXP SDK | |
import sys | |
def FLEXCAN_GetSegments(bitRate, tqNum): | |
IDEAL_SP_LOW = 750 | |
IDEAL_SP_MID = 800 | |
IDEAL_SP_HIGH = 875 | |
IDEAL_SP_FACTOR = 1000 | |
MIN_TIME_SEGMENT1 = 2 | |
MIN_TIME_SEGMENT2 = 2 | |
# TODO: These should ideally not be hardcoded, as they are MCU dependent | |
seg1Max = (0x380000 >> 19) + 1 # MAX_PSEG1 | |
proSegMax = 7 + 1 # MAX_PROPSEG | |
sjwMAX = (0xC00000 >> 22) + 1 # MAX_RJW | |
if bitRate == 1000000: | |
ideal_sp = IDEAL_SP_LOW | |
elif bitRate >= 800000: | |
ideal_sp = IDEAL_SP_MID | |
else: | |
ideal_sp = IDEAL_SP_HIGH | |
# Calculates phaseSeg2 | |
phaseSeg2 = tqNum - (tqNum * ideal_sp) // IDEAL_SP_FACTOR | |
if phaseSeg2 < MIN_TIME_SEGMENT2: | |
phaseSeg2 = MIN_TIME_SEGMENT2 | |
elif phaseSeg2 > seg1Max: | |
phaseSeg2 = seg1Max | |
# Calculates phaseSeg1 and propSeg | |
seg1Temp = tqNum - phaseSeg2 - 1 | |
if seg1Temp > (seg1Max + proSegMax): | |
phaseSeg2 += seg1Temp - seg1Max - proSegMax | |
propSeg = proSegMax | |
phaseSeg1 = seg1Max | |
elif seg1Temp > proSegMax: | |
propSeg = proSegMax | |
phaseSeg1 = seg1Temp - proSegMax | |
else: | |
propSeg = seg1Temp - 1 | |
phaseSeg1 = 1 | |
# Try to make phaseSeg1 equal to phaseSeg2 | |
if phaseSeg1 < phaseSeg2: | |
seg1Temp = proSegMax - propSeg if (phaseSeg2 - phaseSeg1) > (proSegMax - propSeg) else phaseSeg2 - phaseSeg1 | |
propSeg -= seg1Temp | |
phaseSeg1 += seg1Temp | |
else: | |
seg1Temp = proSegMax - propSeg if (phaseSeg1 - phaseSeg2) > (proSegMax - propSeg) else phaseSeg1 - phaseSeg2 | |
propSeg += seg1Temp | |
phaseSeg1 -= seg1Temp | |
# rJumpwidth (SJW) is the minimum value of phaseSeg1 and phaseSeg2 | |
rJumpwidth = phaseSeg2 if phaseSeg1 > phaseSeg2 else phaseSeg1 | |
if rJumpwidth > sjwMAX: | |
rJumpwidth = sjwMAX | |
phaseSeg1 -= 1 | |
phaseSeg2 -= 1 | |
propSeg -= 1 | |
rJumpwidth -= 1 | |
return propSeg, phaseSeg1, phaseSeg2, rJumpwidth | |
def print_timing(bitRate, sourceClock_Hz, propSeg, phaseSeg1, phaseSeg2, rJumpwidth, preDivider): | |
tq = (preDivider + 1) / sourceClock_Hz * 1e9 | |
quantum = 1 + (propSeg + 1) + (phaseSeg1 + 1) + (phaseSeg2 + 1) | |
sample_point_percent = (1 + (propSeg + 1) + (phaseSeg1 + 1)) / quantum * 100. | |
print('Bit-rate: {} kbit/s, clock source: {} MHz, TQ: {:.1f} ns, quantum: {}, Prs: {}, PhS1: {}, PhS2: {}, SJW: {}, BRP: {}, sample point: {:.1f} %'.format(bitRate // 1000, sourceClock_Hz * 1e-6, tq, quantum, propSeg + 1, phaseSeg1 + 1, phaseSeg2 + 1, rJumpwidth + 1, preDivider + 1, sample_point_percent)) | |
def FLEXCAN_CalculateImprovedTimingValues(bitRate, sourceClock_Hz): | |
spTemp = 1000 | |
pdivMAX = 255 | |
tqMin = 8 # CTRL1_MIN_TIME_QUANTA | |
tqNum = 25 # CTRL1_MAX_TIME_QUANTA | |
fgRet = False | |
while tqNum >= tqMin: | |
clk = bitRate * tqNum | |
if clk > sourceClock_Hz: | |
tqNum -= 1 | |
continue | |
if (sourceClock_Hz // clk * clk) != sourceClock_Hz: | |
tqNum -= 1 | |
continue | |
preDividerTemp = (sourceClock_Hz // clk) - 1 | |
if preDividerTemp > pdivMAX: | |
print('The frequency of source clock is too large or the bit rate is too small, the pre-divider could not handle it') | |
break | |
propSegTemp, phaseSeg1Temp, phaseSeg2Temp, rJumpwidthTemp = FLEXCAN_GetSegments(bitRate, tqNum) | |
# Determine whether the calculated timing configuration can get the optimal sampling point | |
if (((phaseSeg2Temp + 1) * 1000) // tqNum) < spTemp: | |
spTemp = ((phaseSeg2Temp + 1) * 1000) // tqNum | |
fgRet = True | |
preDivider, propSeg, phaseSeg1, phaseSeg2, rJumpwidth = preDividerTemp, propSegTemp, phaseSeg1Temp, phaseSeg2Temp, rJumpwidthTemp | |
# print_timing(bitRate, sourceClock_Hz, propSeg, phaseSeg1, phaseSeg2, rJumpwidth, preDivider) | |
tqNum -= 1 | |
if fgRet: | |
print_timing(bitRate, sourceClock_Hz, propSeg, phaseSeg1, phaseSeg2, rJumpwidth, preDivider) | |
return propSeg, phaseSeg1, phaseSeg2, rJumpwidth, preDivider | |
else: | |
print('Failed to find bit timing for bit-rate: {} kbit/s, clock source: {} MHz'.format(bitRate // 1000, sourceClock_Hz * 1e-6)) | |
assert False | |
if __name__ == '__main__': | |
# Adjust these to match your setup or give them as command line arguments | |
if len(sys.argv) > 1: | |
bitRate = int(sys.argv[1]) | |
sourceClock_Hz = int(sys.argv[2]) | |
else: | |
bitRate = 500_000 # 500 kbit/s | |
sourceClock_Hz = 60_000_000 # 60 MHz | |
FLEXCAN_CalculateImprovedTimingValues(bitRate, sourceClock_Hz) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment