Controller Area Network interface module for Python providing common abstractions for CAN hardware devices and message handling utilities
—
CAN bit timing calculation and configuration utilities for both CAN 2.0 and CAN FD, supporting various calculation methods including sample point-based timing, register-based configuration, and bitrate-based parameter calculation.
Configure bit timing parameters for standard CAN 2.0 communication.
class BitTiming:
def __init__(self, f_clock: int, brp: int, tseg1: int, tseg2: int, sjw: int,
nof_samples: int = 1, strict: bool = False):
"""
Create CAN 2.0 bit timing configuration.
Parameters:
- f_clock: CAN system clock frequency in Hz
- brp: Bit rate prescaler (1-1024)
- tseg1: Time segment 1 (number of quanta from sync to sampling point)
- tseg2: Time segment 2 (number of quanta from sampling point to end)
- sjw: Synchronization jump width (1 to min(4, tseg1, tseg2))
- nof_samples: Number of samples (1 or 3)
- strict: If True, raise exceptions for invalid parameters
"""
@classmethod
def from_bitrate_and_segments(cls, f_clock: int, bitrate: int, tseg1: int,
tseg2: int, sjw: int, nof_samples: int = 1):
"""
Create bit timing from bitrate and time segments.
Parameters:
- f_clock: CAN system clock frequency in Hz
- bitrate: Desired bit rate in bits per second
- tseg1: Time segment 1 in quanta
- tseg2: Time segment 2 in quanta
- sjw: Synchronization jump width in quanta
- nof_samples: Number of samples per bit
Returns:
BitTiming instance with calculated prescaler
"""
@classmethod
def from_registers(cls, f_clock: int, btr0: int, btr1: int):
"""
Create bit timing from BTR register values.
Parameters:
- f_clock: CAN system clock frequency in Hz
- btr0: Bus Timing Register 0 value
- btr1: Bus Timing Register 1 value
Returns:
BitTiming instance decoded from register values
"""
@classmethod
def from_sample_point(cls, f_clock: int, bitrate: int, sample_point: float):
"""
Calculate bit timing for desired sample point percentage.
Parameters:
- f_clock: CAN system clock frequency in Hz
- bitrate: Desired bit rate in bits per second
- sample_point: Desired sample point as percentage (e.g., 75.0 for 75%)
Returns:
BitTiming instance with calculated parameters
"""Configure bit timing for CAN FD with separate arbitration and data phase parameters.
class BitTimingFd:
def __init__(self, f_clock: int, nom_brp: int, nom_tseg1: int, nom_tseg2: int,
nom_sjw: int, data_brp: int, data_tseg1: int, data_tseg2: int,
data_sjw: int, nom_sam: bool = False):
"""
Create CAN FD bit timing configuration.
Parameters:
- f_clock: CAN system clock frequency in Hz
- nom_brp: Nominal (arbitration) bit rate prescaler
- nom_tseg1: Nominal time segment 1
- nom_tseg2: Nominal time segment 2
- nom_sjw: Nominal synchronization jump width
- data_brp: Data phase bit rate prescaler
- data_tseg1: Data phase time segment 1
- data_tseg2: Data phase time segment 2
- data_sjw: Data phase synchronization jump width
- nom_sam: Nominal phase sampling (False=1 sample, True=3 samples)
"""
@classmethod
def from_bitrates_and_sample_points(cls, f_clock: int, nom_bitrate: int,
data_bitrate: int, nom_sample_point: float,
data_sample_point: float):
"""
Calculate CAN FD timing from bitrates and sample points.
Parameters:
- f_clock: System clock frequency in Hz
- nom_bitrate: Nominal (arbitration) phase bitrate
- data_bitrate: Data phase bitrate
- nom_sample_point: Nominal phase sample point percentage
- data_sample_point: Data phase sample point percentage
Returns:
BitTimingFd instance with calculated parameters
"""Access calculated timing properties and register values.
# BitTiming properties
@property
def bitrate(self) -> int:
"""Calculated bitrate in bits per second."""
@property
def sample_point(self) -> float:
"""Sample point as percentage of bit time."""
@property
def btr0(self) -> int:
"""Bus Timing Register 0 value."""
@property
def btr1(self) -> int:
"""Bus Timing Register 1 value."""
@property
def tseg(self) -> int:
"""Total time segments (tseg1 + tseg2)."""
# Dictionary-like access
def __getitem__(self, key: str):
"""Access timing parameters as dictionary keys."""
def keys(self):
"""Get available parameter keys."""
def values(self):
"""Get parameter values."""
def items(self):
"""Get parameter key-value pairs."""import can
# Create bit timing for 500 kbps with 8 MHz clock
timing = can.BitTiming(
f_clock=8_000_000, # 8 MHz clock
brp=1, # Prescaler = 1
tseg1=13, # Time segment 1 = 13 quanta
tseg2=2, # Time segment 2 = 2 quanta
sjw=1 # SJW = 1 quantum
)
print(f"Configured bitrate: {timing.bitrate} bps")
print(f"Sample point: {timing.sample_point:.1f}%")
print(f"BTR registers: BTR0=0x{timing.btr0:02X}, BTR1=0x{timing.btr1:02X}")import can
# Calculate timing for 1 Mbps with 87.5% sample point
timing = can.BitTiming.from_sample_point(
f_clock=16_000_000, # 16 MHz clock
bitrate=1_000_000, # 1 Mbps
sample_point=87.5 # 87.5% sample point
)
print(f"Calculated parameters:")
print(f" BRP: {timing['brp']}")
print(f" TSEG1: {timing['tseg1']}")
print(f" TSEG2: {timing['tseg2']}")
print(f" SJW: {timing['sjw']}")
print(f"Actual bitrate: {timing.bitrate} bps")
print(f"Actual sample point: {timing.sample_point:.1f}%")import can
# CAN FD with 500 kbps arbitration, 2 Mbps data phase
fd_timing = can.BitTimingFd(
f_clock=40_000_000, # 40 MHz clock
# Nominal (arbitration) phase - 500 kbps
nom_brp=5,
nom_tseg1=13,
nom_tseg2=2,
nom_sjw=1,
# Data phase - 2 Mbps
data_brp=1,
data_tseg1=15,
data_tseg2=4,
data_sjw=1
)
print(f"Arbitration phase: {fd_timing.nom_bitrate} bps")
print(f"Data phase: {fd_timing.data_bitrate} bps")
print(f"Speed improvement: {fd_timing.data_bitrate / fd_timing.nom_bitrate:.1f}x")import can
# Common CAN bitrates with 8 MHz clock
standard_configs = {
'125k': can.BitTiming(f_clock=8_000_000, brp=4, tseg1=13, tseg2=2, sjw=1),
'250k': can.BitTiming(f_clock=8_000_000, brp=2, tseg1=13, tseg2=2, sjw=1),
'500k': can.BitTiming(f_clock=8_000_000, brp=1, tseg1=13, tseg2=2, sjw=1),
'1M': can.BitTiming(f_clock=8_000_000, brp=1, tseg1=5, tseg2=2, sjw=1)
}
print("Standard CAN bitrate configurations:")
for name, timing in standard_configs.items():
print(f"{name:>4}: {timing.bitrate:>7} bps, "
f"SP={timing.sample_point:4.1f}%, "
f"BTR0=0x{timing.btr0:02X}, BTR1=0x{timing.btr1:02X}")import can
def validate_timing(timing, target_bitrate, tolerance=0.01):
"""Validate bit timing against target bitrate."""
actual_rate = timing.bitrate
error = abs(actual_rate - target_bitrate) / target_bitrate
print(f"Target: {target_bitrate} bps")
print(f"Actual: {actual_rate} bps")
print(f"Error: {error * 100:.3f}%")
if error <= tolerance:
print("✓ Timing within tolerance")
return True
else:
print("✗ Timing exceeds tolerance")
return False
# Test different configurations
configs_to_test = [
(8_000_000, 500_000, {'brp': 1, 'tseg1': 13, 'tseg2': 2, 'sjw': 1}),
(10_000_000, 250_000, {'brp': 5, 'tseg1': 6, 'tseg2': 1, 'sjw': 1}),
(16_000_000, 1_000_000, {'brp': 1, 'tseg1': 13, 'tseg2': 2, 'sjw': 1})
]
for f_clock, target_rate, params in configs_to_test:
print(f"\nTesting {f_clock/1e6:.0f} MHz clock, {target_rate/1000:.0f} kbps:")
timing = can.BitTiming(f_clock=f_clock, **params)
validate_timing(timing, target_rate)import can
# Calculate optimal timing for hardware
timing = can.BitTiming.from_sample_point(
f_clock=16_000_000,
bitrate=500_000,
sample_point=80.0
)
# Use timing parameters with bus (example for socketcan)
bus = can.Bus(
channel='can0',
interface='socketcan',
# Note: Most interfaces handle timing automatically
# This is conceptual - actual parameter names vary by interface
bitrate=timing.bitrate
)
print(f"Bus configured for {timing.bitrate} bps")
bus.shutdown()import can
# Create timing from existing BTR register values
# (e.g., read from hardware or configuration file)
timing_from_regs = can.BitTiming.from_registers(
f_clock=8_000_000,
btr0=0x00, # BRP-1 in lower 6 bits, SJW-1 in upper 2 bits
btr1=0xDE # TSEG1-1 in lower 4 bits, TSEG2-1 in upper 3 bits, SAM in bit 7
)
print(f"Decoded from registers:")
print(f" Bitrate: {timing_from_regs.bitrate} bps")
print(f" BRP: {timing_from_regs['brp']}")
print(f" TSEG1: {timing_from_regs['tseg1']}")
print(f" TSEG2: {timing_from_regs['tseg2']}")from typing import Union, Dict, Any
from collections.abc import Mapping
class BitTiming(Mapping[str, int]):
"""CAN 2.0 bit timing configuration with dictionary-like access."""
def __init__(self, f_clock: int, brp: int, tseg1: int, tseg2: int,
sjw: int, nof_samples: int = 1, strict: bool = False): ...
# Properties
f_clock: int
brp: int
tseg1: int
tseg2: int
sjw: int
nof_samples: int
@property
def bitrate(self) -> int: ...
@property
def sample_point(self) -> float: ...
@property
def btr0(self) -> int: ...
@property
def btr1(self) -> int: ...
class BitTimingFd:
"""CAN FD bit timing configuration with dual-phase parameters."""
def __init__(self, f_clock: int, nom_brp: int, nom_tseg1: int,
nom_tseg2: int, nom_sjw: int, data_brp: int,
data_tseg1: int, data_tseg2: int, data_sjw: int,
nom_sam: bool = False): ...
# Properties for both phases
@property
def nom_bitrate(self) -> int: ...
@property
def data_bitrate(self) -> int: ...
@property
def nom_sample_point(self) -> float: ...
@property
def data_sample_point(self) -> float: ...Install with Tessl CLI
npx tessl i tessl/pypi-python-can