CAN Module - smartel99/NilaiTFO GitHub Wiki
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 });
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.
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:
The filterId
member is composed of a union of sub-members that will be interpreted differently depending on the value of the scale
member.
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
.
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.
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.
In this configuration, maskId.fullId
represents a 32-bits value that will be used to mask the received frames before applying filterId.fullId
.
With this setting, maskId.fullId
represents a second 32-bits value used to filter received frames.
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
.
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.
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
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.
The mode
member represents the filter's mode. This member can take one of two values:
CAN::FilterMode::IdMask
CAN::FilterMode::IdList
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
.
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.
The scale
member represents... The filter's scale. This can be either CAN::FilterScale::Scale32bit
or CAN::FilterScale::Scale16bit
.
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
andmaskId.fullId
bit [31..21] -> STDID -
filterId.fullId
andmaskId.fullId
bit [20..3] -> EXTID -
filterId.fullId
andmaskId.fullId
bit 2 -> IDE -
filterId.fullId
andmaskId.fullId
bit 1 -> RTR -
filterId.fullId
andmaskId.fullId
bit 0 -> Unused
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
andmaskId.fullId
bit [15..5] -> STDID -
filterId.fullId
andmaskId.fullId
bit 4 -> RTR -
filterId.fullId
andmaskId.fullId
bit 3 -> IDE -
filterId.fullId
andmaskId.fullId
bit [2..0] -> EXTID[17..15]
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.
The CAN peripheral offers many interrupts, which the CanModule
has individual control on each one of them.
Triggered whenever a Transmission mailbox becomes empty.
Reception of a new message in FIFO 0 and the FIFO 0 is not full.
FIFO 0 is full.
Reception of a new message in FIFO 0 but there is no room to add it without dropping an un-read message.
Reception of a new message in FIFO 1 and it is not full.
FIFO 1 is full.
Reception of a new message in FIFO 1 but there is no room to add it without dropping an un-read message.
This interrupt is triggered when a SOF bit has been detected while the CAN peripheral was in Sleep mode.
This interrupt is triggered when the CAN peripheral enters Sleep mode.
This interrupt is triggered when the warning limit has been reached (Receive Error Counter of Transmit Error Counter >= 96).
This interrupt is triggered when the Error Passive limit has been reached (Receive Error Counter or Transmit Error Counter > 127).
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).
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 |
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.
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 });
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 });
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!
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!"); });
}
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);
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);
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;
}