Merge branch 'pin-af' of https://github.com/dhylands/micropython into dhylands-pin-af

This commit is contained in:
Damien George 2014-08-08 19:42:07 +01:00
commit 196773505a
19 changed files with 551 additions and 90 deletions

View File

@ -11,7 +11,7 @@ BUILD ?= build-$(BOARD)
include ../py/mkenv.mk
# qstr definitions (must come before including py.mk)
QSTR_DEFS = qstrdefsport.h
QSTR_DEFS = qstrdefsport.h $(BUILD)/pins_qstr.h
# include py core make definitions
include ../py/py.mk
@ -74,6 +74,7 @@ SRC_C = \
timer.c \
led.c \
pin.c \
pin_defs_stmhal.c \
pin_named_pins.c \
bufhelper.c \
i2c.c \
@ -227,6 +228,8 @@ AF_FILE = boards/stm32f4xx-af.csv
PREFIX_FILE = boards/stm32f4xx-prefix.c
GEN_PINS_SRC = $(BUILD)/pins_$(BOARD).c
GEN_PINS_HDR = $(HEADER_BUILD)/pins.h
GEN_PINS_QSTR = $(BUILD)/pins_qstr.h
GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins-af-const.h
INSERT_USB_IDS = ../tools/insert-usb-ids.py
FILE2H = ../tools/file2h.py
@ -247,9 +250,9 @@ $(BUILD)/main.o: $(GEN_CDCINF_HEADER)
# Use a pattern rule here so that make will only call make-pins.py once to make
# both pins_$(BOARD).c and pins.h
$(BUILD)/%_$(BOARD).c $(HEADER_BUILD)/%.h: boards/$(BOARD)/%.csv $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE)
$(BUILD)/%_$(BOARD).c $(HEADER_BUILD)/%.h $(HEADER_BUILD)/%-af-const.h $(BUILD)/%_qstr.h: boards/$(BOARD)/%.csv $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE) | $(HEADER_BUILD)
$(ECHO) "Create $@"
$(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) > $(GEN_PINS_SRC)
$(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) --qstr $(GEN_PINS_QSTR) --af-const $(GEN_PINS_AF_CONST) > $(GEN_PINS_SRC)
$(BUILD)/pins_$(BOARD).o: $(BUILD)/pins_$(BOARD).c
$(call compile_c)

View File

