Skip to content
 
 

Latest commit

 

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nfr-can-library

At its core, this project provides a clean, cross-platform interface for communicating over the CAN communication protocol. This is accomlished by using PIMPL and dependency-injection design principles to minimize the amount of platform-specific code. Provided in this repo is a driver for MCP2515 CAN controller and SPI, GPIO, and clock implementations for ESP32 using Arduino. Also provided are python scripts used to auto-generate library code using a DBC file.

Project Explanation

The CAN interface (CAN_interface.h) describes the functionality that users can interract with when communicating over CAN. The interface defines objects like CAN_Bus, CAN_Message, and CAN_Signal. The bus is essentially the CAN manager. All sending, receiving, and updating of messages happens through the bus. Messages are objects which store related signals and metadata for transmitting information such as message ID, number of used bytes, etc. Messages are not owned by a bus. However, they are managed by one (and only one) bus. Signals store the actual value of interest and define the value's type as well as information on how to decode and encode the value. Signals are used to construct messages.

CAN_Bus objects must be instantiated using a CAN implementation, a derived class of the ICAN abstract class. CAN implementations must implement:

  • bool send(const CAN_Frame& msg)
  • bool recv(CAN_Frame& msg)

Where CAN_Frame is the unit of transmission. Frames are converted to and from messages by the interface. ICAN derived classes are essentially drivers for CAN controllers, and depend on the underlying controller being used.

Depending on the platform which the code is compiled for, implementations for ISpi, IGpio, and IClock abstract classes may be needed to help the driver interact with an external controller. These implementations will be platform-specific. ICAN implementations will take pointers to implementations for each of these abstract classes.

Library Usage

Signals

CAN_Signal objects should be generated using the provided macros:

  • MakeSignal(type, cfg), where type is the type of the value being stored and cfg is a CAN_Signal_config object for the purposes of reducing magic numbers.
  • MakeSignalExp(type, startBit, length, factor, offset) is used for explicit definition of a signal using magic numbers.

When creating a CAN_Signal using these macros, what is really created is a std::shared_ptr to a CAN_Signal object for the purposes of type erasure. Therefore objects generated by the macros should be interacted with using -> notation. Useful function include:

  • T get(), where T is the signal type.
  • void set(T val), where val is the new value.
  • ICAN_Signal_DataBuf toBuf(), which outputs a ICAN_Signal_DataBuf object containing the value converted to raw bytes and the number of bytes used.

Since the macros generate std::shared_ptr, useful type aliases are provided:

  • using CAN_Signal_UINT8 = std::shared_ptr<CAN_Signal<uint8_t>>;
  • CAN_Signal_UINT8 sig = MakeSignalExp(int8_t, 0, 8, 0.1, 0);

Messages

CAN_Message objects should be generated by first using either the TX or RX macro, and then calling the corresponding constructor. Example:

  • RX_CAN_Message(n) rx_msg(bus, id, extended, length, signals...);
  • RX_CAN_Message(n) rx_msg(bus, id, extended, length, callback_func, signals...);
  • TX_CAN_Message(n) tx_msg(bus, id, extended, length, period, timergroup, signals...);

Additionally, RX_can_msg_config and TX_can_msg_config structs are provided to reduce magic numbers in the constructors.

  • RX_CAN_Message(n) rx_msg(rx_cfg, signals...);

Bus

CAN_Bus objects are constructed with an ICAN implementation.

  • CAN_Bus drive_bus(can_impl);

The main function of interest is tick_bus(), which empties any RX buffer held by the CAN implementation and updates all managed messages accordingly.

Auto Generating Code

Signals and messages from a provided DBC file can be auto-generated into a C++ file utilizing a python script. The necessary includes are provided and these signals and messages are encapsulated into a namespace.

Make sure to activate the virtual environment before running the script. The requirements.txt file is provided in scripts/requirements.txt

py can_gen.py [OPTIONS] dbc_file

Options

-h --help                 Print the help text 
-o --output_file          Provide a designated output file. Default is "can_dbc.hpp"
-c --convention           Provide a naming convention. Default is PascalCase. Supports PascalCase, snake_case, and camelCase. 

Requirements

Library

  • C++20 minimum standard.
  • PlatformIO vscode extension.

Code Gen

  • Python 3.14.0
  • pandas
  • cantools

Linking to Other Projects

To link, simply call add_subdirectory(path/to/this/library). This will generate a static library called nfr_canlib which can be linked to other projects.

For example:

target_link_libraries(your_project PRIVATE nfr_canlib)

Credits

  • Daniel Kramer -- danielk125
  • Jalil Hemphill -- jalilhemphill1

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages