Low-level driver
Basic example
Canbus::Canbus::CanbusConfig cfg;
cfg.loopback = true; // Enable loopback to make the peripheral talk to itself
Canbus::Canbus::AutoBitTiming bt;
bt.baud_rate = 500000; // Set baud rate in bits per second
bt.sample_point = 87.5f / 100.0f; // Set sample point (in percentage of the bit length)
// Create a new driver on the CAN1 peripheral
Canbus::Canbus can(CAN1, cfg, bt);
// Add a filter to allow ANY message to be received
Canbus::Mask32FilterBank f2(0, 0, 0, 0, 0, 0, 0);
can.addFilter(f2); // Attention: filters can be added only before calling can.init()
// Finally initialize the bus, allowing the peripheral to start communicating
can.init();
// Send a new packet
CanPacket p;
p.id = 12345; // Message identifier (up to 29 bit if ext==true, 11 if not)
p.ext = true; // Set to true because we want an extended identifier message
p.length = 1; // Length of the payload
p.data[0] = 69420;
can.send(p); // Finally send the packet on the physical bus
// Since we enabled loopback, we should receive back the packet we just sent
Thread::sleep(10);
// Check if we have received a packet
if(!can.getRXBuffer().isEmpty())
{
// Remove it from the buffer for further processing
Canbus::CanRXPacket prx = can.getRXBuffer().pop();
printf("Received new packet! Payload: %d\n", prx.packet.data[0]);
}
Usage
- Create a canbus configuration by setting the fields of the
CanbusConfig
struct. - Select the desired baud rate and sampling point position by either
a. Setting them in theAutoBitTiming
struct. The bit timing register values are then automaticcally calculated by the driver (see section about bit timing calculation further down) b. Manually setting the value of each register in theBitTiming
struct - Construct a new Canbus instance
- Add the desired filters calling the
Canbus::addFilter
function - Enable the peripheral and start communicating calling
Canbus::init
- Send messages using
Canbus::send
- Reception is done automatically by the driver. Received messages are stored in a circular buffer that can be accessed by calling
Canbus::getRXBuffer
Note: the Canbus driver is not thread safe. calls to Canbus::send
must be done by a single thread (the "sender thread") and consumption of the RX buffer must also be done by single thread (the "receiver thread"). The receiver thread may be different from the sender thread.
Driver Structure
-
Canbus.h
Contains the main driver implementation -
CanInterrupt.h
Interrupt handling routines for receiving and sending messages on the bus -
Filters.h
Filter bank configuration helper classes
Filters
Canbus filters are used to filter incoming messages and redirecting them to one of the two receive FIFOs.
Each filter can check the content of the arbitration field of each canbus frame (ID + Extended ID, IDE bit, RTR bit). If a frame arbitration field is matched by a filter, the frame is put in the receive FIFO associated with that filter. If a frame does not match any filter, it is automatically discarded by the can peripheral.
There are two types of filters available:
- ID Mask filters
They are composed of two registers: the ID register and the mask register: bits set to 1 in the mask register signal that the corresponding bit in the arbitration field must exactly match the bit in the ID register; bits set 0 in the mask register signal that the corresponding bit in the arbitration field is "don't care" and is thus matched regardless of its value. - ID Match filters
They are composed of just one register containing the desired arbitration field value. A frame is matched by the filter only if its arbitration field matches exactly the filter ID.
Each filter is stored in a "Filter Bank", which is composed of two 32 bit registers. Because of this, each filter bank can store:
- 1 32 bit ID Mask filter
- 2 32 bit ID Match filters
- 2 16 bit ID Mask filters
- 4 16 bit ID Match filters
! See the Reference Manual for more detailed description of the bxCan peripheral filters.
Filter banks are implemented in software with the classes in the Filters.h
file.
Bit timing register calculation appendix
The objective is, given the desired bus baud rate and position of the sample point, to find the optimal configuration of the CAN_BTR
register that minimizes the difference between the actual baud rate and sample point vs the target ones.
The canbus bit timing is specified with 4 bit fields in the CAN_BTR
register:
-
BRP
Baud Rate Prescaler:f_{q} = f_{APB}/(BRP + 1)
from this, the duration of a time quanta ist_q = 1 / f_{q}
-
TS1
Bit segment 1 duration -
TS2
Bit segment 2 duration SJW
The duration of a single bit is subdivided in 3 segments:
- SYNC_SEG, always 1 time quanta long:
t_{sync} = t_q
- BS1 (Bit segment 1):
t_{BS1} = t_q (TS1 + 1)
- BS2 (Bit segmetn 2):
t_{BS2} = t_q (TS2 + 1)
thus, the duration of a bit is:
t_{bit} = t_q(1 + (TS1 + 1) + (TS2 + 1)) = t_q N = \frac{N}{f_{q}}
The sample point is located at the end of the BS1, that means that the sample point position in percentage of the bit length is:
SP_\% = (t_{sync} + t_{BS1}) / (t_{sync} + t_{BS1} + t_{BS2})
Algorithm
Objective: minimize the cost function over N
\min_{N}\left(f(N)\right) = \min_{N}\left(w_{BR}\frac{t_{bit}(N) - \overline{t_{bit}}}{\overline{t_{bit}}} + w_{SP}\frac{SP_\%(N)- \overline{SP_\%}}{\overline{SP_\%}}\right)
where
-
\overline{t_{bit}} = 1/(\overline{f_{baud}})
is the desired bit duration (obtained from the desired baud rate) -
\overline{SP_\%}
is the desired sample point position -
w_{BR}
andw_{SP}
are weights for the baud rate error and sample point error, respectively. The weight of the baud rate error is usually much higher, as even small errors in the baud rate can be affect the reliability of the Canbus.
Given the short range of values of N
, the function is minimized by iterating over all the possible values:
For each N
in the range [1, 25]
- Find the optimal configuration of the
BRP
bits:
Nt_q = \overline{t_{bit}}\\
N\frac{BRP+1}{f_{APB}} = \overline{t_{bit}}\\
BRP_{temp} = \overline{t_{bit}}\frac{f_{APB}}{N}-1\\
BRP = max\left(min\left(round(BRP_{temp}), 1024\right), 1\right}
- Find the optimal value of
TS1
andTS2
given the desired sample point\overline{SP_\%}
TS1_{temp} = \overline{SP_\%} N - 1\\
TS1 = max\left(min(round(TS1_{temp}), min(16, N-2)), 1\right)\\
TS2 = N - TS1 - 1
Note: the max/min/round functions in the previous equation are required in order to assure that TS1
is an integer number in the range [1, 16] or [1, N-2] if N < 18.
The actual sample point is then given by
SP_\% = \frac{1 + TS1}{1 + TS1 + TS2}
- Evaluate the cost function
f(N)
, if its values is the smallest yet encountered, store the values ofBRP
,TS1
,TS2
,N
calculated in this iteration.
At the end of the algorithm we then obtain BRP
, TS1
, TS2
that minimize f(N)
globally.