@ -72,6 +72,9 @@ class AlternateFunction(object):
return self.func
return '{:s}{:d}'.format(self.func, self.fn_num)
def mux_name(self):
return 'AF{:d}_{:s}'.format(self.idx, self.ptr())
def print(self):
"""Prints the C representation of this AF."""
if self.supported:
@ -84,6 +87,9 @@ class AlternateFunction(object):
print('({:2d}, {:8s}, {:2d}, {:10s}, {:8s}), // {:s}'.format(self.idx,
self.func, fn_num, self.pin_type, self.ptr(), self.af_str))
def qstr_list(self):
return [self.mux_name()]
class Pin(object):
"""Holds the information associated with a pin."""
@ -170,6 +176,14 @@ class Pin(object):
hdr_file.write('extern const pin_af_obj_t pin_{:s}_af[];\n'.
format(self.cpu_pin_name()))
def qstr_list(self):
result = []
for alt_fn in self.alt_fn:
if alt_fn.is_supported():
result += alt_fn.qstr_list()
return result
class NamedPin(object):
def __init__(self, name, pin):
@ -225,13 +239,13 @@ class Pins(object):
self.board_pins.append(NamedPin(row[0], pin))
def print_named(self, label, named_pins):
print('const pin_named_pin_t pin_{:s}_pins[] = {{'.format(label))
print('STATIC const mp_map_elem_t pin_{:s}_pins_locals_dict_table[] = {{'.format(label))
for named_pin in named_pins:
pin = named_pin.pin()
if pin.is_board_pin():
print(' {{ "{:s}", &pin_{:s} }},'.format(named_pin.name(), pin.cpu_pin_name()))
print(' { NULL, NULL }')
print(' {{ MP_OBJ_NEW_QSTR(MP_QSTR_{:s}), (mp_obj_t)&pin_{:s} }},'.format(named_pin.name(), pin.cpu_pin_name()))
print('};')
print('MP_DEFINE_CONST_DICT(pin_{:s}_pins_locals_dict, pin_{:s}_pins_locals_dict_table);'.format(label, label));
def print(self):
for named_pin in self.cpu_pins:
@ -269,6 +283,38 @@ class Pins(object):
hdr_file.write('extern const pin_obj_t * const pin_adc2[];\n')
hdr_file.write('extern const pin_obj_t * const pin_adc3[];\n')
def print_qstr(self, qstr_filename):
with open(qstr_filename, 'wt') as qstr_file:
qstr_set = set([])
for named_pin in self.cpu_pins:
pin = named_pin.pin()
if pin.is_board_pin():
qstr_set |= set(pin.qstr_list())
qstr_set |= set([named_pin.name()])
for named_pin in self.board_pins:
qstr_set |= set([named_pin.name()])
for qstr in sorted(qstr_set):
print('Q({})'.format(qstr), file=qstr_file)
def print_af_hdr(self, af_const_filename):
with open(af_const_filename, 'wt') as af_const_file:
af_hdr_set = set([])
mux_name_width = 0
for named_pin in self.cpu_pins:
pin = named_pin.pin()
if pin.is_board_pin():
for af in pin.alt_fn:
if af.is_supported():
mux_name = af.mux_name()
af_hdr_set |= set([mux_name])
if len(mux_name) > mux_name_width:
mux_name_width = len(mux_name)
for mux_name in sorted(af_hdr_set):
key = 'MP_OBJ_NEW_QSTR(MP_QSTR_{}),'.format(mux_name)
val = 'MP_OBJ_NEW_SMALL_INT(GPIO_{})'.format(mux_name)
print(' { %-*s %s },' % (mux_name_width + 26, key, val),
file=af_const_file)
def main():
parser = argparse.ArgumentParser(
@ -282,6 +328,12 @@ def main():
help="Specifies the alternate function file for the chip",
default="stm32f4xx-af.csv"
)
parser.add_argument(
"--af-const",
dest="af_const_filename",
help="Specifies header file for alternate function constants.",
default="build/pins-af-const.h"
)
parser.add_argument(
"-b", "--board",
dest="board_filename",
@ -293,6 +345,12 @@ def main():
help="Specifies beginning portion of generated pins file",
default="stm32f4xx-prefix.c"
)
parser.add_argument(
"-q", "--qstr",
dest="qstr_filename",
help="Specifies name of generated qstr header file",
default="build/pins-qstr.h"
)
parser.add_argument(
"-r", "--hdr",
dest="hdr_filename",
@ -323,6 +381,8 @@ def main():
pins.print_adc(2)
pins.print_adc(3)
pins.print_header(args.hdr_filename)
pins.print_qstr(args.qstr_filename)
pins.print_af_hdr(args.af_const_filename)
if __name__ == "__main__":

View File

@ -14,6 +14,7 @@
#define AF(af_idx, af_fn, af_unit, af_type, af_ptr) \
{ \
{ &pin_af_type }, \
.name = MP_QSTR_AF ## af_idx ## _ ## af_fn ## af_unit, \
.idx = (af_idx), \
.fn = AF_FN_ ## af_fn, \
.unit = (af_unit), \
@ -24,7 +25,7 @@
#define PIN(p_port, p_pin, p_num_af, p_af, p_adc_num, p_adc_channel) \
{ \
{ &pin_type }, \
.name = #p_port #p_pin, \
.name = MP_QSTR_ ## p_port ## p_pin, \
.port = PORT_ ## p_port, \
.pin = (p_pin), \
.num_af = (p_num_af), \

View File

@ -5,3 +5,4 @@
#define GPIO_read_pin(gpio, pin) (((gpio)->IDR >> (pin)) & 1)
#define GPIO_set_pin(gpio, pin_mask) (((gpio)->BSRRL) = (pin_mask))
#define GPIO_clear_pin(gpio, pin_mask) (((gpio)->BSRRH) = (pin_mask))
#define GPIO_read_output_pin(gpio, pin) (((gpio)->ODR >> (pin)) & 1)

View File

@ -63,7 +63,8 @@
///
/// Users can add their own names:
///
/// pyb.Pin.dict["LeftMotorDir"] = pyb.Pin.cpu.C12
/// MyMapperDict = { 'LeftMotorDir' : pyb.Pin.cpu.C12 }
/// pyb.Pin.dict(MyMapperDict)
/// g = pyb.Pin("LeftMotorDir", pyb.Pin.OUT_OD)
///
/// and can query mappings
@ -155,8 +156,7 @@ const pin_obj_t *pin_find(mp_obj_t user_obj) {
}
// See if the pin name matches a board pin
const char *pin_name = mp_obj_str_get_str(user_obj);
pin_obj = pin_find_named_pin(pin_board_pins, pin_name);
pin_obj = pin_find_named_pin(&pin_board_pins_locals_dict, user_obj);
if (pin_obj) {
if (pin_class_debug) {
printf("Pin.board maps ");
@ -169,7 +169,7 @@ const pin_obj_t *pin_find(mp_obj_t user_obj) {
}
// See if the pin name matches a cpu pin
pin_obj = pin_find_named_pin(pin_cpu_pins, pin_name);
pin_obj = pin_find_named_pin(&pin_cpu_pins_locals_dict, user_obj);
if (pin_obj) {
if (pin_class_debug) {
printf("Pin.cpu maps ");
@ -181,34 +181,69 @@ const pin_obj_t *pin_find(mp_obj_t user_obj) {
return pin_obj;
}
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "pin '%s' not a valid pin identifier", pin_name));
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "pin '%s' not a valid pin identifier", mp_obj_str_get_str(user_obj)));
}
/// \method __str__()
/// Return a string describing the pin object.
STATIC void pin_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
pin_obj_t *self = self_in;
print(env, "<Pin %s>", self->name);
// Need to query mode, pull, af
print(env, "Pin(Pin.cpu.%s", qstr_str(self->name));
uint32_t mode = pin_get_mode(self);
if (mode == GPIO_MODE_ANALOG) {
print(env, ", mode=Pin.ANALOG)", qstr_str(self->name));
} else {
const char *pull_str = "";
uint32_t pull = pin_get_pull(self);
if (pull == GPIO_PULLUP) {
pull_str = ", pull=Pin.PULL_UP";
} else if (pull == GPIO_PULLDOWN) {
pull_str = ", pull=Pin.PULL_DOWN";
}
if (mode == GPIO_MODE_INPUT) {
print(env, ", mode=Pin.IN%s)", pull_str);
} else if (mode == GPIO_MODE_OUTPUT_PP || mode == GPIO_MODE_OUTPUT_OD) {
if (mode == GPIO_MODE_OUTPUT_PP) {
print(env, ", mode=Pin.OUT_PP%s)", pull_str);
} else {
print(env, ", mode=Pin.OUT_OD%s)", pull_str);
}
} else {
if (mode == GPIO_MODE_AF_PP) {
print(env, ", mode=Pin.AF_PP");
} else {
print(env, ", mode=Pin.AF_OD");
}
mp_uint_t af_idx = pin_get_af(self);
const pin_af_obj_t *af = pin_find_af_by_index(self, af_idx);
if (af == NULL) {
print(env, ", af=%d%s)", af_idx, pull_str);
} else {
print(env, ", af=Pin.%s)", qstr_str(af->name), pull_str);
}
}
}
}
STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args);
STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *pin, uint n_args, const mp_obj_t *args, mp_map_t *kw_args);
/// \classmethod \constructor(id, ...)
/// Create a new Pin object associated with the id. If additional arguments are given,
/// they are used to initialise the pin. See `init`.
STATIC mp_obj_t pin_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 3, false);
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
// Run an argument through the mapper and return the result.
const pin_obj_t *pin = pin_find(args[0]);
if (n_args >= 2) {
if (n_args > 1 || n_kw > 0) {
// pin mode given, so configure this GPIO
mp_obj_t args2[3] = {(mp_obj_t)pin, args[1], MP_OBJ_NULL};
if (n_args == 3) {
args2[2] = args[2];
}
pin_obj_init(n_args, args2);
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
pin_obj_init_helper(pin, n_args - 1, args + 1, &kw_args);
}
return (mp_obj_t)pin;
@ -238,6 +273,20 @@ STATIC mp_obj_t pin_map_dict(uint n_args, mp_obj_t *args) {
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_map_dict_fun_obj, 1, 2, pin_map_dict);
STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_map_dict_obj, (mp_obj_t)&pin_map_dict_fun_obj);
/// |classmethod af_list()
/// Returns an array of alternate functions available for this pin.
STATIC mp_obj_t pin_af_list(mp_obj_t self_in) {
pin_obj_t *self = self_in;
mp_obj_t result = mp_obj_new_list(0, NULL);
const pin_af_obj_t *af = self->af;
for (mp_uint_t i = 0; i < self->num_af; i++, af++) {
mp_obj_list_append(result, (mp_obj_t)af);
}
return result;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_af_list_obj, pin_af_list);
/// \classmethod debug([state])
/// Get or set the debugging state (`True` or `False` for on or off).
STATIC mp_obj_t pin_debug(uint n_args, mp_obj_t *args) {
@ -250,7 +299,7 @@ STATIC mp_obj_t pin_debug(uint n_args, mp_obj_t *args) {
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_debug_fun_obj, 1, 2, pin_debug);
STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_debug_obj, (mp_obj_t)&pin_debug_fun_obj);
/// \method init(mode, pull=Pin.PULL_NONE)
/// \method init(mode, pull=Pin.PULL_NONE, af)
/// Initialise the pin:
///
/// - `mode` can be one of:
@ -264,25 +313,52 @@ STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_debug_obj, (mp_obj_t)&pin_debug_fun_o
/// - `Pin.PULL_NONE` - no pull up or down resistors;
/// - `Pin.PULL_UP` - enable the pull-up resistor;
/// - `Pin.PULL_DOWN` - enable the pull-down resistor.
/// - when mode is Pin.AF_PP or Pin.AF_OD, then af can be the index or name
/// of one of the alternate functions associated with a pin.
///
/// Returns: `None`.
// TODO allow keyword args
STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args) {
pin_obj_t *self = args[0];
STATIC const mp_arg_t pin_init_args[] = {
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_pull, MP_ARG_INT, {.u_int = GPIO_NOPULL}},
{ MP_QSTR_af, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none}},
};
#define PIN_INIT_NUM_ARGS MP_ARRAY_SIZE(pin_init_args)
STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *self, uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
// parse args
mp_arg_val_t vals[PIN_INIT_NUM_ARGS];
mp_arg_parse_all(n_args, args, kw_args, PIN_INIT_NUM_ARGS, pin_init_args, vals);
// get io mode
uint mode = mp_obj_get_int(args[1]);
uint mode = vals[0].u_int;
if (!IS_GPIO_MODE(mode)) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid pin mode: %d", mode));
}
// get pull mode
uint pull = GPIO_NOPULL;
if (n_args >= 3) {
pull = mp_obj_get_int(args[2]);
uint pull = vals[1].u_int;
if (!IS_GPIO_PULL(pull)) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid pin pull: %d", pull));
}
// get af (alternate function)
mp_int_t af_idx = -1;
mp_obj_t af_obj = vals[2].u_obj;
if (af_obj != mp_const_none) {
if (MP_OBJ_IS_STR(af_obj)) {
const pin_af_obj_t *af;
const char *af_str = mp_obj_str_get_str(af_obj);
af = pin_find_af_by_name(self, af_str);
if (af == NULL) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid pin af: %s", af_str));
}
af_idx = af->idx;
} else {
af_idx = mp_obj_get_int(af_obj);
}
}
if ((mode == GPIO_MODE_AF_PP || mode == GPIO_MODE_AF_OD) && !IS_GPIO_AF(af_idx)) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "invalid pin af: %d", af_idx));
}
// enable the peripheral clock for the port of this pin
@ -325,12 +401,15 @@ STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args) {
GPIO_InitStructure.Mode = mode;
GPIO_InitStructure.Pull = pull;
GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
GPIO_InitStructure.Alternate = 0;
GPIO_InitStructure.Alternate = af_idx;
HAL_GPIO_Init(self->gpio, &GPIO_InitStructure);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_init_obj, 2, 3, pin_obj_init);
STATIC mp_obj_t pin_obj_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
return pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pin_init_obj, 1, pin_obj_init);
/// \method value([value])
/// Get or set the digital logic level of the pin:
@ -378,10 +457,29 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_high_obj, pin_high);
/// Get the pin name.
STATIC mp_obj_t pin_name(mp_obj_t self_in) {
pin_obj_t *self = self_in;
return MP_OBJ_NEW_QSTR(qstr_from_str(self->name));
return MP_OBJ_NEW_QSTR(self->name);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_name_obj, pin_name);
/// \method names()
/// Returns the cpu and board names for this pin.
STATIC mp_obj_t pin_names(mp_obj_t self_in) {
pin_obj_t *self = self_in;
mp_obj_t result = mp_obj_new_list(0, NULL);
mp_obj_list_append(result, MP_OBJ_NEW_QSTR(self->name));
mp_map_t *map = mp_obj_dict_get_map((mp_obj_t)&pin_board_pins_locals_dict);
mp_map_elem_t *elem = map->table;
for (mp_uint_t i = 0; i < map->used; i++, elem++) {
if (elem->value == self) {
mp_obj_list_append(result, elem->key);
}
}
return result;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_names_obj, pin_names);
/// \method port()
/// Get the pin port.
STATIC mp_obj_t pin_port(mp_obj_t self_in) {
@ -398,6 +496,14 @@ STATIC mp_obj_t pin_pin(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_pin_obj, pin_pin);
/// \method gpio()
/// Returns the base address of the GPIO block associated with this pin.
STATIC mp_obj_t pin_gpio(mp_obj_t self_in) {
pin_obj_t *self = self_in;
return MP_OBJ_NEW_SMALL_INT((mp_int_t)self->gpio);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_gpio_obj, pin_gpio);
STATIC const mp_map_elem_t pin_locals_dict_table[] = {
// instance methods
{ MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pin_init_obj },
@ -405,8 +511,11 @@ STATIC const mp_map_elem_t pin_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_low), (mp_obj_t)&pin_low_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_high), (mp_obj_t)&pin_high_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_name), (mp_obj_t)&pin_name_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_names), (mp_obj_t)&pin_names_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_af_list), (mp_obj_t)&pin_af_list_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_port), (mp_obj_t)&pin_port_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_pin), (mp_obj_t)&pin_pin_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_gpio), (mp_obj_t)&pin_gpio_obj },
// class methods
{ MP_OBJ_NEW_QSTR(MP_QSTR_mapper), (mp_obj_t)&pin_mapper_obj },
@ -414,8 +523,8 @@ STATIC const mp_map_elem_t pin_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_debug), (mp_obj_t)&pin_debug_obj },
// class attributes
{ MP_OBJ_NEW_QSTR(MP_QSTR_board), (mp_obj_t)&pin_board_pins_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_cpu), (mp_obj_t)&pin_cpu_pins_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_board), (mp_obj_t)&pin_board_pins_obj_type },
{ MP_OBJ_NEW_QSTR(MP_QSTR_cpu), (mp_obj_t)&pin_cpu_pins_obj_type },
// class constants
/// \constant IN - initialise the pin to input mode
@ -433,6 +542,7 @@ STATIC const mp_map_elem_t pin_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_PULL_NONE), MP_OBJ_NEW_SMALL_INT(GPIO_NOPULL) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_PULL_UP), MP_OBJ_NEW_SMALL_INT(GPIO_PULLUP) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_PULL_DOWN), MP_OBJ_NEW_SMALL_INT(GPIO_PULLDOWN) },
#include "genhdr/pins-af-const.h"
};
STATIC MP_DEFINE_CONST_DICT(pin_locals_dict, pin_locals_dict_table);
@ -445,14 +555,76 @@ const mp_obj_type_t pin_type = {
.locals_dict = (mp_obj_t)&pin_locals_dict,
};
/// \moduleref pyb
/// \class PinAF - Pin Alternate Functions
///
/// A Pin represents a physical pin on the microcprocessor. Each pin
/// can have a variety of functions (GPIO, I2C SDA, etc). Each PinAF
/// object represents a particular function for a pin.
///
/// Usage Model:
///
/// x3 = pyb.Pin.board.X3
/// x3_af = x3.af_list()
///
/// x3_af will now contain an array of PinAF objects which are availble on
/// pin X3.
///
/// For the pyboard, x3_af would contain:
/// [Pin.AF1_TIM2, Pin.AF2_TIM5, Pin.AF3_TIM9, Pin.AF7_USART2]
///
/// Normally, each peripheral would configure the af automatically, but sometimes
/// the same function is available on multiple pins, and having more control
/// is desired.
///
/// To configure X3 to expose TIM2_CH3, you could use:
/// pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, af=pyb.Pin.AF1_TIM2)
/// or:
/// pin = pyb.Pin(pyb.Pin.board.X3, mode=pyb.Pin.AF_PP, af=1)
/// \method __str__()
/// Return a string describing the alternate function.
STATIC void pin_af_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
pin_af_obj_t *self = self_in;
print(env, "<Pin AF %d fn:%d unit:%d typ:%d>", self->idx, self->fn,
self->unit, self->type);
print(env, "Pin.%s", qstr_str(self->name));
}
/// \method index()
/// Return the alternate function index.
STATIC mp_obj_t pin_af_index(mp_obj_t self_in) {
pin_af_obj_t *af = self_in;
return MP_OBJ_NEW_SMALL_INT(af->idx);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_af_index_obj, pin_af_index);
/// \method index()
/// Return the name of the alternate function.
STATIC mp_obj_t pin_af_name(mp_obj_t self_in) {
pin_af_obj_t *af = self_in;
return MP_OBJ_NEW_QSTR(af->name);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_af_name_obj, pin_af_name);
/// \method index()
/// Return the base register associated with the peripheral assigned to this
/// alternate function. For example, if the alternate function were TIM2_CH3
/// this would return stm.TIM2
STATIC mp_obj_t pin_af_reg(mp_obj_t self_in) {
pin_af_obj_t *af = self_in;
return MP_OBJ_NEW_SMALL_INT((mp_uint_t)af->reg);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_af_reg_obj, pin_af_reg);
STATIC const mp_map_elem_t pin_af_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_index), (mp_obj_t)&pin_af_index_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_name), (mp_obj_t)&pin_af_name_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_reg), (mp_obj_t)&pin_af_reg_obj },
};
STATIC MP_DEFINE_CONST_DICT(pin_af_locals_dict, pin_af_locals_dict_table);
const mp_obj_type_t pin_af_type = {
{ &mp_type_type },
.name = MP_QSTR_PinAF,
.print = pin_af_obj_print,
.locals_dict = (mp_obj_t)&pin_af_locals_dict,
};

