CAN Wrapper - UWCubeSat/DubSat1 GitHub Wiki

DubSat1 CAN Wrapper and Interface

This package allows communication over the CAN bus on DUBSAT1 in an un-intimidating manner.

Intro

On Huskyast-1, we built our own protocol on top of CAN to help us categorize our packets between our nodes, our radio, and the ground. The protocol manages arbitration, helps with categorizing and filtering, how the data payloads are implemented, adds a c library for easily adding packets, and formalizes how we keep track of packets we intend to send.

We built a C library for interacting with CAN packets in an abstract manner.

Schema

We've built more meaning into the CAN IDs, to encode extra information in the ID field of the packet, as well as structured the data in the payloads of the packets. The documentation for the values used for the CAN packets under this schema is available here: https://bit.ly/2qzM1VX

And the actual database containing the actual frames and signals in our protocol is in teams/cdh/CAN/CANDB-MASTER-DS1.dbc

The description of the schema is below.

IDs

Due to the limited capability of MCP 2515 filters, we must have a mini-protocol on top of the CAN IDs to designate certain bits for different functions in the CAN ids:

ID Bits (Counting from LSB, inclusive) Description
24-28 Priority Bits: These give priorities for packets for the purpose of arbitration.
20-22 Ground Class: This categorizes packets for the purpose of COM1 filtering.
16-19 MSP Category: This categorizes packets for the purpose of MSP filtering.
9-0 Abstract Packet ID: We assign these arbitrarily to packets.
Others Unused

And an example of the ID of a packet being decoded: Decoding a CAN ID

Priority Bits:

CAN uses IDs for arbitration. If two packets are trying to be sent at the same time, the one with a lower id will win arbitration and get sent. The one with the higher ID will wait until the lower ID packet is done transmitting. We use the MSBs of the ID as a way to explicitly class which packets should have priority over other packets. Packets with an MSB of 0 will be accepted by all MSPs. RAHS packets will have an ID starting with 11111. Other priorities are defined frame-wise in the spreadsheet linked at the top.

Ground Class:

These bits are important for COM1. They signify what kind of data packets are for the purpose of downlinking. The "classes" sheet of the schema spreadsheet (linked above) has the most current COM1 classes, their corresponding id numbers and descriptions. At the time of writing there are four classes, gnd_none, gnd_realtime, gnd_health, gnd_wod, and gnd_rahs. Realtime being packets so important they should always be downlinked, health being high importance packets about the state of our satellite, wod being whole-orbit-data, and rahs being downlink packets for the RAHS camera. For instance, Sync2 or Roll-call is a critically important packet be able to listen to at all times, so it may be in the gnd_realtime category and those bits would be set to 001. Packets with this category should be sent to ground immediately. This nomenclature has changed. Stand by for more updates.

MSP Category:

These bits are for the MSP filters. To avoid (mostly ADCS traffic) hogging clock cycles of the MSP, we can put MCP25625 filters on each packet so the MSP only gets interrupted on packets that are important to it. MSPs can subscribe to 4-5 of these categories automatically through the can wrapper.

Abstract Packet ID:

We assign every kind of packet we can send its own id, to prevent CAN ID collisions (which are not good in CAN)

Data fields

The fields follow the Vector CAN dbc file format, and the frames and signals correspond 1:1 to the master DBC file in teams/cdh/CAN/CANDB-MASTER-DS1.dbc

Usage

Code Generation

To generate the c code in these packages, there is a python3 script in teams/cdh/CANExperiments/CANHeaderTest.py The script takes one argument, the DBC file to ingest (for our master DBC File, teams/cdh/CAN/CANDB-MASTER-DS1.dbc), and spits out 3 files in the CodeGenOutput folder in your wokring directory, headerCode, mainCode and macros. Those files can be copied and pasted into the corresponding parts of the CANWrapper code. If this doesn't make sense after looking at the c codegen, ask for help, as doing this incorrectly can break a lot of stuff.

Implementing CANWrap

Sending a packet

    // Create a PacketType struct (PacketType being a CANWrap autogen struct)
    PacketType packetType = {0};
    // Create a CAN Packet (This will contain the actual bits for CAN to send)
    CANPacket packet = {0};
    // Put in an abstract parameter
    packetType.NormalSignedInt = 4;
    // The encode function will convert the abstract parameters into the correct bits to send
    encodePacketType(&packetType,&packet);
    canSendPacket(&packet);

Setting a callback

    // The callback function is a void function that takes a pointer to CANPacket.
    void callbackFunction(CANPacket *packet){
        // Do what you want with the packet.

        // Try to handle the callback in the main loop.

        // If you need to decode a packet from a DBC File you can use the function decodePacketType().
        // More details on that are below.
    }
    // Set the callback function
    setCANPacketRxCallback(callbackFunction);

Receiving a callback.

API

structures:

CANPacket

typedef struct CANPacket {
   uint32_t id; // Actual physical ID of the packet
   uint8_t data[8]; // Data
   uint8_t bufferNum; // Only applicable for Rx, which buffer it landed in
   uint8_t length; // Only applies to sending packets. We don't know how long incoming packets are.
} CANPacket;

PacketType (Autogenerated)

typedef struct PacketType {
    int8_t NormalSignedInt; //  Unit1
    int32_t IntFactorOffset; //  Unit2
    float FloatFactor; // FloatUnit
    uint8_t NormalUint; // m/s
} PacketType;

methods:

Type Method and Description
void canPacketInit: Initializes the CAN controller and transceiver
void canSendPacket: Sends a CANPacket over the CAN Bus.
void setCANPacketRXCallback: Changes the CAN Rx Callback function
void encodePacketType: (Autogen) Encodes a PacketType to CANPacket
void decodePacketType: (Autogen) Decodes a CANPacket to PacketType

canPacketInit

void canWrapInit();

Description:

This method initializes the MCP25625 and sets up the callback api. This is the first method to call to use the CAN Wrapper API.

canSendPacket

void canSendPacket(CANPacket *packet);

Description:

Sends a CANPacket over the CAN Bus.

Parameters:

CANPacket *packet: The CAN Packet to send.

setCANPacketRXCallback

void setCANPacketRxCallback(void (*ReceiveCallbackArg)(CANPacket *packet));

Description:

This method changes the Rx Callback function pointer when you receive a new packet over CAN.

Parameters:

(function pointer): the function that you want called when a packet is received. This function needs to take in a CAN Packet pointer. This is the pointer to the packet that was received.

encodePacketType (Autogenerated)

encodePacketType(PacketType *input, CANPacket* output)

Description:

encodePacketType (replacing PacketType for any DBC packet type) is a function that will convert the abstract signal with values that you understand into the bits that will actually get sent over the CAN bus to be sent. You must initialize a pointer to the CANPacket you want encodePacketType to populate.

Parameters:

PacketType* input: the packet you want to encode of PacketType

CANPacket* output: the CANPacket you want the encode function to encode into.

decodePacketType (Autogenerated)

decodePacketType(CANPacket *input, PacketType* output)

Description:

decodePacketType (replacing PacketType for any DBC packet type) is a function that will convert the representation the bits that will actually get sent over the CAN bus of the signal to values that you would actually understand. You must initialize a pointer to the PacketType you want decodePacketType to populate.

Parameters:

CANPacket* input: the packet you want to decode.

PacketType* output: the packet you want the decode function to decode into.