CAN Module - smartel99/NilaiTFO GitHub Wiki

Contents


Initialization

To instantiate a CAN module in your application, you must first configure it in the CubeMX software. Don't forget to call the MX_CANx_Init function before instantiating the CAN module!

To instantiate the module in your application:

void MyApplication::InitializeModules()
{
  AddModule(new CanModule(&hcan1, "can1");
}

The constructor of CanModule takes a pointer to a CAN_HandleTypeDef created by the Cube and a string that is used to identify the module.

To simplify access to this new instance, you can create a macro that will get that instance in the application:

#define CAN1_MODULE()    static_cast<CanModule*>(MyApplication::GetModule("can1")

This macro allows you to use the module like this:

CAN1_MODULE->TransmitFrame(0x12345678, { 0, 1, 2, 3, 4, 5, 6, 7 });

Configuration

The CAN module is extremely versatile in its configuration, allowing individual callbacks for each different interrupts, as well as control on these interrupts, as well as filter configuration.

Filters

NOTE: On the STM32F4 family, the CAN1 peripheral shares the same filter banks as the CAN2 peripheral!

To assign an acceptance filter to the CAN module, a few parameters needs to be provided. All of these are conveniently regrouped into a single data structure, CAN::FilterConfiguration. This structure contains the following fields:

filterId

The filterId member is composed of a union of sub-members that will be interpreted differently depending on the value of the scale member.

scale == CAN::FilterScale::Scale32bit

In this configuration, filterId represents a 32-bits value that will be used to filter the frames received by the peripheral. This member can be acceded with filterId.fullId.

scale == CAN::FilterScale::Scale16bit

In this configuration, filterId represents two 16-bits values, filterId.idLow and filterId.idHigh, both of which will be used to filter the frames received by the peripheral.

maskId

The maskId member is composed of a union of sub-members that will be interpreted differently depending on the value of the mode and the scale members.

mode == CAN::FilterMode::IdMask and scale == CAN::FilterScale::Scale32bit

In this configuration, maskId.fullId represents a 32-bits value that will be used to mask the received frames before applying filterId.fullId.

mode == CAN::FilterMode::IdList and scale == CAN::FilterScale::Scale32bit

With this setting, maskId.fullId represents a second 32-bits value used to filter received frames.

mode == CAN::FilterMode::IdMask and scale == CAN::FilterScale::Scale16bit

In this configuration, maskId represents two 16-bits values, maskId.maskIdLow and maskId.maskIdHigh. These two masks will be used to mask filterId.idLow in the case of maskId.maskIdLow and filterId.idHigh in the case of maskId.maskIdHigh.

mode == CAN::FilterMode::IdList and scale == CAN::FilterScale::Scale16bit

In this configuration, maskId represents two 16-bits values, maskId.maskIdLow and maskId.maskIdHigh. These two values will be used as filters during the reception of frames by the CAN peripheral.

fifo

The fifo member identifies the FIFO buffer on which the filter takes effect. It can be set to the following values:

  • CAN::FilterFifoAssignation::Fifo0 for FIFO 0
  • CAN::FilterFifoAssignation::Fifo1 for FIFO 1

bank

The bank member represents the filter's slot. This must be a value between 0 and 27, and must not currently be used by another filter. Setting this member to a bank that is already in used will overwrite its content with the new filter.

mode

The mode member represents the filter's mode. This member can take one of two values:

  • CAN::FilterMode::IdMask
  • CAN::FilterMode::IdList
CAN::FilterMode::IdMask

In mask mode, the maskId is used to specify which bits of the identifier are handled as "must match" or as "don't care" with the filterId.

CAN::FilterMode::IdList

In identifier list mode, the maskId is used in the same way the filterId member is used. Thus instead of defining an identifier and a mask, two identifiers are specified, doubling the number of single identifiers. All bits of the incoming identifier must match the bits specified in the filter registers.

scale

The scale member represents... The filter's scale. This can be either CAN::FilterScale::Scale32bit or CAN::FilterScale::Scale16bit.

CAN::FilterScale::Scale32bit

In 32-bit mode, the filter and the mask encapsulates the standard ID (STDID), the extended ID (EXTID) the IDE bit and the RTR bit in the following order:

  • filterId.fullId and maskId.fullId bit [31..21] -> STDID
  • filterId.fullId and maskId.fullId bit [20..3] -> EXTID
  • filterId.fullId and maskId.fullId bit 2 -> IDE
  • filterId.fullId and maskId.fullId bit 1 -> RTR
  • filterId.fullId and maskId.fullId bit 0 -> Unused
CAN::FilterScale::Scale16bit

In 16-bit mode, the filter and the mask encapsulates the standard ID (STDID), the RTR and IDE bit and bits 17 through 15 of the extended ID (EXTID) in the following order:

  • filterId.fullId and maskId.fullId bit [15..5] -> STDID
  • filterId.fullId and maskId.fullId bit 4 -> RTR
  • filterId.fullId and maskId.fullId bit 3 -> IDE
  • filterId.fullId and maskId.fullId bit [2..0] -> EXTID[17..15]

activate

The activate member simply tells the activation state of the filter. Set it to CAN::FilterEnable::Enable to enable the filter, or to CAN::FilterEnable::Disable to disable it.

Interrupts

The CAN peripheral offers many interrupts, which the CanModule has individual control on each one of them.

CAN::Irq

CAN::Irq::TxMailboxEmpty

Triggered whenever a Transmission mailbox becomes empty.

CAN::Irq::Fifo0MessagePending

Reception of a new message in FIFO 0 and the FIFO 0 is not full.

CAN::Irq::Fifo0Full

FIFO 0 is full.

CAN::Irq::Fifo0Overrun

Reception of a new message in FIFO 0 but there is no room to add it without dropping an un-read message.

CAN::Irq::Fifo1MessagePending

Reception of a new message in FIFO 1 and it is not full.

CAN::Irq::Fifo1Full

FIFO 1 is full.

CAN::Irq::Fifo1Overrun

Reception of a new message in FIFO 1 but there is no room to add it without dropping an un-read message.

CAN::Irq::Wakeup

This interrupt is triggered when a SOF bit has been detected while the CAN peripheral was in Sleep mode.

CAN::Irq::SleepAck

This interrupt is triggered when the CAN peripheral enters Sleep mode.

CAN::Irq::ErrorWarning

This interrupt is triggered when the warning limit has been reached (Receive Error Counter of Transmit Error Counter >= 96).

CAN::Irq::ErrorPassive

This interrupt is triggered when the Error Passive limit has been reached (Receive Error Counter or Transmit Error Counter > 127).

CAN::Irq::BusOffError

This interrupt is triggered when the CAN peripheral enters the bus-off state. This state is entered when the Transmit Error Counter overflows (More than 255 errors).

CAN::Irq::LastErrorCode

This interrupt is triggered when an error is detected on the CAN bus. The specific error can be known by reading bits [6..4] of the CAN_ESR register.

Value of CAN_ESR[6..4] Error
000 No Error
001 Stuffing Error
010 Form Error
011 Acknowledgment Error
100 Bit Recessive Error
101 Bit Dominant Error
110 CRC Error
111 Unused
CAN::Irq::ErrorStatus

This interrupt is triggered whenever CAN::Irq::Wakeup, CAN::Irq::SleepAck, CAN::Irq::ErrorWarning, CAN::Irq::ErrorPassive, CAN::Irq::BusOffError and CAN::Irq::LastErrorCode occurs.

Enabling an Interrupt

To enable an interrupt, use the EnableInterrupt method with the desired CAN::Irq. To enable more than one interrupt at once, use the EnableInterrupts method, which takes a vector of CAN::Irq.

// Enabling a single interrupt:
CAN1_MODULE->EnableInterrupt(CAN::Irq::Fifo0MessagePending);

// Enabling multiple interrupts at once:
CAN1_MODULE->EnableInterrupts({ CAN::Irq::Fifo0MessagePending, CAN::Irq::Fifo1MessagePending, CAN::Irq::ErrorStatus });

Disabling an Interrupt

To disable an interrupt, use the DisableInterrupt method with the desired CAN::Irq. To disable more than one interrupt at once, use the DisableInterrupts method, which takes a vector of CAN::Irq.

// Disabling a single interrupt:
CAN1_MODULE->DisableInterrupt(CAN::Irq::Fifo0MessagePending);

// Disabling multiple interrupts at once:
CAN1_MODULE->DisableInterrupts({ CAN::Irq::Fifo0MessagePending, CAN::Irq::Fifo1MessagePending, CAN::Irq::ErrorStatus });

Callbacks

It is possible to assign a callback function to each one of the interrupts. This function will be called each time that the interrupt bound to it occurs. The interrupts needs to be enabled in order for the function to be called!

Assigning a Callback to an Interrupt

To bind a function to a specific interrupt, use the SetCallback method, which takes the desired interrupt (CAN::Irq) and a function pointer (std::function<void()>).

void MyFunc()
{
  // do stuff here
}

void MyApplication::InitializeModules()
{
  AddModule(new CanModule(&hcan1, "can1");
  // MyFunc will be called each time a message is received in the FIFO 0.
  CAN1_MODULE->SetCallback(CAN::Irq::Fifo0MessagePending, &MyFunc);

  // A lambda can also be used!
  // Prints an error message each time an error occurs in the CAN peripheral.
  CAN1_MODULE->SetCallback(CAN::Irq::ErrorStatus, [](){ ERROR_LOG("An error occurred with the CAN module!"); });
}

Removing a Callback from an Interrupt

To unbind a callback from an interrupt, use the ClearCallback method with the desired CAN::Irq.

// Don't print an error message anymore.
CAN1_MODULE->ClearCallback(CAN::Irq::ErrorStatus);

Usage

Transmitting a Frame

To transmit a frame using the CAN module, use the TransmitFrame method. This method takes as parameters the address as a uint32_t and optionally a std::vector<uint8_t>. If one is provided, a maximum of 8 bytes of data will be sent. The CAN module automatically detects Standard/Extended ID as well as Remote/Data frames, so no need to configure anything beforehand. In the case where you want the ID to be treated as an Extended ID no matter what it is, simply use the forceExtended parameter.

// Send a remote frame with a standard ID.
CAN1_MODULE->TransmitFrame(0x55);

// Send a data frame with a standard ID.
CAN1_MODULE->TransmitFrame(0x55, { 0, 1, 2, 3 });

// Send a remote frame with an extended ID.
CAN1_MODULE->TransmitFrame(0x12345678);

// Send a data frame with an extended ID.
CAN1_MODULE->TransmitFrame(0x12345678, { 0, 1, 2, 3, 4, 5, 6, 7});

// Send a remote frame with an extended ID.
CAN1_MODULE->TransmitFrame(0x55, {}, false);

Receiving a Frame

You can get the number of frames that have not yet been read by using the GetNumberOfAvailableFrames method, then use the ReceiveFrame method to receive one of those frames.

Note: Calling the ReceiveFrame method removes the frame from the module's buffer.

// If there is at least one frame available:
if(CAN1_MODULE->GetNumberOfAvailableFrames() > 0)
{
  // Read it.
  CAN::Frame frame = CAN1_MODULE->ReceiveFrame();
  // Do stuff with it.
  uint32_t id = frame.frame.ExtId;
}
⚠️ **GitHub.com Fallback** ⚠️