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:
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.