diff options
| -rw-r--r-- | fw/can.c | 79 | ||||
| -rw-r--r-- | fw/can.h | 26 | ||||
| -rw-r--r-- | fw/dac.h | 1 | ||||
| -rw-r--r-- | fw/eeprom.h | 2 | ||||
| -rw-r--r-- | sw/bittiming/bittiming.py | 146 |
5 files changed, 252 insertions, 2 deletions
diff --git a/fw/can.c b/fw/can.c new file mode 100644 index 0000000..b005fc2 --- /dev/null +++ b/fw/can.c @@ -0,0 +1,79 @@ +#include <xc.h> + +#include <stdint.h> + +#include "types.h" + +#include "can.h" + +// Register addresses +enum { + // Pin control and status + REG_BFPCTRL = 0x0C, + + // Filter 0 + REG_RXF0SIDH = 0x00, // standard ID high + REG_RXF0SIDL = 0x01, // standard ID low + REG_RXF0EID8 = 0x02, // extended ID high + REG_RXF0EID0 = 0x03, // extended ID low + + // Filter 1 + REG_RXF1SIDH = 0x04, // standard ID high + REG_RXF1SIDL = 0x05, // standard ID low + REG_RXF1EID8 = 0x06, // extended ID high + REG_RXF1EID0 = 0x07, // extended ID low + + // Filter 2 + REG_RXF2SIDH = 0x08, // standard ID high + REG_RXF2SIDL = 0x09, // standard ID low + REG_RXF2EID8 = 0x0A, // extended ID high + REG_RXF2EID0 = 0x0B, // extended ID low + + // Filter 3 + REG_RXF3SIDH = 0x10, // standard ID high + REG_RXF3SIDL = 0x11, // standard ID low + REG_RXF3EID8 = 0x12, // extended ID high + REG_RXF3EID0 = 0x13, // extended ID low + + // Filter 4 + REG_RXF4SIDH = 0x14, // standard ID high + REG_RXF4SIDL = 0x15, // standard ID low + REG_RXF4EID8 = 0x16, // extended ID high + REG_RXF4EID0 = 0x17, // extended ID low + + // Filter 5 + REG_RXF5SIDH = 0x18, // standard ID high + REG_RXF5SIDL = 0x19, // standard ID low + REG_RXF5EID8 = 0x1A, // extended ID high + REG_RXF5EID0 = 0x1B, // extended ID low + + // Mask 0 + REG_RXM0SIDH = 0x20, standard ID high + REG_RXM0SIDL = 0x21, // standard ID low + REG_RXM0EID8 = 0x22, // extended ID high + REG_RXM0EID0 = 0x23, // extended ID low + + // Mask 1 + REG_RXM1SIDH = 0x24, // standard ID high + REG_RXM1SIDL = 0x25, // standard ID low + REG_RXM1EID8 = 0x26. // extended ID high + REG_RXM1EID0 = 0x27, // extended ID low + + // Receive buffer 0 + REG_RXB0CTRL = 0x60, // control + REG_RXB0SIDH = 0x61, // standard ID high + REG_RXB0SIDL = 0x62, // standard ID low + REG_RXB0EID8 = 0x63, // extended ID high + REG_RXB0EID0 = 0x64, // extended ID low + REG_RXB0DLC = 0x65, // data length code + REG_RXB0DM = 0x66, // data byte <M> [66h+0, 66h+7] + + // Receive buffer 1 + REG_RXB1CTRL = 0x70, // control + REG_RXB1SIDH = 0x71, // standard ID high + REG_RXB1SIDL = 0x72, // standard ID low + REG_RXB1EID8 = 0x73, // extended ID high + REG_RXB1EID0 = 0x74, // extended ID low + REG_RXB1DLC = 0x75, // data length code + REG_RXB1DM = 0x76, // data byte <M> [76h+0, 76h+7] +}; diff --git a/fw/can.h b/fw/can.h new file mode 100644 index 0000000..320c020 --- /dev/null +++ b/fw/can.h @@ -0,0 +1,26 @@ +/* Microchip MCP2515 CAN Controller + * + * Device: PIC16F1459 + * Compiler: XC8 v3.00 + * + * MCP2515 FOSC = 12MHz (48MHz/4 from PIC's CLKOUT) + * + * Usage: + * + * #include <stdint.h> + * #include "types.h" + * #include "can.h" + */ + +// Bit timings (CNF1, CNF2, CNF3) +#define CAN_TIMINGS_10K 0xDE, 0xAD, 0x06 // BRP=30, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4 +#define CAN_TIMINGS_20K 0xCF, 0xAD, 0x06 // BRP=15, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4 +#define CAN_TIMINGS_50K 0xC6, 0xAD, 0x06 // BRP=6, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4 +#define CAN_TIMINGS_100K 0xC3, 0xAD, 0x06 // BRP=3, PropSeg=6, PS1=6, PS2=7, SP=65%, SJW=4 +#define CAN_TIMINGS_125K 0xC4, 0x9A, 0x03 // BRP=4, PropSeg=3, PS1=4, PS2=4, SP=66.7%, SJW=4 +#define CAN_TIMINGS_250K 0xC2, 0x9A, 0x03 // BRP=2, PropSeg=3, PS1=4, PS2=4, SP=66.7%, SJW=4 +#define CAN_TIMINGS_500K 0xC1, 0x9A, 0x03 // BRP=1, PropSeg=3, PS1=4, PS2=4, SP=66.7, SJW=4 +#define CAN_TIMINGS_800K "800kbps unsupported" // not possible with 12MHz clock +#define CAN_TIMINGS_1M "1Mbps unsupported" // not possible with 12MHz clock + +void canInit(void); @@ -7,7 +7,6 @@ * * #include <stdint.h> * #include "types.h" - * #include "spi.h" * #include "dac.h" */ diff --git a/fw/eeprom.h b/fw/eeprom.h index 9b8652e..9ad3cdd 100644 --- a/fw/eeprom.h +++ b/fw/eeprom.h @@ -2,7 +2,7 @@ * * Device: PIC16F1459 * Compiler: XC8 v3.00 - * + * * Usage: * * #include <stdint.h> diff --git a/sw/bittiming/bittiming.py b/sw/bittiming/bittiming.py new file mode 100644 index 0000000..3692813 --- /dev/null +++ b/sw/bittiming/bittiming.py @@ -0,0 +1,146 @@ +from argparse import ArgumentParser +from math import floor, ceil + +MAX_BRP = 2**6 + +MIN_TQ_PER_BIT = 8 +MAX_TQ_PER_BIT = 25 + +MIN_PROP_SEG = 1 +MAX_PROP_SEG = 8 + +MIN_PS1 = 1 +MAX_PS1 = 8 + +MIN_PS2 = 2 +MAX_PS2 = 8 + +MIN_SJW = 1 +MAX_SJW = 4 + +CNF2_BTLMODE = 1 << 7 + +def prescaler(f_osc: int, bitrate: int, tq_per_bit: int) -> int | None: + brp: float = f_osc / (2.0 * bitrate * tq_per_bit) + if brp.is_integer(): + return int(brp) + else: + return None + +def is_valid_tq_per_bit(f_osc: int, bitrate: int, tq_per_bit: int) -> bool: + brp = prescaler(f_osc, bitrate, tq_per_bit) + return (brp is not None) and (brp <= MAX_BRP) + +def valid_tqs_per_bit(f_osc: int, bitrate: int) -> list[int]: + return list(filter( + lambda tq_per_bit: is_valid_tq_per_bit(f_osc, bitrate, tq_per_bit), + range(MIN_TQ_PER_BIT, MAX_TQ_PER_BIT+1))) + +def prop_plus_ps1(tq_per_bit: int, samplepoint: float) -> float: + sync: int = 1 + return samplepoint * tq_per_bit - sync + +def prop_ps1(tq_per_bit, samplepoint: float) -> (int, int): + sync: int = 1 + prop_plus_ps1: float = samplepoint * tq_per_bit - sync + + prop: int = floor(prop_plus_ps1 / 2) + ps1: int = ceil(prop_plus_ps1 / 2) + return (prop, ps1) + +def ps2(tq_per_bit: int, prop: int, ps1: int) -> int: + sync: int = 1 + return tq_per_bit - sync - prop - ps1 + +def parse_int(s: str) -> int: + n: float = float(s) + if n.is_integer(): + return int(n) + else: + raise argparse.ArgumentTypeError(f"invalid int: {s}") + +def unzip(lst: list[tuple[any, any]]) -> (list[any], list[any]): + return zip(*lst) + +def fmt_freq(f: int) -> str: + if f > 1e6: + return f"{f/1e6}MHz" + elif f > 1e3: + return f"{f/1e3}kHz" + else: + return f"{f}Hz" + +def fmt_bitrate(br: int) -> str: + if br > 1e6: + return f"{br/1e6}Mbps" + elif br > 1e3: + return f"{br/1e3}kbps" + else: + return f"{br}bps" + +class Bittiming: + sync: int = 1 + + def __init__(self, fosc: int, bitrate: int, prop: int, ps1: int, ps2: int): + self.fosc = fosc + self.bitrate = bitrate + self.prop = prop + self.ps1 = ps1 + self.ps2 = ps2 + + def samplepoint(self) -> float: + return (self.sync + self.prop + self.ps1) / self.tq_per_bit() + + def tq_per_bit(self) -> int: + return self.sync + self.prop + self.ps1 + self.ps2 + + def prescaler(self) -> int: + brp = self.fosc / (2 * self.bitrate * self.tq_per_bit()) + assert brp.is_integer() + return int(brp) + + def max_sjw(self) -> int: + return min(MAX_SJW, self.ps1, self.ps2) + + def cnf1(self) -> int: + return (((self.max_sjw()-1) & 0x3) << 6) | (self.prescaler() & 0x3F) + + def cnf2(self) -> int: + phseg: int = (self.ps1-1) & 0x7 + prseg: int = (self.prop-1) & 0x7 + return CNF2_BTLMODE | (phseg << 3) | prseg + + def cnf3(self) -> int: + return (self.ps2-1) & 0x7 + + def __str__(self) -> str: + return f"fosc={fmt_freq(self.fosc)}, bitrate={fmt_bitrate(self.bitrate)}, Tq/bit={self.tq_per_bit()}, BRP={self.prescaler()}, sync={self.sync}, prop={self.prop}, PS1={self.ps1}, PS2={self.ps2}, SP={self.samplepoint()*100.0:.1f}%, SJW_max={self.max_sjw()}, (CNF0, CNF1, CNF2)=(0x{self.cnf1():02X}, 0x{self.cnf2():02X}, 0x{self.cnf3():02X})" + +if __name__ == "__main__": + argparser = ArgumentParser(description="Calculate CAN bit-timing parameters for the MCP2515") + argparser.add_argument("-fosc", type=parse_int, required=True, help="Oscillator frequency (Hz)") + argparser.add_argument("-bitrate", type=parse_int, required=True, help="Bitrate (bps)") + argparser.add_argument("-samplepoint", type=float, required=True, help="Sample point (%)") + args = argparser.parse_args() + + # Find valid numbers of time quanta per bit + tqs_per_bit: list[int] = valid_tqs_per_bit(args.fosc, args.bitrate) + + # Calculate Propogation Segment and Phase Segment 1 for each Tq/bit value + props: list[int] + ps1s: list[int] + props, ps1s = unzip(map( + lambda tq_per_bit: prop_ps1(tq_per_bit, args.samplepoint/100.0), + tqs_per_bit)) + + # Calculate Phase Segment 2 for each Tq/bit value + ps2s: list[int] = list(map( + lambda tq_per_bit, prop, ps1: ps2(tq_per_bit, prop, ps1), + tqs_per_bit, props, ps1s)) + + # Print valid bit-timings and MCP2515 config register values + bittimings: list[Bittiming] = list(map( + lambda prop, ps1, ps2: Bittiming(args.fosc, args.bitrate, prop, ps1, ps2), + props, ps1s, ps2s)) + for bt in bittimings: + print(bt) |