diff --git a/stmhal/accel.c b/stmhal/accel.c index f93833d1c3..d3373f1ebd 100644 --- a/stmhal/accel.c +++ b/stmhal/accel.c @@ -14,6 +14,13 @@ #if MICROPY_HW_HAS_MMA7660 +/// \moduleref pyb +/// \class Accel - accelerometer control +/// +/// Accel is an object that controls the accelerometer. +/// +/// Raw values are between -30 and 30. + #define MMA_ADDR (0x98) #define MMA_REG_X (0) #define MMA_REG_Y (1) @@ -83,6 +90,8 @@ typedef struct _pyb_accel_obj_t { STATIC pyb_accel_obj_t pyb_accel_obj; +/// \classmethod \constructor() +/// Create and return an accelerometer object. STATIC mp_obj_t pyb_accel_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -100,32 +109,38 @@ STATIC mp_obj_t read_axis(int axis) { return mp_obj_new_int(MMA_AXIS_SIGNED_VALUE(data[0])); } +/// \method x() +/// Get the x-axis value. STATIC mp_obj_t pyb_accel_x(mp_obj_t self_in) { return read_axis(MMA_REG_X); } - STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_x_obj, pyb_accel_x); +/// \method y() +/// Get the y-axis value. STATIC mp_obj_t pyb_accel_y(mp_obj_t self_in) { return read_axis(MMA_REG_Y); } - STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_y_obj, pyb_accel_y); +/// \method z() +/// Get the z-axis value. STATIC mp_obj_t pyb_accel_z(mp_obj_t self_in) { return read_axis(MMA_REG_Z); } - STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_z_obj, pyb_accel_z); +/// \method tilt() +/// Get the tilt register. STATIC mp_obj_t pyb_accel_tilt(mp_obj_t self_in) { uint8_t data[1]; HAL_I2C_Mem_Read(&I2CHandle1, MMA_ADDR, MMA_REG_TILT, I2C_MEMADD_SIZE_8BIT, data, 1, 200); return mp_obj_new_int(data[0]); } - STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_tilt_obj, pyb_accel_tilt); +/// \method filtered_xyz() +/// Get a 3-tuple of filtered x, y and z values. STATIC mp_obj_t pyb_accel_filtered_xyz(mp_obj_t self_in) { pyb_accel_obj_t *self = self_in; @@ -146,7 +161,6 @@ STATIC mp_obj_t pyb_accel_filtered_xyz(mp_obj_t self_in) { return mp_obj_new_tuple(3, tuple); } - STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_filtered_xyz_obj, pyb_accel_filtered_xyz); STATIC mp_obj_t pyb_accel_read(mp_obj_t self_in, mp_obj_t reg) { @@ -154,7 +168,6 @@ STATIC mp_obj_t pyb_accel_read(mp_obj_t self_in, mp_obj_t reg) { HAL_I2C_Mem_Read(&I2CHandle1, MMA_ADDR, mp_obj_get_int(reg), I2C_MEMADD_SIZE_8BIT, data, 1, 200); return mp_obj_new_int(data[0]); } - MP_DEFINE_CONST_FUN_OBJ_2(pyb_accel_read_obj, pyb_accel_read); STATIC mp_obj_t pyb_accel_write(mp_obj_t self_in, mp_obj_t reg, mp_obj_t val) { @@ -163,7 +176,6 @@ STATIC mp_obj_t pyb_accel_write(mp_obj_t self_in, mp_obj_t reg, mp_obj_t val) { HAL_I2C_Mem_Write(&I2CHandle1, MMA_ADDR, mp_obj_get_int(reg), I2C_MEMADD_SIZE_8BIT, data, 1, 200); return mp_const_none; } - MP_DEFINE_CONST_FUN_OBJ_3(pyb_accel_write_obj, pyb_accel_write); STATIC const mp_map_elem_t pyb_accel_locals_dict_table[] = { diff --git a/stmhal/adc.c b/stmhal/adc.c index d7d2cf0893..0cc2a9a904 100644 --- a/stmhal/adc.c +++ b/stmhal/adc.c @@ -14,16 +14,19 @@ #include "genhdr/pins.h" #include "timer.h" -// Usage Model: -// -// adc = pyb.ADC(pin) -// val = adc.read() -// -// adc = pyb.ADCAll(resolution) -// val = adc.read_channel(channel) -// val = adc.read_core_temp() -// val = adc.read_core_vbat() -// val = adc.read_core_vref() +/// \moduleref pyb +/// \class ADC - analog to digital conversion: read analog values on a pin +/// +/// Usage: +/// +/// adc = pyb.ADC(pin) # create an analog object from a pin +/// val = adc.read() # read an analog value +/// +/// adc = pyb.ADCAll(resolution) # creale an ADCAll object +/// val = adc.read_channel(channel) # read the given channel +/// val = adc.read_core_temp() # read MCU temperature +/// val = adc.read_core_vbat() # read MCU VBAT +/// val = adc.read_core_vref() # read MCU VREF /* ADC defintions */ #define ADCx (ADC1) @@ -118,6 +121,9 @@ STATIC void adc_print(void (*print)(void *env, const char *fmt, ...), void *env, print(env, " channel=%lu>", self->channel); } +/// \classmethod \constructor(pin) +/// Create an ADC object associated with the given pin. +/// This allows you to then read analog values on that pin. STATIC mp_obj_t adc_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // check number of arguments mp_arg_check_num(n_args, n_kw, 1, 1, false); @@ -155,15 +161,30 @@ STATIC mp_obj_t adc_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_ return o; } +/// \method read() +/// Read the value on the analog pin and return it. The returned value +/// will be between 0 and 4095. STATIC mp_obj_t adc_read(mp_obj_t self_in) { pyb_obj_adc_t *self = self_in; uint32_t data = adc_read_channel(&self->handle); return mp_obj_new_int(data); } - STATIC MP_DEFINE_CONST_FUN_OBJ_1(adc_read_obj, adc_read); +/// \method read_timed(buf, freq) +/// Read analog values into the given buffer at the given frequency. +/// +/// Example: +/// +/// adc = pyb.ADC(pyb.Pin.board.X19) # create an ADC on pin X19 +/// buf = bytearray(100) # create a buffer of 100 bytes +/// adc.read_timed(buf, 10) # read analog values into buf at 10Hz +/// # this will take 10 seconds to finish +/// for val in buf: # loop over all values +/// print(val) # print the value out +/// +/// This function does not allocate any memory. STATIC mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_in) { pyb_obj_adc_t *self = self_in; @@ -196,7 +217,6 @@ STATIC mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_ return mp_obj_new_int(bufinfo.len); } - STATIC MP_DEFINE_CONST_FUN_OBJ_3(adc_read_timed_obj, adc_read_timed); STATIC const mp_map_elem_t adc_locals_dict_table[] = { diff --git a/stmhal/dac.c b/stmhal/dac.c index 73f1fb2593..cd62f017fb 100644 --- a/stmhal/dac.c +++ b/stmhal/dac.c @@ -13,6 +13,10 @@ #include "timer.h" #include "dac.h" +/// \moduleref pyb +/// \class DAC - digital to analog conversion +/// + STATIC DAC_HandleTypeDef DAC_Handle; void dac_init(void) { diff --git a/stmhal/extint.c b/stmhal/extint.c index 289a519762..346e2851ee 100644 --- a/stmhal/extint.c +++ b/stmhal/extint.c @@ -15,58 +15,52 @@ #include "pin.h" #include "extint.h" -// Usage Model: -// -// There are a total of 22 interrupt lines. 16 of these can come from GPIO pins -// and the remaining 6 are from internal sources. -// -// For lines 0 thru 15, a given line can map to the corresponding line from an -// arbitrary port. So line 0 can map to Px0 where x is A, B, C, ... and -// line 1 can map to Px1 where x is A, B, C, ... -// -// def callback(line): -// print("line =", line) -// -// # Note: ExtInt will automatically configure the gpio line as an input. -// extint = pyb.ExtInt(pin, pyb.ExtInt.IRQ_FALLING, pyb.GPIO.PULL_UP, callback) -// -// Now every time a falling edge is seen on the X1 pin, the callback will be -// called. Caution: mechanical pushbuttons have "bounce" and pushing or -// releasing a switch will often generate multiple edges. -// See: http://www.eng.utah.edu/~cs5780/debouncing.pdf for a detailed -// explanation, along with various techniques for debouncing. -// -// Trying to register 2 callbacks onto the same pin will throw an exception. -// -// If pin is passed as an integer, then it is assumed to map to one of the -// internal interrupt sources, and must be in the range 16 thru 22. -// -// All other pin objects go through the pin mapper to come up with one of the -// gpio pins. -// -// extint = pyb.ExtInt(pin, mode, pull, callback) -// -// Valid modes are pyb.ExtInt.IRQ_RISING, pyb.ExtInt.IRQ_FALLING, -// pyb.ExtInt.IRQ_RISING_FALLING, pyb.ExtInt.EVT_RISING, -// pyb.ExtInt.EVT_FALLING, and pyb.ExtInt.EVT_RISING_FALLING. -// -// Only the IRQ_xxx modes have been tested. The EVT_xxx modes have -// something to do with sleep mode and the WFE instruction. -// -// Valid pull values are pyb.GPIO.PULL_UP, pyb.GPIO.PULL_DOWN, pyb.GPIO.PULL_NONE. -// -// extint.line() will return the line number that pin was mapped to. -// extint.disable() can be use to disable the interrupt associated with a given -// exti object. This could be useful for debouncing. -// extint.enable() enables a disabled interrupt -// extint.swint() will allow the callback to be triggered from software. -// -// pyb.ExtInt.regs() will dump the values of the EXTI registers. -// -// There is also a C API, so that drivers which require EXTI interrupt lines -// can also use this code. See extint.h for the available functions and -// usrsw.h for an example of using this. -// +/// \moduleref pyb +/// \class ExtInt - configure I/O pins to interrupt on external events +/// +/// There are a total of 22 interrupt lines. 16 of these can come from GPIO pins +/// and the remaining 6 are from internal sources. +/// +/// For lines 0 thru 15, a given line can map to the corresponding line from an +/// arbitrary port. So line 0 can map to Px0 where x is A, B, C, ... and +/// line 1 can map to Px1 where x is A, B, C, ... +/// +/// def callback(line): +/// print("line =", line) +/// +/// Note: ExtInt will automatically configure the gpio line as an input. +/// +/// extint = pyb.ExtInt(pin, pyb.ExtInt.IRQ_FALLING, pyb.GPIO.PULL_UP, callback) +/// +/// Now every time a falling edge is seen on the X1 pin, the callback will be +/// called. Caution: mechanical pushbuttons have "bounce" and pushing or +/// releasing a switch will often generate multiple edges. +/// See: http://www.eng.utah.edu/~cs5780/debouncing.pdf for a detailed +/// explanation, along with various techniques for debouncing. +/// +/// Trying to register 2 callbacks onto the same pin will throw an exception. +/// +/// If pin is passed as an integer, then it is assumed to map to one of the +/// internal interrupt sources, and must be in the range 16 thru 22. +/// +/// All other pin objects go through the pin mapper to come up with one of the +/// gpio pins. +/// +/// extint = pyb.ExtInt(pin, mode, pull, callback) +/// +/// Valid modes are pyb.ExtInt.IRQ_RISING, pyb.ExtInt.IRQ_FALLING, +/// pyb.ExtInt.IRQ_RISING_FALLING, pyb.ExtInt.EVT_RISING, +/// pyb.ExtInt.EVT_FALLING, and pyb.ExtInt.EVT_RISING_FALLING. +/// +/// Only the IRQ_xxx modes have been tested. The EVT_xxx modes have +/// something to do with sleep mode and the WFE instruction. +/// +/// Valid pull values are pyb.GPIO.PULL_UP, pyb.GPIO.PULL_DOWN, pyb.GPIO.PULL_NONE. +/// +/// There is also a C API, so that drivers which require EXTI interrupt lines +/// can also use this code. See extint.h for the available functions and +/// usrsw.h for an example of using this. + // TODO Add python method to change callback object. #define EXTI_OFFSET (EXTI_BASE - PERIPH_BASE) @@ -204,29 +198,45 @@ void extint_swint(uint line) { EXTI->SWIER = (1 << line); } +/// \method line() +/// Return the line number that the pin is mapped to. STATIC mp_obj_t extint_obj_line(mp_obj_t self_in) { extint_obj_t *self = self_in; return MP_OBJ_NEW_SMALL_INT(self->line); } +STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_line_obj,i extint_obj_line); +/// \method enable() +/// Enable a disabled interrupt. STATIC mp_obj_t extint_obj_enable(mp_obj_t self_in) { extint_obj_t *self = self_in; extint_enable(self->line); return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_enable_obj, extint_obj_enable); +/// \method disable() +/// Disable the interrupt associated with the ExtInt object. +/// This could be useful for debouncing. STATIC mp_obj_t extint_obj_disable(mp_obj_t self_in) { extint_obj_t *self = self_in; extint_disable(self->line); return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_disable_obj, extint_obj_disable); +/// \method swint() +/// Trigger the callback from software. STATIC mp_obj_t extint_obj_swint(mp_obj_t self_in) { extint_obj_t *self = self_in; extint_swint(self->line); return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint); +// TODO document as a staticmethod +/// \classmethod regs() +/// Dump the values of the EXTI registers. STATIC mp_obj_t extint_regs(void) { printf("EXTI_IMR %08lx\n", EXTI->IMR); printf("EXTI_EMR %08lx\n", EXTI->EMR); @@ -236,9 +246,24 @@ STATIC mp_obj_t extint_regs(void) { printf("EXTI_PR %08lx\n", EXTI->PR); return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_0(extint_regs_fun_obj, extint_regs); +STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(extint_regs_obj, (mp_obj_t)&extint_regs_fun_obj); -// line_obj = pyb.ExtInt(pin, mode, pull, callback) - +/// \classmethod \constructor(pin, mode, pull, callback) +/// Create an ExtInt object: +/// +/// - `pin` is the pin on which to enable the interrupt (can be a pin object or any valid pin name). +/// - `mode` can be one of: +/// - `ExtInt.IRQ_RISING` - trigger on a rising edge; +/// - `ExtInt.IRQ_FALLING` - trigger on a falling edge; +/// - `ExtInt.IRQ_RISING_FALLING` - trigger on a rising or falling edge. +/// - `pull` can be one of: +/// - `pyb.Pin.PULL_NONE` - no pull up or down resistors; +/// - `pyb.Pin.PULL_UP` - enable the pull-up resistor; +/// - `pyb.Pin.PULL_DOWN` - enable the pull-down resistor. +/// - `callback` is the function to call when the interrupt triggers. The +/// callback function must accept exactly 1 argument, which is the line that +/// triggered the interrupt. STATIC const mp_arg_t pyb_extint_make_new_args[] = { { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, @@ -268,19 +293,17 @@ STATIC void extint_obj_print(void (*print)(void *env, const char *fmt, ...), voi print(env, "", self->line); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_line_obj, extint_obj_line); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_enable_obj, extint_obj_enable); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_disable_obj, extint_obj_disable); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(extint_regs_fun_obj, extint_regs); -STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(extint_regs_obj, (mp_obj_t)&extint_regs_fun_obj); - STATIC const mp_map_elem_t extint_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_line), (mp_obj_t)&extint_obj_line_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_enable), (mp_obj_t)&extint_obj_enable_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_disable), (mp_obj_t)&extint_obj_disable_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_swint), (mp_obj_t)&extint_obj_swint_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_regs), (mp_obj_t)&extint_regs_obj }, + + // class constants + /// \constant IRQ_RISING - interrupt on a rising edge + /// \constant IRQ_FALLING - interrupt on a falling edge + /// \constant IRQ_RISING_FALLING - interrupt on a rising or falling edge { MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_RISING), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_IT_RISING) }, { MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_FALLING), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_IT_FALLING) }, { MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_RISING_FALLING), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_IT_RISING_FALLING) }, diff --git a/stmhal/gendoc.py b/stmhal/gendoc.py new file mode 100644 index 0000000000..634de6f093 --- /dev/null +++ b/stmhal/gendoc.py @@ -0,0 +1,356 @@ +""" +Generate documentation for pyboard API from C files. +""" + +import os +import argparse +import re +import markdown + +# given a list of (name,regex) pairs, find the first one that matches the given line +def re_match_first(regexs, line): + for name, regex in regexs: + match = re.match(regex, line) + if match: + return name, match + return None, None + +def makedirs(d): + if not os.path.isdir(d): + os.makedirs(d) + +class Lexer: + class LexerError(Exception): + pass + + class EOF(Exception): + pass + + class Break(Exception): + pass + + def __init__(self, file): + self.filename = file + with open(file, 'rt') as f: + line_num = 0 + lines = [] + for line in f: + line_num += 1 + line = line.strip() + if line == '///': + lines.append((line_num, '')) + elif line.startswith('/// '): + lines.append((line_num, line[4:])) + elif len(lines) > 0 and lines[-1][1] is not None: + lines.append((line_num, None)) + if len(lines) > 0 and lines[-1][1] is not None: + lines.append((line_num, None)) + self.cur_line = 0 + self.lines = lines + + def opt_break(self): + if len(self.lines) > 0 and self.lines[0][1] is None: + self.lines.pop(0) + + def next(self): + if len(self.lines) == 0: + raise Lexer.EOF + else: + l = self.lines.pop(0) + self.cur_line = l[0] + if l[1] is None: + raise Lexer.Break + else: + return l[1] + + def error(self, msg): + print('({}:{}) {}'.format(self.filename, self.cur_line, msg)) + raise Lexer.LexerError + +class DocItem: + def __init__(self): + self.doc = [] + + def add_doc(self, lex): + try: + while True: + line = lex.next() + if len(line) > 0 or len(self.doc) > 0: + self.doc.append(line) + except Lexer.Break: + pass + + def dump(self): + return '\n'.join(self.doc) + +class DocConstant(DocItem): + def __init__(self, name, descr): + super().__init__() + self.name = name + self.descr = descr + + def dump(self, ctx): + return '{}.{} - {}'.format(ctx, self.name, self.descr) + +class DocFunction(DocItem): + def __init__(self, name, args): + super().__init__() + self.name = name + self.args = args + + def dump(self, ctx): + if self.name == '\\constructor': + s = '### `{}{}`'.format(ctx, self.args) + elif self.name == '\\call': + s = '### `{}{}`'.format(ctx, self.args) + else: + s = '### `{}.{}{}`'.format(ctx, self.name, self.args) + return s + '\n' + super().dump() + +class DocClass(DocItem): + def __init__(self, name, descr): + super().__init__() + self.name = name + self.descr = descr + self.constructors = {} + self.classmethods = {} + self.methods = {} + self.constants = {} + + def process_classmethod(self, lex, d): + name = d['id'] + if name == '\\constructor': + dict_ = self.constructors + else: + dict_ = self.classmethods + if name in dict_: + lex.error("multiple definition of method '{}'".format(name)) + method = dict_[name] = DocFunction(name, d['args']) + method.add_doc(lex) + + def process_method(self, lex, d): + name = d['id'] + dict_ = self.methods + if name in dict_: + lex.error("multiple definition of method '{}'".format(name)) + method = dict_[name] = DocFunction(name, d['args']) + method.add_doc(lex) + + def process_constant(self, lex, d): + name = d['id'] + if name in self.constants: + lex.error("multiple definition of constant '{}'".format(name)) + self.constants[name] = DocConstant(name, d['descr']) + lex.opt_break() + + def dump(self): + s = [] + s.append('') + s.append('# class {}'.format(self.name)) + s.append('') + s.append(super().dump()) + if len(self.constructors) > 0: + s.append('') + s.append("## Constructors") + for f in sorted(self.constructors.values(), key=lambda x:x.name): + s.append('') + s.append(f.dump(self.name)) + if len(self.classmethods) > 0: + s.append('') + s.append("## Class methods") + for f in sorted(self.classmethods.values(), key=lambda x:x.name): + s.append('') + s.append(f.dump(self.name)) + if len(self.methods) > 0: + s.append('') + s.append("## Methods") + for f in sorted(self.methods.values(), key=lambda x:x.name): + s.append('') + s.append(f.dump(self.name.lower())) + if len(self.constants) > 0: + s.append('') + s.append("## Constants") + for c in sorted(self.constants.values(), key=lambda x:x.name): + s.append('') + s.append('`{}`'.format(c.dump(self.name))) + return '\n'.join(s) + +class DocModule(DocItem): + def __init__(self, name, descr): + super().__init__() + self.name = name + self.descr = descr + self.functions = {} + self.constants = {} + self.classes = {} + self.cur_class = None + + def new_file(self): + self.cur_class = None + + def process_function(self, lex, d): + name = d['id'] + if name in self.functions: + lex.error("multiple definition of function '{}'".format(name)) + function = self.functions[name] = DocFunction(name, d['args']) + function.add_doc(lex) + + #def process_classref(self, lex, d): + # name = d['id'] + # self.classes[name] = name + # lex.opt_break() + + def process_class(self, lex, d): + name = d['id'] + if name in self.classes: + lex.error("multiple definition of class '{}'".format(name)) + self.cur_class = self.classes[name] = DocClass(name, d['descr']) + self.cur_class.add_doc(lex) + + def process_classmethod(self, lex, d): + self.cur_class.process_classmethod(lex, d) + + def process_method(self, lex, d): + self.cur_class.process_method(lex, d) + + def process_constant(self, lex, d): + self.cur_class.process_constant(lex, d) + + def dump(self): + s = [] + s.append('# module {}'.format(self.name)) + s.append('') + s.append(super().dump()) + s.append('') + s.append('## Functions') + for f in sorted(self.functions.values(), key=lambda x:x.name): + s.append('') + s.append(f.dump(self.name)) + s.append('') + s.append('## Classes') + for c in sorted(self.classes.values(), key=lambda x:x.name): + s.append('') + s.append('[`{}.{}`]({}/index.html) - {}'.format(self.name, c.name, c.name, c.descr)) + return '\n'.join(s) + + def write(self, dir): + index = markdown.markdown(self.dump()) + with open(os.path.join(dir, 'index.html'), 'wt') as f: + f.write(index) + for c in self.classes.values(): + class_dir = os.path.join(dir, c.name) + makedirs(class_dir) + class_dump = c.dump() + class_dump = 'part of the [{} module](../index.html)'.format(self.name) + '\n' + class_dump + index = markdown.markdown(class_dump) + with open(os.path.join(class_dir, 'index.html'), 'wt') as f: + f.write(index) + +class Doc: + def __init__(self): + self.modules = {} + self.cur_module = None + + def new_file(self): + self.cur_module = None + for m in self.modules.values(): + m.new_file() + + def check_module(self, lex): + if self.cur_module is None: + lex.error('module not defined') + + def process_module(self, lex, d): + name = d['id'] + if name in self.modules: + lex.error("multiple definition of module '{}'".format(name)) + self.cur_module = self.modules[name] = DocModule(name, d['descr']) + self.cur_module.add_doc(lex) + + def process_moduleref(self, lex, d): + name = d['id'] + if name not in self.modules: + lex.error('module {} referenced before definition'.format(name)) + self.cur_module = self.modules[name] + + #def process_classref(self, lex, d): + # self.cur_module.process_classref(lex, d) + + def process_class(self, lex, d): + self.check_module(lex) + self.cur_module.process_class(lex, d) + + def process_function(self, lex, d): + self.check_module(lex) + self.cur_module.process_function(lex, d) + + def process_classmethod(self, lex, d): + self.check_module(lex) + self.cur_module.process_classmethod(lex, d) + + def process_method(self, lex, d): + self.check_module(lex) + self.cur_module.process_method(lex, d) + + def process_constant(self, lex, d): + self.check_module(lex) + self.cur_module.process_constant(lex, d) + + def write(self, dir): + for m in self.modules.values(): + mod_dir = os.path.join(dir, 'module', m.name) + makedirs(mod_dir) + m.write(mod_dir) + +regex_descr = r'(?P.*)' + +doc_regexs = ( + (Doc.process_module, re.compile(r'\\module (?P[a-z]+) - ' + regex_descr + r'$')), + (Doc.process_moduleref, re.compile(r'\\moduleref (?P[a-z]+)$')), + (Doc.process_function, re.compile(r'\\function (?P[a-z0-9_]+)(?P\(.*\))$')), + (Doc.process_classmethod, re.compile(r'\\classmethod (?P\\?[a-z0-9_]+)(?P\(.*\))$')), + (Doc.process_method, re.compile(r'\\method (?P\\?[a-z0-9_]+)(?P\(.*\))$')), + (Doc.process_constant, re.compile(r'\\constant (?P[A-Z0-9_]+) - ' + regex_descr + r'$')), + #(Doc.process_classref, re.compile(r'\\classref (?P[A-Za-z0-9_]+)$')), + (Doc.process_class, re.compile(r'\\class (?P[A-Za-z0-9_]+) - ' + regex_descr + r'$')), +) + +def process_file(file, doc): + lex = Lexer(file) + doc.new_file() + try: + try: + while True: + line = lex.next() + fun, match = re_match_first(doc_regexs, line) + if fun == None: + lex.error('unknown line format: {}'.format(line)) + fun(doc, lex, match.groupdict()) + + except Lexer.Break: + lex.error('unexpected break') + + except Lexer.EOF: + pass + + except Lexer.LexerError: + return False + + return True + +def main(): + cmd_parser = argparse.ArgumentParser(description='Generate documentation for pyboard API from C files.') + cmd_parser.add_argument('--outdir', metavar='', default='gendoc-out', help='ouput directory') + cmd_parser.add_argument('files', nargs='+', help='input files') + args = cmd_parser.parse_args() + + doc = Doc() + for file in args.files: + print('processing', file) + if not process_file(file, doc): + return + doc.write(args.outdir) + print('written to', args.outdir) + +if __name__ == "__main__": + main() diff --git a/stmhal/i2c.c b/stmhal/i2c.c index 2c804a23d0..ef9da496d0 100644 --- a/stmhal/i2c.c +++ b/stmhal/i2c.c @@ -14,50 +14,54 @@ #include "bufhelper.h" #include "i2c.h" -// Usage model: -// -// I2C objects are created attached to a specific bus. They can be initialised -// when created, or initialised later on: -// -// from pyb import I2C -// -// i2c = I2C(1) # create on bus 1 -// i2c = I2C(1, I2C.MASTER) # create and init as a master -// i2c.deinit() # turn off the peripheral -// i2c.init(I2C.MASTER, baudrate=20000) # init as a master -// i2c.init(I2C.SLAVE, addr=0x42) # init as a slave with given address -// -// Printing the i2c object gives you information about its configuration. -// -// Basic methods for slave are send and recv: -// -// i2c.send('abc') # send 3 bytes -// i2c.send(0x42) # send a single byte, given by the number -// data = i2c.recv(3) # receive 3 bytes -// -// To receive inplace, first create a bytearray: -// -// data = bytearray(3) # create a buffer -// i2c.recv(data) # receive 3 bytes, writing them into data -// -// You can specify a timeout (in ms): -// -// i2c.send(b'123', timeout=2000) # timout after 2 seconds -// -// A master must specify the recipient's address: -// -// i2c.init(I2C.MASTER) -// i2c.send('123', 0x42) # send 3 bytes to slave with address 0x42 -// i2c.send(b'456', addr=0x42) # keyword for address -// -// Master also has other methods: -// -// i2c.is_ready(0x42) # check if slave 0x42 is ready -// i2c.scan() # scan for slaves on the bus, returning -// # a list of valid addresses -// i2c.mem_read(3, 0x42, 2) # read 3 bytes from memory of slave 0x42, -// # starting at address 2 in the slave -// i2c.mem_write('abc', 0x42, 2, timeout=1000) +/// \moduleref pyb +/// \class I2C - a two-wire serial protocol +/// +/// I2C is a two-wire protocol for communicating between devices. At the physical +/// level it consists of 2 wires: SCL and SDA, the clock and data lines respectively. +/// +/// I2C objects are created attached to a specific bus. They can be initialised +/// when created, or initialised later on: +/// +/// from pyb import I2C +/// +/// i2c = I2C(1) # create on bus 1 +/// i2c = I2C(1, I2C.MASTER) # create and init as a master +/// i2c.init(I2C.MASTER, baudrate=20000) # init as a master +/// i2c.init(I2C.SLAVE, addr=0x42) # init as a slave with given address +/// i2c.deinit() # turn off the peripheral +/// +/// Printing the i2c object gives you information about its configuration. +/// +/// Basic methods for slave are send and recv: +/// +/// i2c.send('abc') # send 3 bytes +/// i2c.send(0x42) # send a single byte, given by the number +/// data = i2c.recv(3) # receive 3 bytes +/// +/// To receive inplace, first create a bytearray: +/// +/// data = bytearray(3) # create a buffer +/// i2c.recv(data) # receive 3 bytes, writing them into data +/// +/// You can specify a timeout (in ms): +/// +/// i2c.send(b'123', timeout=2000) # timout after 2 seconds +/// +/// A master must specify the recipient's address: +/// +/// i2c.init(I2C.MASTER) +/// i2c.send('123', 0x42) # send 3 bytes to slave with address 0x42 +/// i2c.send(b'456', addr=0x42) # keyword for address +/// +/// Master also has other methods: +/// +/// i2c.is_ready(0x42) # check if slave 0x42 is ready +/// i2c.scan() # scan for slaves on the bus, returning +/// # a list of valid addresses +/// i2c.mem_read(3, 0x42, 2) # read 3 bytes from memory of slave 0x42, +/// # starting at address 2 in the slave +/// i2c.mem_write('abc', 0x42, 2, timeout=1000) #define PYB_I2C_MASTER (0) #define PYB_I2C_SLAVE (1) @@ -176,6 +180,14 @@ STATIC void pyb_i2c_print(void (*print)(void *env, const char *fmt, ...), void * } } +/// \method init(mode, *, addr=0x12, baudrate=400000, gencall=False) +/// +/// Initialise the I2C bus with the given parameters: +/// +/// - `mode` must be either `I2C.MASTER` or `I2C.SLAVE` +/// - `addr` is the 7-bit address (only sensible for a slave) +/// - `baudrate` is the SCL clock rate (only sensible for a master) +/// - `gencall` is whether to support general call mode STATIC const mp_arg_t pyb_i2c_init_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_addr, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0x12} }, @@ -213,6 +225,13 @@ STATIC mp_obj_t pyb_i2c_init_helper(const pyb_i2c_obj_t *self, uint n_args, cons return mp_const_none; } +/// \classmethod \constructor(bus, ...) +/// +/// Construct an I2C object on the given bus. `bus` can be 1 or 2. +/// With no additional parameters, the I2C object is created but not +/// initialised (it has the settings from the last initialisation of +/// the bus, if any). If extra arguments are given, the bus is initialised. +/// See `init` for parameters of initialisation. STATIC mp_obj_t pyb_i2c_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); @@ -243,6 +262,8 @@ STATIC mp_obj_t pyb_i2c_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_init_obj, 1, pyb_i2c_init); +/// \method deinit() +/// Turn off the I2C bus. STATIC mp_obj_t pyb_i2c_deinit(mp_obj_t self_in) { pyb_i2c_obj_t *self = self_in; i2c_deinit(self->i2c); @@ -250,7 +271,8 @@ STATIC mp_obj_t pyb_i2c_deinit(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2c_deinit_obj, pyb_i2c_deinit); -// Check if an I2C device responds to the given address. +/// \method is_ready(addr) +/// Check if an I2C device responds to the given address. Only valid when in master mode. STATIC mp_obj_t pyb_i2c_is_ready(mp_obj_t self_in, mp_obj_t i2c_addr_o) { pyb_i2c_obj_t *self = self_in; @@ -271,7 +293,9 @@ STATIC mp_obj_t pyb_i2c_is_ready(mp_obj_t self_in, mp_obj_t i2c_addr_o) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_i2c_is_ready_obj, pyb_i2c_is_ready); -// Scan all I2C addresses from 0x01 to 0x7f and return a list of those that respond. +/// \method scan() +/// Scan all I2C addresses from 0x01 to 0x7f and return a list of those that respond. +/// Only valid when in master mode. STATIC mp_obj_t pyb_i2c_scan(mp_obj_t self_in) { pyb_i2c_obj_t *self = self_in; @@ -295,6 +319,14 @@ STATIC mp_obj_t pyb_i2c_scan(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2c_scan_obj, pyb_i2c_scan); +/// \method send(send, addr=0x00, timeout=5000) +/// Send data on the bus: +/// +/// - `send` is the data to send (an integer to send, or a buffer object) +/// - `addr` is the address to send to (only required in master mode) +/// - `timeout` is the timeout in milliseconds to wait for the send +/// +/// Return value: `None`. STATIC const mp_arg_t pyb_i2c_send_args[] = { { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_addr, MP_ARG_INT, {.u_int = PYB_I2C_MASTER_ADDRESS} }, @@ -335,6 +367,17 @@ STATIC mp_obj_t pyb_i2c_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_send_obj, 1, pyb_i2c_send); +/// \method recv(send, addr=0x00, timeout=5000) +/// +/// Receive data on the bus: +/// +/// - `recv` can be an integer, which is the number of bytes to receive, +/// or a mutable buffer, which will be filled with received bytes +/// - `addr` is the address to receive from (only required in master mode) +/// - `timeout` is the timeout in milliseconds to wait for the receive +/// +/// Return value: if `recv` is an integer then a new buffer of the bytes received, +/// otherwise the same buffer that was passed in to `recv`. STATIC const mp_arg_t pyb_i2c_recv_args[] = { { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_addr, MP_ARG_INT, {.u_int = PYB_I2C_MASTER_ADDRESS} }, @@ -379,6 +422,17 @@ STATIC mp_obj_t pyb_i2c_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_recv_obj, 1, pyb_i2c_recv); +/// \method mem_read(data, addr, memaddr, timeout=5000) +/// +/// Read from the memory of an I2C device: +/// +/// - `data` can be an integer or a buffer to read into +/// - `addr` is the I2C device address +/// - `memaddr` is the memory location within the I2C device +/// - `timeout` is the timeout in milliseconds to wait for the read +/// +/// Returns the read data. +/// This is only valid in master mode. STATIC const mp_arg_t pyb_i2c_mem_read_args[] = { { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, @@ -422,6 +476,17 @@ STATIC mp_obj_t pyb_i2c_mem_read(uint n_args, const mp_obj_t *args, mp_map_t *kw } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_mem_read_obj, 1, pyb_i2c_mem_read); +/// \method mem_write(data, addr, memaddr, timeout=5000) +/// +/// Write to the memory of an I2C device: +/// +/// - `data` can be an integer or a buffer to write from +/// - `addr` is the I2C device address +/// - `memaddr` is the memory location within the I2C device +/// - `timeout` is the timeout in milliseconds to wait for the write +/// +/// Returns `None`. +/// This is only valid in master mode. STATIC mp_obj_t pyb_i2c_mem_write(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { pyb_i2c_obj_t *self = args[0]; @@ -465,6 +530,8 @@ STATIC const mp_map_elem_t pyb_i2c_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_mem_write), (mp_obj_t)&pyb_i2c_mem_write_obj }, // class constants + /// \constant MASTER - for initialising the bus to master mode + /// \constant SLAVE - for initialising the bus to slave mode { MP_OBJ_NEW_QSTR(MP_QSTR_MASTER), MP_OBJ_NEW_SMALL_INT(PYB_I2C_MASTER) }, { MP_OBJ_NEW_QSTR(MP_QSTR_SLAVE), MP_OBJ_NEW_SMALL_INT(PYB_I2C_SLAVE) }, }; diff --git a/stmhal/led.c b/stmhal/led.c index 5a0dd7b9ac..28c918e0e8 100644 --- a/stmhal/led.c +++ b/stmhal/led.c @@ -12,6 +12,11 @@ #include "pin.h" #include "genhdr/pins.h" +/// \moduleref pyb +/// \class LED - LED object +/// +/// The LED object controls an individual LED (Light Emitting Diode). + typedef struct _pyb_led_obj_t { mp_obj_base_t base; machine_uint_t led_id; @@ -195,6 +200,10 @@ void led_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp print(env, "", self->led_id); } +/// \classmethod \constructor(id) +/// Create an LED object associated with the given LED: +/// +/// - `id` is the LED number, 1-4. STATIC mp_obj_t led_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 1, 1, false); @@ -211,24 +220,34 @@ STATIC mp_obj_t led_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const return (mp_obj_t)&pyb_led_obj[led_id]; } +/// \method on() +/// Turn the LED on. mp_obj_t led_obj_on(mp_obj_t self_in) { pyb_led_obj_t *self = self_in; led_state(self->led_id, 1); return mp_const_none; } +/// \method off() +/// Turn the LED off. mp_obj_t led_obj_off(mp_obj_t self_in) { pyb_led_obj_t *self = self_in; led_state(self->led_id, 0); return mp_const_none; } +/// \method toggle() +/// Toggle the LED between on and off. mp_obj_t led_obj_toggle(mp_obj_t self_in) { pyb_led_obj_t *self = self_in; led_toggle(self->led_id); return mp_const_none; } +/// \method intensity([value]) +/// Get or set the LED intensity. Intensity ranges between 0 (off) and 255 (full on). +/// If no argument is given, return the LED intensity. +/// If an argument is given, set the LED intensity and return `None`. mp_obj_t led_obj_intensity(uint n_args, const mp_obj_t *args) { pyb_led_obj_t *self = args[0]; if (n_args == 1) { diff --git a/stmhal/modpyb.c b/stmhal/modpyb.c index a4e54fb87c..2f53bb5234 100644 --- a/stmhal/modpyb.c +++ b/stmhal/modpyb.c @@ -32,7 +32,12 @@ #include "modpyb.h" #include "ff.h" -// print lots of info about the board +/// \module pyb - functions related to the pyboard +/// +/// The `pyb` module contains specific functions related to the pyboard. + +/// \function info([dump_alloc_table]) +/// Print out lots of information about the board. STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) { // get and print unique id; 96 bits { @@ -99,14 +104,16 @@ STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_info_obj, 0, 1, pyb_info); -// get unique MCU id; 96 bits = 12 bytes +/// \function unique_id() +/// Returns a string of 12 bytes (96 bits), which is the unique ID for the MCU. STATIC mp_obj_t pyb_unique_id(void) { byte *id = (byte*)0x1fff7a10; return mp_obj_new_bytes(id, 12); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_unique_id_obj, pyb_unique_id); -// get clock frequencies +/// \function freq() +/// Return a tuple of clock frequencies: (SYSCLK, HCLK, PCLK1, PCLK2). // TODO should also be able to set frequency via this function STATIC mp_obj_t pyb_freq(void) { mp_obj_t tuple[4] = { @@ -119,24 +126,31 @@ STATIC mp_obj_t pyb_freq(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_freq_obj, pyb_freq); -// sync all file systems +/// \function sync() +/// Sync all file systems. STATIC mp_obj_t pyb_sync(void) { storage_flush(); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_sync_obj, pyb_sync); +/// \function millis() +/// Returns the number of milliseconds since the board was last reset. STATIC mp_obj_t pyb_millis(void) { return mp_obj_new_int(HAL_GetTick()); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_millis_obj, pyb_millis); +/// \function delay(ms) +/// Delay for the given number of milliseconds. STATIC mp_obj_t pyb_delay(mp_obj_t count) { HAL_Delay(mp_obj_get_int(count)); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_delay_obj, pyb_delay); +/// \function udelay(us) +/// Delay for the given number of microseconds. STATIC mp_obj_t pyb_udelay(mp_obj_t usec) { uint32_t count = 0; const uint32_t utime = (168 * mp_obj_get_int(usec) / 5); @@ -146,28 +160,32 @@ STATIC mp_obj_t pyb_udelay(mp_obj_t usec) { } } } - STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_udelay_obj, pyb_udelay); +/// \function wfi() +/// Wait for an interrupt. +/// This executies a `wfi` instruction which reduces power consumption +/// of the MCU until an interrupt occurs, at which point execution continues. STATIC mp_obj_t pyb_wfi(void) { __WFI(); return mp_const_none; } - MP_DEFINE_CONST_FUN_OBJ_0(pyb_wfi_obj, pyb_wfi); +/// \function disable_irq() +/// Disable interrupt requests. STATIC mp_obj_t pyb_disable_irq(void) { __disable_irq(); return mp_const_none; } - MP_DEFINE_CONST_FUN_OBJ_0(pyb_disable_irq_obj, pyb_disable_irq); +/// \function enable_irq() +/// Enable interrupt requests. STATIC mp_obj_t pyb_enable_irq(void) { __enable_irq(); return mp_const_none; } - MP_DEFINE_CONST_FUN_OBJ_0(pyb_enable_irq_obj, pyb_enable_irq); #if 0 @@ -224,12 +242,16 @@ STATIC mp_obj_t pyb_standby(void) { MP_DEFINE_CONST_FUN_OBJ_0(pyb_standby_obj, pyb_standby); +/// \function have_cdc() +/// Return True if USB is connected as a serial device, False otherwise. STATIC mp_obj_t pyb_have_cdc(void ) { return MP_BOOL(usb_vcp_is_connected()); } - STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_have_cdc_obj, pyb_have_cdc); +/// \function hid((buttons, x, y, z)) +/// Takes a 4-tuple (or list) and sends it to the USB host (the PC) to +/// signal a HID mouse-motion event. STATIC mp_obj_t pyb_hid_send_report(mp_obj_t arg) { mp_obj_t *items; mp_obj_get_array_fixed_n(arg, 4, &items); @@ -241,7 +263,6 @@ STATIC mp_obj_t pyb_hid_send_report(mp_obj_t arg) { usb_hid_send_report(data); return mp_const_none; } - STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_hid_send_report_obj, pyb_hid_send_report); MP_DECLARE_CONST_FUN_OBJ(pyb_source_dir_obj); // defined in main.c diff --git a/stmhal/pin.c b/stmhal/pin.c index f6c79d5d28..06923a492d 100644 --- a/stmhal/pin.c +++ b/stmhal/pin.c @@ -12,54 +12,61 @@ #include "runtime.h" #include "pin.h" -// Usage Model: -// -// All Board Pins are predefined as pyb.Pin.board.Name -// -// x1_pin = pyb.Pin.board.X1 -// -// g = pyb.Pin(pyb.Pin.board.X1, pyb.Pin.IN) -// -// CPU pins which correspond to the board pins are available -// as pyb.cpu.Name. For the CPU pins, the names are the port letter -// followed by the pin number. On the PYBV4, pyb.Pin.board.X1 and -// pyb.Pin.cpu.B6 are the same pin. -// -// You can also use strings: -// -// g = pyb.Pin('X1', pyb.Pin.OUT_PP) -// -// Users can add their own names: -// -// pyb.Pin.dict["LeftMotorDir"] = pyb.Pin.cpu.C12 -// g = pyb.Pin("LeftMotorDir", pyb.Pin.OUT_OD) -// -// and can query mappings -// -// pin = pyb.Pin("LeftMotorDir") -// -// Users can also add their own mapping function: -// -// def MyMapper(pin_name): -// if pin_name == "LeftMotorDir": -// return pyb.Pin.cpu.A0 -// -// pyb.Pin.mapper(MyMapper) -// -// So, if you were to call: pyb.Pin("LeftMotorDir", pyb.Pin.OUT_PP) -// then "LeftMotorDir" is passed directly to the mapper function. -// -// To summarize, the following order determines how things get mapped into -// an ordinal pin number: -// -// 1 - Directly specify a pin object -// 2 - User supplied mapping function -// 3 - User supplied mapping (object must be usable as a dictionary key) -// 4 - Supply a string which matches a board pin -// 5 - Supply a string which matches a CPU port/pin -// -// You can set pyb.Pin.debug(True) to get some debug information about -// how a particular object gets mapped to a pin. +/// \moduleref pyb +/// \class Pin - control I/O pins +/// +/// A pin is the basic object to control I/O pins. It has methods to set +/// the mode of the pin (input, output, etc) and methods to get and set the +/// digital logic level. For analog control of a pin, see the ADC class. +/// +/// Usage Model: +/// +/// All Board Pins are predefined as pyb.Pin.board.Name +/// +/// x1_pin = pyb.Pin.board.X1 +/// +/// g = pyb.Pin(pyb.Pin.board.X1, pyb.Pin.IN) +/// +/// CPU pins which correspond to the board pins are available +/// as `pyb.cpu.Name`. For the CPU pins, the names are the port letter +/// followed by the pin number. On the PYBv1.0, `pyb.Pin.board.X1` and +/// `pyb.Pin.cpu.B6` are the same pin. +/// +/// You can also use strings: +/// +/// g = pyb.Pin('X1', pyb.Pin.OUT_PP) +/// +/// Users can add their own names: +/// +/// pyb.Pin.dict["LeftMotorDir"] = pyb.Pin.cpu.C12 +/// g = pyb.Pin("LeftMotorDir", pyb.Pin.OUT_OD) +/// +/// and can query mappings +/// +/// pin = pyb.Pin("LeftMotorDir") +/// +/// Users can also add their own mapping function: +/// +/// def MyMapper(pin_name): +/// if pin_name == "LeftMotorDir": +/// return pyb.Pin.cpu.A0 +/// +/// pyb.Pin.mapper(MyMapper) +/// +/// So, if you were to call: `pyb.Pin("LeftMotorDir", pyb.Pin.OUT_PP)` +/// then `"LeftMotorDir"` is passed directly to the mapper function. +/// +/// To summarise, the following order determines how things get mapped into +/// an ordinal pin number: +/// +/// 1. Directly specify a pin object +/// 2. User supplied mapping function +/// 3. User supplied mapping (object must be usable as a dictionary key) +/// 4. Supply a string which matches a board pin +/// 5. Supply a string which matches a CPU port/pin +/// +/// You can set `pyb.Pin.debug(True)` to get some debug information about +/// how a particular object gets mapped to a pin. // Pin class variables STATIC mp_obj_t pin_class_mapper; @@ -152,6 +159,8 @@ const pin_obj_t *pin_find(mp_obj_t user_obj) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "pin '%s' not a valid pin identifier", pin_name)); } +/// \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, "", self->name); @@ -159,7 +168,9 @@ STATIC void pin_print(void (*print)(void *env, const char *fmt, ...), void *env, STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args); -// Pin constructor +/// \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); @@ -178,7 +189,8 @@ STATIC mp_obj_t pin_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const mp_ return (mp_obj_t)pin; } -// class method +/// \classmethod mapper([fun]) +/// Get or set the pin mapper function. STATIC mp_obj_t pin_mapper(uint n_args, mp_obj_t *args) { if (n_args > 1) { pin_class_mapper = args[1]; @@ -189,7 +201,8 @@ STATIC mp_obj_t pin_mapper(uint n_args, mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_mapper_fun_obj, 1, 2, pin_mapper); STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_mapper_obj, (mp_obj_t)&pin_mapper_fun_obj); -// class method +/// \classmethod dict([dict]) +/// Get or set the pin mapper dictionary. STATIC mp_obj_t pin_map_dict(uint n_args, mp_obj_t *args) { if (n_args > 1) { pin_class_map_dict = args[1]; @@ -200,7 +213,8 @@ 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); -// class method +/// \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) { if (n_args > 1) { pin_class_debug = mp_obj_is_true(args[1]); @@ -211,6 +225,22 @@ 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) +/// Initialise the pin: +/// +/// - `mode` can be one of: +/// - `Pin.IN` - configure the pin for input; +/// - `Pin.OUT_PP` - configure the pin for output, with push-pull control; +/// - `Pin.OUT_OD` - configure the pin for output, with open-drain control; +/// - `Pin.AF_PP` - configure the pin for alternate function, pull-pull; +/// - `Pin.AF_OD` - configure the pin for alternate function, open-drain; +/// - `Pin.ANALOG` - configure the pin for analog. +/// - `pull` can be one of: +/// - `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. +/// +/// Returns: `None`. STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args) { pin_obj_t *self = args[0]; @@ -242,6 +272,13 @@ STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_init_obj, 2, 3, pin_obj_init); +/// \method value([value]) +/// Get or set the digital logic level of the pin: +/// +/// - With no argument, return 0 or 1 depending on the logic level of the pin. +/// - With `value` given, set the logic level of the pin. `value` can be +/// anything that converts to a boolean. If it converts to `True`, the pin +/// is set high, otherwise it is set low. STATIC mp_obj_t pin_value(uint n_args, mp_obj_t *args) { pin_obj_t *self = args[0]; if (n_args == 1) { @@ -259,6 +296,8 @@ STATIC mp_obj_t pin_value(uint n_args, mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_value_obj, 1, 2, pin_value); +/// \method low() +/// Set the pin to a low logic level. STATIC mp_obj_t pin_low(mp_obj_t self_in) { pin_obj_t *self = self_in; self->gpio->BSRRH = self->pin_mask; @@ -266,6 +305,8 @@ STATIC mp_obj_t pin_low(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_low_obj, pin_low); +/// \method high() +/// Set the pin to a high logic level. STATIC mp_obj_t pin_high(mp_obj_t self_in) { pin_obj_t *self = self_in; self->gpio->BSRRL = self->pin_mask; @@ -273,18 +314,24 @@ STATIC mp_obj_t pin_high(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_high_obj, pin_high); +/// \method name() +/// 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)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_name_obj, pin_name); +/// \method port() +/// Get the pin port. STATIC mp_obj_t pin_port(mp_obj_t self_in) { pin_obj_t *self = self_in; return MP_OBJ_NEW_SMALL_INT((mp_small_int_t)self->port); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_port_obj, pin_port); +/// \method pin() +/// Get the pin number. STATIC mp_obj_t pin_pin(mp_obj_t self_in) { pin_obj_t *self = self_in; return MP_OBJ_NEW_SMALL_INT((mp_small_int_t)self->pin); @@ -311,6 +358,12 @@ STATIC const mp_map_elem_t pin_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_cpu), (mp_obj_t)&pin_cpu_pins_obj }, // class constants + /// \constant IN - initialise the pin to input mode + /// \constant OUT_PP - initialise the pin to output mode with a push-pull drive + /// \constant OUT_OD - initialise the pin to output mode with an open-drain drive + /// \constant PULL_NONE - don't enable any pull up or down resistors on the pin + /// \constant PULL_UP - enable the pull-up resistor on the pin + /// \constant PULL_DOWN - enable the pull-down resistor on the pin { MP_OBJ_NEW_QSTR(MP_QSTR_IN), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_INPUT) }, { MP_OBJ_NEW_QSTR(MP_QSTR_OUT_PP), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_OUTPUT_PP) }, { MP_OBJ_NEW_QSTR(MP_QSTR_OUT_OD), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_OUTPUT_OD) }, diff --git a/stmhal/rng.c b/stmhal/rng.c index 19e624f792..beda9a78b7 100644 --- a/stmhal/rng.c +++ b/stmhal/rng.c @@ -10,6 +10,8 @@ #if MICROPY_HW_ENABLE_RNG +/// \moduleref pyb + STATIC RNG_HandleTypeDef RNGHandle = {.Instance = NULL}; void rng_init0(void) { @@ -30,6 +32,8 @@ uint32_t rng_get(void) { return HAL_RNG_GetRandomNumber(&RNGHandle); } +/// \function rng() +/// Return a 30-bit hardware generated random number. STATIC mp_obj_t pyb_rng_get(void) { if (RNGHandle.State == HAL_RNG_STATE_RESET) { rng_init(); diff --git a/stmhal/servo.c b/stmhal/servo.c index 02e2c209b8..2ebe64376f 100644 --- a/stmhal/servo.c +++ b/stmhal/servo.c @@ -11,6 +11,11 @@ #include "timer.h" #include "servo.h" +/// \moduleref pyb +/// \class Servo - 3-wire hobby servo driver +/// +/// Servo controls standard hobby servos with 3-wires (ground, power, signal). + // this servo driver uses hardware PWM to drive servos on PA0, PA1, PA2, PA3 = X1, X2, X3, X4 // TIM2 and TIM5 have CH1, CH2, CH3, CH4 on PA0-PA3 respectively // they are both 32-bit counters with 16-bit prescaler @@ -156,6 +161,8 @@ STATIC void pyb_servo_print(void (*print)(void *env, const char *fmt, ...), void print(env, "", self->servo_id, 10 * self->pulse_cur); } +/// \classmethod \constructor(id) +/// Create a servo object. `id` is 1-4. STATIC mp_obj_t pyb_servo_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 1, 1, false); @@ -177,6 +184,8 @@ STATIC mp_obj_t pyb_servo_make_new(mp_obj_t type_in, uint n_args, uint n_kw, con return s; } +/// \method pulse_width([value]) +/// Get or set the pulse width in milliseconds. STATIC mp_obj_t pyb_servo_pulse_width(uint n_args, const mp_obj_t *args) { pyb_servo_obj_t *self = args[0]; if (n_args == 1) { @@ -190,9 +199,10 @@ STATIC mp_obj_t pyb_servo_pulse_width(uint n_args, const mp_obj_t *args) { return mp_const_none; } } - STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_servo_pulse_width_obj, 1, 2, pyb_servo_pulse_width); +/// \method calibration([pulse_min, pulse_max, pulse_centre, [pulse_angle_90, pulse_speed_100]]) +/// Get or set the calibration of the servo timing. STATIC mp_obj_t pyb_servo_calibration(uint n_args, const mp_obj_t *args) { pyb_servo_obj_t *self = args[0]; if (n_args == 1) { @@ -221,9 +231,13 @@ STATIC mp_obj_t pyb_servo_calibration(uint n_args, const mp_obj_t *args) { // bad number of arguments nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "calibration expecting 1, 4 or 6 arguments, got %d", n_args)); } - STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_servo_calibration_obj, 1, 6, pyb_servo_calibration); +/// \method angle([angle, time=0]) +/// Get or set the angle of the servo. +/// +/// - `angle` is the angle to move to in degrees. +/// - `time` is the number of milliseconds to take to get to the specified angle. STATIC mp_obj_t pyb_servo_angle(uint n_args, const mp_obj_t *args) { pyb_servo_obj_t *self = args[0]; if (n_args == 1) { @@ -247,9 +261,13 @@ STATIC mp_obj_t pyb_servo_angle(uint n_args, const mp_obj_t *args) { return mp_const_none; } } - STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_servo_angle_obj, 1, 3, pyb_servo_angle); +/// \method speed([speed, time=0]) +/// Get or set the speed of a continuous rotation servo. +/// +/// - `speed` is the speed to move to change to, between -100 and 100. +/// - `time` is the number of milliseconds to take to get to the specified speed. STATIC mp_obj_t pyb_servo_speed(uint n_args, const mp_obj_t *args) { pyb_servo_obj_t *self = args[0]; if (n_args == 1) { diff --git a/stmhal/spi.c b/stmhal/spi.c index bad71e87b1..ba550f9799 100644 --- a/stmhal/spi.c +++ b/stmhal/spi.c @@ -14,24 +14,28 @@ #include "bufhelper.h" #include "spi.h" -// Usage model: -// -// See usage model of I2C in i2c.c. SPI is very similar. Main difference is -// parameters to init the SPI bus: -// -// from pyb import SPI -// spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=1, crc=0x7) -// -// Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be -// 0 or 1, and is the level the idle clock line sits at. Phase can be 1 or 2 -// for number of edges. Crc can be None for no CRC, or a polynomial specifier. -// -// Additional method for SPI: -// -// data = spi.send_recv(b'1234') # send 4 bytes and receive 4 bytes -// buf = bytearray(4) -// spi.send_recv(b'1234', buf) # send 4 bytes and receive 4 into buf -// spi.send_recv(buf, buf) # send/recv 4 bytes from/to buf +/// \moduleref pyb +/// \class SPI - a master-driven serial protocol +/// +/// SPI is a serial protocol that is driven by a master. At the physical level +/// there are 3 lines: SCK, MOSI, MISO. +/// +/// See usage model of I2C; SPI is very similar. Main difference is +/// parameters to init the SPI bus: +/// +/// from pyb import SPI +/// spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=1, crc=0x7) +/// +/// Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be +/// 0 or 1, and is the level the idle clock line sits at. Phase can be 1 or 2 +/// for number of edges. Crc can be None for no CRC, or a polynomial specifier. +/// +/// Additional method for SPI: +/// +/// data = spi.send_recv(b'1234') # send 4 bytes and receive 4 bytes +/// buf = bytearray(4) +/// spi.send_recv(b'1234', buf) # send 4 bytes and receive 4 into buf +/// spi.send_recv(buf, buf) # send/recv 4 bytes from/to buf #if MICROPY_HW_ENABLE_SPI1 SPI_HandleTypeDef SPIHandle1 = {.Instance = NULL}; @@ -194,6 +198,12 @@ STATIC void pyb_spi_print(void (*print)(void *env, const char *fmt, ...), void * } } +/// \method init(mode, baudrate=328125, *, polarity=1, phase=1, bits=8, firstbit=SPI.MSB, ti=false, crc=None) +/// +/// Initialise the SPI bus with the given parameters: +/// +/// - `mode` must be either `SPI.MASTER` or `SPI.SLAVE`. +/// - `baudrate` is the SCK clock rate (only sensible for a master). STATIC const mp_arg_t pyb_spi_init_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 328125} }, @@ -258,6 +268,13 @@ STATIC mp_obj_t pyb_spi_init_helper(const pyb_spi_obj_t *self, uint n_args, cons return mp_const_none; } +/// \classmethod \constructor(bus, ...) +/// +/// Construct an SPI object on the given bus. `bus` can be 1 or 2. +/// With no additional parameters, the SPI object is created but not +/// initialised (it has the settings from the last initialisation of +/// the bus, if any). If extra arguments are given, the bus is initialised. +/// See `init` for parameters of initialisation. STATIC mp_obj_t pyb_spi_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); @@ -288,6 +305,8 @@ STATIC mp_obj_t pyb_spi_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_init_obj, 1, pyb_spi_init); +/// \method deinit() +/// Turn off the SPI bus. STATIC mp_obj_t pyb_spi_deinit(mp_obj_t self_in) { pyb_spi_obj_t *self = self_in; spi_deinit(self->spi); @@ -295,6 +314,13 @@ STATIC mp_obj_t pyb_spi_deinit(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_spi_deinit_obj, pyb_spi_deinit); +/// \method send(send, *, timeout=5000) +/// Send data on the bus: +/// +/// - `send` is the data to send (an integer to send, or a buffer object). +/// - `timeout` is the timeout in milliseconds to wait for the send. +/// +/// Return value: `None`. STATIC const mp_arg_t pyb_spi_send_args[] = { { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, @@ -327,6 +353,16 @@ STATIC mp_obj_t pyb_spi_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_obj, 1, pyb_spi_send); +/// \method recv(recv, *, timeout=5000) +/// +/// Receive data on the bus: +/// +/// - `recv` can be an integer, which is the number of bytes to receive, +/// or a mutable buffer, which will be filled with received bytes. +/// - `timeout` is the timeout in milliseconds to wait for the receive. +/// +/// Return value: if `recv` is an integer then a new buffer of the bytes received, +/// otherwise the same buffer that was passed in to `recv`. STATIC const mp_arg_t pyb_spi_recv_args[] = { { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, @@ -363,6 +399,17 @@ STATIC mp_obj_t pyb_spi_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_recv_obj, 1, pyb_spi_recv); +/// \method send_recv(send, recv=None, *, timeout=5000) +/// +/// Send and receive data on the bus at the same time: +/// +/// - `send` is the data to send (an integer to send, or a buffer object). +/// - `recv` is a mutable buffer which will be filled with received bytes. +/// It can be the same as `send`, or omitted. If omitted, a new buffer will +/// be created. +/// - `timeout` is the timeout in milliseconds to wait for the receive. +/// +/// Return value: the buffer with the received bytes. STATIC const mp_arg_t pyb_spi_send_recv_args[] = { { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_recv, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, @@ -436,6 +483,10 @@ STATIC const mp_map_elem_t pyb_spi_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_send_recv), (mp_obj_t)&pyb_spi_send_recv_obj }, // class constants + /// \constant MASTER - for initialising the bus to master mode + /// \constant SLAVE - for initialising the bus to slave mode + /// \constant MSB - set the first bit to MSB + /// \constant LSB - set the first bit to LSB { MP_OBJ_NEW_QSTR(MP_QSTR_MASTER), MP_OBJ_NEW_SMALL_INT(SPI_MODE_MASTER) }, { MP_OBJ_NEW_QSTR(MP_QSTR_SLAVE), MP_OBJ_NEW_SMALL_INT(SPI_MODE_SLAVE) }, { MP_OBJ_NEW_QSTR(MP_QSTR_MSB), MP_OBJ_NEW_SMALL_INT(SPI_FIRSTBIT_MSB) }, diff --git a/stmhal/uart.c b/stmhal/uart.c index a31de44f8e..6dc60ca084 100644 --- a/stmhal/uart.c +++ b/stmhal/uart.c @@ -12,20 +12,25 @@ #include "bufhelper.h" #include "uart.h" -// Usage model: -// -// See usage model of I2C in i2c.c. UART is very similar. Main difference is -// parameters to init the UART bus: -// -// from pyb import UART -// uart = UART(1, 9600) # init with given baudrate -// uart.init(9600, bits=8, stop=1, parity=None) # init with given parameters -// -// Bits can be 8 or 9, stop can be 1 or 2, parity can be None, 0 (even), 1 (odd). -// -// Extra method: -// -// uart.any() # returns True if any characters waiting +/// \moduleref pyb +/// \class UART - duplex serial communication bus +/// +/// UART implements the standard UART/USART duplex serial communications protocol. At +/// the physical level it consists of 2 lines: RX and TX. +/// +/// See usage model of I2C. UART is very similar. Main difference is +/// parameters to init the UART bus: +/// +/// from pyb import UART +/// +/// uart = UART(1, 9600) # init with given baudrate +/// uart.init(9600, bits=8, stop=1, parity=None) # init with given parameters +/// +/// Bits can be 8 or 9, stop can be 1 or 2, parity can be None, 0 (even), 1 (odd). +/// +/// Extra method: +/// +/// uart.any() # returns True if any characters waiting struct _pyb_uart_obj_t { mp_obj_base_t base; @@ -225,6 +230,14 @@ STATIC void pyb_uart_print(void (*print)(void *env, const char *fmt, ...), void } } +/// \method init(baudrate, *, bits=8, stop=1, parity=None) +/// +/// Initialise the SPI bus with the given parameters: +/// +/// - `baudrate` is the clock rate. +/// - `bits` is the number of bits per byte, 8 or 9. +/// - `stop` is the number of stop bits, 1 or 2. +/// - `parity` is the parity, `None`, 0 (even) or 1 (odd). STATIC const mp_arg_t pyb_uart_init_args[] = { { MP_QSTR_baudrate, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 9600} }, { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, @@ -265,6 +278,13 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, uint n_args, const mp return mp_const_none; } +/// \classmethod \constructor(bus, ...) +/// +/// Construct a UART object on the given bus. `bus` can be 1-6, or 'XA', 'XB', 'YA', or 'YB'. +/// With no additional parameters, the UART object is created but not +/// initialised (it has the settings from the last initialisation of +/// the bus, if any). If extra arguments are given, the bus is initialised. +/// See `init` for parameters of initialisation. STATIC mp_obj_t pyb_uart_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); @@ -310,6 +330,8 @@ STATIC mp_obj_t pyb_uart_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_init_obj, 1, pyb_uart_init); +/// \method deinit() +/// Turn off the UART bus. STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) { pyb_uart_obj_t *self = self_in; uart_deinit(self); @@ -317,6 +339,8 @@ STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_deinit_obj, pyb_uart_deinit); +/// \method any() +/// Return `True` if any characters waiting, else `False`. STATIC mp_obj_t pyb_uart_any(mp_obj_t self_in) { pyb_uart_obj_t *self = self_in; if (uart_rx_any(self)) { @@ -327,6 +351,13 @@ STATIC mp_obj_t pyb_uart_any(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_any_obj, pyb_uart_any); +/// \method send(send, *, timeout=5000) +/// Send data on the bus: +/// +/// - `send` is the data to send (an integer to send, or a buffer object). +/// - `timeout` is the timeout in milliseconds to wait for the send. +/// +/// Return value: `None`. STATIC const mp_arg_t pyb_uart_send_args[] = { { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, @@ -359,6 +390,16 @@ STATIC mp_obj_t pyb_uart_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_send_obj, 1, pyb_uart_send); +/// \method recv(recv, *, timeout=5000) +/// +/// Receive data on the bus: +/// +/// - `recv` can be an integer, which is the number of bytes to receive, +/// or a mutable buffer, which will be filled with received bytes. +/// - `timeout` is the timeout in milliseconds to wait for the receive. +/// +/// Return value: if `recv` is an integer then a new buffer of the bytes received, +/// otherwise the same buffer that was passed in to `recv`. STATIC const mp_arg_t pyb_uart_recv_args[] = { { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, diff --git a/stmhal/usrsw.c b/stmhal/usrsw.c index 7a077e1dbb..1e8d35487a 100644 --- a/stmhal/usrsw.c +++ b/stmhal/usrsw.c @@ -14,17 +14,22 @@ #if MICROPY_HW_HAS_SWITCH -// Usage Model: -// -// sw = pyb.Switch() # create a switch object -// sw() # get state (True if pressed, False otherwise) -// sw.callback(f) # register a callback to be called when the -// # switch is pressed down -// sw.callback(None) # remove the callback -// -// Example: -// -// pyb.Switch().callback(lambda: pyb.LED(1).toggle()) +/// \moduleref pyb +/// \class Switch - switch object +/// +/// A Switch object is used to control a push-button switch. +/// +/// Usage: +/// +/// sw = pyb.Switch() # create a switch object +/// sw() # get state (True if pressed, False otherwise) +/// sw.callback(f) # register a callback to be called when the +/// # switch is pressed down +/// sw.callback(None) # remove the callback +/// +/// Example: +/// +/// pyb.Switch().callback(lambda: pyb.LED(1).toggle()) // this function inits the switch GPIO so that it can be used void switch_init0(void) { @@ -55,6 +60,8 @@ void pyb_switch_print(void (*print)(void *env, const char *fmt, ...), void *env, print(env, "Switch()"); } +/// \classmethod \constructor() +/// Create and return a switch object. STATIC mp_obj_t pyb_switch_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -70,6 +77,8 @@ STATIC mp_obj_t pyb_switch_make_new(mp_obj_t type_in, uint n_args, uint n_kw, co return (mp_obj_t)&pyb_switch_obj; } +/// \method \call() +/// Return the switch state: `True` if pressed down, `False` otherwise. mp_obj_t pyb_switch_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) { // get switch state mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -84,6 +93,9 @@ STATIC mp_obj_t switch_callback(mp_obj_t line) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(switch_callback_obj, switch_callback); +/// \method callback(fun) +/// Register the given function to be called when the switch is pressed down. +/// If `fun` is `None`, then it disables the callback. mp_obj_t pyb_switch_callback(mp_obj_t self_in, mp_obj_t callback) { pyb_switch_obj_t *self = self_in; self->callback = callback;