mirror of
https://github.com/Architeuthis-Flux/JumperlessV5.git
synced 2025-09-05 10:47:58 +00:00
288 lines
10 KiB
C
288 lines
10 KiB
C
/*
|
|
* Minimal machine.Pin implementation for Jumperless embed port (RP23xx)
|
|
* Provides basic Pin IN/OUT/OPEN_DRAIN functionality with pull control.
|
|
*
|
|
* Behaviour mirrors the rp2 port where practical, but omits IRQ and
|
|
* board/cpu pin name tables for now. Pins are addressed by integer id.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "py/runtime.h"
|
|
#include "py/mphal.h"
|
|
|
|
|
|
|
|
// No need to include extmod/modmachine.h here
|
|
|
|
#include "hardware/gpio.h"
|
|
|
|
// Fallback Pin protocol definitions if extmod/virtpin.h is not packaged
|
|
#ifndef MP_PIN_READ
|
|
#define MP_PIN_READ (1)
|
|
#define MP_PIN_WRITE (2)
|
|
typedef struct _mp_pin_p_t {
|
|
mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
|
|
} mp_pin_p_t;
|
|
#endif
|
|
|
|
// Pull flags (match rp2 values used by machine.Pin)
|
|
#define JL_GPIO_PULL_UP (1)
|
|
#define JL_GPIO_PULL_DOWN (2)
|
|
|
|
// Default number of GPIOs on RP2 family bank0 (GP0..GP29)
|
|
#ifndef JL_NUM_GPIOS
|
|
#define JL_NUM_GPIOS (30)
|
|
#endif
|
|
|
|
typedef struct _machine_pin_obj_t {
|
|
mp_obj_base_t base;
|
|
uint8_t id;
|
|
} machine_pin_obj_t;
|
|
|
|
// Forward declare the type used in static initialiser below
|
|
extern const mp_obj_type_t machine_pin_type;
|
|
|
|
// Track which pins are configured as OPEN_DRAIN to emulate behaviour
|
|
static uint64_t jl_open_drain_mask;
|
|
|
|
// Forward decls
|
|
static mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
|
|
|
|
// Statically-defined pin objects for GPIO 0..JL_NUM_GPIOS-1
|
|
static const machine_pin_obj_t machine_pin_obj_table[JL_NUM_GPIOS] = {
|
|
#define PIN_OBJ_ENTRY(n) { .base = { &machine_pin_type }, .id = (uint8_t)(n) }
|
|
PIN_OBJ_ENTRY(0), PIN_OBJ_ENTRY(1), PIN_OBJ_ENTRY(2), PIN_OBJ_ENTRY(3),
|
|
PIN_OBJ_ENTRY(4), PIN_OBJ_ENTRY(5), PIN_OBJ_ENTRY(6), PIN_OBJ_ENTRY(7),
|
|
PIN_OBJ_ENTRY(8), PIN_OBJ_ENTRY(9), PIN_OBJ_ENTRY(10), PIN_OBJ_ENTRY(11),
|
|
PIN_OBJ_ENTRY(12), PIN_OBJ_ENTRY(13), PIN_OBJ_ENTRY(14), PIN_OBJ_ENTRY(15),
|
|
PIN_OBJ_ENTRY(16), PIN_OBJ_ENTRY(17), PIN_OBJ_ENTRY(18), PIN_OBJ_ENTRY(19),
|
|
PIN_OBJ_ENTRY(20), PIN_OBJ_ENTRY(21), PIN_OBJ_ENTRY(22), PIN_OBJ_ENTRY(23),
|
|
PIN_OBJ_ENTRY(24), PIN_OBJ_ENTRY(25), PIN_OBJ_ENTRY(26), PIN_OBJ_ENTRY(27),
|
|
PIN_OBJ_ENTRY(28), PIN_OBJ_ENTRY(29)
|
|
#undef PIN_OBJ_ENTRY
|
|
};
|
|
|
|
static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
|
(void)kind;
|
|
const machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
|
mp_printf(print, "Pin(%u)", (unsigned)self->id);
|
|
}
|
|
|
|
enum {
|
|
ARG_mode, ARG_pull, ARG_value, ARG_alt
|
|
};
|
|
|
|
static const mp_arg_t jl_allowed_args[] = {
|
|
{ MP_QSTR_mode, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
|
{ MP_QSTR_pull, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
|
{ MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_NONE } },
|
|
{ MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = GPIO_FUNC_SIO } },
|
|
};
|
|
|
|
static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(jl_allowed_args)];
|
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(jl_allowed_args), jl_allowed_args, args);
|
|
|
|
// Determine initial value for OUT/OPEN_DRAIN modes
|
|
int initial_value = -1;
|
|
if (args[ARG_value].u_obj != mp_const_none) {
|
|
initial_value = mp_obj_is_true(args[ARG_value].u_obj);
|
|
}
|
|
|
|
// Configure mode if provided
|
|
if (args[ARG_mode].u_obj != mp_const_none) {
|
|
mp_int_t mode = mp_obj_get_int(args[ARG_mode].u_obj);
|
|
if (mode == 0 /* MACHINE_PIN_MODE_IN */) {
|
|
gpio_set_dir(self->id, GPIO_IN);
|
|
gpio_set_function(self->id, GPIO_FUNC_SIO);
|
|
jl_open_drain_mask &= ~(1ULL << self->id);
|
|
} else if (mode == 1 /* MACHINE_PIN_MODE_OUT */) {
|
|
// Set initial value before configuring as output
|
|
if (initial_value != -1) {
|
|
gpio_put(self->id, initial_value);
|
|
}
|
|
gpio_set_dir(self->id, GPIO_OUT);
|
|
gpio_set_function(self->id, GPIO_FUNC_SIO);
|
|
jl_open_drain_mask &= ~(1ULL << self->id);
|
|
} else if (mode == 2 /* MACHINE_PIN_MODE_OPEN_DRAIN */) {
|
|
// Open-drain: high=INPUT, low=OUTPUT driving 0
|
|
int od_high = initial_value == -1 ? 1 : initial_value;
|
|
if (od_high) {
|
|
gpio_set_dir(self->id, GPIO_IN); // float
|
|
} else {
|
|
gpio_put(self->id, 0);
|
|
gpio_set_dir(self->id, GPIO_OUT); // drive low
|
|
}
|
|
gpio_set_function(self->id, GPIO_FUNC_SIO);
|
|
jl_open_drain_mask |= (1ULL << self->id);
|
|
} else if (mode == 3 /* MACHINE_PIN_MODE_ALT */) {
|
|
uint32_t af = (uint32_t)args[ARG_alt].u_int;
|
|
gpio_set_function(self->id, af);
|
|
jl_open_drain_mask &= ~(1ULL << self->id);
|
|
} else {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin mode"));
|
|
}
|
|
}
|
|
|
|
// Configure pull (unconditionally because None means no-pull)
|
|
uint32_t pull_bits = 0;
|
|
if (args[ARG_pull].u_obj != mp_const_none) {
|
|
pull_bits = (uint32_t)mp_obj_get_int(args[ARG_pull].u_obj);
|
|
}
|
|
bool up = pull_bits & JL_GPIO_PULL_UP;
|
|
bool down = pull_bits & JL_GPIO_PULL_DOWN;
|
|
|
|
gpio_set_pulls(self->id, up, down);
|
|
|
|
return mp_const_none;
|
|
}
|
|
|
|
// constructor(id, ...)
|
|
static mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
|
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
|
|
|
|
// Accept only integers: allow physical 0..29
|
|
if (!mp_obj_is_int(all_args[0])) {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("Pin id must be integer"));
|
|
}
|
|
int wanted_pin = mp_obj_get_int(all_args[0]);
|
|
bool valid = (wanted_pin >= 0 && wanted_pin < JL_NUM_GPIOS);
|
|
if (!valid) {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
|
|
}
|
|
|
|
const machine_pin_obj_t *self = &machine_pin_obj_table[wanted_pin];
|
|
|
|
if (n_args > 1 || n_kw > 0) {
|
|
// pin mode given, so configure this GPIO
|
|
mp_map_t kw_args;
|
|
mp_map_init_fixed_table(&kw_args, n_kw, all_args + n_args);
|
|
machine_pin_obj_init_helper(self, n_args - 1, all_args + 1, &kw_args);
|
|
}
|
|
|
|
return MP_OBJ_FROM_PTR(self);
|
|
}
|
|
|
|
// fast method for getting/setting pin value
|
|
static mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
|
(void)n_kw;
|
|
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
|
const machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
|
if (n_args == 0) {
|
|
return MP_OBJ_NEW_SMALL_INT(gpio_get(self->id));
|
|
} else {
|
|
bool value = mp_obj_is_true(args[0]);
|
|
if (jl_open_drain_mask & (1ULL << self->id)) {
|
|
// Open-drain: drive low = output, drive high = input (float)
|
|
if (!value) {
|
|
gpio_put(self->id, 0);
|
|
gpio_set_dir(self->id, GPIO_OUT);
|
|
} else {
|
|
gpio_set_dir(self->id, GPIO_IN);
|
|
}
|
|
} else {
|
|
gpio_put(self->id, value);
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
}
|
|
|
|
// pin.init(mode=..., pull=..., *, value=None, alt=GPIO_FUNC_SIO)
|
|
static mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
|
return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init);
|
|
|
|
// pin.value([value])
|
|
static mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) {
|
|
return machine_pin_call(args[0], n_args - 1, 0, args + 1);
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value);
|
|
|
|
// pin.low()
|
|
static mp_obj_t machine_pin_low(mp_obj_t self_in) {
|
|
const machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
|
if (jl_open_drain_mask & (1ULL << self->id)) {
|
|
gpio_put(self->id, 0);
|
|
gpio_set_dir(self->id, GPIO_OUT);
|
|
} else {
|
|
gpio_put(self->id, 0);
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low);
|
|
|
|
// pin.high()
|
|
static mp_obj_t machine_pin_high(mp_obj_t self_in) {
|
|
const machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
|
if (jl_open_drain_mask & (1ULL << self->id)) {
|
|
gpio_set_dir(self->id, GPIO_IN);
|
|
} else {
|
|
gpio_put(self->id, 1);
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high);
|
|
|
|
// pin.toggle()
|
|
static mp_obj_t machine_pin_toggle(mp_obj_t self_in) {
|
|
const machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
|
int v = gpio_get(self->id);
|
|
mp_obj_t args[2] = { self_in, MP_OBJ_NEW_SMALL_INT(!v) };
|
|
return machine_pin_value(2, args);
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle);
|
|
|
|
// Pin protocol support
|
|
static mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
|
(void)errcode;
|
|
const machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
|
switch (request) {
|
|
case MP_PIN_READ:
|
|
return gpio_get(self->id);
|
|
case MP_PIN_WRITE:
|
|
gpio_put(self->id, arg);
|
|
return 0;
|
|
}
|
|
return (mp_uint_t)-1;
|
|
}
|
|
|
|
static const mp_pin_p_t pin_pin_p = {
|
|
.ioctl = pin_ioctl,
|
|
};
|
|
|
|
// Locals dict
|
|
static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
|
|
// instance methods
|
|
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&machine_pin_low_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_high_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) },
|
|
|
|
// class constants (match rp2 values)
|
|
{ MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(0) },
|
|
{ MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(1) },
|
|
{ MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(2) },
|
|
{ MP_ROM_QSTR(MP_QSTR_ALT), MP_ROM_INT(3) },
|
|
{ MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(JL_GPIO_PULL_UP) },
|
|
{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(JL_GPIO_PULL_DOWN) },
|
|
};
|
|
|
|
static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
|
|
|
|
MP_DEFINE_CONST_OBJ_TYPE(
|
|
machine_pin_type,
|
|
MP_QSTR_Pin,
|
|
MP_TYPE_FLAG_NONE,
|
|
make_new, mp_pin_make_new,
|
|
print, machine_pin_print,
|
|
call, machine_pin_call,
|
|
protocol, &pin_pin_p,
|
|
locals_dict, &machine_pin_locals_dict
|
|
);
|
|
|
|
|