Skip to content

Commit 8a089ba

Browse files
authored
📝 Filter adaptors to canbus (#66)
1 parent 7a78da6 commit 8a089ba

1 file changed

Lines changed: 120 additions & 50 deletions

File tree

mkdocs/education/canbus.md

Lines changed: 120 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,16 @@
33
!!! warning
44
This document describes the CAN for libhal 5.0.0 which is not available yet.
55

6-
Welcome to the libhal controller area network (CAN) tutorial. CAN BUS is used as a reliable broadcast communication
7-
8-
are used to sample analog voltage
9-
signals and convert them into a number that can be used by controllers to sense
10-
the world .
6+
Welcome to the libhal controller area network (CAN) tutorial. CAN BUS is used
7+
as a reliable broadcast communication.
118

129
## Learning about CAN BUS
1310

1411
To learn more about CAN BUS we recommend these online resources:
1512

1613
- [🎥 CAN Bus: Serial Communication - How It Works?](https://www.youtube.com/watch?v=JZSCzRT9TTo)
17-
11m 24s video going over the basics of CAN BUS. It does not go over message ID arbitration which is quite important to understand with CAN BUS, but not necessary to use CAN APIs.
14+
11m 24s video going over the basics of CAN BUS. It does not go over message
15+
ID arbitration which is quite important to understand with CAN BUS, but not necessary to use CAN APIs.
1816
- [📄 Introduction to the Controller Area Network (CAN) by Texas Instruments](https://www.ti.com/lit/an/sloa101b/sloa101b.pdf):
1917
A fully featured document going over most of the important aspects of CAN
2018
bus. You'll learn everything you need to know about CAN with this document.
@@ -47,45 +45,21 @@ various capabilities of hardware. For example, the number of filters of each
4745
type can vary wildly between different devices, but the predominately fit in
4846
these three categories.
4947

50-
### Using `hal::can_transceiver`
51-
52-
For now, lets set aside how we acquire a `hal::can_transceiver` and consider
53-
what you can do once you have one.
54-
55-
```C++
56-
u32 baud_rate() = 0;
57-
```
58-
59-
This function returns the baud rate in hertz of the CAN BUS. The baud rate represents the communication rate. Common baud rates for CAN BUS are:
48+
### The `hal::can_message`
6049

61-
- 100 kHz or Kbit/s
62-
- 125 kHz or Kbit/s
63-
- 250 kHz or Kbit/s
64-
- 500 kHz or Kbit/s
65-
- 800 kHz or Kbit/s
66-
- 1 MHz or Mbit/s
67-
68-
This function exists to ensure that drivers that share a `hal::can_transceiver`
69-
can detect if the baud rate doesn't match a fixed baud rate required by another
70-
device on the bus. CAN BUS driver may provide an out-of-interface function for
71-
setting the baud rate, but this interface does not allow such control.
50+
The `hal::can_message` struct contains all of the information stored within a
51+
typical message. The object `hal::can_message` represents a standard CAN
52+
message in libhal. It provides all of the information you'd need in a typical
53+
CAN message. It is used for sending and receiving messages on the can bus. Note
54+
that the remote request and extended fields utilize bits 29th and 30th,
55+
respectively, within the of the 32-bit `id_flags` field. The 31st bit is
56+
reserved for now and must remain 0.
7257

73-
When a can driver is constructed it is passed a baud rate it should set itself
74-
to. If the baud rate cannot be achieved the constructor will throw
75-
`hal::argument_out_of_domain`. Note when setting up a CAN BUS network that
76-
every device on the bus must have the same baud rate. The baud rate is not
77-
changeable via the `hal::can_transceiver`.
58+
The can message and its APIs are defined below.
7859

7960
```C++
80-
/**
81-
* @brief A standard CAN message
82-
*
83-
*/
8461
struct can_message
8562
{
86-
static constexpr u32 id_mask = (1 << 29) - 1;
87-
static constexpr u32 remote_request_mask = (1 << 29);
88-
static constexpr u32 extended_mask = (1 << 30);
8963
/**
9064
* @brief Memory containing the ID and remote request and extended flags
9165
*
@@ -153,11 +127,35 @@ struct can_message
153127
};
154128
```
155129

156-
`hal::can_message` presents a standard CAN message in libhal. It provides all
157-
of the information you'd need in a typical CAN message. Note that the remote
158-
request and extended fields utilize bits 29th and 30th, respectively, within
159-
the of the 32-bit `id_flags` field. The 31st bit is reserved for now and must
160-
remain 0.
130+
### Using `hal::can_transceiver`
131+
132+
For now, lets set aside how we acquire a `hal::can_transceiver` and consider
133+
what you can do once you have one.
134+
135+
```C++
136+
u32 baud_rate() = 0;
137+
```
138+
139+
This function returns the baud rate in hertz of the CAN BUS. The baud rate
140+
represents the communication rate. Common baud rates for CAN BUS are:
141+
142+
- 100 kHz or Kbit/s
143+
- 125 kHz or Kbit/s
144+
- 250 kHz or Kbit/s
145+
- 500 kHz or Kbit/s
146+
- 800 kHz or Kbit/s
147+
- 1 MHz or Mbit/s
148+
149+
This function exists to ensure that drivers that share a `hal::can_transceiver`
150+
can detect if the baud rate doesn't match a fixed baud rate required by another
151+
device on the bus. CAN BUS driver may provide an out-of-interface function for
152+
setting the baud rate, but this interface does not allow such control.
153+
154+
When a can driver is constructed it is passed a baud rate it should set itself
155+
to. If the baud rate cannot be achieved the constructor will throw
156+
`hal::argument_out_of_domain`. Note when setting up a CAN BUS network that
157+
every device on the bus must have the same baud rate. The baud rate is not
158+
changeable via the `hal::can_transceiver`.
161159

162160
```C++
163161
void send(can_message const& p_message) = 0;
@@ -174,7 +172,13 @@ std::span<can_message const> receive_buffer() = 0;
174172
std::size_t receive_cursor() = 0;
175173
```
176174

177-
`hal::can_transceiver` are mandated to hold a buffer can messages received over the bus. The user is allowed, at object construction to provide buffer memory for the driver. That buffer is then exposed by the `receive_buffer()` API to allow applications and drivers to scan it and to find messages meant for them. The buffer returned from `receive_buffer()` updated as a circular buffer where the `receive_cursor()` API indicates where the driver's write cursor position is located. Any value returned from `receive_cursor()` will always work like so:
175+
`hal::can_transceiver` are mandated to hold a buffer can messages received over
176+
the bus. The user is allowed, at object construction to provide buffer memory
177+
for the driver. That buffer is then exposed by the `receive_buffer()` API to
178+
allow applications and drivers to scan it and to find messages meant for them.
179+
The buffer returned from `receive_buffer()` updated as a circular buffer where
180+
the `receive_cursor()` API indicates where the driver's write cursor position
181+
is located. Any value returned from `receive_cursor()` will always work like so:
178182

179183
```C++
180184
// `can.receive_cursor()` always returns a value between 0 and
@@ -185,7 +189,8 @@ can.receive_buffer()[can.receive_cursor()];
185189

186190
See [⛓️‍💥how interface circular buffers in libhal work](.).
187191

188-
Using the circular buffer APIs directly can be tedious and error prone, so we provide some utility classes in `libhal-util/can.hpp`.
192+
Using the circular buffer APIs directly can be tedious and error prone, so we
193+
provide some utility classes in `libhal-util/can.hpp`.
189194

190195
#### `hal::can_message_finder`
191196

@@ -248,7 +253,8 @@ if (new_messages) {
248253
}
249254
```
250255

251-
The lifetime of the `hal::can_message_reader` is bound to the `hal::can_transceiver` passed to it and must not exceed the lifetime of that
256+
The lifetime of the `hal::can_message_reader` is bound to the
257+
`hal::can_transceiver` passed to it and must not exceed the lifetime of that
252258
`hal::can_transceiver`.
253259

254260
### CAN BUS device manager
@@ -262,14 +268,17 @@ namespace hal::stm32f1 {
262268
class can {
263269
public:
264270
// Constructor
265-
can(can_pins p_pins = can_pins::pa11_pa12, hal::u32 baud_rate = 100_kHz);
271+
can(std::span<hal::can_message> p_receive_buffer,
272+
can_pins p_pins = can_pins::pa11_pa12,
273+
hal::u32 baud_rate = 100_kHz);
266274
// The rest...
267275
}
268276
}
269277

270278
// Constructing a can object using pins PB9 & PB8 on the stm32f103c8 and
271279
// setting the bus baud rate to 1MHz.
272-
hal::stm32f1::can can(hal::stm32f1::can_pins::pb9_pb8, 1_MHz);
280+
std::array<hal::can_message, 16> receive_buffer;
281+
hal::stm32f1::can can(receive_buffer, hal::stm32f1::can_pins::pb9_pb8, 1_MHz);
273282

274283
// Acquiring resources from device manager...
275284

@@ -383,7 +392,8 @@ In general, filters do not need to be captured. Filters can be set at driver
383392
construction and then given back to the caller. Capturing a filter is only
384393
necessary if the filter will have its ID modified at runtime.
385394
386-
The example above assumes that the driver only ever needs to write can messages and never needs to receive them.
395+
The example above assumes that the driver only ever needs to write can messages
396+
and never needs to receive them.
387397
388398
### Interfaces to Avoid
389399
@@ -442,6 +452,66 @@ private:
442452

443453
The transceiver can be accessed using the `transceiver()` API.
444454

455+
### Selecting the right filter type
456+
457+
1. Select `hal::can_identifier_filter` if the device driver only needs to
458+
filter a single standard message ID.
459+
2. Select `hal::can_extended_identifier_filter` if the device driver only needs
460+
to filter a single extended message ID.
461+
3. Select `hal::can_range_filter` if the device driver expects to see messages
462+
in a range of standard message IDs.
463+
4. Select `hal::can_extended_range_filter` if the device driver expects to see
464+
messages in a range of standard message IDs.
465+
5. Select `hal::can_mask_filter` if the device driver expects to see messages
466+
that fit a standard identifier with some bits that can change. In general,
467+
this filter type isn't particular useful for device drivers.
468+
6. Select `hal::can_extended_mask_filter` if the device driver expects to see
469+
messages that fit an extended identifier with some bits that can change. In
470+
general, this filter type isn't particular useful for device drivers.
471+
472+
### Filter adaptors
473+
474+
Can peripherals may not provide all of the filters needed by device drivers or
475+
application. In these cases, we can use an adaptor from `libhal-util` to
476+
convert one filter to another.
477+
478+
### Range to identifier filter
479+
480+
libhal provides `hal::range_to_identifier_can_adaptor` which can take a
481+
`hal::can_range_filter` and generate multiple `hal::can_identifier_filters`.
482+
This can be beneficial when the set of IDs is contiguous OR close enough
483+
together.
484+
485+
```C++
486+
hal::can_range_filter& range_filter = /* ... */;
487+
hal::range_to_identifier_can_adaptor id_generator(range_filter);
488+
auto id_filter_1 = id_generator.create();
489+
auto id_filter_2 = id_generator.create();
490+
auto id_filter_3 = id_generator.create();
491+
492+
id_filter_1.allow(0x111); // range 0x111 ↔ 0x111
493+
id_filter_2.allow(0x112); // range 0x111 ↔ 0x112
494+
id_filter_3.allow(0x117); // range 0x111 ↔ 0x117
495+
id_filter_3.allow(0x110); // range 0x110 ↔ 0x115
496+
id_filter_3.allow(0x116); // range 0x110 ↔ 0x115 (no change)
497+
```
498+
499+
Each id generated links back to the `hal::range_to_identifier_can_adaptor`
500+
object. They are bound by its lifetime. Each time a filter calls `allow()`, it
501+
expands the range to fit the max and min values passed previously.
502+
503+
The filter is not as useful if the range between IDs is large and there are
504+
messages within that ID range.
505+
506+
There exist an equivalent for extended IDs.
507+
508+
### Mask to identifier filter
509+
510+
libhal provides `hal::mask_to_identifier_can_adaptor` which can take a
511+
`hal::can_mask_filter` and generate multiple `hal::can_identifier_filters`.
512+
513+
TBD...
514+
445515
## Making a CAN driver
446516
447517
TBD...

0 commit comments

Comments
 (0)