View File

@ -31,6 +31,7 @@
typedef struct {
mp_obj_base_t base;
qstr name;
uint8_t idx;
uint8_t fn;
uint8_t unit;
@ -45,7 +46,7 @@ typedef struct {
typedef struct {
mp_obj_base_t base;
const char *name;
qstr name;
uint32_t port : 4;
uint32_t pin : 5; // Some ARM processors use 32 bits/PORT
uint32_t num_af : 4;
@ -75,10 +76,18 @@ typedef struct {
const pin_named_pin_t *named_pins;
} pin_named_pins_obj_t;
extern const pin_named_pins_obj_t pin_board_pins_obj;
extern const pin_named_pins_obj_t pin_cpu_pins_obj;
extern const mp_obj_type_t pin_board_pins_obj_type;
extern const mp_obj_type_t pin_cpu_pins_obj_type;
extern const mp_obj_dict_t pin_cpu_pins_locals_dict;
extern const mp_obj_dict_t pin_board_pins_locals_dict;
void pin_init0(void);
uint32_t pin_get_mode(const pin_obj_t *pin);
uint32_t pin_get_pull(const pin_obj_t *pin);
uint32_t pin_get_af(const pin_obj_t *pin);
const pin_obj_t *pin_find(mp_obj_t user_obj);
const pin_obj_t *pin_find_named_pin(const pin_named_pin_t *pins, const char *name);
const pin_obj_t *pin_find_named_pin(const mp_obj_dict_t *named_pins, mp_obj_t name);
const pin_af_obj_t *pin_find_af(const pin_obj_t *pin, uint8_t fn, uint8_t unit, uint8_t pin_type);
const pin_af_obj_t *pin_find_af_by_index(const pin_obj_t *pin, mp_uint_t af_idx);
const pin_af_obj_t *pin_find_af_by_name(const pin_obj_t *pin, const char *name);

37
stmhal/pin_defs_stmhal.c Normal file
View File

@ -0,0 +1,37 @@
#include "mpconfig.h"
#include "nlr.h"
#include "misc.h"
#include "qstr.h"
#include "obj.h"
#include "runtime.h"
#include MICROPY_HAL_H
#include "pin.h"
// Returns the pin mode. This value returned by this macro should be one of:
// GPIO_MODE_INPUT, GPIO_MODE_OUTPUT_PP, GPIO_MODE_OUTPUT_OD,
// GPIO_MODE_AF_PP, GPIO_MODE_AF_OD, or GPIO_MODE_ANALOG.
uint32_t pin_get_mode(const pin_obj_t *pin) {
GPIO_TypeDef *gpio = pin->gpio;
uint32_t mode = (gpio->MODER >> (pin->pin * 2)) & 3;
if (mode != GPIO_MODE_ANALOG) {
if (gpio->OTYPER & pin->pin_mask) {
mode |= 1 << 4;
}
}
return mode;
}
// Returns the pin pullup/pulldown. The value returned by this macro should
// be one of GPIO_NOPULL, GPIO_PULLUP, or GPIO_PULLDOWN.
uint32_t pin_get_pull(const pin_obj_t *pin) {
return (pin->gpio->PUPDR >> (pin->pin * 2)) & 3;
}
// Returns the af (alternate function) index currently set for a pin.
uint32_t pin_get_af(const pin_obj_t *pin) {
return (pin->gpio->AFR[pin->pin >> 3] >> ((pin->pin & 7) * 4)) & 0xf;
}

View File

@ -41,52 +41,55 @@ STATIC void pin_named_pins_obj_print(void (*print)(void *env, const char *fmt, .
print(env, "<Pin.%s>", qstr_str(self->name));
}
STATIC void pin_named_pins_obj_load_attr(mp_obj_t self_in, qstr attr_qstr, mp_obj_t *dest) {
pin_named_pins_obj_t *self = self_in;
const char *attr = qstr_str(attr_qstr);
const pin_obj_t *pin = pin_find_named_pin(self->named_pins, attr);
if (pin) {
dest[0] = (mp_obj_t)pin;
dest[1] = MP_OBJ_NULL;
}
}
static const mp_obj_type_t pin_named_pins_obj_type = {
const mp_obj_type_t pin_cpu_pins_obj_type = {
{ &mp_type_type },
.name = MP_QSTR_PinNamed,
.print = pin_named_pins_obj_print,
.load_attr = pin_named_pins_obj_load_attr,
};
const pin_named_pins_obj_t pin_board_pins_obj = {
{ &pin_named_pins_obj_type },
.name = MP_QSTR_board,
.named_pins = pin_board_pins,
};
const pin_named_pins_obj_t pin_cpu_pins_obj = {
{ &pin_named_pins_obj_type },
.name = MP_QSTR_cpu,
.named_pins = pin_cpu_pins,
.print = pin_named_pins_obj_print,
.locals_dict = (mp_obj_t)&pin_cpu_pins_locals_dict,
};
const pin_obj_t *pin_find_named_pin(const pin_named_pin_t *named_pins, const char *name) {
const pin_named_pin_t *named_pin = named_pins;
while (named_pin->name) {
if (!strcmp(name, named_pin->name)) {
return named_pin->pin;
}
named_pin++;
const mp_obj_type_t pin_board_pins_obj_type = {
{ &mp_type_type },
.name = MP_QSTR_board,
.print = pin_named_pins_obj_print,
.locals_dict = (mp_obj_t)&pin_board_pins_locals_dict,
};
const pin_obj_t *pin_find_named_pin(const mp_obj_dict_t *named_pins, mp_obj_t name) {
mp_map_t *named_map = mp_obj_dict_get_map((mp_obj_t)named_pins);
mp_map_elem_t *named_elem = mp_map_lookup(named_map, name, MP_MAP_LOOKUP);
if (named_elem != NULL && named_elem->value != NULL) {
return named_elem->value;
}
return NULL;
}
const pin_af_obj_t *pin_find_af(const pin_obj_t *pin, uint8_t fn, uint8_t unit, uint8_t type) {
const pin_af_obj_t *af = pin->af;
for (int i = 0; i < pin->num_af; i++, af++) {
for (mp_uint_t i = 0; i < pin->num_af; i++, af++) {
if (af->fn == fn && af->unit == unit && af->type == type) {
return af;
}
}
return NULL;
}
const pin_af_obj_t *pin_find_af_by_index(const pin_obj_t *pin, mp_uint_t af_idx) {
const pin_af_obj_t *af = pin->af;
for (mp_uint_t i = 0; i < pin->num_af; i++, af++) {
if (af->idx == af_idx) {
return af;
}
}
return NULL;
}
const pin_af_obj_t *pin_find_af_by_name(const pin_obj_t *pin, const char *name) {
const pin_af_obj_t *af = pin->af;
for (mp_uint_t i = 0; i < pin->num_af; i++, af++) {
if (strcmp(name, qstr_str(af->name)) == 0) {
return af;
}
}
return NULL;
}

View File

@ -93,13 +93,22 @@ Q(value)
Q(low)
Q(high)
Q(name)
Q(names)
Q(af)
Q(af_list)
Q(port)
Q(pin)
Q(gpio)
Q(mapper)
Q(dict)
Q(debug)
Q(board)
Q(cpu)
Q(af)
Q(mode)
Q(pull)
Q(index)
Q(reg)
Q(IN)
Q(OUT_PP)
Q(OUT_OD)

View File

@ -1,7 +1,7 @@
include ../py/mkenv.mk
# qstr definitions (must come before including py.mk)
QSTR_DEFS = qstrdefsport.h
QSTR_DEFS = qstrdefsport.h $(BUILD)/pins_qstr.h
# include py core make definitions
include ../py/py.mk
@ -53,6 +53,7 @@ SRC_C = \
lexermemzip.c \
memzip.c \
modpyb.c \
pin_defs_teensy.c \
teensy_hal.c \
uart.c \
usb.c \
@ -137,6 +138,8 @@ AF_FILE = mk20dx256-af.csv
PREFIX_FILE = mk20dx256-prefix.c
GEN_PINS_SRC = $(BUILD)/pins_gen.c
GEN_PINS_HDR = $(HEADER_BUILD)/pins.h
GEN_PINS_QSTR = $(BUILD)/pins_qstr.h
GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins-af-const.h
# Making OBJ use an order-only depenedency on the generated pins.h file
# has the side effect of making the pins.h file before we actually compile
@ -147,9 +150,9 @@ $(OBJ): | $(HEADER_BUILD)/pins.h
# Use a pattern rule here so that make will only call make-pins.py once to make
# both pins_$(BOARD).c and pins.h
$(BUILD)/%_gen.c $(HEADER_BUILD)/%.h: teensy-%.csv $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE)
$(BUILD)/%_gen.c $(HEADER_BUILD)/%.h $(HEADER_BUILD)/%-af-const.h $(BUILD)/%_qstr.h: teensy-%.csv $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE) | $(HEADER_BUILD)
$(ECHO) "Create $@"
$(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) > $(GEN_PINS_SRC)
$(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) --qstr $(GEN_PINS_QSTR) --af-const $(GEN_PINS_AF_CONST) > $(GEN_PINS_SRC)
$(BUILD)/pins_gen.o: $(BUILD)/pins_gen.c
$(call compile_c)

View File

@ -25,11 +25,17 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
if ((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)) {
/* Check the Alternate function parameter */
assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
/* Configure Alternate function mapped with the current IO */
}
else if (GPIO_Init->Mode == GPIO_MODE_ANALOG) {
GPIO_Init->Alternate = 0;
}
else {
GPIO_Init->Alternate = 1;
}
/* Configure Alternate function mapped with the current IO */
*port_pcr &= ~PORT_PCR_MUX_MASK;
*port_pcr |= PORT_PCR_MUX(GPIO_Init->Alternate);
}
/* Configure IO Direction mode (Input, Output, Alternate or Analog) */
if (GPIO_Init->Mode == GPIO_MODE_INPUT || GPIO_Init->Mode == GPIO_MODE_ANALOG) {
@ -112,4 +118,3 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
#endif
}
}

View File

@ -24,7 +24,7 @@
#include "usb.h"
#include "led.h"
#include "uart.h"
//#include "pin.h"
#include "pin.h"
#include "pybstdio.h"
@ -272,7 +272,7 @@ soft_reset:
readline_init0();
//pin_init();
pin_init0();
#if 0
// add some functions to the python namespace

View File

@ -71,6 +71,9 @@ class AlternateFunction(object):
return self.func
return '{:s}{:d}'.format(self.func, self.fn_num)
def mux_name(self):
return 'AF{:d}_{:s}'.format(self.idx, self.ptr())
def print(self):
"""Prints the C representation of this AF."""
if self.supported:
@ -83,6 +86,9 @@ class AlternateFunction(object):
print('({:2d}, {:8s}, {:2d}, {:10s}, {:8s}), // {:s}'.format(self.idx,
self.func, fn_num, self.pin_type, self.ptr(), self.af_str))
def qstr_list(self):
return [self.mux_name()]
class Pin(object):
"""Holds the information associated with a pin."""
@ -169,6 +175,14 @@ class Pin(object):
hdr_file.write('extern const pin_af_obj_t pin_{:s}_af[];\n'.
format(self.cpu_pin_name()))
def qstr_list(self):
result = []
for alt_fn in self.alt_fn:
if alt_fn.is_supported():
result += alt_fn.qstr_list()
return result
class NamedPin(object):
def __init__(self, name, pin):
@ -222,13 +236,13 @@ class Pins(object):
self.board_pins.append(NamedPin(row[0], pin))
def print_named(self, label, named_pins):
print('const pin_named_pin_t pin_{:s}_pins[] = {{'.format(label))
print('STATIC const mp_map_elem_t pin_{:s}_pins_locals_dict_table[] = {{'.format(label))
for named_pin in named_pins:
pin = named_pin.pin()
if pin.is_board_pin():
print(' {{ "{:s}", &pin_{:s} }},'.format(named_pin.name(), pin.cpu_pin_name()))
print(' { NULL, NULL }')
print(' {{ MP_OBJ_NEW_QSTR(MP_QSTR_{:s}), (mp_obj_t)&pin_{:s} }},'.format(named_pin.name(), pin.cpu_pin_name()))
print('};')
print('MP_DEFINE_CONST_DICT(pin_{:s}_pins_locals_dict, pin_{:s}_pins_locals_dict_table);'.format(label, label));
def print(self):
for named_pin in self.cpu_pins:
@ -266,6 +280,39 @@ class Pins(object):
hdr_file.write('extern const pin_obj_t * const pin_adc2[];\n')
hdr_file.write('extern const pin_obj_t * const pin_adc3[];\n')
def print_qstr(self, qstr_filename):
with open(qstr_filename, 'wt') as qstr_file:
qstr_set = set([])
for named_pin in self.cpu_pins:
pin = named_pin.pin()
if pin.is_board_pin():
qstr_set |= set(pin.qstr_list())
qstr_set |= set([named_pin.name()])
for named_pin in self.board_pins:
qstr_set |= set([named_pin.name()])
for qstr in sorted(qstr_set):
print('Q({})'.format(qstr), file=qstr_file)
def print_af_hdr(self, af_const_filename):
with open(af_const_filename, 'wt') as af_const_file:
af_hdr_set = set([])
mux_name_width = 0
for named_pin in self.cpu_pins:
pin = named_pin.pin()
if pin.is_board_pin():
for af in pin.alt_fn:
if af.is_supported():
mux_name = af.mux_name()
af_hdr_set |= set([mux_name])
if len(mux_name) > mux_name_width:
mux_name_width = len(mux_name)
for mux_name in sorted(af_hdr_set):
key = 'MP_OBJ_NEW_QSTR(MP_QSTR_{}),'.format(mux_name)
val = 'MP_OBJ_NEW_SMALL_INT(GPIO_{})'.format(mux_name)
print(' { %-*s %s },' % (mux_name_width + 26, key, val),
file=af_const_file)
def main():
parser = argparse.ArgumentParser(
@ -279,6 +326,12 @@ def main():
help="Specifies the alternate function file for the chip",
default="stm32f4xx-af.csv"
)
parser.add_argument(
"--af-const",
dest="af_const_filename",
help="Specifies header file for alternate function constants.",
default="build/pins-af-const.h"
)
parser.add_argument(
"-b", "--board",
dest="board_filename",
@ -290,6 +343,12 @@ def main():
help="Specifies beginning portion of generated pins file",
default="stm32f4xx-prefix.c"
)
parser.add_argument(
"-q", "--qstr",
dest="qstr_filename",
help="Specifies name of generated qstr header file",
default="build/pins-qstr.h"
)
parser.add_argument(
"-r", "--hdr",
dest="hdr_filename",
@ -320,6 +379,8 @@ def main():
pins.print_adc(2)
pins.print_adc(3)
pins.print_header(args.hdr_filename)
pins.print_qstr(args.qstr_filename)
pins.print_af_hdr(args.af_const_filename)
if __name__ == "__main__":

View File

@ -1,2 +1,12 @@
import pyb
print("Executing boot.py")
def pins():
for pin_name in dir(pyb.Pin.board):
pin = pyb.Pin(pin_name)
print('{:10s} {:s}'.format(pin_name, str(pin)))
def af():
for pin_name in dir(pyb.Pin.board):
pin = pyb.Pin(pin_name)
print('{:10s} {:s}'.format(pin_name, str(pin.af_list())))

View File

@ -15,6 +15,7 @@
#define AF(af_idx, af_fn, af_unit, af_type, af_ptr) \
{ \
{ &pin_af_type }, \
.name = MP_QSTR_AF ## af_idx ## _ ## af_fn ## af_unit, \
.idx = (af_idx), \
.fn = AF_FN_ ## af_fn, \
.unit = (af_unit), \
@ -25,7 +26,7 @@
#define PIN(p_port, p_pin, p_num_af, p_af, p_adc_num, p_adc_channel) \
{ \
{ &pin_type }, \
.name = #p_port #p_pin, \
.name = MP_QSTR_ ## p_port ## p_pin, \
.port = PORT_ ## p_port, \
.pin = (p_pin), \
.num_af = (p_num_af), \

61
teensy/pin_defs_teensy.c Normal file
View File

@ -0,0 +1,61 @@
#include <stdint.h>
#include <mk20dx128.h>
#include "mpconfig.h"
#include "nlr.h"
#include "misc.h"
#include "qstr.h"
#include "obj.h"
#include "runtime.h"
#include MICROPY_HAL_H
#include "pin.h"
// Returns the pin mode. This value returned by this macro should be one of:
// GPIO_MODE_INPUT, GPIO_MODE_OUTPUT_PP, GPIO_MODE_OUTPUT_OD,
// GPIO_MODE_AF_PP, GPIO_MODE_AF_OD, or GPIO_MODE_ANALOG.
uint32_t pin_get_mode(const pin_obj_t *pin) {
volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin);
uint32_t pcr = *port_pcr;
uint32_t af = (*port_pcr & PORT_PCR_MUX_MASK) >> 8;;
if (af == 0) {
return GPIO_MODE_ANALOG;
}
if (af == 1) {
if (pin->gpio->PDDR & (1 << pin->pin)) {
if (pcr & PORT_PCR_ODE) {
return GPIO_MODE_OUTPUT_OD;
}
return GPIO_MODE_OUTPUT_PP;
}
return GPIO_MODE_INPUT;
}
if (pcr & PORT_PCR_ODE) {
return GPIO_MODE_AF_OD;
}
return GPIO_MODE_AF_PP;
}
// Returns the pin pullup/pulldown. The value returned by this macro should
// be one of GPIO_NOPULL, GPIO_PULLUP, or GPIO_PULLDOWN.
uint32_t pin_get_pull(const pin_obj_t *pin) {
volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin);
uint32_t pcr = *port_pcr;
if (pcr & PORT_PCR_PE) {
if (pcr & PORT_PCR_PS) {
return GPIO_PULLUP;
}
return GPIO_PULLDOWN;
}
return GPIO_NOPULL;
}
// Returns the af (alternate function) index currently set for a pin.
uint32_t pin_get_af(const pin_obj_t *pin) {
volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(pin->gpio, pin->pin);
return (*port_pcr & PORT_PCR_MUX_MASK) >> 8;
}

View File

@ -43,4 +43,3 @@ enum {
SPI_TypeDef *SPI;
typedef GPIO_TypeDef pin_gpio_t;

View File

@ -62,13 +62,21 @@ Q(value)
Q(low)
Q(high)
Q(name)
Q(names)
Q(af)
Q(af_list)
Q(port)
Q(pin)
Q(gpio)
Q(mapper)
Q(dict)
Q(debug)
Q(board)
Q(cpu)
Q(mode)
Q(pull)
Q(index)
Q(reg)
Q(IN)
Q(OUT_PP)
Q(OUT_OD)

View File

@ -81,6 +81,7 @@ typedef struct {
#define GPIO_SPEED_FAST ((uint32_t)2)
#define GPIO_SPEED_HIGH ((uint32_t)3)
#define IS_GPIO_AF(af) ((af) >= 0 && (af) <= 7)
typedef struct {
uint32_t Pin;
@ -96,6 +97,22 @@ typedef struct {
#define GPIO_PIN_TO_PORT_PCR(GPIOx, pin) \
(&PORTA_PCR0 + GPIO_PORT_TO_PORT_NUM(GPIOx) * 32 + (pin))
#define GPIO_AF2_I2C0 2
#define GPIO_AF2_I2C1 2
#define GPIO_AF2_SPI0 2
#define GPIO_AF3_FTM0 3
#define GPIO_AF3_FTM1 3
#define GPIO_AF3_FTM2 3
#define GPIO_AF3_UART0 3
#define GPIO_AF3_UART1 3
#define GPIO_AF3_UART2 3
#define GPIO_AF4_FTM0 4
#define GPIO_AF6_FTM1 6
#define GPIO_AF6_FTM2 6
#define GPIO_AF6_I2C1 6
#define GPIO_AF7_FTM1 7
__attribute__(( always_inline )) static inline void __WFI(void)
{
__asm volatile ("wfi");
@ -109,3 +126,4 @@ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *init);
#define GPIO_read_pin(gpio, pin) (((gpio)->PDIR >> (pin)) & 1)
#define GPIO_set_pin(gpio, pin_mask) (((gpio)->PSOR) = (pin_mask))
#define GPIO_clear_pin(gpio, pin_mask) (((gpio)->PCOR) = (pin_mask))
#define GPIO_read_output_pin(gpio, pin) (((gpio)->PDOR >> (pin)) & 1)