rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
link_can.h File Reference

The page protocol over CAN (Controller Area Network). More...

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "link_msg.h"

Go to the source code of this file.

Data Structures

 One CAN frame: its identifier and up to 8 payload bytes. More...

Macros

#define LINK_CAN_PRIO_SHIFT   26
 The 29-bit extended identifier, most significant field first:
#define LINK_CAN_OP_SHIFT   22
#define LINK_CAN_PAGE_SHIFT   14
#define LINK_CAN_OFFSET_SHIFT   6
#define LINK_CAN_COUNT_SHIFT   0
#define LINK_CAN_PRIO_MASK   0x7u
 Masks of the fields of the 29-bit CAN identifier, after shifting.
#define LINK_CAN_OP_MASK   0xFu
#define LINK_CAN_PAGE_MASK   0xFFu
#define LINK_CAN_OFFSET_MASK   0xFFu
#define LINK_CAN_COUNT_MASK   0x3Fu
#define LINK_CAN_REGS_PER_FRAME   4
 Registers that fit one 8-byte payload.
#define LINK_CAN_MAX_FRAMES   (LINK_MAX_REGS / LINK_CAN_REGS_PER_FRAME)
 Frames a full page takes: 32 registers, four to a frame.

Enumerations

enum  link_can_prio_t { LINK_CAN_PRIO_CONTROL = 0 , LINK_CAN_PRIO_NORMAL = 1 , LINK_CAN_PRIO_BULK = 2 }
 Arbitration classes. More...

Functions

link_can_prio_t link_can_priority (uint8_t page, uint8_t op)
 The arbitration class a page and op belong to.
uint32_t link_can_id (link_can_prio_t prio, uint8_t op, uint8_t page, uint8_t offset, uint8_t count)
 Build a 29-bit identifier from its fields.
void link_can_id_split (uint32_t id, link_can_prio_t *prio, uint8_t *op, uint8_t *page, uint8_t *offset, uint8_t *count)
 Split an identifier back into its fields.
size_t link_can_encode (const link_msg_t *msg, link_can_frame_t *out, size_t cap)
 Render a message as one or more CAN frames.
bool link_can_decode (const link_can_frame_t *f, link_msg_t *out)
 Turn one received frame back into a message carrying up to four registers.
bool link_can_decode_counted (const link_can_frame_t *f, link_msg_t *out, uint32_t *refused)
 link_can_decode(), and a count of the frames it refuses.

Detailed Description

The page protocol over CAN (Controller Area Network).

link_msg_t (an op, a page, an offset, a count and up to a page of registers) carries no transport. This file maps it onto CAN frames.

Three properties of the mapping:

  1. A request has no payload. A read is entirely described by its identifier, so polling costs one zero-byte frame.
  2. Every frame is self-describing, so there is no reassembly in the transport. Each frame carries its own offset and count; a reply split across four frames is four independent messages, a dropped frame costs one register range rather than a transfer, and no timer waits for a continuation.
  3. There is no frame CRC (cyclic redundancy check) in the payload. CAN has a 15-bit CRC, an acknowledge slot and automatic retransmission in silicon; another two bytes would spend a quarter of an 8-byte payload on the same check. End-to-end integrity over something larger than a page, such as a firmware image, belongs to that transfer.

CAN arbitrates by identifier, lowest wins, so priority is a property of the address: a write to the control page wins arbitration against telemetry already in flight, on the wire, with no software involved at either end.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ LINK_CAN_PRIO_SHIFT

#define LINK_CAN_PRIO_SHIFT   26

The 29-bit extended identifier, most significant field first:

28..26  priority   3 bits   lower wins arbitration
25..22  op         4 bits   a link_op_t
21..14  page       8 bits   the page map
13..6   offset     8 bits   first register this frame carries
 5..0   count      6 bits   registers this frame is about, 0..32

Exactly 29 bits: the whole extended identifier.

Enumeration Type Documentation

◆ link_can_prio_t

Arbitration classes.

Three: stopping the bench beats reading it, and reading it beats moving a firmware image.

Enumerator
LINK_CAN_PRIO_CONTROL 

anything that changes what the bench does

LINK_CAN_PRIO_NORMAL 

telemetry, status, identity

LINK_CAN_PRIO_BULK 

transfers that must never delay a stop

Function Documentation

◆ link_can_decode()

bool link_can_decode ( const link_can_frame_t * f,
link_msg_t * out )

Turn one received frame back into a message carrying up to four registers.

False if the identifier describes something impossible: an unknown op, a count past the end of a page, a payload that disagrees with the count. The layer above accumulates the parts into the transfer it asked for, because only it knows what it asked.

Parameters
fThe received frame.
outReceives the message.
Returns
true when the frame is a valid message, else false.

◆ link_can_decode_counted()

bool link_can_decode_counted ( const link_can_frame_t * f,
link_msg_t * out,
uint32_t * refused )

link_can_decode(), and a count of the frames it refuses.

A refused frame adds 1 to refused, modulo 2^32; NULL counts nothing. A NULL frame or message is not a frame received and is not counted. The IOmcu publishes the count on the STATUS page (LINK_ST_REFUSED_LO and _HI, protocol 4.21).

Parameters
fThe received frame.
outReceives the message.
refusedRefused-frame counter; may be NULL.
Returns
true when the frame is a valid message, else false.

◆ link_can_encode()

size_t link_can_encode ( const link_msg_t * msg,
link_can_frame_t * out,
size_t cap )

Render a message as one or more CAN frames.

Returns how many were written, or 0 if the message is malformed or would not fit. A READ carries no registers and is always one frame.

Parameters
msgThe message to render.
outReceives the frames.
capNumber of frames out can hold.
Returns
The number of frames written, or 0.

◆ link_can_id()

uint32_t link_can_id ( link_can_prio_t prio,
uint8_t op,
uint8_t page,
uint8_t offset,
uint8_t count )

Build a 29-bit identifier from its fields.

Each field is masked to its width before it is shifted into place.

Parameters
prioThe arbitration class.
opThe op, a link_op_t; masked to 4 bits.
pageThe page number; masked to 8 bits.
offsetFirst register the frame carries; masked to 8 bits.
countRegisters the frame is about; masked to 6 bits, 0..63.
Returns
The 29-bit identifier.

◆ link_can_id_split()

void link_can_id_split ( uint32_t id,
link_can_prio_t * prio,
uint8_t * op,
uint8_t * page,
uint8_t * offset,
uint8_t * count )

Split an identifier back into its fields.

Any pointer may be NULL.

Parameters
idThe 29-bit identifier.
prioReceives the arbitration class; may be NULL.
opReceives the op; may be NULL.
pageReceives the page number; may be NULL.
offsetReceives the first register; may be NULL.
countReceives the register count; may be NULL.

◆ link_can_priority()

link_can_prio_t link_can_priority ( uint8_t page,
uint8_t op )

The arbitration class a page and op belong to.

Derived rather than chosen per call site, so a new page cannot be added at the wrong priority. The control, limits, failsafe and supply pages stop a bench – the supply's OFF switches off a powered load – and everything that touches them outranks everything else, their own acknowledgements included.

Parameters
pageThe page number.
opThe op, a link_op_t.
Returns
LINK_CAN_PRIO_CONTROL for the control, failsafe, limits and supply pages; LINK_CAN_PRIO_BULK for the update pages; else LINK_CAN_PRIO_NORMAL.