|
| 1 | +#include <string.h> |
| 2 | + |
| 3 | +#include "extmod/modmachine.h" |
| 4 | +#include "py/obj.h" |
| 5 | +#include "py/runtime.h" |
| 6 | + |
| 7 | +typedef struct { |
| 8 | + uint32_t output; |
| 9 | + uint32_t input; |
| 10 | + uint32_t debounced_input; |
| 11 | + uint32_t debounce_threshold; |
| 12 | +} gpio_block_t; |
| 13 | + |
| 14 | +typedef struct _machine_pin_obj_t { |
| 15 | + mp_obj_base_t base; |
| 16 | + volatile gpio_block_t *port; |
| 17 | + uint32_t pin; |
| 18 | + uint32_t mode; |
| 19 | +} machine_pin_obj_t; |
| 20 | + |
| 21 | +enum { |
| 22 | + MACHINE_PIN_MODE_IN = 0, |
| 23 | + MACHINE_PIN_MODE_OUT = 1, |
| 24 | + MACHINE_PIN_MODE_OPEN_DRAIN = 2, |
| 25 | + // MACHINE_PIN_MODE_ANALOG = 3 |
| 26 | +}; |
| 27 | + |
| 28 | +#define GET_BUFFER(self) ((self->port->output >> self->pin) & 1) |
| 29 | + |
| 30 | +static uint32_t set_pin_value(uint32_t block, uint32_t pin, bool value) { |
| 31 | + if (value) { |
| 32 | + return block | (1 << pin); |
| 33 | + } else { |
| 34 | + return block & ~(1 << pin); |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | +static uint32_t set_pin_enable(uint32_t block, uint32_t pin, bool value) { |
| 39 | + if (value) { |
| 40 | + return block | (1 << (pin + 16)); |
| 41 | + } else { |
| 42 | + return block & ~(1 << (pin + 16)); |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +volatile gpio_block_t *get_port(const char *drv_name) { |
| 47 | + // !! MMIO_CAPABILITY names were custom hard-coded into the boards/sonata.json |
| 48 | + // !! file since the headers are currently out of date. |
| 49 | + switch (drv_name[0]) { |
| 50 | + case 'g': |
| 51 | + if (strcmp(drv_name, "gpio") == 0) { |
| 52 | + return MMIO_CAPABILITY(gpio_block_t, gpio); |
| 53 | + } |
| 54 | + #if 0 |
| 55 | + case 'r': |
| 56 | + if (strcmp(drv_name, "rpi") == 0) { |
| 57 | + return MMIO_CAPABILITY(gpio_block_t, rpi); |
| 58 | + } |
| 59 | + case 'a': |
| 60 | + if (strcmp(drv_name, "arduino") == 0) { |
| 61 | + return MMIO_CAPABILITY(gpio_block_t, arduino); |
| 62 | + } |
| 63 | + case 'p': |
| 64 | + if (strcmp(drv_name, "pmod") == 0) { |
| 65 | + return MMIO_CAPABILITY(gpio_block_t, pmod); |
| 66 | + } |
| 67 | + #endif |
| 68 | + default: |
| 69 | + mp_raise_msg_varg(&mp_type_ValueError, |
| 70 | + MP_ERROR_TEXT("Unknown port \"%s\""), drv_name); |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +static void machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_args, |
| 75 | + const mp_obj_t *pos_args, |
| 76 | + mp_map_t *kw_args) { |
| 77 | + enum { ARG_mode, ARG_value }; |
| 78 | + |
| 79 | + static const mp_arg_t allowed_args[] = { |
| 80 | + {MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT}, |
| 81 | + {MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}} |
| 82 | + }; |
| 83 | + |
| 84 | + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; |
| 85 | + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), |
| 86 | + allowed_args, args); |
| 87 | + |
| 88 | + self->mode = args[ARG_mode].u_int; |
| 89 | + |
| 90 | + uint32_t init = GET_BUFFER(self); |
| 91 | + if (args[ARG_value].u_obj != MP_OBJ_NULL) { |
| 92 | + init = mp_obj_is_true(args[ARG_value].u_obj); |
| 93 | + } |
| 94 | + |
| 95 | + bool enabled = (self->mode == MACHINE_PIN_MODE_OUT) || |
| 96 | + (self->mode == MACHINE_PIN_MODE_OPEN_DRAIN && !init); |
| 97 | + |
| 98 | + uint32_t block = self->port->output; |
| 99 | + block = set_pin_enable(block, self->pin, enabled); |
| 100 | + block = set_pin_value(block, self->pin, init); |
| 101 | + self->port->output = block; |
| 102 | +} |
| 103 | + |
| 104 | +// constructor Pin(id, mode, *, value) |
| 105 | +mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, |
| 106 | + const mp_obj_t *args) { |
| 107 | + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); |
| 108 | + |
| 109 | + if (!mp_obj_is_type(args[0], &mp_type_tuple)) { |
| 110 | + mp_raise_ValueError( |
| 111 | + MP_ERROR_TEXT("Pin id must be a tuple of (\"name\", pin#)")); |
| 112 | + } |
| 113 | + |
| 114 | + mp_obj_t *pin_id; |
| 115 | + mp_obj_get_array_fixed_n(args[0], 2, &pin_id); |
| 116 | + |
| 117 | + const char *drv_name = mp_obj_str_get_str(pin_id[0]); |
| 118 | + int wanted_pin = mp_obj_get_int(pin_id[1]); |
| 119 | + |
| 120 | + volatile gpio_block_t *wanted_port = get_port(drv_name); |
| 121 | + |
| 122 | + machine_pin_obj_t *pin = m_new_obj(machine_pin_obj_t); |
| 123 | + pin->base.type = &machine_pin_type; |
| 124 | + pin->port = wanted_port; |
| 125 | + pin->pin = wanted_pin; |
| 126 | + |
| 127 | + if (n_args > 1 || n_kw > 0) { |
| 128 | + mp_map_t kw_args; |
| 129 | + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); |
| 130 | + machine_pin_obj_init_helper(pin, n_args - 1, args + 1, &kw_args); |
| 131 | + } |
| 132 | + |
| 133 | + return MP_OBJ_FROM_PTR(pin); |
| 134 | +} |
| 135 | + |
| 136 | +// Pin.init(mode, *, value) |
| 137 | +static mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, |
| 138 | + mp_map_t *kw_args) { |
| 139 | + if (mp_obj_is_type(args[0], &machine_pin_type)) { |
| 140 | + machine_pin_obj_init_helper(MP_OBJ_TO_PTR(args[0]), n_args - 1, args + 1, |
| 141 | + kw_args); |
| 142 | + return args[0]; |
| 143 | + } else { |
| 144 | + mp_raise_TypeError( |
| 145 | + MP_ERROR_TEXT("Cannot call Pin.init on object that is not Pin")); |
| 146 | + } |
| 147 | +} |
| 148 | +MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init); |
| 149 | + |
| 150 | +static mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, |
| 151 | + int *errcode) { |
| 152 | + (void)errcode; |
| 153 | + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); |
| 154 | + |
| 155 | + switch (request) { |
| 156 | + case MP_PIN_READ: { |
| 157 | + if (self->mode == MACHINE_PIN_MODE_IN) { |
| 158 | + return (self->port->input >> self->pin) & 1; |
| 159 | + } else { |
| 160 | + return GET_BUFFER(self); |
| 161 | + } |
| 162 | + } |
| 163 | + case MP_PIN_WRITE: { |
| 164 | + uint32_t block = set_pin_value(self->port->output, self->pin, (bool)arg); |
| 165 | + |
| 166 | + if (self->mode == MACHINE_PIN_MODE_OPEN_DRAIN) { |
| 167 | + block = set_pin_enable(block, self->pin, !(bool)arg); |
| 168 | + } |
| 169 | + |
| 170 | + self->port->output = block; |
| 171 | + return 0; |
| 172 | + } |
| 173 | + } |
| 174 | + return -1; |
| 175 | +} |
| 176 | + |
| 177 | +static mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, |
| 178 | + const mp_obj_t *args) { |
| 179 | + mp_arg_check_num(n_args, n_kw, 0, 1, false); |
| 180 | + |
| 181 | + if (n_args == 0) { |
| 182 | + return MP_OBJ_NEW_SMALL_INT(mp_virtual_pin_read(self_in)); |
| 183 | + } else { |
| 184 | + mp_virtual_pin_write(self_in, mp_obj_is_true(args[0])); |
| 185 | + return mp_const_none; |
| 186 | + } |
| 187 | +} |
| 188 | + |
| 189 | +// Pin.value([value]) |
| 190 | +static mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) { |
| 191 | + return machine_pin_call(args[0], n_args - 1, 0, args + 1); |
| 192 | +} |
| 193 | +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, |
| 194 | + machine_pin_value); |
| 195 | + |
| 196 | +static mp_obj_t machine_pin_off(mp_obj_t self_in) { |
| 197 | + mp_virtual_pin_write(self_in, 0); |
| 198 | + return mp_const_none; |
| 199 | +} |
| 200 | +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_off_obj, machine_pin_off); |
| 201 | + |
| 202 | +static mp_obj_t machine_pin_on(mp_obj_t self_in) { |
| 203 | + mp_virtual_pin_write(self_in, 1); |
| 204 | + return mp_const_none; |
| 205 | +} |
| 206 | +static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_on_obj, machine_pin_on); |
| 207 | + |
| 208 | +// print(pin) |
| 209 | +static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, |
| 210 | + mp_print_kind_t kind) { |
| 211 | + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); |
| 212 | + const char *mode_str; |
| 213 | + |
| 214 | + switch (self->mode) { |
| 215 | + case MACHINE_PIN_MODE_IN: |
| 216 | + mode_str = "IN"; |
| 217 | + break; |
| 218 | + case MACHINE_PIN_MODE_OUT: |
| 219 | + mode_str = "OUT"; |
| 220 | + break; |
| 221 | + case MACHINE_PIN_MODE_OPEN_DRAIN: |
| 222 | + mode_str = "OPEN DRAIN"; |
| 223 | + break; |
| 224 | + default: |
| 225 | + mode_str = "unknown mode"; |
| 226 | + } |
| 227 | + |
| 228 | + const char *name_str; |
| 229 | + |
| 230 | + switch ((ptraddr_t)self->port) { |
| 231 | + case 0x80000000: |
| 232 | + name_str = "gpio"; |
| 233 | + case 0x80006000: |
| 234 | + name_str = "rpi"; |
| 235 | + case 0x80007000: |
| 236 | + name_str = "arduino"; |
| 237 | + case 0x80008000: |
| 238 | + name_str = "pmod"; |
| 239 | + default: |
| 240 | + name_str = "unknown port"; |
| 241 | + } |
| 242 | + |
| 243 | + mp_printf(print, "Pin(%s(%d) mode=%s)", name_str, self->pin, mode_str); |
| 244 | +} |
| 245 | + |
| 246 | +static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { |
| 247 | + // instance methods |
| 248 | + {MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj)}, |
| 249 | + {MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj)}, |
| 250 | + {MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&machine_pin_off_obj)}, |
| 251 | + {MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_on_obj)}, |
| 252 | + {MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_off_obj)}, |
| 253 | + {MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_on_obj)}, |
| 254 | + |
| 255 | + // class constants |
| 256 | + {MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(MACHINE_PIN_MODE_IN)}, |
| 257 | + {MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(MACHINE_PIN_MODE_OUT)}, |
| 258 | + {MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(MACHINE_PIN_MODE_OPEN_DRAIN)}, |
| 259 | +}; |
| 260 | +static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, |
| 261 | + machine_pin_locals_dict_table); |
| 262 | + |
| 263 | +static const mp_pin_p_t pin_pin_p = { |
| 264 | + .ioctl = pin_ioctl, |
| 265 | +}; |
| 266 | + |
| 267 | +/** |
| 268 | + * TODO: |
| 269 | + * pin.mode() |
| 270 | + */ |
| 271 | + |
| 272 | +MP_DEFINE_CONST_OBJ_TYPE(machine_pin_type, MP_QSTR_Pin, MP_TYPE_FLAG_NONE, |
| 273 | + make_new, mp_pin_make_new, print, machine_pin_print, |
| 274 | + call, machine_pin_call, protocol, &pin_pin_p, |
| 275 | + locals_dict, &machine_pin_locals_dict); |
0 commit comments