|
|
# Low-level driver
|
|
|
## Basic example
|
|
|
```cpp
|
|
|
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
|
|
|
1. Create a canbus configuration by setting the fields of the `CanbusConfig` struct.
|
|
|
2. Select the desired baud rate and sampling point position by either
|
|
|
a. Setting them in the `AutoBitTiming` 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 the `BitTiming` struct
|
|
|
3. Construct a new Canbus instance
|
|
|
4. Add the desired filters calling the `Canbus::addFilter` function
|
|
|
5. Enable the peripheral and start communicating calling `Canbus::init`
|
|
|
6. Send messages using `Canbus::send`
|
|
|
7. 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 is $`t_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:
|
|
|
1. SYNC_SEG, always 1 time quanta long: $`t_{sync} = t_q`$
|
|
|
2. BS1 (Bit segment 1): $`t_{BS1} = t_q (TS1 + 1)`$
|
|
|
3. BS2 (Bit segmetn 2): $`t_{BS2} = t_q (TS2 + 1)`$
|
|
|
|
|
|
thus, the duration of a bit is:
|
|
|
```math
|
|
|
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:
|
|
|
```math
|
|
|
SP_\% = (t_{sync} + t_{BS1}) / (t_{sync} + t_{BS1} + t_{BS2})
|
|
|
```
|
|
|
|
|
|
|
|
|
### Algorithm
|
|
|
|
|
|
Objective: minimize the cost function over `N`
|
|
|
```math
|
|
|
\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}`$ and $`w_{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]
|
|
|
|
|
|
1. Find the optimal configuration of the `BRP` bits:
|
|
|
```math
|
|
|
Nt_q = \overline{t_{bit}}\\
|
|
|
N\frac{BRP+1}{f_{APB}} = \overline{t_{bit}}\\
|
|
|
BRP = \overline{t_{bit}}\frac{f_{APB}}{N}-1
|
|
|
```
|
|
|
|
|
|
|
|
|
2. Find the optimal value of `TS1` and `TS2` given the desired sample point $\overline{SP_\%}$
|
|
|
```math
|
|
|
TS1 = \overline{SP_\%} N - 1\\
|
|
|
TS2 = N - TS1 - 1
|
|
|
```
|
|
|
|
|
|
The actual sample point is then given by
|
|
|
```math
|
|
|
SP_\% = \frac{1 + TS1}{1 + TS1 + TS2}
|
|
|
```
|
|
|
|
|
|
3. Evaluate the cost function $`f(N)`$, if its values is the smallest yet encountered, store the values of `BRP`, `TS1`, `TS2`, `N` calculated in this iteration.
|
|
|
|
|
|
At the end of the algorithm we then obtain `BRP`, `TS1`, `TS2` that minimize $`f(N)`$ globally. |