Files
micropython/extmod/machine_can_port.h
T
Angus Gratton 022570445b extmod,docs: Add generic machine.CAN helpers & docs.
API is different to the original machine.CAN proposal, as numerous
shortcomings were found during initial implementation.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
2026-03-19 17:36:46 +11:00

189 lines
6.8 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2024-2026 Angus Gratton
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_EXTMOD_MACHINE_CAN_PORT_H
#define MICROPY_INCLUDED_EXTMOD_MACHINE_CAN_PORT_H
#include "py/obj.h"
#include "py/objarray.h"
#include "py/runtime.h"
#include "shared/runtime/mpirq.h"
// This header is included into both extmod/machine_can.c and port-specific
// machine_can.c implementations and provides shared (static) function
// declarations to both.
//
// In a MicroPython build including this header from port-specific machine_can.c
// include is a no-op (as the file is included directly into
// extmod/machine_can.c). However, including it anyway means that Language
// Servers and IDEs can correctly analyse the machine_can.c file while the
// developer is writing it.
typedef enum {
MP_CAN_STATE_STOPPED,
MP_CAN_STATE_ACTIVE,
MP_CAN_STATE_WARNING,
MP_CAN_STATE_PASSIVE,
MP_CAN_STATE_BUS_OFF,
} machine_can_state_t;
typedef enum {
MP_CAN_MODE_NORMAL,
MP_CAN_MODE_SLEEP,
MP_CAN_MODE_LOOPBACK,
MP_CAN_MODE_SILENT,
MP_CAN_MODE_SILENT_LOOPBACK,
MP_CAN_MODE_MAX,
} machine_can_mode_t;
// CAN IRQ Flags
// (currently the same for all ports)
#define MP_CAN_IRQ_TX (1 << 0)
#define MP_CAN_IRQ_RX (1 << 1)
#define MP_CAN_IRQ_TX_FAILED (1 << 2)
#define MP_CAN_IRQ_STATE (1 << 3)
// Transmit buffer index is encoded into the irq().flags() response for MP_CAN_IRQ_TX
#define MP_CAN_IRQ_IDX_SHIFT 16
#define MP_CAN_IRQ_IDX_MASK 0xFF
#if MICROPY_HW_ENABLE_FDCAN
#define MP_CAN_MAX_LEN 64
#else
#define MP_CAN_MAX_LEN 8
#endif
struct machine_can_port;
// These values appear in the same order as the result of CAN.get_counters()
typedef struct {
mp_uint_t tec;
mp_uint_t rec;
mp_uint_t num_warning; // Number of "Error Warning" transitions
mp_uint_t num_passive; // Number of "Error Passive" transitions
mp_uint_t num_bus_off; // Number of "Bus-Off" transitions
mp_uint_t tx_pending;
mp_uint_t rx_pending;
mp_uint_t rx_overruns;
} machine_can_counters_t;
typedef struct _machine_can_obj_t {
mp_obj_base_t base;
mp_uint_t can_idx;
// Timing register settings
byte tseg1;
byte tseg2;
byte brp;
byte sjw;
machine_can_mode_t mode;
mp_irq_obj_t *mp_irq_obj;
uint16_t mp_irq_trigger;
mp_uint_t rx_error_flags;
// Assumed some of these counters are updated from different port ISRs, etc. and some
// are updated by calling machine_can_port_update_counters()
machine_can_counters_t counters;
struct machine_can_port *port;
} machine_can_obj_t;
// Indexes for recv result list
typedef enum {
RECV_ARG_ID,
RECV_ARG_DATA,
RECV_ARG_FLAGS,
RECV_ARG_ERRORS,
RECV_ARG_LEN, // Overall length, not an index
} recv_arg_idx_t;
#define CAN_STD_ID_MASK 0x7FF
#define CAN_EXT_ID_MASK 0x1fffffff
// CAN Message Flags
#define CAN_MSG_FLAG_RTR (1 << 0)
#define CAN_MSG_FLAG_EXT_ID (1 << 1)
#define CAN_MSG_FLAG_FD_F (1 << 2)
#define CAN_MSG_FLAG_BRS (1 << 3)
#define CAN_MSG_FLAG_UNORDERED (1 << 4)
// CAN recv() Error Flags
#define CAN_RECV_ERR_FULL (1 << 0)
#define CAN_RECV_ERR_OVERRUN (1 << 1)
#define CAN_RECV_ERR_ESI (1 << 2)
// The port must provide implementations of these low-level CAN functions
static int machine_can_port_f_clock(const machine_can_obj_t *self);
static bool machine_can_port_supports_mode(const machine_can_obj_t *self, machine_can_mode_t mode);
static void machine_can_port_clear_filters(machine_can_obj_t *self);
static mp_uint_t machine_can_port_max_data_len(mp_uint_t flags);
// The extmod layer calls this function in a loop with incrementing filter_idx
// values. It's up to the port how to apply the filters from here, and to raise
// an exception if there are too many.
//
// If the CAN_FILTERS_STD_EXT_SEPARATE flag is set to 1, filter_idx will
// enumerate standard id filters separately to extended id filters (the
// CAN_MSG_FLAG_EXT_ID bit in 'flags' differentiates the type).
static void machine_can_port_set_filter(machine_can_obj_t *self, int filter_idx, mp_uint_t can_id, mp_uint_t mask, mp_uint_t flags);
// Update interrupt configuration based on the new contents of 'self'
static void machine_can_update_irqs(machine_can_obj_t *self);
// Return the irq().flags() result. Calling this function may also update the hardware state machine.
static mp_uint_t machine_can_port_irq_flags(machine_can_obj_t *self);
static void machine_can_port_init(machine_can_obj_t *self);
static void machine_can_port_deinit(machine_can_obj_t *self);
static mp_int_t machine_can_port_send(machine_can_obj_t *self, mp_uint_t id, const byte *data, size_t data_len, mp_uint_t flags);
static bool machine_can_port_cancel_send(machine_can_obj_t *self, mp_uint_t idx);
static bool machine_can_port_recv(machine_can_obj_t *self, void *data, size_t *dlen, mp_uint_t *id, mp_uint_t *flags, mp_uint_t *errors);
static machine_can_state_t machine_can_port_get_state(machine_can_obj_t *self);
static void machine_can_port_restart(machine_can_obj_t *self);
// Updates values in self->counters (which counters are updated by this function versus from ISRs and the like
// is port specific
static void machine_can_port_update_counters(machine_can_obj_t *self);
// Hook for port to fill in the final item of the get_timings() result list with controller-specific values
static mp_obj_t machine_can_port_get_additional_timings(machine_can_obj_t *self, mp_obj_t optional_arg);
// This function is only optionally defined by the port. If macro CAN_PORT_PRINT_FUNCTION is not set
// then a default machine_can_print function will be used.
static void machine_can_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
#endif // MICROPY_INCLUDED_EXTMOD_MACHINE_CAN_PORT_H